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