• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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