xref: /aosp_15_r20/tools/asuite/atest/atest_main_unittest.py (revision c2e18aaa1096c836b086f94603d04f4eb9cf37f5)
1#!/usr/bin/env python3
2#
3# Copyright 2017, 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."""
18
19# pylint: disable=invalid-name
20
21import datetime
22from importlib import reload
23from io import StringIO
24import os
25import sys
26import tempfile
27import unittest
28from unittest import mock
29from atest import arg_parser
30from atest import atest_main
31from atest import atest_utils
32from atest import constants
33from atest import module_info
34from atest.atest_enum import DetectType
35from atest.metrics import metrics
36from atest.metrics import metrics_utils
37from atest.test_finders import test_info
38from pyfakefs import fake_filesystem_unittest
39
40GREEN = '\x1b[1;32m'
41CYAN = '\x1b[1;36m'
42MAGENTA = '\x1b[1;35m'
43END = '\x1b[0m'
44
45
46# pylint: disable=protected-access
47class AtestUnittests(unittest.TestCase):
48  """Unit tests for atest_main.py"""
49
50  @mock.patch('os.environ.get', return_value=None)
51  def test_missing_environment_variables_uninitialized(self, _):
52    """Test _has_environment_variables when no env vars."""
53    self.assertTrue(atest_main._missing_environment_variables())
54
55  @mock.patch('os.environ.get', return_value='out/testcases/')
56  def test_missing_environment_variables_initialized(self, _):
57    """Test _has_environment_variables when env vars."""
58    self.assertFalse(atest_main._missing_environment_variables())
59
60  def test_parse_args(self):
61    """Test _parse_args parses command line args."""
62    test_one = 'test_name_one'
63    test_two = 'test_name_two'
64    custom_arg = '--custom_arg'
65    custom_arg_val = 'custom_arg_val'
66    pos_custom_arg = 'pos_custom_arg'
67
68    # Test out test and custom args are properly retrieved.
69    args = [test_one, test_two, '--', custom_arg, custom_arg_val]
70    parsed_args = atest_main._parse_args(args)
71    self.assertEqual(parsed_args.tests, [test_one, test_two])
72    self.assertEqual(parsed_args.custom_args, [custom_arg, custom_arg_val])
73
74    # Test out custom positional args with no test args.
75    args = ['--', pos_custom_arg, custom_arg_val]
76    parsed_args = atest_main._parse_args(args)
77    self.assertEqual(parsed_args.tests, [])
78    self.assertEqual(parsed_args.custom_args, [pos_custom_arg, custom_arg_val])
79
80  def test_has_valid_test_mapping_args(self):
81    """Test _has_valid_test_mapping_args method."""
82    # Test test mapping related args are not mixed with incompatible args.
83    options_no_tm_support = [
84        (
85            '--annotation-filter',
86            'androidx.test.filters.SmallTest',
87        ),
88    ]
89    tm_options = ['--test-mapping', '--include-subdirs']
90
91    for tm_option in tm_options:
92      for no_tm_option, no_tm_option_value in options_no_tm_support:
93        args = [tm_option, no_tm_option]
94        if no_tm_option_value is not None:
95          args.append(no_tm_option_value)
96        parsed_args = atest_main._parse_args(args)
97        self.assertFalse(
98            atest_main._has_valid_test_mapping_args(parsed_args),
99            'Failed to validate: %s' % args,
100        )
101
102  @mock.patch.object(atest_utils, 'get_adb_devices')
103  @mock.patch.object(metrics_utils, 'send_exit_event')
104  def test_validate_exec_mode(self, _send_exit, _devs):
105    """Test _validate_exec_mode."""
106    _devs.return_value = ['127.0.0.1:34556']
107    args = []
108    no_install_test_info = test_info.TestInfo(
109        'mod',
110        '',
111        set(),
112        data={},
113        module_class=['JAVA_LIBRARIES'],
114        install_locations=set(['device']),
115    )
116    host_test_info = test_info.TestInfo(
117        'mod',
118        '',
119        set(),
120        data={},
121        module_class=['NATIVE_TESTS'],
122        install_locations=set(['host']),
123    )
124    device_test_info = test_info.TestInfo(
125        'mod',
126        '',
127        set(),
128        data={},
129        module_class=['NATIVE_TESTS'],
130        install_locations=set(['device']),
131    )
132    both_test_info = test_info.TestInfo(
133        'mod',
134        '',
135        set(),
136        data={},
137        module_class=['NATIVE_TESTS'],
138        install_locations=set(['host', 'device']),
139    )
140
141    # $atest <Both-support>
142    parsed_args = atest_main._parse_args(args)
143    test_infos = [host_test_info]
144    atest_main._validate_exec_mode(parsed_args, test_infos)
145    self.assertFalse(parsed_args.host)
146
147    # $atest <Both-support> with host_tests set to True
148    parsed_args = atest_main._parse_args([])
149    test_infos = [host_test_info]
150    atest_main._validate_exec_mode(parsed_args, test_infos, host_tests=True)
151    # Make sure the host option is not set.
152    self.assertFalse(parsed_args.host)
153
154    # $atest <Both-support> with host_tests set to False
155    parsed_args = atest_main._parse_args([])
156    test_infos = [host_test_info]
157    atest_main._validate_exec_mode(parsed_args, test_infos, host_tests=False)
158    self.assertFalse(parsed_args.host)
159
160    # $atest <device-only> with host_tests set to False
161    parsed_args = atest_main._parse_args([])
162    test_infos = [device_test_info]
163    atest_main._validate_exec_mode(parsed_args, test_infos, host_tests=False)
164    # Make sure the host option is not set.
165    self.assertFalse(parsed_args.host)
166
167    # $atest <device-only> with host_tests set to True
168    parsed_args = atest_main._parse_args([])
169    test_infos = [device_test_info]
170    self.assertRaises(
171        SystemExit,
172        atest_main._validate_exec_mode,
173        parsed_args,
174        test_infos,
175        host_tests=True,
176    )
177
178    # $atest <Both-support>
179    parsed_args = atest_main._parse_args([])
180    test_infos = [both_test_info]
181    atest_main._validate_exec_mode(parsed_args, test_infos)
182    self.assertFalse(parsed_args.host)
183
184    # $atest <no_install_test_info>
185    parsed_args = atest_main._parse_args([])
186    test_infos = [no_install_test_info]
187    atest_main._validate_exec_mode(parsed_args, test_infos)
188    self.assertFalse(parsed_args.host)
189
190  def test_make_test_run_dir(self):
191    """Test make_test_run_dir."""
192    tmp_dir = tempfile.mkdtemp()
193    constants.ATEST_RESULT_ROOT = tmp_dir
194    date_time = None
195
196    work_dir = atest_main.make_test_run_dir()
197    folder_name = os.path.basename(work_dir)
198    date_time = datetime.datetime.strptime(
199        '_'.join(folder_name.split('_')[0:2]), atest_main.TEST_RUN_DIR_PREFIX
200    )
201    reload(constants)
202    self.assertTrue(date_time)
203
204  def test_has_set_sufficient_devices_no_device_no_require(self):
205    required_num = 0
206    self.assertTrue(atest_main.has_set_sufficient_devices(required_num))
207
208  def test_has_set_sufficient_devices_equal_required_attached_devices(self):
209    required_num = 2
210    attached_devices = ['serial1', 'serial2']
211
212    self.assertTrue(
213        atest_main.has_set_sufficient_devices(required_num, attached_devices)
214    )
215
216  def test_has_set_sufficient_devices_attached_devices_more_than_required(self):
217    required_num = 2
218    attached_devices = ['serial1', 'serial2', 'serial3']
219
220    self.assertTrue(
221        atest_main.has_set_sufficient_devices(required_num, attached_devices)
222    )
223
224  def test_has_set_sufficient_devices_not_enough_devices(self):
225    required_num = 2
226    attached_devices = ['serial1']
227
228    self.assertFalse(
229        atest_main.has_set_sufficient_devices(required_num, attached_devices)
230    )
231
232  def test_ravenwood_tests_is_deviceless(self):
233    ravenwood_test_info = test_info.TestInfo(
234        'mod',
235        '',
236        set(),
237        compatibility_suites=[
238            test_info.MODULE_COMPATIBILITY_SUITES_RAVENWOOD_TESTS
239        ],
240    )
241
242    self.assertEqual(
243        constants.DEVICELESS_TEST,
244        ravenwood_test_info.get_supported_exec_mode(),
245        'If compatibility suites contains ravenwood-tests, '
246        'the test should be recognized as deviceless.',
247    )
248
249
250class AtestMainUnitTests(unittest.TestCase):
251
252  def test_performance_tests_inject_default_args(self):
253    non_perf_test_info = test_info.TestInfo(
254        'some_module',
255        'TestRunner',
256        set(),
257        compatibility_suites=['not-performance'],
258    )
259    perf_test_info = test_info.TestInfo(
260        'some_module',
261        'TestRunner',
262        set(),
263        compatibility_suites=['performance-tests'],
264    )
265    args_original = atest_main._parse_args([])
266    args = atest_main._parse_args([])
267
268    with self.subTest(name='does not inject default args for non-perf tests'):
269      atest_main._AtestMain._inject_default_arguments_based_on_test_infos(
270          [non_perf_test_info], args
271      )
272
273      self.assertEqual(args_original, args)
274
275    with self.subTest(name='injects default args for perf tests'):
276      atest_main._AtestMain._inject_default_arguments_based_on_test_infos(
277          [perf_test_info], args
278      )
279
280      self.assertNotEqual(args_original, args)
281
282
283# pylint: disable=missing-function-docstring
284class AtestUnittestFixture(fake_filesystem_unittest.TestCase):
285  """Fixture for ModuleInfo tests."""
286
287  def setUp(self):
288    self.setUpPyfakefs()
289
290  # pylint: disable=protected-access
291  def create_empty_module_info(self):
292    fake_temp_file_name = next(tempfile._get_candidate_names())
293    self.fs.create_file(fake_temp_file_name, contents='{}')
294    return module_info.load_from_file(module_file=fake_temp_file_name)
295
296  def create_module_info(self, modules=None):
297    mod_info = self.create_empty_module_info()
298    modules = modules or []
299
300    for m in modules:
301      mod_info.name_to_module_info[m['module_name']] = m
302
303    return mod_info
304
305  def create_test_info(
306      self,
307      test_name='hello_world_test',
308      test_runner='AtestTradefedRunner',
309      build_targets=None,
310  ):
311    """Create a test_info.TestInfo object."""
312    if not build_targets:
313      build_targets = set()
314    return test_info.TestInfo(test_name, test_runner, build_targets)
315
316
317class PrintModuleInfoTest(AtestUnittestFixture):
318  """Test conditions for _print_module_info."""
319
320  def tearDown(self):
321    sys.stdout = sys.__stdout__
322
323  def test_has_valid_test_mapping_args_is_test_mapping_detect_event_send_1(
324      self,
325  ):
326    # Arrange
327    expected_detect_type = DetectType.IS_TEST_MAPPING
328    expected_result = 1
329    metrics.LocalDetectEvent = mock.MagicMock()
330    args = arg_parser.create_atest_arg_parser().parse_args([])
331
332    # Act
333    atest_main._has_valid_test_mapping_args(args)
334
335    # Assert
336    metrics.LocalDetectEvent.assert_called_once_with(
337        detect_type=expected_detect_type, result=expected_result
338    )
339
340  def test_has_valid_test_mapping_args_mpt_test_mapping_detect_event_send_0(
341      self,
342  ):
343    # Arrange
344    expected_detect_type = DetectType.IS_TEST_MAPPING
345    expected_result = 0
346    metrics.LocalDetectEvent = mock.MagicMock()
347    args = arg_parser.create_atest_arg_parser().parse_args(['test1'])
348
349    # Act
350    atest_main._has_valid_test_mapping_args(args)
351
352    # Assert
353    metrics.LocalDetectEvent.assert_called_once_with(
354        detect_type=expected_detect_type, result=expected_result
355    )
356
357
358# pylint: disable=too-many-arguments
359def module(
360    name=None,
361    path=None,
362    installed=None,
363    classes=None,
364    auto_test_config=None,
365    test_config=None,
366    shared_libs=None,
367    dependencies=None,
368    runtime_dependencies=None,
369    data=None,
370    data_dependencies=None,
371    compatibility_suites=None,
372    host_dependencies=None,
373    srcs=None,
374):
375  name = name or 'libhello'
376
377  m = {}
378
379  m['module_name'] = name
380  m['class'] = classes
381  m['path'] = [path or '']
382  m['installed'] = installed or []
383  m['is_unit_test'] = 'false'
384  m['auto_test_config'] = auto_test_config or []
385  m['test_config'] = test_config or []
386  m['shared_libs'] = shared_libs or []
387  m['runtime_dependencies'] = runtime_dependencies or []
388  m['dependencies'] = dependencies or []
389  m['data'] = data or []
390  m['data_dependencies'] = data_dependencies or []
391  m['compatibility_suites'] = compatibility_suites or []
392  m['host_dependencies'] = host_dependencies or []
393  m['srcs'] = srcs or []
394  return m
395
396
397if __name__ == '__main__':
398  unittest.main()
399