• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python3
2#
3# Copyright 2018, The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9#     http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17"""Unittests for atest_tf_test_runner."""
18
19# pylint: disable=line-too-long
20# pylint: disable=missing-function-docstring
21# pylint: disable=too-many-lines
22# pylint: disable=unused-argument
23
24import os
25import shlex
26import sys
27import tempfile
28import unittest
29import json
30
31from argparse import Namespace
32from io import StringIO
33from pathlib import Path
34from unittest import mock
35
36from atest import atest_configs
37from atest import atest_utils
38from atest import constants
39from atest import unittest_constants as uc
40from atest import unittest_utils
41
42from atest.test_finders import test_finder_utils
43from atest.test_finders import test_info
44from atest.test_runners import event_handler
45from atest.test_runners import atest_tf_test_runner as atf_tr
46
47#pylint: disable=protected-access
48#pylint: disable=invalid-name
49METRICS_DIR = '%s/baseline-metrics' % uc.TEST_INFO_DIR
50METRICS_DIR_ARG = '--metrics-folder %s ' % METRICS_DIR
51# TODO(147567606): Replace {serial} with {extra_args} for general extra
52# arguments testing.
53RUN_CMD_ARGS = ('{metrics}--log-level-display VERBOSE --log-level VERBOSE '
54                '--skip-all-system-status-check=true'
55                '{device_early_release}{serial}')
56LOG_ARGS = atf_tr.AtestTradefedTestRunner._LOG_ARGS.format(
57    log_root_option_name=constants.LOG_ROOT_OPTION_NAME,
58    log_ext_option=constants.LOG_SAVER_EXT_OPTION,
59    log_path=os.path.join(uc.TEST_INFO_DIR, atf_tr.LOG_FOLDER_NAME),
60    proto_path=os.path.join(uc.TEST_INFO_DIR, constants.ATEST_TEST_RECORD_PROTO))
61RUN_ENV_STR = ''
62RUN_CMD = atf_tr.AtestTradefedTestRunner._RUN_CMD.format(
63    env=RUN_ENV_STR,
64    exe=atf_tr.AtestTradefedTestRunner.EXECUTABLE,
65    template=atf_tr.AtestTradefedTestRunner._TF_TEMPLATE,
66    log_saver=constants.ATEST_TF_LOG_SAVER,
67    tf_customize_template='{tf_customize_template}',
68    args=RUN_CMD_ARGS,
69    log_args=LOG_ARGS)
70FULL_CLASS2_NAME = 'android.jank.cts.ui.SomeOtherClass'
71CLASS2_FILTER = test_info.TestFilter(FULL_CLASS2_NAME, frozenset())
72METHOD2_FILTER = test_info.TestFilter(uc.FULL_CLASS_NAME, frozenset([uc.METHOD2_NAME]))
73MODULE_ARG1 = [(constants.TF_INCLUDE_FILTER_OPTION, "A"),
74               (constants.TF_INCLUDE_FILTER_OPTION, "B")]
75MODULE_ARG2 = []
76CLASS2_METHOD_FILTER = test_info.TestFilter(FULL_CLASS2_NAME,
77                                            frozenset([uc.METHOD_NAME, uc.METHOD2_NAME]))
78MODULE2_INFO = test_info.TestInfo(uc.MODULE2_NAME,
79                                  atf_tr.AtestTradefedTestRunner.NAME,
80                                  set(),
81                                  data={constants.TI_REL_CONFIG: uc.CONFIG2_FILE,
82                                        constants.TI_FILTER: frozenset()})
83CLASS1_BUILD_TARGETS = {'class_1_build_target'}
84CLASS1_INFO = test_info.TestInfo(uc.MODULE_NAME,
85                                 atf_tr.AtestTradefedTestRunner.NAME,
86                                 CLASS1_BUILD_TARGETS,
87                                 data={constants.TI_REL_CONFIG: uc.CONFIG_FILE,
88                                       constants.TI_FILTER: frozenset([uc.CLASS_FILTER])})
89CLASS2_BUILD_TARGETS = {'class_2_build_target'}
90CLASS2_INFO = test_info.TestInfo(uc.MODULE_NAME,
91                                 atf_tr.AtestTradefedTestRunner.NAME,
92                                 CLASS2_BUILD_TARGETS,
93                                 data={constants.TI_REL_CONFIG: uc.CONFIG_FILE,
94                                       constants.TI_FILTER: frozenset([CLASS2_FILTER])})
95CLASS3_BUILD_TARGETS = {'class_3_build_target'}
96CLASS3_INFO = test_info.TestInfo(uc.MODULE_NAME,
97                                 atf_tr.AtestTradefedTestRunner.NAME,
98                                 CLASS3_BUILD_TARGETS,
99                                 data={constants.TI_REL_CONFIG: uc.CONFIG_FILE,
100                                       constants.TI_FILTER: frozenset(),
101                                       constants.TI_MODULE_ARG: MODULE_ARG1})
102CLASS4_BUILD_TARGETS = {'class_4_build_target'}
103CLASS4_INFO = test_info.TestInfo(uc.MODULE_NAME,
104                                 atf_tr.AtestTradefedTestRunner.NAME,
105                                 CLASS4_BUILD_TARGETS,
106                                 data={constants.TI_REL_CONFIG: uc.CONFIG_FILE,
107                                       constants.TI_FILTER: frozenset(),
108                                       constants.TI_MODULE_ARG: MODULE_ARG2})
109CLASS1_CLASS2_MODULE_INFO = test_info.TestInfo(
110    uc.MODULE_NAME,
111    atf_tr.AtestTradefedTestRunner.NAME,
112    uc.MODULE_BUILD_TARGETS | CLASS1_BUILD_TARGETS | CLASS2_BUILD_TARGETS,
113    uc.MODULE_DATA)
114FLAT_CLASS_INFO = test_info.TestInfo(
115    uc.MODULE_NAME,
116    atf_tr.AtestTradefedTestRunner.NAME,
117    CLASS1_BUILD_TARGETS | CLASS2_BUILD_TARGETS,
118    data={constants.TI_REL_CONFIG: uc.CONFIG_FILE,
119          constants.TI_FILTER: frozenset([uc.CLASS_FILTER, CLASS2_FILTER])})
120FLAT2_CLASS_INFO = test_info.TestInfo(
121    uc.MODULE_NAME,
122    atf_tr.AtestTradefedTestRunner.NAME,
123    CLASS3_BUILD_TARGETS | CLASS4_BUILD_TARGETS,
124    data={constants.TI_REL_CONFIG: uc.CONFIG_FILE,
125          constants.TI_FILTER: frozenset(),
126          constants.TI_MODULE_ARG: MODULE_ARG1 + MODULE_ARG2})
127GTF_INT_CONFIG = os.path.join(uc.GTF_INT_DIR, uc.GTF_INT_NAME + '.xml')
128CLASS2_METHOD_INFO = test_info.TestInfo(
129    uc.MODULE_NAME,
130    atf_tr.AtestTradefedTestRunner.NAME,
131    set(),
132    data={constants.TI_REL_CONFIG: uc.CONFIG_FILE,
133          constants.TI_FILTER:
134              frozenset([test_info.TestFilter(
135                  FULL_CLASS2_NAME, frozenset([uc.METHOD_NAME, uc.METHOD2_NAME]))])})
136METHOD_AND_CLASS2_METHOD = test_info.TestInfo(
137    uc.MODULE_NAME,
138    atf_tr.AtestTradefedTestRunner.NAME,
139    uc.MODULE_BUILD_TARGETS,
140    data={constants.TI_REL_CONFIG: uc.CONFIG_FILE,
141          constants.TI_FILTER: frozenset([uc.METHOD_FILTER, CLASS2_METHOD_FILTER])})
142METHOD_METHOD2_AND_CLASS2_METHOD = test_info.TestInfo(
143    uc.MODULE_NAME,
144    atf_tr.AtestTradefedTestRunner.NAME,
145    uc.MODULE_BUILD_TARGETS,
146    data={constants.TI_REL_CONFIG: uc.CONFIG_FILE,
147          constants.TI_FILTER: frozenset([uc.FLAT_METHOD_FILTER, CLASS2_METHOD_FILTER])})
148METHOD2_INFO = test_info.TestInfo(
149    uc.MODULE_NAME,
150    atf_tr.AtestTradefedTestRunner.NAME,
151    set(),
152    data={constants.TI_REL_CONFIG: uc.CONFIG_FILE,
153          constants.TI_FILTER: frozenset([METHOD2_FILTER])})
154
155INT_INFO = test_info.TestInfo(
156    uc.INT_NAME,
157    atf_tr.AtestTradefedTestRunner.NAME,
158    set(),
159    test_finder='INTEGRATION')
160
161MOD_INFO = test_info.TestInfo(
162    uc.MODULE_NAME,
163    atf_tr.AtestTradefedTestRunner.NAME,
164    set(),
165    test_finder='MODULE')
166
167MOD_INFO_NO_TEST_FINDER = test_info.TestInfo(
168    uc.MODULE_NAME,
169    atf_tr.AtestTradefedTestRunner.NAME,
170    set())
171
172EVENTS_NORMAL = [
173    ('TEST_MODULE_STARTED', {
174        'moduleContextFileName':'serial-util1146216{974}2772610436.ser',
175        'moduleName':'someTestModule'}),
176    ('TEST_RUN_STARTED', {'testCount': 2}),
177    ('TEST_STARTED', {'start_time':52, 'className':'someClassName',
178                      'testName':'someTestName'}),
179    ('TEST_ENDED', {'end_time':1048, 'className':'someClassName',
180                    'testName':'someTestName'}),
181    ('TEST_STARTED', {'start_time':48, 'className':'someClassName2',
182                      'testName':'someTestName2'}),
183    ('TEST_FAILED', {'className':'someClassName2', 'testName':'someTestName2',
184                     'trace': 'someTrace'}),
185    ('TEST_ENDED', {'end_time':9876450, 'className':'someClassName2',
186                    'testName':'someTestName2'}),
187    ('TEST_RUN_ENDED', {}),
188    ('TEST_MODULE_ENDED', {'foo': 'bar'}),
189]
190
191#pylint: disable=too-many-public-methods
192class AtestTradefedTestRunnerUnittests(unittest.TestCase):
193    """Unit tests for atest_tf_test_runner.py"""
194
195    #pylint: disable=arguments-differ
196    @mock.patch.object(atf_tr.AtestTradefedTestRunner, '_get_ld_library_path')
197    def setUp(self, mock_get_ld_library_path):
198        mock_get_ld_library_path.return_value = RUN_ENV_STR
199        self.tr = atf_tr.AtestTradefedTestRunner(results_dir=uc.TEST_INFO_DIR)
200        if not atest_configs.GLOBAL_ARGS:
201            atest_configs.GLOBAL_ARGS = Namespace()
202        atest_configs.GLOBAL_ARGS.device_count_config = None
203
204    def tearDown(self):
205        mock.patch.stopall()
206
207    @mock.patch.object(atf_tr.AtestTradefedTestRunner,
208                       '_start_socket_server')
209    @mock.patch.object(atf_tr.AtestTradefedTestRunner,
210                       'run')
211    @mock.patch.object(atf_tr.AtestTradefedTestRunner,
212                       '_create_test_args', return_value=['some_args'])
213    @mock.patch.object(atf_tr.AtestTradefedTestRunner,
214                       'generate_run_commands', return_value='some_cmd')
215    @mock.patch.object(atf_tr.AtestTradefedTestRunner,
216                       '_process_connection', return_value=None)
217    @mock.patch('select.select')
218    @mock.patch('os.killpg', return_value=None)
219    @mock.patch('os.getpgid', return_value=None)
220    @mock.patch('signal.signal', return_value=None)
221    def test_run_tests_pretty(self, _signal, _pgid, _killpg, mock_select,
222                              _process, _run_cmd, _test_args,
223                              mock_run, mock_start_socket_server):
224        """Test _run_tests_pretty method."""
225        mock_subproc = mock.Mock()
226        mock_run.return_value = mock_subproc
227        mock_subproc.returncode = 0
228        mock_subproc.poll.side_effect = [True, True, None]
229        mock_server = mock.Mock()
230        mock_server.getsockname.return_value = ('', '')
231        mock_start_socket_server.return_value = mock_server
232        mock_reporter = mock.Mock()
233
234        # Test no early TF exit
235        mock_conn = mock.Mock()
236        mock_server.accept.return_value = (mock_conn, 'some_addr')
237        mock_server.close.return_value = True
238        mock_select.side_effect = [([mock_server], None, None),
239                                   ([mock_conn], None, None)]
240        self.tr.run_tests_pretty([MODULE2_INFO], {}, mock_reporter)
241
242        # Test early TF exit
243        tmp_file = tempfile.NamedTemporaryFile()
244        with open(tmp_file.name, 'w') as f:
245            f.write("tf msg")
246        self.tr.test_log_file = tmp_file
247        mock_select.side_effect = [([], None, None)]
248        mock_subproc.poll.side_effect = None
249        capture_output = StringIO()
250        sys.stdout = capture_output
251        self.assertRaises(atf_tr.TradeFedExitError, self.tr.run_tests_pretty,
252                          [MODULE2_INFO], {}, mock_reporter)
253        sys.stdout = sys.__stdout__
254        self.assertTrue('tf msg' in capture_output.getvalue())
255
256    @mock.patch.object(atf_tr.AtestTradefedTestRunner, '_process_connection')
257    @mock.patch('select.select')
258    def test_start_monitor_2_connection(self, mock_select, mock_process):
259        """Test _start_monitor method."""
260        mock_server = mock.Mock()
261        mock_subproc = mock.Mock()
262        mock_reporter = mock.Mock()
263        mock_conn1 = mock.Mock()
264        mock_conn2 = mock.Mock()
265        mock_server.accept.side_effect = [(mock_conn1, 'addr 1'),
266                                          (mock_conn2, 'addr 2')]
267        mock_select.side_effect = [([mock_server], None, None),
268                                   ([mock_server], None, None),
269                                   ([mock_conn1], None, None),
270                                   ([mock_conn2], None, None),
271                                   ([mock_conn1], None, None),
272                                   ([mock_conn2], None, None)]
273        mock_process.side_effect = ['abc', 'def', False, False]
274        mock_subproc.poll.side_effect = [None, None, None, None,
275                                         None, True]
276        self.tr._start_monitor(mock_server, mock_subproc, mock_reporter, {})
277        self.assertEqual(mock_process.call_count, 4)
278        calls = [mock.call.accept(), mock.call.close()]
279        mock_server.assert_has_calls(calls)
280        mock_conn1.assert_has_calls([mock.call.close()])
281        mock_conn2.assert_has_calls([mock.call.close()])
282
283    @mock.patch.object(atf_tr.AtestTradefedTestRunner, '_process_connection')
284    @mock.patch('select.select')
285    def test_start_monitor_tf_exit_before_2nd_connection(self,
286                                                         mock_select,
287                                                         mock_process):
288        """Test _start_monitor method."""
289        mock_server = mock.Mock()
290        mock_subproc = mock.Mock()
291        mock_reporter = mock.Mock()
292        mock_conn1 = mock.Mock()
293        mock_conn2 = mock.Mock()
294        mock_server.accept.side_effect = [(mock_conn1, 'addr 1'),
295                                          (mock_conn2, 'addr 2')]
296        mock_select.side_effect = [([mock_server], None, None),
297                                   ([mock_server], None, None),
298                                   ([mock_conn1], None, None),
299                                   ([mock_conn2], None, None),
300                                   ([mock_conn1], None, None),
301                                   ([mock_conn2], None, None)]
302        mock_process.side_effect = ['abc', 'def', False, False]
303        # TF exit early but have not processed data in socket buffer.
304        mock_subproc.poll.side_effect = [None, None, True, True,
305                                         True, True]
306        self.tr._start_monitor(mock_server, mock_subproc, mock_reporter, {})
307        self.assertEqual(mock_process.call_count, 4)
308        calls = [mock.call.accept(), mock.call.close()]
309        mock_server.assert_has_calls(calls)
310        mock_conn1.assert_has_calls([mock.call.close()])
311        mock_conn2.assert_has_calls([mock.call.close()])
312
313
314    def test_start_socket_server(self):
315        """Test start_socket_server method."""
316        server = self.tr._start_socket_server()
317        host, port = server.getsockname()
318        self.assertEqual(host, atf_tr.SOCKET_HOST)
319        self.assertLessEqual(port, 65535)
320        self.assertGreaterEqual(port, 1024)
321        server.close()
322
323    @mock.patch('os.path.exists')
324    @mock.patch.dict('os.environ', {'APE_API_KEY':'/tmp/123.json'})
325    def test_try_set_gts_authentication_key_is_set_by_user(self, mock_exist):
326        """Test try_set_authentication_key_is_set_by_user method."""
327        # Test key is set by user.
328        self.tr._try_set_gts_authentication_key()
329        mock_exist.assert_not_called()
330
331    @mock.patch('os.path.join', return_value='/tmp/file_not_exist.json')
332    def test_try_set_gts_authentication_key_not_set(self, _):
333        """Test try_set_authentication_key_not_set method."""
334        # Delete the environment variable if it's set. This is fine for this
335        # method because it's for validating the APE_API_KEY isn't set.
336        if os.environ.get('APE_API_KEY'):
337            del os.environ['APE_API_KEY']
338        self.tr._try_set_gts_authentication_key()
339        self.assertEqual(os.environ.get('APE_API_KEY'), None)
340
341    @mock.patch.object(event_handler.EventHandler, 'process_event')
342    def test_process_connection(self, mock_pe):
343        """Test _process_connection method."""
344        mock_socket = mock.Mock()
345        for name, data in EVENTS_NORMAL:
346            data_map = {mock_socket: ''}
347            socket_data = '%s %s' % (name, json.dumps(data))
348            mock_socket.recv.return_value = socket_data
349            self.tr._process_connection(data_map, mock_socket, mock_pe)
350
351        calls = [mock.call.process_event(name, data) for name, data in EVENTS_NORMAL]
352        mock_pe.assert_has_calls(calls)
353        mock_socket.recv.return_value = ''
354        self.assertFalse(self.tr._process_connection(data_map, mock_socket, mock_pe))
355
356    @mock.patch.object(event_handler.EventHandler, 'process_event')
357    def test_process_connection_multiple_lines_in_single_recv(self, mock_pe):
358        """Test _process_connection when recv reads multiple lines in one go."""
359        mock_socket = mock.Mock()
360        squashed_events = '\n'.join(['%s %s' % (name, json.dumps(data))
361                                     for name, data in EVENTS_NORMAL])
362        socket_data = [squashed_events, '']
363        mock_socket.recv.side_effect = socket_data
364        data_map = {mock_socket: ''}
365        self.tr._process_connection(data_map, mock_socket, mock_pe)
366        calls = [mock.call.process_event(name, data) for name, data in EVENTS_NORMAL]
367        mock_pe.assert_has_calls(calls)
368
369    @mock.patch.object(event_handler.EventHandler, 'process_event')
370    def test_process_connection_with_buffering(self, mock_pe):
371        """Test _process_connection when events overflow socket buffer size."""
372        mock_socket = mock.Mock()
373        module_events = [EVENTS_NORMAL[0], EVENTS_NORMAL[-1]]
374        socket_events = ['%s %s' % (name, json.dumps(data))
375                         for name, data in module_events]
376        # test try-block code by breaking apart first event after first }
377        index = socket_events[0].index('}') + 1
378        socket_data = [socket_events[0][:index], socket_events[0][index:]]
379        # test non-try block buffering with second event
380        socket_data.extend([socket_events[1][:-4], socket_events[1][-4:], ''])
381        mock_socket.recv.side_effect = socket_data
382        data_map = {mock_socket: ''}
383        self.tr._process_connection(data_map, mock_socket, mock_pe)
384        self.tr._process_connection(data_map, mock_socket, mock_pe)
385        self.tr._process_connection(data_map, mock_socket, mock_pe)
386        self.tr._process_connection(data_map, mock_socket, mock_pe)
387        calls = [mock.call.process_event(name, data) for name, data in module_events]
388        mock_pe.assert_has_calls(calls)
389
390    @mock.patch.object(event_handler.EventHandler, 'process_event')
391    def test_process_connection_with_not_completed_event_data(self, mock_pe):
392        """Test _process_connection when event have \n prefix."""
393        mock_socket = mock.Mock()
394        mock_socket.recv.return_value = ('\n%s %s'
395                                         %(EVENTS_NORMAL[0][0],
396                                           json.dumps(EVENTS_NORMAL[0][1])))
397        data_map = {mock_socket: ''}
398        self.tr._process_connection(data_map, mock_socket, mock_pe)
399        calls = [mock.call.process_event(EVENTS_NORMAL[0][0],
400                                         EVENTS_NORMAL[0][1])]
401        mock_pe.assert_has_calls(calls)
402
403    @mock.patch.object(atf_tr.AtestTradefedTestRunner,
404                       '_is_all_tests_parameter_auto_enabled',
405                       return_value=False)
406    @mock.patch('os.environ.get', return_value=None)
407    @mock.patch.object(atf_tr.AtestTradefedTestRunner,
408                       '_generate_metrics_folder')
409    @mock.patch('atest.atest_utils.get_result_server_args')
410    def test_generate_run_commands_without_serial_env(
411        self, mock_resultargs, mock_mertrics, _, _mock_all):
412        """Test generate_run_command method."""
413        # Basic Run Cmd
414        mock_resultargs.return_value = []
415        mock_mertrics.return_value = ''
416        unittest_utils.assert_strict_equal(
417            self,
418            self.tr.generate_run_commands([], {}),
419            [RUN_CMD.format(env=RUN_ENV_STR,
420                            metrics='',
421                            serial='',
422                            tf_customize_template='',
423                            device_early_release=' --no-early-device-release')])
424        mock_mertrics.return_value = METRICS_DIR
425        unittest_utils.assert_strict_equal(
426            self,
427            self.tr.generate_run_commands([], {}),
428            [RUN_CMD.format(metrics=METRICS_DIR_ARG,
429                            serial='',
430                            tf_customize_template='',
431                            device_early_release=' --no-early-device-release')])
432        # Run cmd with result server args.
433        result_arg = '--result_arg'
434        mock_resultargs.return_value = [result_arg]
435        mock_mertrics.return_value = ''
436        unittest_utils.assert_strict_equal(
437            self,
438            self.tr.generate_run_commands(
439                [], {}),
440            [RUN_CMD.format(metrics='',
441                            serial='',
442                            tf_customize_template='',
443                            device_early_release=' --no-early-device-release') + ' ' + result_arg])
444
445    @mock.patch.object(atf_tr.AtestTradefedTestRunner,
446                       '_is_all_tests_parameter_auto_enabled',
447                       return_value=False)
448    @mock.patch('os.environ.get')
449    @mock.patch.object(atf_tr.AtestTradefedTestRunner, '_generate_metrics_folder')
450    @mock.patch('atest.atest_utils.get_result_server_args')
451    def test_generate_run_commands_with_serial_env(
452        self, mock_resultargs, mock_mertrics, mock_env, _mock_all):
453        """Test generate_run_command method."""
454        # Basic Run Cmd
455        env_device_serial = 'env-device-0'
456        mock_resultargs.return_value = []
457        mock_mertrics.return_value = ''
458        mock_env.return_value = env_device_serial
459        env_serial_arg = ' --serial %s' % env_device_serial
460        # Serial env be set and without --serial arg.
461        unittest_utils.assert_strict_equal(
462            self,
463            self.tr.generate_run_commands([], {}),
464            [RUN_CMD.format(metrics='',
465                            serial=env_serial_arg,
466                            tf_customize_template='',
467                            device_early_release=' --no-early-device-release')])
468        # Serial env be set but with --serial arg.
469        arg_device_serial = 'arg-device-0'
470        arg_serial_arg = ' --serial %s' % arg_device_serial
471        unittest_utils.assert_strict_equal(
472            self,
473            self.tr.generate_run_commands(
474                [], {constants.SERIAL: [arg_device_serial]}),
475            [RUN_CMD.format(metrics='',
476                            serial=arg_serial_arg,
477                            tf_customize_template='',
478                            device_early_release=' --no-early-device-release')])
479        # Serial env be set but with -n arg
480        unittest_utils.assert_strict_equal(
481            self,
482            self.tr.generate_run_commands([], {constants.HOST: True}),
483            [RUN_CMD.format(metrics='',
484                            serial='',
485                            tf_customize_template='',
486                            device_early_release=' --no-early-device-release') +
487             ' -n --prioritize-host-config --skip-host-arch-check'])
488
489    def test_flatten_test_filters(self):
490        """Test _flatten_test_filters method."""
491        # No Flattening
492        filters = self.tr._flatten_test_filters({uc.CLASS_FILTER})
493        unittest_utils.assert_strict_equal(self, frozenset([uc.CLASS_FILTER]),
494                                           filters)
495        filters = self.tr._flatten_test_filters({CLASS2_FILTER})
496        unittest_utils.assert_strict_equal(
497            self, frozenset([CLASS2_FILTER]), filters)
498        filters = self.tr._flatten_test_filters({uc.METHOD_FILTER})
499        unittest_utils.assert_strict_equal(
500            self, frozenset([uc.METHOD_FILTER]), filters)
501        filters = self.tr._flatten_test_filters({uc.METHOD_FILTER,
502                                                 CLASS2_METHOD_FILTER})
503        unittest_utils.assert_strict_equal(
504            self, frozenset([uc.METHOD_FILTER, CLASS2_METHOD_FILTER]), filters)
505        # Flattening
506        filters = self.tr._flatten_test_filters({uc.METHOD_FILTER,
507                                                 METHOD2_FILTER})
508        unittest_utils.assert_strict_equal(
509            self, filters, frozenset([uc.FLAT_METHOD_FILTER]))
510        filters = self.tr._flatten_test_filters({uc.METHOD_FILTER,
511                                                 METHOD2_FILTER,
512                                                 CLASS2_METHOD_FILTER,})
513        unittest_utils.assert_strict_equal(
514            self, filters, frozenset([uc.FLAT_METHOD_FILTER,
515                                      CLASS2_METHOD_FILTER]))
516
517    def test_flatten_test_infos(self):
518        """Test _flatten_test_infos method."""
519        # No Flattening
520        test_infos = self.tr._flatten_test_infos({uc.MODULE_INFO})
521        unittest_utils.assert_equal_testinfo_sets(self, test_infos,
522                                                  {uc.MODULE_INFO})
523
524        test_infos = self.tr._flatten_test_infos([uc.MODULE_INFO, MODULE2_INFO])
525        unittest_utils.assert_equal_testinfo_sets(
526            self, test_infos, {uc.MODULE_INFO, MODULE2_INFO})
527
528        test_infos = self.tr._flatten_test_infos({CLASS1_INFO})
529        unittest_utils.assert_equal_testinfo_sets(self, test_infos,
530                                                  {CLASS1_INFO})
531
532        test_infos = self.tr._flatten_test_infos({uc.INT_INFO})
533        unittest_utils.assert_equal_testinfo_sets(self, test_infos,
534                                                  {uc.INT_INFO})
535
536        test_infos = self.tr._flatten_test_infos({uc.METHOD_INFO})
537        unittest_utils.assert_equal_testinfo_sets(self, test_infos,
538                                                  {uc.METHOD_INFO})
539
540        # Flattening
541        test_infos = self.tr._flatten_test_infos({CLASS1_INFO, CLASS2_INFO})
542        unittest_utils.assert_equal_testinfo_sets(self, test_infos,
543                                                  {FLAT_CLASS_INFO})
544
545        test_infos = self.tr._flatten_test_infos({CLASS1_INFO, uc.INT_INFO,
546                                                  CLASS2_INFO})
547        unittest_utils.assert_equal_testinfo_sets(self, test_infos,
548                                                  {uc.INT_INFO,
549                                                   FLAT_CLASS_INFO})
550
551        test_infos = self.tr._flatten_test_infos({CLASS1_INFO, uc.MODULE_INFO,
552                                                  CLASS2_INFO})
553        unittest_utils.assert_equal_testinfo_sets(self, test_infos,
554                                                  {CLASS1_CLASS2_MODULE_INFO})
555
556        test_infos = self.tr._flatten_test_infos({MODULE2_INFO, uc.INT_INFO,
557                                                  CLASS1_INFO, CLASS2_INFO,
558                                                  uc.GTF_INT_INFO})
559        unittest_utils.assert_equal_testinfo_sets(self, test_infos,
560                                                  {uc.INT_INFO, uc.GTF_INT_INFO,
561                                                   FLAT_CLASS_INFO,
562                                                   MODULE2_INFO})
563
564        test_infos = self.tr._flatten_test_infos({uc.METHOD_INFO,
565                                                  CLASS2_METHOD_INFO})
566        unittest_utils.assert_equal_testinfo_sets(self, test_infos,
567                                                  {METHOD_AND_CLASS2_METHOD})
568
569        test_infos = self.tr._flatten_test_infos({uc.METHOD_INFO, METHOD2_INFO,
570                                                  CLASS2_METHOD_INFO})
571        unittest_utils.assert_equal_testinfo_sets(
572            self, test_infos, {METHOD_METHOD2_AND_CLASS2_METHOD})
573        test_infos = self.tr._flatten_test_infos({uc.METHOD_INFO, METHOD2_INFO,
574                                                  CLASS2_METHOD_INFO,
575                                                  MODULE2_INFO,
576                                                  uc.INT_INFO})
577        unittest_utils.assert_equal_testinfo_sets(
578            self, test_infos, {uc.INT_INFO, MODULE2_INFO,
579                               METHOD_METHOD2_AND_CLASS2_METHOD})
580
581        test_infos = self.tr._flatten_test_infos({CLASS3_INFO, CLASS4_INFO})
582        unittest_utils.assert_equal_testinfo_sets(self, test_infos,
583                                                  {FLAT2_CLASS_INFO})
584
585    @mock.patch.object(test_finder_utils, 'get_test_config_and_srcs')
586    def test_create_test_args(self, mock_config):
587        """Test _create_test_args method."""
588        # Only compile '--skip-loading-config-jar' in TF if it's not
589        # INTEGRATION finder or the finder property isn't set.
590        mock_config.return_value = '', ''
591        args = self.tr._create_test_args([MOD_INFO])
592        self.assertTrue(constants.TF_SKIP_LOADING_CONFIG_JAR in args)
593
594        args = self.tr._create_test_args([INT_INFO])
595        self.assertFalse(constants.TF_SKIP_LOADING_CONFIG_JAR in args)
596
597        args = self.tr._create_test_args([MOD_INFO_NO_TEST_FINDER])
598        self.assertFalse(constants.TF_SKIP_LOADING_CONFIG_JAR in args)
599
600        args = self.tr._create_test_args([MOD_INFO_NO_TEST_FINDER, INT_INFO])
601        self.assertFalse(constants.TF_SKIP_LOADING_CONFIG_JAR in args)
602
603        args = self.tr._create_test_args([MOD_INFO_NO_TEST_FINDER])
604        self.assertFalse(constants.TF_SKIP_LOADING_CONFIG_JAR in args)
605
606        args = self.tr._create_test_args([MOD_INFO_NO_TEST_FINDER, INT_INFO, MOD_INFO])
607        self.assertFalse(constants.TF_SKIP_LOADING_CONFIG_JAR in args)
608
609    @mock.patch.object(atf_tr.AtestTradefedTestRunner,
610                       '_is_all_tests_parameter_auto_enabled',
611                       return_value=False)
612    @mock.patch('os.environ.get', return_value=None)
613    @mock.patch.object(atf_tr.AtestTradefedTestRunner,
614                       '_generate_metrics_folder')
615    @mock.patch('atest.atest_utils.get_result_server_args')
616    def test_generate_run_commands_collect_tests_only(
617        self, mock_resultargs, mock_mertrics, _, _mock_is_all):
618        """Test generate_run_command method."""
619        # Testing  without collect-tests-only
620        mock_resultargs.return_value = []
621        mock_mertrics.return_value = ''
622        extra_args = {}
623        unittest_utils.assert_strict_equal(
624            self,
625            self.tr.generate_run_commands([], extra_args),
626            [RUN_CMD.format(
627                metrics='',
628                serial='',
629                tf_customize_template='',
630                device_early_release=' --no-early-device-release')])
631        # Testing  with collect-tests-only
632        mock_resultargs.return_value = []
633        mock_mertrics.return_value = ''
634        extra_args = {constants.COLLECT_TESTS_ONLY: True}
635        unittest_utils.assert_strict_equal(
636            self,
637            self.tr.generate_run_commands([], extra_args),
638            [RUN_CMD.format(
639                metrics='',
640                serial=' --collect-tests-only',
641                tf_customize_template='',
642                device_early_release=' --no-early-device-release')])
643
644
645    @mock.patch.object(atf_tr.AtestTradefedTestRunner,
646                       '_is_all_tests_parameter_auto_enabled',
647                       return_value=False)
648    @mock.patch('os.environ.get', return_value=None)
649    @mock.patch.object(atf_tr.AtestTradefedTestRunner, '_generate_metrics_folder')
650    @mock.patch('atest.atest_utils.get_result_server_args')
651    def test_generate_run_commands_with_tf_template(
652        self, mock_resultargs, mock_mertrics, _, _mock_all):
653        """Test generate_run_command method."""
654        tf_tmplate_key1 = 'tf_tmplate_key1'
655        tf_tmplate_val1 = 'tf_tmplate_val1'
656        tf_tmplate_key2 = 'tf_tmplate_key2'
657        tf_tmplate_val2 = 'tf_tmplate_val2'
658        # Testing with only one tradefed template command
659        mock_resultargs.return_value = []
660        mock_mertrics.return_value = ''
661        extra_args = {constants.TF_TEMPLATE:
662                          ['{}={}'.format(tf_tmplate_key1,
663                                          tf_tmplate_val1)]}
664        unittest_utils.assert_strict_equal(
665            self,
666            self.tr.generate_run_commands([], extra_args),
667            [RUN_CMD.format(
668                metrics='',
669                serial='',
670                device_early_release=' --no-early-device-release',
671                tf_customize_template=
672                '--template:map {}={}').format(tf_tmplate_key1,
673                                               tf_tmplate_val1)])
674        # Testing with two tradefed template commands
675        extra_args = {constants.TF_TEMPLATE:
676                          ['{}={}'.format(tf_tmplate_key1,
677                                          tf_tmplate_val1),
678                           '{}={}'.format(tf_tmplate_key2,
679                                          tf_tmplate_val2)]}
680        unittest_utils.assert_strict_equal(
681            self,
682            self.tr.generate_run_commands([], extra_args),
683            [RUN_CMD.format(
684                metrics='',
685                serial='',
686                device_early_release=' --no-early-device-release',
687                tf_customize_template=
688                '--template:map {}={} --template:map {}={}').format(
689                    tf_tmplate_key1,
690                    tf_tmplate_val1,
691                    tf_tmplate_key2,
692                    tf_tmplate_val2)])
693
694    @mock.patch.object(atf_tr.AtestTradefedTestRunner,
695                       '_is_all_tests_parameter_auto_enabled',
696                       return_value=False)
697    @mock.patch('os.environ.get', return_value=None)
698    @mock.patch.object(atf_tr.AtestTradefedTestRunner, '_generate_metrics_folder')
699    @mock.patch('atest.atest_utils.get_result_server_args')
700    def test_generate_run_commands_with_tf_early_device_release(
701            self, mock_resultargs, mock_mertrics, _, _mock_all):
702        """Test generate_run_command method."""
703        # Testing  without collect-tests-only
704        mock_resultargs.return_value = []
705        mock_mertrics.return_value = ''
706        extra_args = {constants.TF_EARLY_DEVICE_RELEASE: True}
707        unittest_utils.assert_strict_equal(
708            self,
709            self.tr.generate_run_commands([], extra_args),
710            [RUN_CMD.format(
711                metrics='',
712                serial='',
713                tf_customize_template='',
714                device_early_release='')])
715
716    @mock.patch.object(test_finder_utils, 'get_test_config_and_srcs')
717    def test_has_instant_app_config(self, mock_config):
718        """test _has_instant_app_config method."""
719        no_instant_config = os.path.join(
720            uc.TEST_DATA_DIR, "parameter_config", "parameter.cfg")
721        instant_config = os.path.join(
722            uc.TEST_DATA_DIR, "parameter_config", "instant_app_parameter.cfg")
723        # Test find instant app config
724        mock_config.return_value = instant_config, ''
725        self.assertTrue(
726            atf_tr.AtestTradefedTestRunner._has_instant_app_config(
727                ['test_info'], 'module_info_obj'))
728        # Test not find instant app config
729        mock_config.return_value = no_instant_config, ''
730        self.assertFalse(
731            atf_tr.AtestTradefedTestRunner._has_instant_app_config(
732                ['test_info'], 'module_info_obj'))
733
734    @mock.patch.object(atf_tr.AtestTradefedTestRunner,
735                       '_is_all_tests_parameter_auto_enabled',
736                       return_value=False)
737    @mock.patch.object(atf_tr.AtestTradefedTestRunner,
738                       '_has_instant_app_config', return_value=True)
739    @mock.patch('os.environ.get', return_value=None)
740    @mock.patch.object(atf_tr.AtestTradefedTestRunner,
741                       '_generate_metrics_folder')
742    @mock.patch('atest.atest_utils.get_result_server_args')
743    def test_generate_run_commands_has_instant_app_config(
744        self, mock_resultargs, mock_mertrics, _, _mock_has_config,
745        _mock_is_all):
746        """Test generate_run_command method which has instant app config."""
747        # Basic Run Cmd
748        mock_resultargs.return_value = []
749        mock_mertrics.return_value = ''
750        extra_tf_arg = (
751            '{tf_test_arg} {tf_class}:{option_name}:{option_value}'.format(
752            tf_test_arg = constants.TF_TEST_ARG,
753            tf_class=constants.TF_AND_JUNIT_CLASS,
754            option_name=constants.TF_EXCLUDE_ANNOTATE,
755            option_value=constants.INSTANT_MODE_ANNOTATE))
756        unittest_utils.assert_strict_equal(
757            self,
758            self.tr.generate_run_commands([], {}),
759            [RUN_CMD.format(env=RUN_ENV_STR,
760                            metrics='',
761                            serial='',
762                            tf_customize_template='',
763                            device_early_release=' --no-early-device-release '
764                                                 + extra_tf_arg)])
765
766    @mock.patch.object(atest_utils, 'get_config_parameter')
767    @mock.patch.object(test_finder_utils, 'get_test_config_and_srcs')
768    def test_is_parameter_auto_enabled_cfg(self, mock_config, mock_cfg_para):
769        """test _is_parameter_auto_enabled_cfg method."""
770        # Test if TF_PARA_INSTANT_APP is match
771        mock_config.return_value = 'test_config', ''
772        mock_cfg_para.return_value = {list(constants.DEFAULT_EXCLUDE_PARAS)[1],
773                                      list(constants.DEFAULT_EXCLUDE_PARAS)[0]}
774        self.assertFalse(
775            atf_tr.AtestTradefedTestRunner._is_parameter_auto_enabled_cfg(
776                ['test_info'], 'module_info_obj'))
777        # Test if DEFAULT_EXCLUDE_NOT_PARAS is match
778        mock_cfg_para.return_value = {
779            list(constants.DEFAULT_EXCLUDE_NOT_PARAS)[2],
780            list(constants.DEFAULT_EXCLUDE_NOT_PARAS)[0]}
781        self.assertFalse(
782            atf_tr.AtestTradefedTestRunner._is_parameter_auto_enabled_cfg(
783                ['test_info'], 'module_info_obj'))
784        # Test if have parameter not in default exclude paras
785        mock_cfg_para.return_value = {
786            'not match parameter',
787            list(constants.DEFAULT_EXCLUDE_PARAS)[1],
788            list(constants.DEFAULT_EXCLUDE_NOT_PARAS)[2]}
789        self.assertTrue(
790            atf_tr.AtestTradefedTestRunner._is_parameter_auto_enabled_cfg(
791                ['test_info'], 'module_info_obj'))
792
793    @mock.patch.object(atf_tr.AtestTradefedTestRunner,
794                       '_is_parameter_auto_enabled_cfg',
795                       return_value=True)
796    @mock.patch.object(test_finder_utils, 'get_test_config_and_srcs')
797    def test_create_test_args_with_auto_enable_parameter(
798        self, mock_config, _mock_is_enable):
799        """Test _create_test_args method with auto enabled parameter config."""
800        # Should have --m on args and should not have --include-filter.
801        mock_config.return_value = '', ''
802        args = self.tr._create_test_args([MOD_INFO])
803        self.assertTrue(constants.TF_MODULE_FILTER in args)
804        self.assertFalse(constants.TF_INCLUDE_FILTER in args)
805
806    @mock.patch.object(atf_tr.AtestTradefedTestRunner,
807                       '_is_parameter_auto_enabled_cfg')
808    @mock.patch.object(test_finder_utils, 'get_test_config_and_srcs')
809    def test_parse_extra_args(self, mock_config, _mock_is_enable):
810        """Test _parse_extra_args ."""
811        # If extra_arg enable instant_app or secondary users, should not have
812        # --exclude-module-parameters even though test config parameter is auto
813        # enabled.
814        mock_config.return_value = '', ''
815        _mock_is_enable.return_value = True
816        args, _ = self.tr._parse_extra_args([MOD_INFO], {constants.INSTANT: ''})
817        self.assertFalse('--exclude-module-parameters' in args)
818
819        # If extra_arg not enable instant_app or secondary users, should have
820        # --exclude-module-rameters if config parameter is auto enabled.
821        _mock_is_enable.return_value = True
822        args, _ = self.tr._parse_extra_args([MOD_INFO], {constants.ALL_ABI: ''})
823        self.assertTrue('--exclude-module-parameters' in args)
824
825        # If extra_arg not enable instant_app or secondary users, should not
826        # have --exclude-module-rameters if config parameter is not auto enabled
827        _mock_is_enable.return_value = False
828        args, _ = self.tr._parse_extra_args([MOD_INFO], {constants.ALL_ABI: ''})
829        self.assertFalse('--exclude-module-parameters' in args)
830
831    @mock.patch.object(atf_tr.AtestTradefedTestRunner,
832                       '_is_parameter_auto_enabled_cfg', return_value=False)
833    @mock.patch.object(atf_tr.AtestTradefedTestRunner,
834                       '_has_instant_app_config', return_value=False)
835    def test_parse_extra_args_has_instant_app(
836        self, _mock_has_instant, _mock_is_para):
837        """Test _parse_extra_args with instant app in customize flag."""
838        # If customize_arg has module-parameter should also include
839        # --enable-parameterized-modules.
840        args, _ = self.tr._parse_extra_args(
841            [MOD_INFO],
842            {constants.CUSTOM_ARGS: [constants.TF_MODULE_PARAMETER]})
843        self.assertTrue(constants.TF_ENABLE_PARAMETERIZED_MODULES in args)
844
845    @mock.patch('atest.atest_utils.get_prebuilt_sdk_tools_dir')
846    @mock.patch.object(atf_tr.AtestTradefedTestRunner,
847                       '_is_missing_exec', return_value=False)
848    def test_generate_env_vars_aapt_already_in_system_path(
849        self, _mock_is_missing_exec, mock_prebuilt_sdk_dir):
850        """Test generate_env_vars if aapt already in system path."""
851        prebuilt_sdk_dir = Path('/my/test/sdk/dir')
852        mock_prebuilt_sdk_dir.return_value = prebuilt_sdk_dir
853
854        env_vars = self.tr.generate_env_vars(extra_args={})
855
856        self.assertFalse(
857            str(prebuilt_sdk_dir) + ':' in env_vars.get('PATH', ''))
858
859    @mock.patch('os.path.exists', return_value=True)
860    @mock.patch('atest.atest_utils.get_prebuilt_sdk_tools_dir')
861    @mock.patch.object(atf_tr.AtestTradefedTestRunner,
862                       '_is_missing_exec', return_value=True)
863    def test_generate_env_vars_aapt_not_in_system_path(
864        self, _mock_is_missing_exec, mock_prebuilt_sdk_dir, _mock_exist):
865        """Test generate_env_vars if aapt not in system path."""
866        prebuilt_sdk_dir = Path('/my/test/sdk/dir')
867        mock_prebuilt_sdk_dir.return_value = prebuilt_sdk_dir
868
869        env_vars = self.tr.generate_env_vars(extra_args={})
870
871        self.assertTrue(str(prebuilt_sdk_dir) + ':' in env_vars.get('PATH', ''))
872
873    @mock.patch.object(atf_tr.AtestTradefedTestRunner, '_handle_native_tests')
874    @mock.patch.object(atf_tr.AtestTradefedTestRunner, '_parse_extra_args')
875    @mock.patch.object(atf_tr.AtestTradefedTestRunner, '_create_test_args')
876    @mock.patch('os.environ.get', return_value=None)
877    @mock.patch.object(
878        atf_tr.AtestTradefedTestRunner, '_generate_metrics_folder')
879    @mock.patch('atest.atest_utils.get_result_server_args')
880    def test_generate_run_commands_for_aggregate_metric_result(
881        self, mock_resultargs, mock_mertrics, _mock_env, _mock_create, _mock_parse, _mock_handle_native):
882        """Test generate_run_command method for test need aggregate metric."""
883        mock_resultargs.return_value = []
884        mock_mertrics.return_value = ''
885        _mock_create.return_value = []
886        _mock_parse.return_value = [], []
887        test_info_with_aggregate_metrics = test_info.TestInfo(
888            test_name='perf_test', test_runner='test_runner',
889            build_targets=set())
890        test_info_with_aggregate_metrics.aggregate_metrics_result = True
891
892        run_cmd = self.tr.generate_run_commands(
893            [test_info_with_aggregate_metrics], extra_args={})
894
895        self.assertTrue(
896            str(run_cmd).find(
897                'metric_post_processor='
898                'google/template/postprocessors/metric-file-aggregate') > 0)
899
900    @mock.patch.object(atf_tr.AtestTradefedTestRunner, '_handle_native_tests')
901    @mock.patch.object(atf_tr.AtestTradefedTestRunner, '_parse_extra_args')
902    @mock.patch.object(atf_tr.AtestTradefedTestRunner, '_create_test_args')
903    @mock.patch('os.environ.get', return_value=None)
904    @mock.patch.object(
905        atf_tr.AtestTradefedTestRunner, '_generate_metrics_folder')
906    @mock.patch('atest.atest_utils.get_result_server_args')
907    def test_run_commands_for_aggregate_metric_result_with_manually_input(
908        self, mock_resultargs, mock_mertrics, _mock_env, _mock_create,
909            _mock_parse, _mock_handle_native):
910        """Test generate_run_command method for test need aggregate metric."""
911        mock_resultargs.return_value = []
912        mock_mertrics.return_value = ''
913        _mock_create.return_value = []
914        _mock_parse.return_value = [], []
915        test_info_with_aggregate_metrics = test_info.TestInfo(
916            test_name='perf_test', test_runner='test_runner',
917            build_targets=set())
918        test_info_with_aggregate_metrics.aggregate_metrics_result = True
919
920        run_cmd = self.tr.generate_run_commands(
921            [test_info_with_aggregate_metrics],
922            extra_args={constants.TF_TEMPLATE: ['metric_post_processor=a/b/c']})
923
924        self.assertTrue(
925            str(run_cmd).find(
926                'metric_post_processor='
927                'google/template/postprocessors/metric-file-aggregate') < 0)
928
929        self.assertTrue(
930            str(run_cmd).find('metric_post_processor=a/b/c') > 0)
931
932    @mock.patch.object(atf_tr.AtestTradefedTestRunner,
933                       '_is_all_tests_parameter_auto_enabled',
934                       return_value=False)
935    @mock.patch.object(test_finder_utils, 'get_test_config_and_srcs')
936    def test_create_test_args_with_auto_enable_parameter_but_not_all(
937        self, mock_config, _mock_is_all):
938        """
939        Test _create_test_args method with not all configs are auto enabled
940        parameter.
941        """
942        # Should not --m on args and should have --include-filter.
943        mock_config.return_value = '', ''
944        args = self.tr._create_test_args([MOD_INFO])
945
946        self.assertFalse(constants.TF_MODULE_FILTER in args)
947        self.assertTrue(constants.TF_INCLUDE_FILTER in args)
948
949    @mock.patch.object(atf_tr.AtestTradefedTestRunner,
950                       '_is_all_tests_parameter_auto_enabled',
951                       return_value=False)
952    @mock.patch.object(atf_tr.AtestTradefedTestRunner,
953                       '_is_parameter_auto_enabled_cfg')
954    @mock.patch.object(test_finder_utils, 'get_test_config_and_srcs')
955    def test_parse_extra_args_not_all_cfg_auto_enable_parameter(
956        self, mock_config, _mock_is_enable, _mock_is_all):
957        """Test _parse_extra_args without all config is auto parameter."""
958        # If not all test config is parameter auto enabled, should not find
959        # --enable-parameterized-modules in test config.
960        mock_config.return_value = '', ''
961        _mock_is_enable.return_value = True
962
963        args, _ = self.tr._parse_extra_args([MOD_INFO], {})
964        self.assertFalse('--enable-parameterized-modules' in args)
965
966    def assertTokensIn(self, expected_tokens, s):
967        tokens = shlex.split(s)
968        for token in expected_tokens:
969            self.assertIn(token, tokens)
970
971    def assertTokensNotIn(self, unwanted_tokens, s):
972        tokens = shlex.split(s)
973        for token in unwanted_tokens:
974            self.assertNotIn(token, tokens)
975
976
977class ExtraArgsTest(AtestTradefedTestRunnerUnittests):
978    """Unit tests for parsing extra args"""
979
980    def test_args_with_wait_for_debug_and_generate_in_run_cmd(self):
981        extra_args = {constants.WAIT_FOR_DEBUGGER: None}
982
983        cmd = self.tr.generate_run_commands([], extra_args)
984
985        self.assertTokensIn(['--wait-for-debugger'], cmd[0])
986
987    def test_args_with_disable_installed_and_generate_in_run_cmd(self):
988        extra_args = {constants.DISABLE_INSTALL: None}
989
990        cmd = self.tr.generate_run_commands([], extra_args)
991
992        self.assertTokensIn(['--disable-target-preparers'], cmd[0])
993
994    def test_multidevice_in_config_and_generate_in_run_cmd(self):
995        atest_configs.GLOBAL_ARGS.device_count_config = 2
996        cmd = self.tr.generate_run_commands([], {})
997        self.assertTokensIn(
998            ['--replicate-parent-setup', '--multi-device-count', '2'], cmd[0])
999
1000        atest_configs.GLOBAL_ARGS.device_count_config = 1
1001        cmd = self.tr.generate_run_commands([], {})
1002        self.assertTokensNotIn(
1003            ['--replicate-parent-setup', '--multi-device-count'], cmd[0])
1004
1005        atest_configs.GLOBAL_ARGS.device_count_config = None
1006        cmd = self.tr.generate_run_commands([], {})
1007        self.assertTokensNotIn(
1008            ['--replicate-parent-setup', '--multi-device-count'], cmd[0])
1009
1010    def test_args_with_serial_no_and_generate_in_run_cmd(self):
1011        extra_args = {constants.SERIAL: ['device1']}
1012
1013        cmd = self.tr.generate_run_commands([], extra_args)
1014
1015        self.assertTokensIn(['--serial', 'device1'], cmd[0])
1016
1017    def test_args_with_multi_serial_no_and_generate_in_run_cmd(self):
1018        extra_args = {constants.SERIAL: ['device1', 'device2']}
1019
1020        cmd = self.tr.generate_run_commands([], extra_args)
1021
1022        self.assertIn('--serial device1 --serial device2', cmd[0])
1023
1024    def test_args_with_sharding_and_generate_in_run_cmd(self):
1025        extra_args = {constants.SHARDING: 2}
1026
1027        cmd = self.tr.generate_run_commands([], extra_args)
1028
1029        self.assertIn('--shard-count 2', cmd[0])
1030
1031    def test_args_with_disable_teardown_and_generate_in_run_cmd(self):
1032        extra_args = {constants.DISABLE_TEARDOWN: True}
1033
1034        cmd = self.tr.generate_run_commands([], extra_args)
1035
1036        self.assertTokensIn(['--disable-teardown'], cmd[0])
1037
1038    def test_args_with_host_and_generate_in_run_cmd(self):
1039        extra_args = {constants.HOST: True}
1040
1041        cmd = self.tr.generate_run_commands([], extra_args)
1042
1043        self.assertTokensIn(['-n', '--prioritize-host-config',
1044                             '--skip-host-arch-check'], cmd[0])
1045
1046    def test_args_with_custom_args_and_generate_in_run_cmd(self):
1047        extra_args = {constants.CUSTOM_ARGS: ['--a=b']}
1048
1049        cmd = self.tr.generate_run_commands([], extra_args)
1050
1051        self.assertTokensIn(['--a=b'], cmd[0])
1052
1053    def test_args_with_multi_custom_args_and_generate_in_run_cmd(self):
1054        extra_args = {constants.CUSTOM_ARGS: ['--a=b', '--c=d']}
1055
1056        cmd = self.tr.generate_run_commands([], extra_args)
1057
1058        self.assertTokensIn(['--a=b', '--c=d'], cmd[0])
1059
1060    def test_args_with_all_abi_and_generate_in_run_cmd(self):
1061        extra_args = {constants.ALL_ABI: True}
1062
1063        cmd = self.tr.generate_run_commands([], extra_args)
1064
1065        self.assertTokensIn(['--all-abi'], cmd[0])
1066
1067    def test_args_with_dry_run_but_not_generate_in_run_cmd(self):
1068        extra_args = {constants.DRY_RUN: True}
1069
1070        cmd = self.tr.generate_run_commands([], extra_args)
1071
1072        self.assertTokensNotIn(['--dry-run'], cmd[0])
1073
1074    def test_args_with_verify_env_variable_but_not_generate_in_run_cmd(self):
1075        extra_args = {constants.VERIFY_ENV_VARIABLE: True}
1076
1077        cmd = self.tr.generate_run_commands([], extra_args)
1078
1079        self.assertTokensNotIn(['--verify-env-variable'], cmd[0])
1080
1081    def test_args_with_fakes_info_but_not_generate_in_run_cmd(self):
1082        extra_args = {constants.FLAKES_INFO: True}
1083
1084        cmd = self.tr.generate_run_commands([], extra_args)
1085
1086        self.assertTokensNotIn(['--flakes-info'], cmd[0])
1087
1088    def test_args_with_instant_and_generate_in_run_cmd(self):
1089        extra_args = {constants.INSTANT: True}
1090
1091        cmd = self.tr.generate_run_commands([], extra_args)
1092
1093        self.assertTokensIn(['--enable-parameterized-modules',
1094                             '--module-parameter', 'instant_app'], cmd[0])
1095
1096    def test_args_with_user_type_and_generate_in_run_cmd(self):
1097        extra_args = {constants.USER_TYPE: 'hello_user'}
1098
1099        cmd = self.tr.generate_run_commands([], extra_args)
1100
1101        self.assertTokensIn(['--enable-parameterized-modules',
1102                             '--enable-optional-parameterization',
1103                             '--module-parameter', 'hello_user'], cmd[0])
1104
1105    def test_args_with_iterations_and_generate_in_run_cmd(self):
1106        extra_args = {constants.ITERATIONS: 2}
1107
1108        cmd = self.tr.generate_run_commands([], extra_args)
1109
1110        self.assertIn('--retry-strategy ITERATIONS', cmd[0])
1111        self.assertIn('--max-testcase-run-count 2', cmd[0])
1112
1113    def test_args_with_retry_until_failure_and_generate_in_run_cmd(self):
1114        extra_args = {constants.RERUN_UNTIL_FAILURE: 2}
1115
1116        cmd = self.tr.generate_run_commands([], extra_args)
1117
1118        self.assertIn('--retry-strategy RERUN_UNTIL_FAILURE', cmd[0])
1119        self.assertIn('--max-testcase-run-count 2', cmd[0])
1120
1121    def test_args_with_retry_any_failure_and_generate_in_run_cmd(self):
1122        extra_args = {constants.RETRY_ANY_FAILURE: 2}
1123
1124        cmd = self.tr.generate_run_commands([], extra_args)
1125
1126        self.assertIn('--retry-strategy RETRY_ANY_FAILURE', cmd[0])
1127        self.assertIn('--max-testcase-run-count 2', cmd[0])
1128
1129    def test_args_with_collect_test_only_and_generate_in_run_cmd(self):
1130        extra_args = {constants.COLLECT_TESTS_ONLY: True}
1131
1132        cmd = self.tr.generate_run_commands([], extra_args)
1133
1134        self.assertTokensIn(['--collect-tests-only'], cmd[0])
1135
1136    def test_args_with_no_enable_root_and_generate_in_run_cmd(self):
1137        extra_args = {constants.NO_ENABLE_ROOT: True}
1138
1139        cmd = self.tr.generate_run_commands([], extra_args)
1140
1141        self.assertTokensIn(['--no-enable-root'], cmd[0])
1142
1143    def test_args_with_tf_template_but_not_generate_in_run_cmd(self):
1144        extra_args = {constants.TF_TEMPLATE: ['hello']}
1145
1146        cmd = self.tr.generate_run_commands([], extra_args)
1147
1148        self.assertTokensNotIn(['--tf-template'], cmd[0])
1149
1150    def test_args_with_tf_early_device_release_but_not_generate_in_cmd(self):
1151        extra_args = {constants.TF_EARLY_DEVICE_RELEASE: True}
1152
1153        cmd = self.tr.generate_run_commands([], extra_args)
1154
1155        self.assertTokensNotIn(['--tf-early-device-release'], cmd[0])
1156
1157    def test_args_with_timeout_and_generate_in_run_cmd(self):
1158        extra_args = {constants.TEST_TIMEOUT: 10000}
1159
1160        cmd = self.tr.generate_run_commands([], extra_args)
1161
1162        self.assertTokensIn(
1163            ['--test-arg',
1164             'com.android.tradefed.testtype.AndroidJUnitTest:'
1165             'shell-timeout:10000',
1166             '--test-arg',
1167             'com.android.tradefed.testtype.AndroidJUnitTest:'
1168             'test-timeout:10000',
1169             '--test-arg',
1170             'com.android.tradefed.testtype.HostGTest:'
1171             'native-test-timeout:10000',
1172             '--test-arg',
1173             'com.android.tradefed.testtype.GTest:'
1174             'native-test-timeout:10000'],
1175            cmd[0])
1176
1177if __name__ == '__main__':
1178    unittest.main()
1179