xref: /aosp_15_r20/system/extras/torq/tests/torq_unit_test.py (revision 288bf5226967eb3dac5cce6c939ccc2a7f2b4fe5)
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