1# 2# Copyright (C) 2024 The Android Open Source Project 3# 4# Licensed under the Apache License, Version 2.0 (the "License"); 5# you may not use this file except in compliance with the License. 6# You may obtain a copy of the License at 7# 8# http://www.apache.org/licenses/LICENSE-2.0 9# 10# Unless required by applicable law or agreed to in writing, software 11# distributed under the License is distributed on an "AS IS" BASIS, 12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13# See the License for the specific language governing permissions and 14# limitations under the License. 15# 16 17import unittest 18import sys 19import os 20from unittest import mock 21from src.torq import create_parser, verify_args, get_command_type,\ 22 DEFAULT_DUR_MS, DEFAULT_OUT_DIR 23 24TEST_USER_ID = 10 25TEST_PACKAGE = "com.android.contacts" 26TEST_FILE = "file.pbtxt" 27SYMBOLS_PATH = "/folder/symbols" 28 29 30class TorqUnitTest(unittest.TestCase): 31 32 def set_up_parser(self, command_string): 33 sys.argv = command_string.split() 34 return create_parser() 35 36 # TODO(b/285191111): Parameterize the test functions. 37 def test_create_parser_default_values(self): 38 parser = self.set_up_parser("torq.py") 39 40 args = parser.parse_args() 41 args, error = verify_args(args) 42 43 self.assertEqual(error, None) 44 self.assertEqual(args.event, "custom") 45 self.assertEqual(args.profiler, "perfetto") 46 self.assertEqual(args.out_dir, DEFAULT_OUT_DIR) 47 self.assertEqual(args.runs, 1) 48 self.assertEqual(args.perfetto_config, "default") 49 self.assertEqual(args.dur_ms, DEFAULT_DUR_MS) 50 self.assertEqual(args.between_dur_ms, DEFAULT_DUR_MS) 51 52 def test_create_parser_valid_event_names(self): 53 parser = self.set_up_parser("torq.py -e custom") 54 55 args = parser.parse_args() 56 args, error = verify_args(args) 57 58 self.assertEqual(error, None) 59 self.assertEqual(args.event, "custom") 60 61 parser = self.set_up_parser("torq.py -e boot") 62 63 args = parser.parse_args() 64 args, error = verify_args(args) 65 66 self.assertEqual(error, None) 67 self.assertEqual(args.event, "boot") 68 69 parser = self.set_up_parser( 70 "torq.py -e user-switch --to-user %s" % str(TEST_USER_ID)) 71 72 args = parser.parse_args() 73 args, error = verify_args(args) 74 75 self.assertEqual(error, None) 76 self.assertEqual(args.event, "user-switch") 77 78 parser = self.set_up_parser( 79 "torq.py -e app-startup --app %s" % TEST_PACKAGE) 80 81 args = parser.parse_args() 82 args, error = verify_args(args) 83 84 self.assertEqual(error, None) 85 self.assertEqual(args.event, "app-startup") 86 87 def test_create_parser_invalid_event_names(self): 88 parser = self.set_up_parser("torq.py -e fake-event") 89 90 with self.assertRaises(SystemExit): 91 parser.parse_args() 92 93 @mock.patch.object(os.path, "exists", autospec=True) 94 @mock.patch.object(os.path, "isdir", autospec=True) 95 def test_create_parser_valid_profiler_names(self, mock_isdir, mock_exists): 96 mock_isdir.return_value = True 97 mock_exists.return_value = True 98 parser = self.set_up_parser("torq.py -p perfetto") 99 100 args = parser.parse_args() 101 args, error = verify_args(args) 102 103 self.assertEqual(error, None) 104 self.assertEqual(args.profiler, "perfetto") 105 106 parser = self.set_up_parser("torq.py -p simpleperf --symbols %s" 107 % SYMBOLS_PATH) 108 109 args = parser.parse_args() 110 args, error = verify_args(args) 111 112 self.assertEqual(error, None) 113 self.assertEqual(args.profiler, "simpleperf") 114 115 def test_create_parser_invalid_profiler_names(self): 116 parser = self.set_up_parser("torq.py -p fake-profiler") 117 118 with self.assertRaises(SystemExit): 119 parser.parse_args() 120 121 @mock.patch.object(os.path, "isdir", autospec=True) 122 def test_verify_args_valid_out_dir_path(self, mock_is_dir): 123 mock_is_dir.return_value = True 124 parser = self.set_up_parser("torq.py -o mock-directory") 125 126 args = parser.parse_args() 127 args, error = verify_args(args) 128 129 self.assertEqual(error, None) 130 self.assertEqual(args.out_dir, "mock-directory") 131 132 @mock.patch.object(os.path, "isdir", autospec=True) 133 def test_verify_args_invalid_out_dir_paths(self, mock_is_dir): 134 mock_is_dir.return_value = False 135 parser = self.set_up_parser("torq.py -o mock-file") 136 137 args = parser.parse_args() 138 args, error = verify_args(args) 139 140 self.assertEqual(error.message, ("Command is invalid because" 141 " --out-dir is not a valid" 142 " directory path: mock-file.")) 143 self.assertEqual(error.suggestion, None) 144 145 def test_create_parser_valid_ui(self): 146 parser = self.set_up_parser("torq.py --ui") 147 148 args = parser.parse_args() 149 args, error = verify_args(args) 150 151 self.assertEqual(error, None) 152 self.assertEqual(args.ui, True) 153 154 parser = self.set_up_parser("torq.py --no-ui") 155 156 args = parser.parse_args() 157 args, error = verify_args(args) 158 159 self.assertEqual(error, None) 160 self.assertEqual(args.ui, False) 161 162 def test_verify_args_valid_dur_ms_values(self): 163 parser = self.set_up_parser("torq.py -d 100000") 164 165 args = parser.parse_args() 166 args, error = verify_args(args) 167 168 self.assertEqual(error, None) 169 self.assertEqual(args.dur_ms, 100000) 170 171 def test_verify_args_ui_and_runs_valid_dependency(self): 172 parser = self.set_up_parser("torq.py -r 2 --no-ui") 173 174 args = parser.parse_args() 175 args, error = verify_args(args) 176 177 self.assertEqual(error, None) 178 179 def test_verify_args_ui_and_runs_invalid_dependency(self): 180 parser = self.set_up_parser("torq.py -r 2 --ui") 181 182 args = parser.parse_args() 183 args, error = verify_args(args) 184 185 self.assertEqual(error.message, ("Command is invalid because --ui cannot be" 186 " passed if --runs is set to a value" 187 " greater than 1.")) 188 self.assertEqual(error.suggestion, ("Set torq -r 2 --no-ui to perform 2" 189 " runs.")) 190 191 def test_verify_args_ui_bool_true_and_runs_default_dependencies(self): 192 parser = self.set_up_parser("torq.py") 193 194 args = parser.parse_args() 195 args, error = verify_args(args) 196 197 self.assertEqual(error, None) 198 self.assertEqual(args.ui, True) 199 200 parser = self.set_up_parser("torq.py -r 1") 201 202 args = parser.parse_args() 203 args, error = verify_args(args) 204 205 self.assertEqual(error, None) 206 self.assertEqual(args.ui, True) 207 208 # UI is false by default when multiple runs are specified. 209 def test_verify_args_ui_bool_false_and_runs_default_dependency(self): 210 parser = self.set_up_parser("torq.py -r 2") 211 212 args = parser.parse_args() 213 args, error = verify_args(args) 214 215 self.assertEqual(error, None) 216 self.assertEqual(args.ui, False) 217 218 def test_verify_args_invalid_dur_ms_values(self): 219 parser = self.set_up_parser("torq.py -d -200") 220 221 args = parser.parse_args() 222 args, error = verify_args(args) 223 224 self.assertEqual(error.message, ("Command is invalid because" 225 " --dur-ms cannot be set to a value" 226 " smaller than 3000.")) 227 self.assertEqual(error.suggestion, ("Set --dur-ms 3000 to capture a" 228 " trace for 3 seconds.")) 229 230 parser = self.set_up_parser("torq.py -d 0") 231 232 args = parser.parse_args() 233 args, error = verify_args(args) 234 235 self.assertEqual(error.message, ("Command is invalid because" 236 " --dur-ms cannot be set to a value" 237 " smaller than 3000.")) 238 self.assertEqual(error.suggestion, ("Set --dur-ms 3000 to capture a" 239 " trace for 3 seconds.")) 240 241 parser = self.set_up_parser("torq.py -d 20") 242 243 args = parser.parse_args() 244 args, error = verify_args(args) 245 246 self.assertEqual(error.message, ("Command is invalid because" 247 " --dur-ms cannot be set to a value" 248 " smaller than 3000.")) 249 self.assertEqual(error.suggestion, ("Set --dur-ms 3000 to capture a" 250 " trace for 3 seconds.")) 251 252 def test_verify_args_valid_between_dur_ms_values(self): 253 parser = self.set_up_parser("torq.py -r 2 --between-dur-ms 10000") 254 255 args = parser.parse_args() 256 args, error = verify_args(args) 257 258 self.assertEqual(error, None) 259 self.assertEqual(args.between_dur_ms, 10000) 260 261 def test_verify_args_invalid_between_dur_ms_values(self): 262 parser = self.set_up_parser("torq.py -r 2 --between-dur-ms -200") 263 264 args = parser.parse_args() 265 args, error = verify_args(args) 266 267 self.assertEqual(error.message, ("Command is invalid because" 268 " --between-dur-ms cannot be set to" 269 " a smaller value than 3000.")) 270 self.assertEqual(error.suggestion, ("Set --between-dur-ms 3000 to wait" 271 " 3 seconds between each run.")) 272 273 parser = self.set_up_parser("torq.py -r 2 --between-dur-ms 0") 274 275 args = parser.parse_args() 276 args, error = verify_args(args) 277 278 self.assertEqual(error.message, ("Command is invalid because" 279 " --between-dur-ms cannot be set to a" 280 " smaller value than 3000.")) 281 self.assertEqual(error.suggestion, ("Set --between-dur-ms 3000 to wait" 282 " 3 seconds between each run.")) 283 284 parser = self.set_up_parser("torq.py -r 2 --between-dur-ms 20") 285 286 args = parser.parse_args() 287 args, error = verify_args(args) 288 289 self.assertEqual(error.message, ("Command is invalid because" 290 " --between-dur-ms cannot be set to a" 291 " smaller value than 3000.")) 292 self.assertEqual(error.suggestion, ("Set --between-dur-ms 3000 to wait" 293 " 3 seconds between each run.")) 294 295 def test_verify_args_valid_runs_values(self): 296 parser = self.set_up_parser("torq.py -r 4") 297 298 args = parser.parse_args() 299 args, error = verify_args(args) 300 301 self.assertEqual(error, None) 302 self.assertEqual(args.runs, 4) 303 304 def test_verify_args_invalid_runs_values(self): 305 parser = self.set_up_parser("torq.py -r -2") 306 307 args = parser.parse_args() 308 args, error = verify_args(args) 309 310 self.assertEqual(error.message, ("Command is invalid because --runs" 311 " cannot be set to a value smaller" 312 " than 1.")) 313 self.assertEqual(error.suggestion, None) 314 315 parser = self.set_up_parser("torq.py -r 0") 316 317 args = parser.parse_args() 318 args, error = verify_args(args) 319 320 self.assertEqual(error.message, ("Command is invalid because --runs" 321 " cannot be set to a value smaller" 322 " than 1.")) 323 self.assertEqual(error.suggestion, None) 324 325 @mock.patch.object(os.path, "isfile", autospec=True) 326 def test_verify_args_valid_perfetto_config_path(self, mock_is_file): 327 mock_is_file.return_value = True 328 parser = self.set_up_parser("torq.py --perfetto-config mock-file") 329 330 args = parser.parse_args() 331 args, error = verify_args(args) 332 333 self.assertEqual(error, None) 334 self.assertEqual(args.perfetto_config, "mock-file") 335 336 parser = self.set_up_parser("torq.py --perfetto-config default") 337 338 args = parser.parse_args() 339 args, error = verify_args(args) 340 341 self.assertEqual(error, None) 342 self.assertEqual(args.perfetto_config, "default") 343 344 parser = self.set_up_parser("torq.py --perfetto-config lightweight") 345 346 args = parser.parse_args() 347 args, error = verify_args(args) 348 349 self.assertEqual(error, None) 350 self.assertEqual(args.perfetto_config, "lightweight") 351 352 parser = self.set_up_parser("torq.py --perfetto-config memory") 353 354 args = parser.parse_args() 355 args, error = verify_args(args) 356 357 self.assertEqual(error, None) 358 self.assertEqual(args.perfetto_config, "memory") 359 360 @mock.patch.object(os.path, "isfile", autospec=True) 361 def test_verify_args_invalid_perfetto_config_path(self, mock_is_file): 362 mock_is_file.return_value = False 363 parser = self.set_up_parser("torq.py --perfetto-config unexisting-file") 364 365 args = parser.parse_args() 366 args, error = verify_args(args) 367 368 self.assertEqual(error.message, ("Command is invalid because" 369 " --perfetto-config is not a" 370 " valid file path: unexisting-file")) 371 self.assertEqual(error.suggestion, ("Predefined perfetto configs can be" 372 " used:\n" 373 "\t torq --perfetto-config default\n" 374 "\t torq --perfetto-config" 375 " lightweight\n" 376 "\t torq --perfetto-config memory\n" 377 "\t A filepath with a config can also" 378 " be used:\n" 379 "\t torq --perfetto-config" 380 " <config-filepath>")) 381 382 parser = self.set_up_parser("torq.py --perfetto-config mock-directory") 383 384 args = parser.parse_args() 385 args, error = verify_args(args) 386 387 self.assertEqual(error.message, ("Command is invalid because" 388 " --perfetto-config is not a" 389 " valid file path: mock-directory")) 390 self.assertEqual(error.suggestion, ("Predefined perfetto configs can be" 391 " used:\n" 392 "\t torq --perfetto-config default\n" 393 "\t torq --perfetto-config" 394 " lightweight\n" 395 "\t torq --perfetto-config memory\n" 396 "\t A filepath with a config can also" 397 " be used:\n" 398 "\t torq --perfetto-config" 399 " <config-filepath>")) 400 401 def test_verify_args_from_user_and_event_valid_dependency(self): 402 parser = self.set_up_parser(("torq.py -e user-switch --from-user 0" 403 " --to-user %s") % str(TEST_USER_ID)) 404 405 args = parser.parse_args() 406 args, error = verify_args(args) 407 408 self.assertEqual(error, None) 409 410 def test_verify_args_from_user_and_event_invalid_dependency(self): 411 parser = self.set_up_parser("torq.py --from-user %s" % str(TEST_USER_ID)) 412 413 args = parser.parse_args() 414 args, error = verify_args(args) 415 416 self.assertEqual(error.message, ("Command is invalid because --from-user" 417 " is passed, but --event is not set to" 418 " user-switch.")) 419 self.assertEqual(error.suggestion, ("Set --event user-switch --from-user %s" 420 " to perform a user-switch from user" 421 " %s." % (str(TEST_USER_ID), 422 str(TEST_USER_ID)))) 423 424 def test_verify_args_to_user_and_event_valid_dependency(self): 425 parser = self.set_up_parser( 426 "torq.py -e user-switch --to-user %s" % str(TEST_USER_ID)) 427 428 args = parser.parse_args() 429 args, error = verify_args(args) 430 431 self.assertEqual(error, None) 432 433 def test_verify_args_to_user_not_passed_and_event_invalid_dependency(self): 434 parser = self.set_up_parser("torq.py -e user-switch") 435 436 args = parser.parse_args() 437 args, error = verify_args(args) 438 439 self.assertEqual(error.message, ("Command is invalid because --to-user is" 440 " not passed.")) 441 self.assertEqual(error.suggestion, ("Set --event user-switch --to-user" 442 " <user-id> to perform a user-switch.")) 443 444 def test_verify_args_to_user_and_user_switch_not_set_invalid_dependency(self): 445 parser = self.set_up_parser("torq.py --to-user %s" % str(TEST_USER_ID)) 446 447 args = parser.parse_args() 448 args, error = verify_args(args) 449 450 self.assertEqual(error.message, ("Command is invalid because --to-user" 451 " is passed, but --event is not set to" 452 " user-switch.")) 453 self.assertEqual(error.suggestion, ("Set --event user-switch --to-user %s" 454 " to perform a user-switch to user" 455 " %s." % (str(TEST_USER_ID), 456 str(TEST_USER_ID)))) 457 458 def test_verify_args_app_and_event_valid_dependency(self): 459 parser = self.set_up_parser("torq.py -e app-startup -a %s" % TEST_PACKAGE) 460 461 args = parser.parse_args() 462 args, error = verify_args(args) 463 464 self.assertEqual(error, None) 465 466 def test_verify_args_app_not_passed_and_event_invalid_dependency(self): 467 parser = self.set_up_parser("torq.py -e app-startup") 468 469 args = parser.parse_args() 470 args, error = verify_args(args) 471 472 self.assertEqual(error.message, 473 "Command is invalid because --app is not passed.") 474 self.assertEqual(error.suggestion, ("Set --event app-startup --app " 475 "<package> to perform an app-startup.")) 476 477 def test_verify_args_app_and_app_startup_not_set_invalid_dependency(self): 478 parser = self.set_up_parser("torq.py -a %s" % TEST_PACKAGE) 479 480 args = parser.parse_args() 481 args, error = verify_args(args) 482 483 self.assertEqual(error.message, ("Command is invalid because --app is" 484 " passed and --event is not set to" 485 " app-startup.")) 486 self.assertEqual(error.suggestion, ("To profile an app startup run:" 487 " torq --event app-startup --app" 488 " <package-name>")) 489 490 @mock.patch.object(os.path, "exists", autospec=True) 491 @mock.patch.object(os.path, "isdir", autospec=True) 492 def test_verify_args_profiler_and_simpleperf_event_valid_dependencies(self, 493 mock_isdir, mock_exists): 494 mock_isdir.return_value = True 495 mock_exists.return_value = True 496 parser = self.set_up_parser("torq.py -p simpleperf --symbols %s" 497 % SYMBOLS_PATH) 498 499 args = parser.parse_args() 500 args, error = verify_args(args) 501 502 self.assertEqual(error, None) 503 self.assertEqual(len(args.simpleperf_event), 1) 504 self.assertEqual(args.simpleperf_event[0], "cpu-cycles") 505 506 parser = self.set_up_parser("torq.py -p simpleperf -s cpu-cycles " 507 "--symbols %s" % SYMBOLS_PATH) 508 509 args = parser.parse_args() 510 args, error = verify_args(args) 511 512 self.assertEqual(error, None) 513 self.assertEqual(len(args.simpleperf_event), 1) 514 self.assertEqual(args.simpleperf_event[0], "cpu-cycles") 515 516 def test_verify_args_profiler_and_simpleperf_event_invalid_dependencies( 517 self): 518 parser = self.set_up_parser("torq.py -s cpu-cycles") 519 520 args = parser.parse_args() 521 args, error = verify_args(args) 522 523 self.assertEqual(error.message, ("Command is invalid because" 524 " --simpleperf-event cannot be passed if" 525 " --profiler is not set to simpleperf.")) 526 self.assertEqual(error.suggestion, ("To capture the simpleperf event run:" 527 " torq --profiler simpleperf" 528 " --simpleperf-event cpu-cycles")) 529 530 def test_profiler_and_perfetto_config_valid_dependency(self): 531 parser = self.set_up_parser(("torq.py -p perfetto --perfetto-config" 532 " lightweight")) 533 534 args = parser.parse_args() 535 args, error = verify_args(args) 536 537 self.assertEqual(error, None) 538 539 def test_verify_args_profiler_and_perfetto_config_invalid_dependency(self): 540 parser = self.set_up_parser("torq.py -p simpleperf --perfetto-config" 541 " lightweight") 542 543 args = parser.parse_args() 544 args, error = verify_args(args) 545 546 self.assertEqual(error.message, ("Command is invalid because" 547 " --perfetto-config cannot be passed if" 548 " --profiler is not set to perfetto.")) 549 self.assertEqual(error.suggestion, ("Set --profiler perfetto to choose a" 550 " perfetto-config to use.")) 551 552 def test_verify_args_runs_and_between_dur_ms_valid_dependency(self): 553 parser = self.set_up_parser("torq.py -r 2 --between-dur-ms 5000") 554 555 args = parser.parse_args() 556 args, error = verify_args(args) 557 558 self.assertEqual(error, None) 559 560 def test_verify_args_runs_and_between_dur_ms_invalid_dependency(self): 561 parser = self.set_up_parser("torq.py --between-dur-ms 5000") 562 563 args = parser.parse_args() 564 args, error = verify_args(args) 565 566 self.assertEqual(error.message, ("Command is invalid because" 567 " --between-dur-ms cannot be passed" 568 " if --runs is not a value greater" 569 " than 1.")) 570 self.assertEqual(error.suggestion, "Set --runs 2 to run 2 tests.") 571 572 parser = self.set_up_parser("torq.py -r 1 --between-dur-ms 5000") 573 574 args = parser.parse_args() 575 args, error = verify_args(args) 576 577 self.assertEqual(error.message, ("Command is invalid because" 578 " --between-dur-ms cannot be passed" 579 " if --runs is not a value greater" 580 " than 1.")) 581 self.assertEqual(error.suggestion, "Set --runs 2 to run 2 tests.") 582 583 def test_verify_args_profiler_and_ftrace_events_valid_dependencies(self): 584 parser = self.set_up_parser(("torq.py --excluded-ftrace-events" 585 " syscall-enter")) 586 587 args = parser.parse_args() 588 args, error = verify_args(args) 589 590 self.assertEqual(error, None) 591 self.assertEqual(args.excluded_ftrace_events, ["syscall-enter"]) 592 593 parser = self.set_up_parser(("torq.py -p perfetto --excluded-ftrace-events" 594 " syscall-enter")) 595 596 args = parser.parse_args() 597 args, error = verify_args(args) 598 599 self.assertEqual(args.excluded_ftrace_events, ["syscall-enter"]) 600 self.assertEqual(error, None) 601 602 parser = self.set_up_parser(("torq.py -p perfetto --included-ftrace-events" 603 " syscall-enter")) 604 605 args = parser.parse_args() 606 args, error = verify_args(args) 607 608 self.assertEqual(args.included_ftrace_events, ["syscall-enter"]) 609 self.assertEqual(error, None) 610 611 def test_verify_args_profiler_and_ftrace_events_invalid_dependencies(self): 612 parser = self.set_up_parser(("torq.py -p simpleperf" 613 " --excluded-ftrace-events syscall-enter")) 614 615 args = parser.parse_args() 616 args, error = verify_args(args) 617 618 self.assertEqual(error.message, ("Command is invalid because" 619 " --excluded-ftrace-events cannot be" 620 " passed if --profiler is not set to" 621 " perfetto.")) 622 self.assertEqual(error.suggestion, ("Set --profiler perfetto to exclude an" 623 " ftrace event from perfetto config.")) 624 625 parser = self.set_up_parser(("torq.py -p simpleperf" 626 " --included-ftrace-events syscall-enter")) 627 628 args = parser.parse_args() 629 args, error = verify_args(args) 630 631 self.assertEqual(error.message, ("Command is invalid because" 632 " --included-ftrace-events cannot be" 633 " passed if --profiler is not set to" 634 " perfetto.")) 635 self.assertEqual(error.suggestion, ("Set --profiler perfetto to include" 636 " an ftrace event in perfetto config.")) 637 638 def test_verify_args_multiple_valid_excluded_ftrace_events(self): 639 parser = self.set_up_parser(("torq.py --excluded-ftrace-events" 640 " power/cpu_idle --excluded-ftrace-events" 641 " ion/ion_stat")) 642 643 args = parser.parse_args() 644 args, error = verify_args(args) 645 646 self.assertEqual(error, None) 647 self.assertEqual(args.excluded_ftrace_events, ["power/cpu_idle", 648 "ion/ion_stat"]) 649 650 def test_verify_args_multiple_invalid_excluded_ftrace_events(self): 651 parser = self.set_up_parser(("torq.py --excluded-ftrace-events" 652 " power/cpu_idle --excluded-ftrace-events" 653 " power/cpu_idle")) 654 655 args = parser.parse_args() 656 args, error = verify_args(args) 657 658 self.assertEqual(error.message, ("Command is invalid because duplicate" 659 " ftrace events cannot be" 660 " included in --excluded-ftrace-events.")) 661 self.assertEqual(error.suggestion, ("--excluded-ftrace-events should only" 662 " include one instance of an ftrace" 663 " event.")) 664 665 def test_verify_args_multiple_valid_included_ftrace_events(self): 666 parser = self.set_up_parser(("torq.py --included-ftrace-events" 667 " power/cpu_idle --included-ftrace-events" 668 " ion/ion_stat")) 669 670 args = parser.parse_args() 671 args, error = verify_args(args) 672 673 self.assertEqual(error, None) 674 self.assertEqual(args.included_ftrace_events, ["power/cpu_idle", 675 "ion/ion_stat"]) 676 677 def test_verify_args_multiple_invalid_included_ftrace_events(self): 678 parser = self.set_up_parser(("torq.py --included-ftrace-events" 679 " power/cpu_idle --included-ftrace-events" 680 " power/cpu_idle")) 681 682 args = parser.parse_args() 683 args, error = verify_args(args) 684 685 self.assertEqual(error.message, ("Command is invalid because duplicate" 686 " ftrace events cannot be" 687 " included in --included-ftrace-events.")) 688 self.assertEqual(error.suggestion, ("--included-ftrace-events should only" 689 " include one instance of an ftrace" 690 " event.")) 691 692 def test_verify_args_invalid_overlap_ftrace_events(self): 693 parser = self.set_up_parser(("torq.py --excluded-ftrace-events" 694 " ion/ion_stat --excluded-ftrace-events" 695 " power/cpu_idle --excluded-ftrace-events" 696 " power/gpu_frequency --included-ftrace-events" 697 " ion/ion_stat --included-ftrace-events" 698 " power/cpu_idle --included-ftrace-events" 699 " ftrace/print")) 700 701 args = parser.parse_args() 702 args, error = verify_args(args) 703 704 self.assertEqual(error.message, ("Command is invalid because ftrace" 705 " event(s): ion/ion_stat, power/cpu_idle" 706 " cannot be both included and excluded.")) 707 self.assertEqual(error.suggestion, ("Only set --excluded-ftrace-events" 708 " ion/ion_stat if you want to" 709 " exclude ion/ion_stat from the" 710 " config or --included-ftrace-events" 711 " ion/ion_stat if you want to" 712 " include ion/ion_stat in the" 713 " config.\n\t" 714 " Only set --excluded-ftrace-events" 715 " power/cpu_idle if you want to" 716 " exclude power/cpu_idle from the" 717 " config or --included-ftrace-events" 718 " power/cpu_idle if you want to" 719 " include power/cpu_idle in the" 720 " config.")) 721 722 @mock.patch.object(os.path, "exists", autospec=True) 723 @mock.patch.object(os.path, "isdir", autospec=True) 724 def test_verify_args_multiple_valid_simpleperf_events(self, mock_isdir, 725 mock_exists): 726 mock_isdir.return_value = True 727 mock_exists.return_value = True 728 parser = self.set_up_parser(("torq.py -p simpleperf -s cpu-cycles" 729 " -s instructions --symbols %s" 730 % SYMBOLS_PATH)) 731 732 args = parser.parse_args() 733 args, error = verify_args(args) 734 735 self.assertEqual(error, None) 736 self.assertEqual(args.simpleperf_event, ["cpu-cycles", "instructions"]) 737 738 def test_verify_args_multiple_invalid_simpleperf_events(self): 739 parser = self.set_up_parser(("torq.py -p simpleperf -s cpu-cycles" 740 " -s cpu-cycles")) 741 742 args = parser.parse_args() 743 args, error = verify_args(args) 744 745 self.assertEqual(error.message, ("Command is invalid because redundant" 746 " calls to --simpleperf-event cannot" 747 " be made.")) 748 self.assertEqual(error.suggestion, ("Only set --simpleperf-event cpu-cycles" 749 " once if you want to collect" 750 " cpu-cycles.")) 751 752 def test_create_parser_invalid_perfetto_config_command(self): 753 parser = self.set_up_parser("torq.py --perfetto-config") 754 755 with self.assertRaises(SystemExit): 756 parser.parse_args() 757 758 def test_verify_args_invalid_mixing_of_profiler_and_config_subcommand(self): 759 parser = self.set_up_parser("torq.py -d 20000 config pull lightweight") 760 761 with self.assertRaises(SystemExit): 762 parser.parse_args() 763 764 def test_get_command_type_profiler(self): 765 parser = self.set_up_parser("torq.py -d 20000") 766 767 args = parser.parse_args() 768 args, error = verify_args(args) 769 command = get_command_type(args) 770 771 self.assertEqual(error, None) 772 self.assertEqual(command.get_type(), "profiler") 773 774 def test_create_parser_valid_config_show_values(self): 775 parser = self.set_up_parser("torq.py config show default") 776 777 args = parser.parse_args() 778 args, error = verify_args(args) 779 780 self.assertEqual(error, None) 781 self.assertEqual(args.config_name, "default") 782 783 parser = self.set_up_parser("torq.py config show lightweight") 784 785 args = parser.parse_args() 786 args, error = verify_args(args) 787 788 self.assertEqual(error, None) 789 self.assertEqual(args.config_name, "lightweight") 790 791 parser = self.set_up_parser("torq.py config show memory") 792 793 args = parser.parse_args() 794 args, error = verify_args(args) 795 796 self.assertEqual(error, None) 797 self.assertEqual(args.config_name, "memory") 798 799 def test_create_parser_invalid_config_show_values(self): 800 parser = self.set_up_parser("torq.py config show fake-config") 801 802 with self.assertRaises(SystemExit): 803 parser.parse_args() 804 805 def test_create_parser_valid_config_pull_values(self): 806 parser = self.set_up_parser("torq.py config pull default") 807 808 args = parser.parse_args() 809 args, error = verify_args(args) 810 811 self.assertEqual(error, None) 812 self.assertEqual(args.config_name, "default") 813 814 parser = self.set_up_parser("torq.py config pull lightweight") 815 816 args = parser.parse_args() 817 args, error = verify_args(args) 818 819 self.assertEqual(error, None) 820 self.assertEqual(args.config_name, "lightweight") 821 822 parser = self.set_up_parser("torq.py config pull memory") 823 824 args = parser.parse_args() 825 args, error = verify_args(args) 826 827 self.assertEqual(error, None) 828 self.assertEqual(args.config_name, "memory") 829 830 def test_create_parser_invalid_config_pull_values(self): 831 parser = self.set_up_parser("torq.py config pull fake-config") 832 833 with self.assertRaises(SystemExit): 834 parser.parse_args() 835 836 def test_verify_args_invalid_config_subcommands(self): 837 parser = self.set_up_parser("torq.py config") 838 839 args = parser.parse_args() 840 args, error = verify_args(args) 841 842 self.assertEqual(error.message, ("Command is invalid because torq config" 843 " cannot be called without a" 844 " subcommand.")) 845 self.assertEqual(error.suggestion, ("Use one of the following" 846 " subcommands:\n" 847 "\t torq config list\n" 848 "\t torq config show\n" 849 "\t torq config pull\n")) 850 851 def test_create_parser_invalid_config_subcommands(self): 852 parser = self.set_up_parser("torq.py config get") 853 854 with self.assertRaises(SystemExit): 855 parser.parse_args() 856 857 def test_verify_args_default_config_pull_filepath(self): 858 parser = self.set_up_parser("torq.py config pull default") 859 860 args = parser.parse_args() 861 args, error = verify_args(args) 862 863 self.assertEqual(error, None) 864 self.assertEqual(args.file_path, "./default.pbtxt") 865 866 parser = self.set_up_parser("torq.py config pull lightweight") 867 868 args = parser.parse_args() 869 args, error = verify_args(args) 870 871 self.assertEqual(error, None) 872 self.assertEqual(args.file_path, "./lightweight.pbtxt") 873 874 parser = self.set_up_parser("torq.py config pull memory") 875 876 args = parser.parse_args() 877 args, error = verify_args(args) 878 879 self.assertEqual(error, None) 880 self.assertEqual(args.file_path, "./memory.pbtxt") 881 882 @mock.patch.object(os.path, "isfile", autospec=True) 883 def test_verify_args_default_config_pull_invalid_filepath(self, mock_is_file): 884 mock_invalid_file_path = "mock-invalid-file-path" 885 mock_is_file.return_value = False 886 parser = self.set_up_parser(("torq.py config pull default %s" 887 % mock_invalid_file_path)) 888 889 args = parser.parse_args() 890 args, error = verify_args(args) 891 892 self.assertEqual(error.message, ( 893 "Command is invalid because %s is not a valid filepath." 894 % mock_invalid_file_path)) 895 self.assertEqual(error.suggestion, ( 896 "A default filepath can be used if you do not specify a file-path:\n\t" 897 " torq pull default to copy to ./default.pbtxt\n\t" 898 " torq pull lightweight to copy to ./lightweight.pbtxt\n\t " 899 "torq pull memory to copy to ./memory.pbtxt")) 900 901 def test_get_command_type_config_list(self): 902 parser = self.set_up_parser("torq.py config list") 903 904 args = parser.parse_args() 905 args, error = verify_args(args) 906 command = get_command_type(args) 907 908 self.assertEqual(error, None) 909 self.assertEqual(command.get_type(), "config list") 910 911 def test_get_command_type_config_show(self): 912 parser = self.set_up_parser("torq.py config show default") 913 914 args = parser.parse_args() 915 args, error = verify_args(args) 916 command = get_command_type(args) 917 918 self.assertEqual(error, None) 919 self.assertEqual(command.get_type(), "config show") 920 921 def test_get_command_type_config_pull(self): 922 parser = self.set_up_parser("torq.py config pull default") 923 924 args = parser.parse_args() 925 args, error = verify_args(args) 926 command = get_command_type(args) 927 928 self.assertEqual(error, None) 929 self.assertEqual(command.get_type(), "config pull") 930 931 @mock.patch.object(os.path, "exists", autospec=True) 932 def test_create_parser_valid_open_subcommand(self, mock_exists): 933 mock_exists.return_value = True 934 parser = self.set_up_parser("torq.py open %s" % TEST_FILE) 935 936 args = parser.parse_args() 937 args, error = verify_args(args) 938 939 self.assertEqual(error, None) 940 self.assertEqual(args.file_path, TEST_FILE) 941 942 def test_create_parser_open_subcommand_no_file(self): 943 parser = self.set_up_parser("torq.py open") 944 945 with self.assertRaises(SystemExit): 946 parser.parse_args() 947 948 @mock.patch.object(os.path, "exists", autospec=True) 949 def test_create_parser_open_subcommand_invalid_file(self, mock_exists): 950 mock_exists.return_value = False 951 parser = self.set_up_parser("torq.py open %s" % TEST_FILE) 952 953 args = parser.parse_args() 954 args, error = verify_args(args) 955 956 self.assertEqual(error.message, "Command is invalid because %s is an " 957 "invalid file path." % TEST_FILE) 958 self.assertEqual(error.suggestion, "Make sure your file exists.") 959 960 961if __name__ == '__main__': 962 unittest.main() 963