xref: /aosp_15_r20/tools/asuite/atest/test_runners/atest_tf_test_runner_unittest.py (revision c2e18aaa1096c836b086f94603d04f4eb9cf37f5)
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=missing-function-docstring
20# pylint: disable=too-many-lines
21# pylint: disable=unused-argument
22
23from io import StringIO
24import json
25import os
26from pathlib import Path
27import shlex
28import sys
29import tempfile
30import unittest
31from unittest import mock
32
33from atest import arg_parser
34from atest import atest_configs
35from atest import atest_utils
36from atest import constants
37from atest import module_info
38from atest import module_info_unittest_base
39from atest import unittest_constants as uc
40from atest import unittest_utils
41from atest.test_finders import test_finder_utils
42from atest.test_finders import test_info
43from atest.test_runner_invocation import TestRunnerInvocation
44from atest.test_runners import atest_tf_test_runner as atf_tr
45from atest.test_runners import event_handler
46from pyfakefs import fake_filesystem_unittest
47
48# pylint: disable=protected-access
49# pylint: disable=invalid-name
50# TODO(147567606): Replace {serial} with {extra_args} for general extra
51# arguments testing.
52RUN_CMD_ARGS = (
53    '--log-level-display VERBOSE --log-level VERBOSE'
54    '{device_early_release}{serial}'
55)
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(
61        uc.TEST_INFO_DIR, constants.ATEST_TEST_RECORD_PROTO
62    ),
63)
64RUN_ENV_STR = ''
65RUN_CMD = atf_tr.AtestTradefedTestRunner._RUN_CMD.format(
66    env=RUN_ENV_STR,
67    exe=atf_tr.AtestTradefedTestRunner.EXECUTABLE,
68    template='{template}',
69    log_saver=constants.ATEST_TF_LOG_SAVER,
70    tf_customize_template='{tf_customize_template}',
71    args=RUN_CMD_ARGS,
72    log_args=LOG_ARGS,
73)
74FULL_CLASS2_NAME = 'android.jank.cts.ui.SomeOtherClass'
75CLASS2_FILTER = test_info.TestFilter(FULL_CLASS2_NAME, frozenset())
76METHOD2_FILTER = test_info.TestFilter(
77    uc.FULL_CLASS_NAME, frozenset([uc.METHOD2_NAME])
78)
79MODULE_ARG1 = [
80    (constants.TF_INCLUDE_FILTER_OPTION, 'A'),
81    (constants.TF_INCLUDE_FILTER_OPTION, 'B'),
82]
83MODULE_ARG2 = []
84CLASS2_METHOD_FILTER = test_info.TestFilter(
85    FULL_CLASS2_NAME, frozenset([uc.METHOD_NAME, uc.METHOD2_NAME])
86)
87MODULE2_INFO = test_info.TestInfo(
88    uc.MODULE2_NAME,
89    atf_tr.AtestTradefedTestRunner.NAME,
90    set(),
91    data={
92        constants.TI_REL_CONFIG: uc.CONFIG2_FILE,
93        constants.TI_FILTER: frozenset(),
94    },
95)
96CLASS1_BUILD_TARGETS = {'class_1_build_target'}
97CLASS1_INFO = test_info.TestInfo(
98    uc.MODULE_NAME,
99    atf_tr.AtestTradefedTestRunner.NAME,
100    CLASS1_BUILD_TARGETS,
101    data={
102        constants.TI_REL_CONFIG: uc.CONFIG_FILE,
103        constants.TI_FILTER: frozenset([uc.CLASS_FILTER]),
104    },
105)
106CLASS2_BUILD_TARGETS = {'class_2_build_target'}
107CLASS2_INFO = test_info.TestInfo(
108    uc.MODULE_NAME,
109    atf_tr.AtestTradefedTestRunner.NAME,
110    CLASS2_BUILD_TARGETS,
111    data={
112        constants.TI_REL_CONFIG: uc.CONFIG_FILE,
113        constants.TI_FILTER: frozenset([CLASS2_FILTER]),
114    },
115)
116CLASS3_BUILD_TARGETS = {'class_3_build_target'}
117CLASS3_INFO = test_info.TestInfo(
118    uc.MODULE_NAME,
119    atf_tr.AtestTradefedTestRunner.NAME,
120    CLASS3_BUILD_TARGETS,
121    data={
122        constants.TI_REL_CONFIG: uc.CONFIG_FILE,
123        constants.TI_FILTER: frozenset(),
124        constants.TI_MODULE_ARG: MODULE_ARG1,
125    },
126)
127CLASS4_BUILD_TARGETS = {'class_4_build_target'}
128CLASS4_INFO = test_info.TestInfo(
129    uc.MODULE_NAME,
130    atf_tr.AtestTradefedTestRunner.NAME,
131    CLASS4_BUILD_TARGETS,
132    data={
133        constants.TI_REL_CONFIG: uc.CONFIG_FILE,
134        constants.TI_FILTER: frozenset(),
135        constants.TI_MODULE_ARG: MODULE_ARG2,
136    },
137)
138CLASS1_CLASS2_MODULE_INFO = test_info.TestInfo(
139    uc.MODULE_NAME,
140    atf_tr.AtestTradefedTestRunner.NAME,
141    uc.MODULE_BUILD_TARGETS | CLASS1_BUILD_TARGETS | CLASS2_BUILD_TARGETS,
142    uc.MODULE_DATA,
143)
144FLAT_CLASS_INFO = test_info.TestInfo(
145    uc.MODULE_NAME,
146    atf_tr.AtestTradefedTestRunner.NAME,
147    CLASS1_BUILD_TARGETS | CLASS2_BUILD_TARGETS,
148    data={
149        constants.TI_REL_CONFIG: uc.CONFIG_FILE,
150        constants.TI_FILTER: frozenset([uc.CLASS_FILTER, CLASS2_FILTER]),
151    },
152)
153FLAT2_CLASS_INFO = test_info.TestInfo(
154    uc.MODULE_NAME,
155    atf_tr.AtestTradefedTestRunner.NAME,
156    CLASS3_BUILD_TARGETS | CLASS4_BUILD_TARGETS,
157    data={
158        constants.TI_REL_CONFIG: uc.CONFIG_FILE,
159        constants.TI_FILTER: frozenset(),
160        constants.TI_MODULE_ARG: MODULE_ARG1 + MODULE_ARG2,
161    },
162)
163GTF_INT_CONFIG = os.path.join(uc.GTF_INT_DIR, uc.GTF_INT_NAME + '.xml')
164CLASS2_METHOD_INFO = test_info.TestInfo(
165    uc.MODULE_NAME,
166    atf_tr.AtestTradefedTestRunner.NAME,
167    set(),
168    data={
169        constants.TI_REL_CONFIG: uc.CONFIG_FILE,
170        constants.TI_FILTER: frozenset([
171            test_info.TestFilter(
172                FULL_CLASS2_NAME, frozenset([uc.METHOD_NAME, uc.METHOD2_NAME])
173            )
174        ]),
175    },
176)
177METHOD_AND_CLASS2_METHOD = test_info.TestInfo(
178    uc.MODULE_NAME,
179    atf_tr.AtestTradefedTestRunner.NAME,
180    uc.MODULE_BUILD_TARGETS,
181    data={
182        constants.TI_REL_CONFIG: uc.CONFIG_FILE,
183        constants.TI_FILTER: frozenset(
184            [uc.METHOD_FILTER, CLASS2_METHOD_FILTER]
185        ),
186    },
187)
188METHOD_METHOD2_AND_CLASS2_METHOD = test_info.TestInfo(
189    uc.MODULE_NAME,
190    atf_tr.AtestTradefedTestRunner.NAME,
191    uc.MODULE_BUILD_TARGETS,
192    data={
193        constants.TI_REL_CONFIG: uc.CONFIG_FILE,
194        constants.TI_FILTER: frozenset(
195            [uc.FLAT_METHOD_FILTER, CLASS2_METHOD_FILTER]
196        ),
197    },
198)
199METHOD2_INFO = test_info.TestInfo(
200    uc.MODULE_NAME,
201    atf_tr.AtestTradefedTestRunner.NAME,
202    set(),
203    data={
204        constants.TI_REL_CONFIG: uc.CONFIG_FILE,
205        constants.TI_FILTER: frozenset([METHOD2_FILTER]),
206    },
207)
208
209INT_INFO = test_info.TestInfo(
210    uc.INT_NAME,
211    atf_tr.AtestTradefedTestRunner.NAME,
212    set(),
213    test_finder='INTEGRATION',
214)
215
216MOD_INFO = test_info.TestInfo(
217    uc.MODULE_NAME,
218    atf_tr.AtestTradefedTestRunner.NAME,
219    set(),
220    test_finder='MODULE',
221)
222
223MOD_INFO_NO_TEST_FINDER = test_info.TestInfo(
224    uc.MODULE_NAME, atf_tr.AtestTradefedTestRunner.NAME, set()
225)
226
227EVENTS_NORMAL = [
228    (
229        'TEST_MODULE_STARTED',
230        {
231            'moduleContextFileName': 'serial-util1146216{974}2772610436.ser',
232            'moduleName': 'someTestModule',
233        },
234    ),
235    ('TEST_RUN_STARTED', {'testCount': 2}),
236    (
237        'TEST_STARTED',
238        {
239            'start_time': 52,
240            'className': 'someClassName',
241            'testName': 'someTestName',
242        },
243    ),
244    (
245        'TEST_ENDED',
246        {
247            'end_time': 1048,
248            'className': 'someClassName',
249            'testName': 'someTestName',
250        },
251    ),
252    (
253        'TEST_STARTED',
254        {
255            'start_time': 48,
256            'className': 'someClassName2',
257            'testName': 'someTestName2',
258        },
259    ),
260    (
261        'TEST_FAILED',
262        {
263            'className': 'someClassName2',
264            'testName': 'someTestName2',
265            'trace': 'someTrace',
266        },
267    ),
268    (
269        'TEST_ENDED',
270        {
271            'end_time': 9876450,
272            'className': 'someClassName2',
273            'testName': 'someTestName2',
274        },
275    ),
276    ('TEST_RUN_ENDED', {}),
277    ('TEST_MODULE_ENDED', {'foo': 'bar'}),
278]
279
280ANDROID_HOST_OUT = '/my/android/out/host/abc'
281
282
283# pylint: disable=too-many-public-methods
284class AtestTradefedTestRunnerUnittests(unittest.TestCase):
285  """Unit tests for atest_tf_test_runner.py"""
286
287  def setUp(self):
288    self.tr = atf_tr.AtestTradefedTestRunner(
289        results_dir=uc.TEST_INFO_DIR, extra_args={constants.HOST: False}
290    )
291    self._global_args = arg_parser.create_atest_arg_parser().parse_args([])
292    self._global_args.device_count_config = 0
293    mock.patch.object(atest_configs, 'GLOBAL_ARGS', self._global_args).start()
294
295  def tearDown(self):
296    mock.patch.stopall()
297
298  @mock.patch.object(atf_tr.AtestTradefedTestRunner, '_start_socket_server')
299  @mock.patch.object(atf_tr.AtestTradefedTestRunner, 'run')
300  @mock.patch.object(
301      atf_tr.AtestTradefedTestRunner,
302      '_create_test_args',
303      return_value=['some_args'],
304  )
305  @mock.patch.object(
306      atf_tr.AtestTradefedTestRunner,
307      'generate_run_commands',
308      return_value='some_cmd',
309  )
310  @mock.patch.object(
311      atf_tr.AtestTradefedTestRunner, '_process_connection', return_value=None
312  )
313  @mock.patch('select.select')
314  @mock.patch('os.killpg', return_value=None)
315  @mock.patch('os.getpgid', return_value=None)
316  @mock.patch('signal.signal', return_value=None)
317  def test_run_tests_pretty(
318      self,
319      _signal,
320      _pgid,
321      _killpg,
322      mock_select,
323      _process,
324      _run_cmd,
325      _test_args,
326      mock_run,
327      mock_start_socket_server,
328  ):
329    """Test _run_tests_pretty method."""
330    mock_subproc = mock.Mock()
331    mock_run.return_value = mock_subproc
332    mock_subproc.returncode = 0
333    mock_subproc.poll.side_effect = [True, True, None]
334    mock_server = mock.Mock()
335    mock_server.getsockname.return_value = ('', '')
336    mock_start_socket_server.return_value = mock_server
337    mock_reporter = mock.Mock()
338
339    # Test no early TF exit
340    mock_conn = mock.Mock()
341    mock_server.accept.return_value = (mock_conn, 'some_addr')
342    mock_server.close.return_value = True
343    mock_select.side_effect = [
344        ([mock_server], None, None),
345        ([mock_conn], None, None),
346    ]
347    self.tr.run_tests_pretty([MODULE2_INFO], {}, mock_reporter)
348
349    # Test early TF exit
350    with tempfile.NamedTemporaryFile() as tmp_file:
351      with open(tmp_file.name, 'w', encoding='utf-8') as f:
352        f.write('tf msg')
353      self.tr.test_log_file = tmp_file
354      mock_select.side_effect = [([], None, None)]
355      mock_subproc.poll.side_effect = None
356      capture_output = StringIO()
357      sys.stdout = capture_output
358      self.assertRaises(
359          atf_tr.TradeFedExitError,
360          self.tr.run_tests_pretty,
361          [MODULE2_INFO],
362          {},
363          mock_reporter,
364      )
365
366    sys.stdout = sys.__stdout__
367    self.assertTrue('tf msg' in capture_output.getvalue())
368
369  @mock.patch.object(atf_tr.AtestTradefedTestRunner, '_process_connection')
370  @mock.patch('select.select')
371  def test_start_monitor_2_connection(self, mock_select, mock_process):
372    """Test _start_monitor method."""
373    mock_server = mock.Mock()
374    mock_subproc = mock.Mock()
375    mock_reporter = mock.Mock()
376    mock_conn1 = mock.Mock()
377    mock_conn2 = mock.Mock()
378    mock_server.accept.side_effect = [
379        (mock_conn1, 'addr 1'),
380        (mock_conn2, 'addr 2'),
381    ]
382    mock_select.side_effect = [
383        ([mock_server], None, None),
384        ([mock_server], None, None),
385        ([mock_conn1], None, None),
386        ([mock_conn2], None, None),
387        ([mock_conn1], None, None),
388        ([mock_conn2], None, None),
389    ]
390    mock_process.side_effect = ['abc', 'def', False, False]
391    mock_subproc.poll.side_effect = [None, None, None, None, None, True]
392    self.tr._start_monitor(mock_server, mock_subproc, mock_reporter, {})
393    self.assertEqual(mock_process.call_count, 4)
394    calls = [mock.call.accept(), mock.call.close()]
395    mock_server.assert_has_calls(calls)
396    mock_conn1.assert_has_calls([mock.call.close()])
397    mock_conn2.assert_has_calls([mock.call.close()])
398
399  @mock.patch.object(atf_tr.AtestTradefedTestRunner, '_process_connection')
400  @mock.patch('select.select')
401  def test_start_monitor_tf_exit_before_2nd_connection(
402      self, mock_select, mock_process
403  ):
404    """Test _start_monitor method."""
405    mock_server = mock.Mock()
406    mock_subproc = mock.Mock()
407    mock_reporter = mock.Mock()
408    mock_conn1 = mock.Mock()
409    mock_conn2 = mock.Mock()
410    mock_server.accept.side_effect = [
411        (mock_conn1, 'addr 1'),
412        (mock_conn2, 'addr 2'),
413    ]
414    mock_select.side_effect = [
415        ([mock_server], None, None),
416        ([mock_server], None, None),
417        ([mock_conn1], None, None),
418        ([mock_conn2], None, None),
419        ([mock_conn1], None, None),
420        ([mock_conn2], None, None),
421    ]
422    mock_process.side_effect = ['abc', 'def', False, False]
423    # TF exit early but have not processed data in socket buffer.
424    mock_subproc.poll.side_effect = [None, None, True, True, True, True]
425    self.tr._start_monitor(mock_server, mock_subproc, mock_reporter, {})
426    self.assertEqual(mock_process.call_count, 4)
427    calls = [mock.call.accept(), mock.call.close()]
428    mock_server.assert_has_calls(calls)
429    mock_conn1.assert_has_calls([mock.call.close()])
430    mock_conn2.assert_has_calls([mock.call.close()])
431
432  def test_start_socket_server(self):
433    """Test start_socket_server method."""
434    server = self.tr._start_socket_server()
435    host, port = server.getsockname()
436    self.assertEqual(host, atf_tr.SOCKET_HOST)
437    self.assertLessEqual(port, 65535)
438    self.assertGreaterEqual(port, 1024)
439    server.close()
440
441  @mock.patch('os.path.exists')
442  @mock.patch.dict('os.environ', {'APE_API_KEY': '/tmp/123.json'})
443  def test_try_set_gts_authentication_key_is_set_by_user(self, mock_exist):
444    """Test try_set_authentication_key_is_set_by_user method."""
445    # Test key is set by user.
446    self.tr._try_set_gts_authentication_key()
447    mock_exist.assert_not_called()
448
449  @mock.patch('os.path.join', return_value='/tmp/file_not_exist.json')
450  def test_try_set_gts_authentication_key_not_set(self, _):
451    """Test try_set_authentication_key_not_set method."""
452    # Delete the environment variable if it's set. This is fine for this
453    # method because it's for validating the APE_API_KEY isn't set.
454    if os.environ.get('APE_API_KEY'):
455      del os.environ['APE_API_KEY']
456    self.tr._try_set_gts_authentication_key()
457    self.assertEqual(os.environ.get('APE_API_KEY'), None)
458
459  @mock.patch.object(event_handler.EventHandler, 'process_event')
460  def test_process_connection(self, mock_pe):
461    """Test _process_connection method."""
462    mock_socket = mock.Mock()
463    for name, data in EVENTS_NORMAL:
464      data_map = {mock_socket: ''}
465      socket_data = '%s %s' % (name, json.dumps(data))
466      mock_socket.recv.return_value = socket_data
467      self.tr._process_connection(data_map, mock_socket, mock_pe)
468
469    calls = [
470        mock.call.process_event(name, data) for name, data in EVENTS_NORMAL
471    ]
472    mock_pe.assert_has_calls(calls)
473    mock_socket.recv.return_value = ''
474    self.assertFalse(
475        self.tr._process_connection(data_map, mock_socket, mock_pe)
476    )
477
478  @mock.patch.object(event_handler.EventHandler, 'process_event')
479  def test_process_connection_multiple_lines_in_single_recv(self, mock_pe):
480    """Test _process_connection when recv reads multiple lines in one go."""
481    mock_socket = mock.Mock()
482    squashed_events = '\n'.join(
483        ['%s %s' % (name, json.dumps(data)) for name, data in EVENTS_NORMAL]
484    )
485    socket_data = [squashed_events, '']
486    mock_socket.recv.side_effect = socket_data
487    data_map = {mock_socket: ''}
488    self.tr._process_connection(data_map, mock_socket, mock_pe)
489    calls = [
490        mock.call.process_event(name, data) for name, data in EVENTS_NORMAL
491    ]
492    mock_pe.assert_has_calls(calls)
493
494  @mock.patch.object(event_handler.EventHandler, 'process_event')
495  def test_process_connection_with_buffering(self, mock_pe):
496    """Test _process_connection when events overflow socket buffer size."""
497    mock_socket = mock.Mock()
498    module_events = [EVENTS_NORMAL[0], EVENTS_NORMAL[-1]]
499    socket_events = [
500        '%s %s' % (name, json.dumps(data)) for name, data in module_events
501    ]
502    # test try-block code by breaking apart first event after first }
503    index = socket_events[0].index('}') + 1
504    socket_data = [socket_events[0][:index], socket_events[0][index:]]
505    # test non-try block buffering with second event
506    socket_data.extend([socket_events[1][:-4], socket_events[1][-4:], ''])
507    mock_socket.recv.side_effect = socket_data
508    data_map = {mock_socket: ''}
509    self.tr._process_connection(data_map, mock_socket, mock_pe)
510    self.tr._process_connection(data_map, mock_socket, mock_pe)
511    self.tr._process_connection(data_map, mock_socket, mock_pe)
512    self.tr._process_connection(data_map, mock_socket, mock_pe)
513    calls = [
514        mock.call.process_event(name, data) for name, data in module_events
515    ]
516    mock_pe.assert_has_calls(calls)
517
518  @mock.patch.object(event_handler.EventHandler, 'process_event')
519  def test_process_connection_with_not_completed_event_data(self, mock_pe):
520    """Test _process_connection when event have \n prefix."""
521    mock_socket = mock.Mock()
522    mock_socket.recv.return_value = '\n%s %s' % (
523        EVENTS_NORMAL[0][0],
524        json.dumps(EVENTS_NORMAL[0][1]),
525    )
526    data_map = {mock_socket: ''}
527    self.tr._process_connection(data_map, mock_socket, mock_pe)
528    calls = [mock.call.process_event(EVENTS_NORMAL[0][0], EVENTS_NORMAL[0][1])]
529    mock_pe.assert_has_calls(calls)
530
531  @mock.patch.object(
532      atf_tr.AtestTradefedTestRunner,
533      '_is_all_tests_parameter_auto_enabled',
534      return_value=False,
535  )
536  @mock.patch('os.environ.get', return_value=None)
537  @mock.patch('atest.atest_utils.get_result_server_args')
538  def test_generate_run_commands_without_serial_env(
539      self, mock_resultargs, _, _mock_all
540  ):
541    """Test generate_run_command method."""
542    # Basic Run Cmd
543    mock_resultargs.return_value = []
544    unittest_utils.assert_strict_equal(
545        self,
546        self.tr.generate_run_commands([], {}),
547        [
548            RUN_CMD.format(
549                serial='',
550                template=self.tr._TF_DEVICE_TEST_TEMPLATE,
551                tf_customize_template='',
552                device_early_release=' --no-early-device-release',
553            )
554        ],
555    )
556    unittest_utils.assert_strict_equal(
557        self,
558        self.tr.generate_run_commands([], {}),
559        [
560            RUN_CMD.format(
561                serial='',
562                template=self.tr._TF_DEVICE_TEST_TEMPLATE,
563                tf_customize_template='',
564                device_early_release=' --no-early-device-release',
565            )
566        ],
567    )
568
569  @mock.patch(
570      'atest.test_runners.atest_tf_test_runner.is_log_upload_enabled',
571      return_value=True,
572  )
573  @mock.patch.object(
574      atf_tr.AtestTradefedTestRunner,
575      '_is_all_tests_parameter_auto_enabled',
576      return_value=False,
577  )
578  @mock.patch('os.environ.get', return_value=None)
579  @mock.patch('atest.atest_utils.get_result_server_args')
580  def test_generate_run_commands_with_upload_enabled(
581      self, mock_resultargs, _, _mock_all, _upload_enabled
582  ):
583    result_arg = '--result_arg'
584    mock_resultargs.return_value = [result_arg]
585
586    run_command = self.tr.generate_run_commands([], {})
587
588    self.assertIn('--result_arg', run_command[0].split())
589
590  @mock.patch(
591      'atest.test_runners.atest_tf_test_runner.is_log_upload_enabled',
592      return_value=False,
593  )
594  @mock.patch.object(
595      atf_tr.AtestTradefedTestRunner,
596      '_is_all_tests_parameter_auto_enabled',
597      return_value=False,
598  )
599  @mock.patch('os.environ.get', return_value=None)
600  @mock.patch('atest.atest_utils.get_result_server_args')
601  def test_generate_run_commands_without_upload_enabled(
602      self, mock_resultargs, _, _mock_all, _upload_enabled
603  ):
604    result_arg = '--result_arg'
605    mock_resultargs.return_value = [result_arg]
606
607    run_command = self.tr.generate_run_commands([], {})
608
609    self.assertNotIn('--result_arg', run_command[0].split())
610
611  @mock.patch.object(
612      atf_tr.AtestTradefedTestRunner,
613      '_is_all_tests_parameter_auto_enabled',
614      return_value=False,
615  )
616  @mock.patch('os.environ.get')
617  @mock.patch('atest.atest_utils.get_result_server_args')
618  def test_generate_run_commands_with_serial_env(
619      self, mock_resultargs, mock_env, _mock_all
620  ):
621    """Test generate_run_command method."""
622    # Basic Run Cmd
623    env_device_serial = 'env-device-0'
624    mock_resultargs.return_value = []
625    mock_env.return_value = env_device_serial
626    env_serial_arg = ' --serial %s' % env_device_serial
627    # Serial env be set and without --serial arg.
628    unittest_utils.assert_strict_equal(
629        self,
630        self.tr.generate_run_commands([], {}),
631        [
632            RUN_CMD.format(
633                serial=env_serial_arg,
634                template=self.tr._TF_DEVICE_TEST_TEMPLATE,
635                tf_customize_template='',
636                device_early_release=' --no-early-device-release',
637            )
638        ],
639    )
640    # Serial env be set but with --serial arg.
641    arg_device_serial = 'arg-device-0'
642    arg_serial_arg = ' --serial %s' % arg_device_serial
643    unittest_utils.assert_strict_equal(
644        self,
645        self.tr.generate_run_commands(
646            [], {constants.SERIAL: [arg_device_serial]}
647        ),
648        [
649            RUN_CMD.format(
650                serial=arg_serial_arg,
651                template=self.tr._TF_DEVICE_TEST_TEMPLATE,
652                tf_customize_template='',
653                device_early_release=' --no-early-device-release',
654            )
655        ],
656    )
657    # Serial env be set but with -n arg
658    unittest_utils.assert_strict_equal(
659        self,
660        self.tr.generate_run_commands([], {constants.HOST: True}),
661        [
662            RUN_CMD.format(
663                serial='',
664                template=self.tr._TF_DEVICELESS_TEST_TEMPLATE,
665                tf_customize_template='',
666                device_early_release=' --no-early-device-release',
667            )
668            + ' -n --prioritize-host-config --skip-host-arch-check'
669        ],
670    )
671
672  def test_flatten_test_filters(self):
673    """Test flatten_test_filters method."""
674    # No Flattening
675    filters = self.tr.flatten_test_filters({uc.CLASS_FILTER})
676    unittest_utils.assert_strict_equal(
677        self, frozenset([uc.CLASS_FILTER]), filters
678    )
679    filters = self.tr.flatten_test_filters({CLASS2_FILTER})
680    unittest_utils.assert_strict_equal(
681        self, frozenset([CLASS2_FILTER]), filters
682    )
683    filters = self.tr.flatten_test_filters({uc.METHOD_FILTER})
684    unittest_utils.assert_strict_equal(
685        self, frozenset([uc.METHOD_FILTER]), filters
686    )
687    filters = self.tr.flatten_test_filters(
688        {uc.METHOD_FILTER, CLASS2_METHOD_FILTER}
689    )
690    unittest_utils.assert_strict_equal(
691        self, frozenset([uc.METHOD_FILTER, CLASS2_METHOD_FILTER]), filters
692    )
693    # Flattening
694    filters = self.tr.flatten_test_filters({uc.METHOD_FILTER, METHOD2_FILTER})
695    unittest_utils.assert_strict_equal(
696        self, filters, frozenset([uc.FLAT_METHOD_FILTER])
697    )
698    filters = self.tr.flatten_test_filters({
699        uc.METHOD_FILTER,
700        METHOD2_FILTER,
701        CLASS2_METHOD_FILTER,
702    })
703    unittest_utils.assert_strict_equal(
704        self, filters, frozenset([uc.FLAT_METHOD_FILTER, CLASS2_METHOD_FILTER])
705    )
706
707  def test_flatten_test_infos(self):
708    """Test _flatten_test_infos method."""
709    # No Flattening
710    test_infos = self.tr._flatten_test_infos({uc.MODULE_INFO})
711    unittest_utils.assert_equal_testinfo_sets(
712        self, test_infos, {uc.MODULE_INFO}
713    )
714
715    test_infos = self.tr._flatten_test_infos([uc.MODULE_INFO, MODULE2_INFO])
716    unittest_utils.assert_equal_testinfo_sets(
717        self, test_infos, {uc.MODULE_INFO, MODULE2_INFO}
718    )
719
720    test_infos = self.tr._flatten_test_infos({CLASS1_INFO})
721    unittest_utils.assert_equal_testinfo_sets(self, test_infos, {CLASS1_INFO})
722
723    test_infos = self.tr._flatten_test_infos({uc.INT_INFO})
724    unittest_utils.assert_equal_testinfo_sets(self, test_infos, {uc.INT_INFO})
725
726    test_infos = self.tr._flatten_test_infos({uc.METHOD_INFO})
727    unittest_utils.assert_equal_testinfo_sets(
728        self, test_infos, {uc.METHOD_INFO}
729    )
730
731    # Flattening
732    test_infos = self.tr._flatten_test_infos({CLASS1_INFO, CLASS2_INFO})
733    unittest_utils.assert_equal_testinfo_sets(
734        self, test_infos, {FLAT_CLASS_INFO}
735    )
736
737    test_infos = self.tr._flatten_test_infos(
738        {CLASS1_INFO, uc.INT_INFO, CLASS2_INFO}
739    )
740    unittest_utils.assert_equal_testinfo_sets(
741        self, test_infos, {uc.INT_INFO, FLAT_CLASS_INFO}
742    )
743
744    test_infos = self.tr._flatten_test_infos(
745        {CLASS1_INFO, uc.MODULE_INFO, CLASS2_INFO}
746    )
747    unittest_utils.assert_equal_testinfo_sets(
748        self, test_infos, {CLASS1_CLASS2_MODULE_INFO}
749    )
750
751    test_infos = self.tr._flatten_test_infos(
752        {MODULE2_INFO, uc.INT_INFO, CLASS1_INFO, CLASS2_INFO, uc.GTF_INT_INFO}
753    )
754    unittest_utils.assert_equal_testinfo_sets(
755        self,
756        test_infos,
757        {uc.INT_INFO, uc.GTF_INT_INFO, FLAT_CLASS_INFO, MODULE2_INFO},
758    )
759
760    test_infos = self.tr._flatten_test_infos(
761        {uc.METHOD_INFO, CLASS2_METHOD_INFO}
762    )
763    unittest_utils.assert_equal_testinfo_sets(
764        self, test_infos, {METHOD_AND_CLASS2_METHOD}
765    )
766
767    test_infos = self.tr._flatten_test_infos(
768        {uc.METHOD_INFO, METHOD2_INFO, CLASS2_METHOD_INFO}
769    )
770    unittest_utils.assert_equal_testinfo_sets(
771        self, test_infos, {METHOD_METHOD2_AND_CLASS2_METHOD}
772    )
773    test_infos = self.tr._flatten_test_infos({
774        uc.METHOD_INFO,
775        METHOD2_INFO,
776        CLASS2_METHOD_INFO,
777        MODULE2_INFO,
778        uc.INT_INFO,
779    })
780    unittest_utils.assert_equal_testinfo_sets(
781        self,
782        test_infos,
783        {uc.INT_INFO, MODULE2_INFO, METHOD_METHOD2_AND_CLASS2_METHOD},
784    )
785
786    test_infos = self.tr._flatten_test_infos({CLASS3_INFO, CLASS4_INFO})
787    unittest_utils.assert_equal_testinfo_sets(
788        self, test_infos, {FLAT2_CLASS_INFO}
789    )
790
791  @mock.patch.object(test_finder_utils, 'get_test_config_and_srcs')
792  def test_create_test_args(self, mock_config):
793    """Test _create_test_args method."""
794    # Only compile '--skip-loading-config-jar' in TF if it's not
795    # INTEGRATION finder or the finder property isn't set.
796    mock_config.return_value = '', ''
797    args = self.tr._create_test_args([MOD_INFO], {})
798    self.assertTrue(constants.TF_SKIP_LOADING_CONFIG_JAR in args)
799
800    args = self.tr._create_test_args([INT_INFO], {})
801    self.assertFalse(constants.TF_SKIP_LOADING_CONFIG_JAR in args)
802
803    args = self.tr._create_test_args([MOD_INFO_NO_TEST_FINDER], {})
804    self.assertFalse(constants.TF_SKIP_LOADING_CONFIG_JAR in args)
805
806    args = self.tr._create_test_args([MOD_INFO_NO_TEST_FINDER, INT_INFO], {})
807    self.assertFalse(constants.TF_SKIP_LOADING_CONFIG_JAR in args)
808
809    args = self.tr._create_test_args([MOD_INFO_NO_TEST_FINDER], {})
810    self.assertFalse(constants.TF_SKIP_LOADING_CONFIG_JAR in args)
811
812    args = self.tr._create_test_args(
813        [MOD_INFO_NO_TEST_FINDER, INT_INFO, MOD_INFO], {}
814    )
815    self.assertFalse(constants.TF_SKIP_LOADING_CONFIG_JAR in args)
816
817  @mock.patch.object(test_finder_utils, 'get_test_config_and_srcs')
818  def test_create_test_args_with_test_filter_appends_to_atest_include_filter(
819      self, mock_config
820  ):
821    mock_config.return_value = '', ''
822    args = self.tr._create_test_args(
823        [MOD_INFO], {constants.TEST_FILTER: '*MyTestFilter*'}
824    )
825
826    self.assertEqual(args.count(constants.TF_ATEST_INCLUDE_FILTER), 1)
827    self.assertEqual(
828        args[args.index(constants.TF_ATEST_INCLUDE_FILTER) + 1],
829        uc.MODULE_NAME + ':*MyTestFilter*',
830    )
831
832  @mock.patch.object(
833      atf_tr.AtestTradefedTestRunner,
834      '_is_all_tests_parameter_auto_enabled',
835      return_value=False,
836  )
837  @mock.patch('os.environ.get', return_value=None)
838  @mock.patch('atest.atest_utils.get_result_server_args')
839  def test_generate_run_commands_collect_tests_only(
840      self, mock_resultargs, _, _mock_is_all
841  ):
842    """Test generate_run_command method."""
843    # Testing  without collect-tests-only
844    mock_resultargs.return_value = []
845    extra_args = {}
846    unittest_utils.assert_strict_equal(
847        self,
848        self.tr.generate_run_commands([], extra_args),
849        [
850            RUN_CMD.format(
851                serial='',
852                template=self.tr._TF_DEVICE_TEST_TEMPLATE,
853                tf_customize_template='',
854                device_early_release=' --no-early-device-release',
855            )
856        ],
857    )
858    # Testing  with collect-tests-only
859    mock_resultargs.return_value = []
860    extra_args = {constants.COLLECT_TESTS_ONLY: True}
861    unittest_utils.assert_strict_equal(
862        self,
863        self.tr.generate_run_commands([], extra_args),
864        [
865            RUN_CMD.format(
866                serial=' --collect-tests-only',
867                template=self.tr._TF_DEVICE_TEST_TEMPLATE,
868                tf_customize_template='',
869                device_early_release=' --no-early-device-release',
870            )
871        ],
872    )
873
874  @mock.patch.object(
875      atf_tr.AtestTradefedTestRunner,
876      '_is_all_tests_parameter_auto_enabled',
877      return_value=False,
878  )
879  @mock.patch('os.environ.get', return_value=None)
880  @mock.patch('atest.atest_utils.get_result_server_args')
881  def test_generate_run_commands_incremental_setup(
882      self, mock_resultargs, _, _mock_is_all
883  ):
884    """Test generate_run_command method with incremental setup."""
885    mock_resultargs.return_value = []
886    extra_args = {atf_tr._INCREMENTAL_SETUP_KEY: True}
887
888    run_commands = self.tr.generate_run_commands([], extra_args)
889
890    unittest_utils.assert_strict_equal(
891        self,
892        run_commands,
893        [
894            RUN_CMD.format(
895                serial=' --incremental-setup=YES',
896                template=self.tr._TF_DEVICE_TEST_TEMPLATE,
897                tf_customize_template='',
898                device_early_release=' --no-early-device-release',
899            )
900        ],
901    )
902
903  @mock.patch.object(
904      atf_tr.AtestTradefedTestRunner,
905      '_is_all_tests_parameter_auto_enabled',
906      return_value=False,
907  )
908  @mock.patch('os.environ.get', return_value=None)
909  @mock.patch('atest.atest_utils.get_result_server_args')
910  def test_generate_run_commands_with_tf_template(
911      self, mock_resultargs, _, _mock_all
912  ):
913    """Test generate_run_command method."""
914    tf_tmplate_key1 = 'tf_tmplate_key1'
915    tf_tmplate_val1 = 'tf_tmplate_val1'
916    tf_tmplate_key2 = 'tf_tmplate_key2'
917    tf_tmplate_val2 = 'tf_tmplate_val2'
918    # Testing with only one tradefed template command
919    mock_resultargs.return_value = []
920    extra_args = {
921        constants.TF_TEMPLATE: [
922            '{}={}'.format(tf_tmplate_key1, tf_tmplate_val1)
923        ]
924    }
925    unittest_utils.assert_strict_equal(
926        self,
927        self.tr.generate_run_commands([], extra_args),
928        [
929            RUN_CMD.format(
930                serial='',
931                template=self.tr._TF_DEVICE_TEST_TEMPLATE,
932                device_early_release=' --no-early-device-release',
933                tf_customize_template='--template:map {}={}',
934            ).format(tf_tmplate_key1, tf_tmplate_val1)
935        ],
936    )
937    # Testing with two tradefed template commands
938    extra_args = {
939        constants.TF_TEMPLATE: [
940            '{}={}'.format(tf_tmplate_key1, tf_tmplate_val1),
941            '{}={}'.format(tf_tmplate_key2, tf_tmplate_val2),
942        ]
943    }
944    unittest_utils.assert_strict_equal(
945        self,
946        self.tr.generate_run_commands([], extra_args),
947        [
948            RUN_CMD.format(
949                serial='',
950                template=self.tr._TF_DEVICE_TEST_TEMPLATE,
951                device_early_release=' --no-early-device-release',
952                tf_customize_template=(
953                    '--template:map {}={} --template:map {}={}'
954                ),
955            ).format(
956                tf_tmplate_key1,
957                tf_tmplate_val1,
958                tf_tmplate_key2,
959                tf_tmplate_val2,
960            )
961        ],
962    )
963
964  @mock.patch.object(test_finder_utils, 'get_test_config_and_srcs')
965  def test_has_instant_app_config(self, mock_config):
966    """test _has_instant_app_config method."""
967    no_instant_config = os.path.join(
968        uc.TEST_DATA_DIR, 'parameter_config', 'parameter.cfg'
969    )
970    instant_config = os.path.join(
971        uc.TEST_DATA_DIR, 'parameter_config', 'instant_app_parameter.cfg'
972    )
973    # Test find instant app config
974    mock_config.return_value = instant_config, ''
975    self.assertTrue(
976        atf_tr.AtestTradefedTestRunner._has_instant_app_config(
977            ['test_info'], 'module_info_obj'
978        )
979    )
980    # Test not find instant app config
981    mock_config.return_value = no_instant_config, ''
982    self.assertFalse(
983        atf_tr.AtestTradefedTestRunner._has_instant_app_config(
984            ['test_info'], 'module_info_obj'
985        )
986    )
987
988  @mock.patch.object(
989      atf_tr.AtestTradefedTestRunner,
990      '_is_all_tests_parameter_auto_enabled',
991      return_value=False,
992  )
993  @mock.patch.object(
994      atf_tr.AtestTradefedTestRunner,
995      '_has_instant_app_config',
996      return_value=True,
997  )
998  @mock.patch('os.environ.get', return_value=None)
999  @mock.patch('atest.atest_utils.get_result_server_args')
1000  def test_generate_run_commands_has_instant_app_config(
1001      self, mock_resultargs, _, _mock_has_config, _mock_is_all
1002  ):
1003    """Test generate_run_command method which has instant app config."""
1004    # Basic Run Cmd
1005    mock_resultargs.return_value = []
1006    extra_tf_arg = (
1007        '{tf_test_arg} {tf_class}:{option_name}:{option_value}'.format(
1008            tf_test_arg=constants.TF_TEST_ARG,
1009            tf_class=constants.TF_AND_JUNIT_CLASS,
1010            option_name=constants.TF_EXCLUDE_ANNOTATE,
1011            option_value=constants.INSTANT_MODE_ANNOTATE,
1012        )
1013    )
1014    unittest_utils.assert_strict_equal(
1015        self,
1016        self.tr.generate_run_commands([], {}),
1017        [
1018            RUN_CMD.format(
1019                serial='',
1020                template=self.tr._TF_DEVICE_TEST_TEMPLATE,
1021                tf_customize_template='',
1022                device_early_release=' --no-early-device-release '
1023                + extra_tf_arg,
1024            )
1025        ],
1026    )
1027
1028  @mock.patch.object(atest_utils, 'get_config_parameter')
1029  @mock.patch.object(test_finder_utils, 'get_test_config_and_srcs')
1030  def test_is_parameter_auto_enabled_cfg(self, mock_config, mock_cfg_para):
1031    """test _is_parameter_auto_enabled_cfg method."""
1032    # Test if TF_PARA_INSTANT_APP is match
1033    mock_config.return_value = 'test_config', ''
1034    mock_cfg_para.return_value = {
1035        list(constants.DEFAULT_EXCLUDE_PARAS)[1],
1036        list(constants.DEFAULT_EXCLUDE_PARAS)[0],
1037    }
1038    self.assertFalse(
1039        atf_tr.AtestTradefedTestRunner._is_parameter_auto_enabled_cfg(
1040            ['test_info'], 'module_info_obj'
1041        )
1042    )
1043    # Test if DEFAULT_EXCLUDE_NOT_PARAS is match
1044    mock_cfg_para.return_value = {
1045        list(constants.DEFAULT_EXCLUDE_NOT_PARAS)[2],
1046        list(constants.DEFAULT_EXCLUDE_NOT_PARAS)[0],
1047    }
1048    self.assertFalse(
1049        atf_tr.AtestTradefedTestRunner._is_parameter_auto_enabled_cfg(
1050            ['test_info'], 'module_info_obj'
1051        )
1052    )
1053    # Test if have parameter not in default exclude paras
1054    mock_cfg_para.return_value = {
1055        'not match parameter',
1056        list(constants.DEFAULT_EXCLUDE_PARAS)[1],
1057        list(constants.DEFAULT_EXCLUDE_NOT_PARAS)[2],
1058    }
1059    self.assertTrue(
1060        atf_tr.AtestTradefedTestRunner._is_parameter_auto_enabled_cfg(
1061            ['test_info'], 'module_info_obj'
1062        )
1063    )
1064
1065  @mock.patch.object(
1066      atf_tr.AtestTradefedTestRunner,
1067      '_is_parameter_auto_enabled_cfg',
1068      return_value=True,
1069  )
1070  @mock.patch.object(test_finder_utils, 'get_test_config_and_srcs')
1071  def test_create_test_args_with_auto_enable_parameter(
1072      self, mock_config, _mock_is_enable
1073  ):
1074    """Test _create_test_args method with auto enabled parameter config."""
1075    # Should have --m on args and should not have --include-filter.
1076    mock_config.return_value = '', ''
1077    args = self.tr._create_test_args([MOD_INFO], {})
1078    self.assertTrue(constants.TF_MODULE_FILTER in args)
1079    self.assertFalse(constants.TF_INCLUDE_FILTER in args)
1080
1081  @mock.patch.object(
1082      atf_tr.AtestTradefedTestRunner, '_is_parameter_auto_enabled_cfg'
1083  )
1084  @mock.patch.object(test_finder_utils, 'get_test_config_and_srcs')
1085  def test_parse_extra_args(self, mock_config, _mock_is_enable):
1086    """Test _parse_extra_args ."""
1087    # If extra_arg enable instant_app or secondary users, should not have
1088    # --exclude-module-parameters even though test config parameter is auto
1089    # enabled.
1090    mock_config.return_value = '', ''
1091    _mock_is_enable.return_value = True
1092    args, _ = self.tr._parse_extra_args([MOD_INFO], {constants.INSTANT: ''})
1093    self.assertFalse('--exclude-module-parameters' in args)
1094
1095    # If extra_arg not enable instant_app or secondary users, should have
1096    # --exclude-module-rameters if config parameter is auto enabled.
1097    _mock_is_enable.return_value = True
1098    args, _ = self.tr._parse_extra_args([MOD_INFO], {constants.ALL_ABI: ''})
1099    self.assertTrue('--exclude-module-parameters' in args)
1100
1101    # If extra_arg not enable instant_app or secondary users, should not
1102    # have --exclude-module-rameters if config parameter is not auto enabled
1103    _mock_is_enable.return_value = False
1104    args, _ = self.tr._parse_extra_args([MOD_INFO], {constants.ALL_ABI: ''})
1105    self.assertFalse('--exclude-module-parameters' in args)
1106
1107  @mock.patch.object(
1108      atf_tr.AtestTradefedTestRunner,
1109      '_is_parameter_auto_enabled_cfg',
1110      return_value=False,
1111  )
1112  @mock.patch.object(
1113      atf_tr.AtestTradefedTestRunner,
1114      '_has_instant_app_config',
1115      return_value=False,
1116  )
1117  def test_parse_extra_args_has_instant_app(
1118      self, _mock_has_instant, _mock_is_para
1119  ):
1120    """Test _parse_extra_args with instant app in customize flag."""
1121    # If customize_arg has module-parameter should also include
1122    # --enable-parameterized-modules.
1123    args, _ = self.tr._parse_extra_args(
1124        [MOD_INFO], {constants.CUSTOM_ARGS: [constants.TF_MODULE_PARAMETER]}
1125    )
1126    self.assertTrue(constants.TF_ENABLE_PARAMETERIZED_MODULES in args)
1127
1128  @mock.patch('atest.atest_utils.get_prebuilt_sdk_tools_dir')
1129  @mock.patch.object(
1130      atf_tr.AtestTradefedTestRunner, '_is_missing_exec', return_value=False
1131  )
1132  def test_generate_env_vars_aapt_already_in_system_path(
1133      self, _mock_is_missing_exec, mock_prebuilt_sdk_dir
1134  ):
1135    """Test generate_env_vars if aapt already in system path."""
1136    prebuilt_sdk_dir = Path('/my/test/sdk/dir')
1137    mock_prebuilt_sdk_dir.return_value = prebuilt_sdk_dir
1138
1139    env_vars = self.tr.generate_env_vars(extra_args={})
1140
1141    self.assertFalse(str(prebuilt_sdk_dir) + ':' in env_vars.get('PATH', ''))
1142
1143  @mock.patch('os.path.exists', return_value=True)
1144  @mock.patch('atest.atest_utils.get_prebuilt_sdk_tools_dir')
1145  @mock.patch.object(
1146      atf_tr.AtestTradefedTestRunner, '_is_missing_exec', return_value=True
1147  )
1148  def test_generate_env_vars_aapt_not_in_system_path(
1149      self, _mock_is_missing_exec, mock_prebuilt_sdk_dir, _mock_exist
1150  ):
1151    """Test generate_env_vars if aapt not in system path."""
1152    prebuilt_sdk_dir = Path('/my/test/sdk/dir')
1153    mock_prebuilt_sdk_dir.return_value = prebuilt_sdk_dir
1154
1155    env_vars = self.tr.generate_env_vars(extra_args={})
1156
1157    self.assertTrue(str(prebuilt_sdk_dir) + ':' in env_vars.get('PATH', ''))
1158
1159  @mock.patch.object(atf_tr.AtestTradefedTestRunner, '_handle_native_tests')
1160  @mock.patch.object(atf_tr.AtestTradefedTestRunner, '_parse_extra_args')
1161  @mock.patch.object(atf_tr.AtestTradefedTestRunner, '_create_test_args')
1162  @mock.patch('os.environ.get', return_value=None)
1163  @mock.patch('atest.atest_utils.get_result_server_args')
1164  def test_run_commands_for_aggregate_metric_result_with_manually_input(
1165      self,
1166      mock_resultargs,
1167      _mock_env,
1168      _mock_create,
1169      _mock_parse,
1170      _mock_handle_native,
1171  ):
1172    """Test generate_run_command method for test need aggregate metric."""
1173    mock_resultargs.return_value = []
1174    _mock_create.return_value = []
1175    _mock_parse.return_value = [], []
1176    test_info_with_aggregate_metrics = test_info.TestInfo(
1177        test_name='perf_test', test_runner='test_runner', build_targets=set()
1178    )
1179    test_info_with_aggregate_metrics.aggregate_metrics_result = True
1180
1181    run_cmd = self.tr.generate_run_commands(
1182        [test_info_with_aggregate_metrics],
1183        extra_args={constants.TF_TEMPLATE: ['metric_post_processor=a/b/c']},
1184    )
1185
1186    self.assertTrue(
1187        str(run_cmd).find(
1188            'metric_post_processor='
1189            'google/template/postprocessors/metric-file-aggregate-disabled'
1190        )
1191        < 0
1192    )
1193
1194    self.assertTrue(str(run_cmd).find('metric_post_processor=a/b/c') > 0)
1195
1196  @mock.patch.object(
1197      atf_tr.AtestTradefedTestRunner,
1198      '_is_all_tests_parameter_auto_enabled',
1199      return_value=False,
1200  )
1201  @mock.patch.object(test_finder_utils, 'get_test_config_and_srcs')
1202  def test_create_test_args_with_auto_enable_parameter_but_not_all(
1203      self, mock_config, _mock_is_all
1204  ):
1205    """Test _create_test_args method with not all configs are auto enabled
1206
1207    parameter.
1208    """
1209    # Should not --m on args and should have --include-filter.
1210    mock_config.return_value = '', ''
1211    args = self.tr._create_test_args([MOD_INFO], {})
1212
1213    self.assertFalse(constants.TF_MODULE_FILTER in args)
1214    self.assertTrue(constants.TF_INCLUDE_FILTER in args)
1215
1216  @mock.patch.object(
1217      atf_tr.AtestTradefedTestRunner,
1218      '_is_all_tests_parameter_auto_enabled',
1219      return_value=False,
1220  )
1221  @mock.patch.object(
1222      atf_tr.AtestTradefedTestRunner, '_is_parameter_auto_enabled_cfg'
1223  )
1224  @mock.patch.object(test_finder_utils, 'get_test_config_and_srcs')
1225  def test_parse_extra_args_not_all_cfg_auto_enable_parameter(
1226      self, mock_config, _mock_is_enable, _mock_is_all
1227  ):
1228    """Test _parse_extra_args without all config is auto parameter."""
1229    # If not all test config is parameter auto enabled, should not find
1230    # --enable-parameterized-modules in test config.
1231    mock_config.return_value = '', ''
1232    _mock_is_enable.return_value = True
1233
1234    args, _ = self.tr._parse_extra_args([MOD_INFO], {})
1235    self.assertFalse('--enable-parameterized-modules' in args)
1236
1237  def test_create_invocations_returns_invocations_for_device_and_deviceless_tests(
1238      self,
1239  ):
1240    self.tr.module_info = module_info.ModuleInfo(
1241        name_to_module_info={
1242            'deviceless_test': (
1243                module_info_unittest_base.robolectric_test_module(
1244                    name='deviceless_test'
1245                )
1246            ),
1247            'device_test': module_info_unittest_base.device_driven_test_module(
1248                name='device_test'
1249            ),
1250        }
1251    )
1252    test_info_deviceless = test_info_of('deviceless_test')
1253    test_info_device = test_info_of('device_test')
1254
1255    invocations = self.tr.create_invocations(
1256        {}, [test_info_deviceless, test_info_device]
1257    )
1258    expected_invocation_deviceless = TestRunnerInvocation(
1259        test_runner=self.tr,
1260        extra_args={constants.HOST: True},
1261        test_infos=[test_info_deviceless],
1262    )
1263    expected_invocation_device = TestRunnerInvocation(
1264        test_runner=self.tr, extra_args={}, test_infos=[test_info_device]
1265    )
1266
1267    self.assertEqual(
1268        invocations,
1269        [expected_invocation_deviceless, expected_invocation_device],
1270    )
1271
1272  def test_create_invocations_returns_invocation_only_for_device_tests(self):
1273    self.tr.module_info = module_info.ModuleInfo(
1274        name_to_module_info={
1275            'device_test_1': (
1276                module_info_unittest_base.device_driven_test_module(
1277                    name='device_test_1'
1278                )
1279            ),
1280            'device_test_2': (
1281                module_info_unittest_base.host_driven_device_test_module(
1282                    name='device_test_2'
1283                )
1284            ),
1285        }
1286    )
1287    test_info_device_1 = test_info_of('device_test_1')
1288    test_info_device_2 = test_info_of('device_test_2')
1289
1290    invocations = self.tr.create_invocations(
1291        {}, [test_info_device_1, test_info_device_2]
1292    )
1293    expected_invocation = TestRunnerInvocation(
1294        test_runner=self.tr,
1295        extra_args={},
1296        test_infos=[test_info_device_1, test_info_device_2],
1297    )
1298
1299    self.assertEqual(invocations, [expected_invocation])
1300
1301  def test_create_invocations_returns_invocations_for_device_tests_without_module_info(
1302      self,
1303  ):
1304    self.tr.module_info = module_info.ModuleInfo()
1305
1306    test_info_device = test_info_of('device_test_without_module_info')
1307
1308    invocations = self.tr.create_invocations({}, [test_info_device])
1309    expected_invocation = TestRunnerInvocation(
1310        test_runner=self.tr,
1311        extra_args={},
1312        test_infos=[test_info_device],
1313    )
1314
1315    self.assertEqual(invocations, [expected_invocation])
1316
1317  def assertTokensIn(self, expected_tokens, s):
1318    tokens = shlex.split(s)
1319    for token in expected_tokens:
1320      self.assertIn(token, tokens)
1321
1322  def assertTokensNotIn(self, unwanted_tokens, s):
1323    tokens = shlex.split(s)
1324    for token in unwanted_tokens:
1325      self.assertNotIn(token, tokens)
1326
1327
1328class ExtraArgsTest(AtestTradefedTestRunnerUnittests):
1329  """Unit tests for parsing extra args"""
1330
1331  def test_args_with_wait_for_debug_and_generate_in_run_cmd(self):
1332    extra_args = {constants.WAIT_FOR_DEBUGGER: None}
1333
1334    cmd = self.tr.generate_run_commands([], extra_args)
1335
1336    self.assertTokensIn(['--wait-for-debugger'], cmd[0])
1337
1338  def test_args_with_disable_installed_and_generate_in_run_cmd(self):
1339    extra_args = {constants.DISABLE_INSTALL: None}
1340
1341    cmd = self.tr.generate_run_commands([], extra_args)
1342
1343    self.assertTokensIn(['--disable-target-preparers'], cmd[0])
1344
1345  def test_multidevice_in_config_and_generate_in_run_cmd(self):
1346    self._global_args.device_count_config = 2
1347    cmd = self.tr.generate_run_commands([], {})
1348    self.assertTokensIn(
1349        ['--replicate-parent-setup', '--multi-device-count', '2'], cmd[0]
1350    )
1351
1352    self._global_args.device_count_config = 1
1353    cmd = self.tr.generate_run_commands([], {})
1354    self.assertTokensNotIn(
1355        ['--replicate-parent-setup', '--multi-device-count'], cmd[0]
1356    )
1357
1358    self._global_args.device_count_config = None
1359    cmd = self.tr.generate_run_commands([], {})
1360    self.assertTokensNotIn(
1361        ['--replicate-parent-setup', '--multi-device-count'], cmd[0]
1362    )
1363
1364  def test_args_with_serial_no_and_generate_in_run_cmd(self):
1365    extra_args = {constants.SERIAL: ['device1']}
1366
1367    cmd = self.tr.generate_run_commands([], extra_args)
1368
1369    self.assertTokensIn(['--serial', 'device1'], cmd[0])
1370
1371  def test_args_with_multi_serial_no_and_generate_in_run_cmd(self):
1372    extra_args = {constants.SERIAL: ['device1', 'device2']}
1373
1374    cmd = self.tr.generate_run_commands([], extra_args)
1375
1376    self.assertIn('--serial device1 --serial device2', cmd[0])
1377
1378  def test_args_with_sharding_and_generate_in_run_cmd(self):
1379    extra_args = {constants.SHARDING: 2}
1380
1381    cmd = self.tr.generate_run_commands([], extra_args)
1382
1383    self.assertIn('--shard-count 2', cmd[0])
1384
1385  def test_args_with_disable_teardown_and_generate_in_run_cmd(self):
1386    extra_args = {constants.DISABLE_TEARDOWN: True}
1387
1388    cmd = self.tr.generate_run_commands([], extra_args)
1389
1390    self.assertTokensIn(['--disable-teardown'], cmd[0])
1391
1392  def test_args_with_host_and_generate_in_run_cmd(self):
1393    extra_args = {constants.HOST: True}
1394
1395    cmd = self.tr.generate_run_commands([], extra_args)
1396
1397    self.assertTokensIn(
1398        ['-n', '--prioritize-host-config', '--skip-host-arch-check'], cmd[0]
1399    )
1400
1401  def test_args_with_custom_args_and_generate_in_run_cmd(self):
1402    extra_args = {constants.CUSTOM_ARGS: ['--a=b']}
1403
1404    cmd = self.tr.generate_run_commands([], extra_args)
1405
1406    self.assertTokensIn(['--a=b'], cmd[0])
1407
1408  def test_args_with_multi_custom_args_and_generate_in_run_cmd(self):
1409    extra_args = {constants.CUSTOM_ARGS: ['--a=b', '--c=d']}
1410
1411    cmd = self.tr.generate_run_commands([], extra_args)
1412
1413    self.assertTokensIn(['--a=b', '--c=d'], cmd[0])
1414
1415  def test_args_with_all_abi_and_generate_in_run_cmd(self):
1416    extra_args = {constants.ALL_ABI: True}
1417
1418    cmd = self.tr.generate_run_commands([], extra_args)
1419
1420    self.assertTokensIn(['--all-abi'], cmd[0])
1421
1422  def test_args_with_dry_run_but_not_generate_in_run_cmd(self):
1423    extra_args = {constants.DRY_RUN: True}
1424
1425    cmd = self.tr.generate_run_commands([], extra_args)
1426
1427    self.assertTokensNotIn(['--dry-run'], cmd[0])
1428
1429  def test_args_with_instant_and_generate_in_run_cmd(self):
1430    extra_args = {constants.INSTANT: True}
1431
1432    cmd = self.tr.generate_run_commands([], extra_args)
1433
1434    self.assertTokensIn(
1435        ['--enable-parameterized-modules', '--module-parameter', 'instant_app'],
1436        cmd[0],
1437    )
1438
1439  def test_args_with_user_type_and_generate_in_run_cmd(self):
1440    extra_args = {constants.USER_TYPE: 'hello_user'}
1441
1442    cmd = self.tr.generate_run_commands([], extra_args)
1443
1444    self.assertTokensIn(
1445        [
1446            '--enable-parameterized-modules',
1447            '--enable-optional-parameterization',
1448            '--module-parameter',
1449            'hello_user',
1450        ],
1451        cmd[0],
1452    )
1453
1454  def test_args_with_iterations_and_generate_in_run_cmd(self):
1455    extra_args = {constants.ITERATIONS: 2}
1456
1457    cmd = self.tr.generate_run_commands([], extra_args)
1458
1459    self.assertIn('--retry-strategy ITERATIONS', cmd[0])
1460    self.assertIn('--max-testcase-run-count 2', cmd[0])
1461
1462  def test_args_with_retry_until_failure_and_generate_in_run_cmd(self):
1463    extra_args = {constants.RERUN_UNTIL_FAILURE: 2}
1464
1465    cmd = self.tr.generate_run_commands([], extra_args)
1466
1467    self.assertIn('--retry-strategy RERUN_UNTIL_FAILURE', cmd[0])
1468    self.assertIn('--max-testcase-run-count 2', cmd[0])
1469
1470  def test_args_with_retry_any_failure_and_generate_in_run_cmd(self):
1471    extra_args = {constants.RETRY_ANY_FAILURE: 2}
1472
1473    cmd = self.tr.generate_run_commands([], extra_args)
1474
1475    self.assertIn('--retry-strategy RETRY_ANY_FAILURE', cmd[0])
1476    self.assertIn('--max-testcase-run-count 2', cmd[0])
1477
1478  def test_args_with_collect_test_only_and_generate_in_run_cmd(self):
1479    extra_args = {constants.COLLECT_TESTS_ONLY: True}
1480
1481    cmd = self.tr.generate_run_commands([], extra_args)
1482
1483    self.assertTokensIn(['--collect-tests-only'], cmd[0])
1484
1485  def test_args_with_tf_template_but_not_generate_in_run_cmd(self):
1486    extra_args = {constants.TF_TEMPLATE: ['hello']}
1487
1488    cmd = self.tr.generate_run_commands([], extra_args)
1489
1490    self.assertTokensNotIn(['--tf-template'], cmd[0])
1491
1492  def test_args_with_timeout_and_generate_in_run_cmd(self):
1493    extra_args = {constants.TEST_TIMEOUT: 10000}
1494
1495    cmd = self.tr.generate_run_commands([], extra_args)
1496
1497    self.assertTokensIn(
1498        [
1499            '--test-arg',
1500            (
1501                'com.android.tradefed.testtype.AndroidJUnitTest:'
1502                'shell-timeout:10000'
1503            ),
1504            '--test-arg',
1505            'com.android.tradefed.testtype.AndroidJUnitTest:test-timeout:10000',
1506            '--test-arg',
1507            'com.android.tradefed.testtype.HostGTest:native-test-timeout:10000',
1508            '--test-arg',
1509            'com.android.tradefed.testtype.GTest:native-test-timeout:10000',
1510        ],
1511        cmd[0],
1512    )
1513
1514
1515def test_info_of(module_name):
1516  return test_info.TestInfo(
1517      module_name, atf_tr.AtestTradefedTestRunner.NAME, []
1518  )
1519
1520
1521class DeviceDrivenTestTest(module_info_unittest_base.ModuleInfoTest):
1522  """Tests for device driven test."""
1523
1524  def test_multi_config_unit_test_without_host_arg(self):
1525    mod_info = self.create_module_info(
1526        modules=[
1527            module_info_unittest_base.multi_variant_unit_test_module(
1528                name='hello_world_test'
1529            )
1530        ]
1531    )
1532    test_infos = [test_info_of('hello_world_test')]
1533    runner = atf_tr.AtestTradefedTestRunner(
1534        'result_dir', {constants.HOST: False}, mod_info, minimal_build=True
1535    )
1536    expect_deps = {
1537        'aapt2-host',
1538        'hello_world_test-target',
1539        'compatibility-tradefed-host',
1540        'adb-host',
1541        'atest-tradefed-host',
1542        'aapt-host',
1543        'atest_tradefed.sh-host',
1544        'tradefed-host',
1545        'atest_script_help.sh-host',
1546    }
1547
1548    deps = runner.get_test_runner_build_reqs(test_infos)
1549
1550    self.assertContainsSubset(expect_deps, deps)
1551
1552  @mock.patch.dict('os.environ', {'ANDROID_HOST_OUT': ANDROID_HOST_OUT})
1553  def test_host_jar_env_device_driven_test_without_host_arg(self):
1554    tf_host_jar_path = os.path.join(ANDROID_HOST_OUT, 'tradefed.jar')
1555    atest_host_jar_path = os.path.join(ANDROID_HOST_OUT, 'atest-tradefed.jar')
1556    host_required_jar_path = os.path.join(ANDROID_HOST_OUT, 'host-required.jar')
1557    mod_info = self.create_module_info(
1558        modules=[
1559            module_info_unittest_base.device_driven_test_module(
1560                name='hello_world_test', host_deps=['host-required']
1561            ),
1562            module_info_unittest_base.host_jar_module(
1563                name='tradefed', installed=[tf_host_jar_path]
1564            ),
1565            module_info_unittest_base.host_jar_module(
1566                name='atest-tradefed', installed=[atest_host_jar_path]
1567            ),
1568            module_info_unittest_base.host_jar_module(
1569                name='host-required', installed=[host_required_jar_path]
1570            ),
1571        ]
1572    )
1573    test_infos = [test_info_of('hello_world_test')]
1574    runner = atf_tr.AtestTradefedTestRunner(
1575        'result_dir', {constants.HOST: False}, mod_info, minimal_build=True
1576    )
1577    expect_path = {
1578        tf_host_jar_path,
1579        atest_host_jar_path,
1580        host_required_jar_path,
1581    }
1582
1583    runner.get_test_runner_build_reqs(test_infos)
1584    env = runner.generate_env_vars(dict())
1585
1586    self.assertSetEqual(set(env['ATEST_HOST_JARS'].split(':')), expect_path)
1587
1588  def test_host_driven_device_test(self):
1589    mod_info = self.create_module_info(
1590        modules=[
1591            module_info_unittest_base.host_driven_device_test_module(
1592                name='hello_world_test', libs=['lib']
1593            ),
1594            module_info_unittest_base.module(
1595                name='lib',
1596                supported_variants=['HOST'],
1597                installed=[f'out/host/linux-x86/lib/lib.jar'],
1598            ),
1599        ]
1600    )
1601    test_infos = [test_info_of('hello_world_test')]
1602    runner = atf_tr.AtestTradefedTestRunner(
1603        'result_dir', {constants.HOST: False}, mod_info, minimal_build=True
1604    )
1605
1606    deps = runner.get_test_runner_build_reqs(test_infos)
1607
1608    self.assertContainsSubset({'hello_world_test-host', 'lib-host'}, deps)
1609
1610  def test_device_driven_test_with_mainline_modules(self):
1611    mod_info = self.create_module_info(
1612        modules=[
1613            module_info_unittest_base.device_driven_test_module(
1614                name='hello_world_test'
1615            )
1616        ]
1617    )
1618    t_info = test_info_of('hello_world_test')
1619    t_info.add_mainline_module('mainline_module_1')
1620    t_info.add_mainline_module('mainline_module_2')
1621    runner = atf_tr.AtestTradefedTestRunner(
1622        'result_dir', {constants.HOST: False}, mod_info, minimal_build=True
1623    )
1624
1625    deps = runner.get_test_runner_build_reqs([t_info])
1626
1627    self.assertContainsSubset(
1628        {'hello_world_test-target', 'mainline_module_1', 'mainline_module_2'},
1629        deps,
1630    )
1631
1632  def test_device_driven_test_with_vts_suite(self):
1633    mod_info = self.create_module_info(
1634        modules=[
1635            module_info_unittest_base.device_driven_test_module(
1636                name='hello_world_test', compatibility_suites=['vts']
1637            ),
1638        ]
1639    )
1640    test_infos = [test_info_of('hello_world_test')]
1641    runner = atf_tr.AtestTradefedTestRunner(
1642        'result_dir', {constants.HOST: False}, mod_info, minimal_build=True
1643    )
1644
1645    deps = runner.get_test_runner_build_reqs(test_infos)
1646
1647    self.assertIn('vts-core-tradefed-harness-host', deps)
1648    self.assertNotIn('compatibility-tradefed-host', deps)
1649
1650  def test_deps_contain_google_tradefed(self):
1651    mod_info = self.create_module_info(
1652        modules=[
1653            module_info_unittest_base.multi_variant_unit_test_module(
1654                name='hello_world_test'
1655            )
1656        ]
1657    )
1658    test_infos = [test_info_of('hello_world_test')]
1659    runner = atf_tr.AtestTradefedTestRunner(
1660        'result_dir', {constants.HOST: False}, mod_info, minimal_build=True
1661    )
1662    gtf_target = set([str(t) + '-host' for t in constants.GTF_TARGETS])
1663
1664    deps = runner.get_test_runner_build_reqs(test_infos)
1665
1666    self.assertContainsSubset(gtf_target, deps)
1667
1668  def test_java_device_test(self):
1669    mod_info = self.create_module_info(
1670        modules=[
1671            module_info_unittest_base.device_driven_test_module(
1672                name='HelloWorldTest',
1673                installed=['out/product/vsoc_x86/testcases/HelloWorldTest.jar'],
1674                class_type=['JAVA_LIBRARIES'],
1675            )
1676        ]
1677    )
1678    t_info = test_info_of('HelloWorldTest')
1679    runner = atf_tr.AtestTradefedTestRunner(
1680        'result_dir', {constants.HOST: False}, mod_info, minimal_build=True
1681    )
1682
1683    deps = runner.get_test_runner_build_reqs([t_info])
1684
1685    self.assertContainsSubset(
1686        {
1687            'cts-dalvik-device-test-runner-target',
1688            'cts-dalvik-host-test-runner-host',
1689        },
1690        deps,
1691    )
1692
1693  def test_requires_device_update_is_device_driven_test_returns_true(self):
1694    mod_info = self.create_module_info(
1695        modules=[
1696            module_info_unittest_base.device_driven_test_module(
1697                name='hello_world_test',
1698            ),
1699        ]
1700    )
1701    test_infos = [test_info_of('hello_world_test')]
1702    runner = atf_tr.AtestTradefedTestRunner(
1703        'result_dir', {constants.HOST: False}, mod_info, minimal_build=True
1704    )
1705
1706    requires_device_update = runner.requires_device_update(test_infos)
1707
1708    self.assertTrue(requires_device_update)
1709
1710  def test_requires_device_update_is_unit_test_returns_false(self):
1711    mod_info = self.create_module_info(
1712        modules=[
1713            module_info_unittest_base.device_driven_test_module(
1714                name='hello_world_test',
1715                is_unit_test='true',
1716            ),
1717        ]
1718    )
1719    test_infos = [test_info_of('hello_world_test')]
1720    runner = atf_tr.AtestTradefedTestRunner(
1721        'result_dir', {constants.HOST: False}, mod_info, minimal_build=True
1722    )
1723
1724    requires_device_update = runner.requires_device_update(test_infos)
1725
1726    self.assertFalse(requires_device_update)
1727
1728  def test_requires_device_update_no_module_info_returns_true(self):
1729    mod_info = self.create_module_info(modules=[])
1730    test_infos = [test_info_of('hello_world_test')]
1731    runner = atf_tr.AtestTradefedTestRunner(
1732        'result_dir', {constants.HOST: False}, mod_info, minimal_build=True
1733    )
1734
1735    requires_device_update = runner.requires_device_update(test_infos)
1736
1737    self.assertTrue(requires_device_update)
1738
1739
1740class DevicelessTestTest(module_info_unittest_base.ModuleInfoTest):
1741  """Tests for deviceless test."""
1742
1743  def test_multi_config_unit_test_with_host_arg(self):
1744    mod_info = self.create_module_info(
1745        modules=[
1746            module_info_unittest_base.multi_variant_unit_test_module(
1747                name='hello_world_test'
1748            )
1749        ]
1750    )
1751    test_infos = [test_info_of('hello_world_test')]
1752    runner = atf_tr.AtestTradefedTestRunner(
1753        'result_dir', {constants.HOST: True}, mod_info, minimal_build=True
1754    )
1755    expect_deps = {
1756        'hello_world_test-host',
1757        'adb-host',
1758        'atest-tradefed-host',
1759        'atest_tradefed.sh-host',
1760        'tradefed-host',
1761        'atest_script_help.sh-host',
1762    }
1763
1764    deps = runner.get_test_runner_build_reqs(test_infos)
1765
1766    self.assertContainsSubset(expect_deps, deps)
1767
1768  def test_robolectric_test(self):
1769    mod_info = self.create_module_info(
1770        modules=[
1771            module_info_unittest_base.robolectric_test_module(
1772                name='hello_world_test'
1773            )
1774        ]
1775    )
1776    test_infos = [test_info_of('hello_world_test')]
1777    runner = atf_tr.AtestTradefedTestRunner(
1778        'result_dir', {constants.HOST: False}, mod_info, minimal_build=True
1779    )
1780    expect_deps = {
1781        'hello_world_test-target',
1782        'adb-host',
1783        'atest-tradefed-host',
1784        'atest_tradefed.sh-host',
1785        'tradefed-host',
1786        'atest_script_help.sh-host',
1787    }
1788
1789    deps = runner.get_test_runner_build_reqs(test_infos)
1790
1791    self.assertContainsSubset(expect_deps, deps)
1792
1793
1794class TestExtraArgsToTfArgs(unittest.TestCase):
1795  """Tests for extra_args_to_tf_args function."""
1796
1797  def test_supported_args(self):
1798    extra_args = {
1799        constants.WAIT_FOR_DEBUGGER: True,
1800        constants.SHARDING: 5,
1801        constants.SERIAL: ['device1', 'device2'],
1802        constants.CUSTOM_ARGS: ['arg1', 'arg2'],
1803        constants.ITERATIONS: 3,
1804    }
1805    expected_supported_args = [
1806        '--wait-for-debugger',
1807        '--shard-count',
1808        '5',
1809        '--serial',
1810        'device1',
1811        '--serial',
1812        'device2',
1813        'arg1',
1814        'arg2',
1815        '--retry-strategy',
1816        constants.ITERATIONS,
1817        '--max-testcase-run-count',
1818        '3',
1819    ]
1820
1821    supported_args, _ = atf_tr.extra_args_to_tf_args(extra_args)
1822
1823    self.assertEqual(expected_supported_args, supported_args)
1824
1825  def test_unsupported_args(self):
1826    extra_args = {
1827        'invalid_arg': 'value',
1828        'tf_template': 'template',
1829    }
1830    expected_unsupported_args = ['invalid_arg', 'tf_template']
1831
1832    _, unsupported_args = atf_tr.extra_args_to_tf_args(extra_args)
1833
1834    self.assertEqual(expected_unsupported_args, unsupported_args)
1835
1836  def test_empty_extra_args(self):
1837    extra_args = {}
1838
1839    supported_args, unsupported_args = atf_tr.extra_args_to_tf_args(extra_args)
1840
1841    self.assertEqual([], supported_args)
1842    self.assertEqual([], unsupported_args)
1843
1844
1845if __name__ == '__main__':
1846  unittest.main()
1847