• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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=line-too-long
20
21import datetime
22import os
23import sys
24import tempfile
25import unittest
26
27from importlib import reload
28from io import StringIO
29from unittest import mock
30from pyfakefs import fake_filesystem_unittest
31
32from atest import atest_main
33from atest import atest_utils
34from atest import constants
35from atest import module_info
36
37from atest.metrics import metrics_utils
38from atest.test_finders import test_info
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,
79                                                   custom_arg_val])
80
81    def test_has_valid_test_mapping_args(self):
82        """Test _has_valid_test_mapping_args method."""
83        # Test test mapping related args are not mixed with incompatible args.
84        options_no_tm_support = [
85            ('--generate-baseline', '5'),
86            ('--detect-regression', 'path'),
87            ('--generate-new-metrics', '5')
88        ]
89        tm_options = [
90            '--test-mapping',
91            '--include-subdirs'
92        ]
93
94        for tm_option in tm_options:
95            for no_tm_option, no_tm_option_value in options_no_tm_support:
96                args = [tm_option, no_tm_option]
97                if no_tm_option_value is not None:
98                    args.append(no_tm_option_value)
99                parsed_args = atest_main._parse_args(args)
100                self.assertFalse(
101                    atest_main._has_valid_test_mapping_args(parsed_args),
102                    'Failed to validate: %s' % args)
103
104    @mock.patch.object(atest_utils, 'get_adb_devices')
105    @mock.patch.object(metrics_utils, 'send_exit_event')
106    def test_validate_exec_mode(self, _send_exit, _devs):
107        """Test _validate_exec_mode."""
108        _devs.return_value = ['127.0.0.1:34556']
109        args = []
110        no_install_test_info = test_info.TestInfo(
111            'mod', '', set(), data={}, module_class=["JAVA_LIBRARIES"],
112            install_locations=set(['device']))
113        host_test_info = test_info.TestInfo(
114            'mod', '', set(), data={}, module_class=["NATIVE_TESTS"],
115            install_locations=set(['host']))
116        device_test_info = test_info.TestInfo(
117            'mod', '', set(), data={}, module_class=["NATIVE_TESTS"],
118            install_locations=set(['device']))
119        both_test_info = test_info.TestInfo(
120            'mod', '', set(), data={}, module_class=["NATIVE_TESTS"],
121            install_locations=set(['host', 'device']))
122
123        # $atest <Both-support>
124        parsed_args = atest_main._parse_args(args)
125        test_infos = [host_test_info]
126        atest_main._validate_exec_mode(parsed_args, test_infos)
127        self.assertFalse(parsed_args.host)
128
129        # $atest <Both-support> with host_tests set to True
130        parsed_args = atest_main._parse_args([])
131        test_infos = [host_test_info]
132        atest_main._validate_exec_mode(parsed_args, test_infos, host_tests=True)
133        # Make sure the host option is not set.
134        self.assertFalse(parsed_args.host)
135
136        # $atest <Both-support> with host_tests set to False
137        parsed_args = atest_main._parse_args([])
138        test_infos = [host_test_info]
139        atest_main._validate_exec_mode(parsed_args, test_infos, host_tests=False)
140        self.assertFalse(parsed_args.host)
141
142        # $atest <device-only> with host_tests set to False
143        parsed_args = atest_main._parse_args([])
144        test_infos = [device_test_info]
145        atest_main._validate_exec_mode(parsed_args, test_infos, host_tests=False)
146        # Make sure the host option is not set.
147        self.assertFalse(parsed_args.host)
148
149        # $atest <device-only> with host_tests set to True
150        parsed_args = atest_main._parse_args([])
151        test_infos = [device_test_info]
152        self.assertRaises(SystemExit, atest_main._validate_exec_mode,
153                          parsed_args, test_infos, host_tests=True)
154
155        # $atest <Both-support>
156        parsed_args = atest_main._parse_args([])
157        test_infos = [both_test_info]
158        atest_main._validate_exec_mode(parsed_args, test_infos)
159        self.assertFalse(parsed_args.host)
160
161        # $atest <no_install_test_info>
162        parsed_args = atest_main._parse_args([])
163        test_infos = [no_install_test_info]
164        atest_main._validate_exec_mode(parsed_args, test_infos)
165        self.assertFalse(parsed_args.host)
166
167    def test_make_test_run_dir(self):
168        """Test make_test_run_dir."""
169        tmp_dir = tempfile.mkdtemp()
170        constants.ATEST_RESULT_ROOT = tmp_dir
171        date_time = None
172
173        work_dir = atest_main.make_test_run_dir()
174        folder_name = os.path.basename(work_dir)
175        date_time = datetime.datetime.strptime('_'.join(folder_name.split('_')[0:2]),
176                                               atest_main.TEST_RUN_DIR_PREFIX)
177        reload(constants)
178        self.assertTrue(date_time)
179
180
181# pylint: disable=missing-function-docstring
182class AtestUnittestFixture(fake_filesystem_unittest.TestCase):
183    """Fixture for ModuleInfo tests."""
184
185    def setUp(self):
186        self.setUpPyfakefs()
187
188    # pylint: disable=protected-access
189    def create_empty_module_info(self):
190        fake_temp_file_name = next(tempfile._get_candidate_names())
191        self.fs.create_file(fake_temp_file_name, contents='{}')
192        return module_info.ModuleInfo(module_file=fake_temp_file_name)
193
194    def create_module_info(self, modules=None):
195        mod_info = self.create_empty_module_info()
196        modules = modules or []
197
198        for m in modules:
199            mod_info.name_to_module_info[m['module_name']] = m
200
201        return mod_info
202
203    def create_test_info(
204            self,
205            test_name='hello_world_test',
206            test_runner='AtestTradefedRunner',
207            build_targets=None):
208        """Create a test_info.TestInfo object."""
209        if not build_targets:
210            build_targets = set()
211        return test_info.TestInfo(test_name, test_runner, build_targets)
212
213
214class PrintModuleInfoTest(AtestUnittestFixture):
215    """Test conditions for _print_module_info."""
216
217    def tearDown(self):
218        sys.stdout = sys.__stdout__
219
220    @mock.patch('atest.atest_utils._has_colors', return_value=True)
221    def test_print_module_info_from_module_name(self, _):
222        """Test _print_module_info_from_module_name method."""
223        mod_info = self.create_module_info(
224            [module(
225                name='mod1',
226                path=['src/path/mod1'],
227                installed=['installed/path/mod1'],
228                compatibility_suites=['device_test_mod1', 'native_test_mod1']
229            )]
230        )
231        correct_output = (f'{GREEN}mod1{END}\n'
232                          f'{CYAN}\tCompatibility suite{END}\n'
233                          '\t\tdevice_test_mod1\n'
234                          '\t\tnative_test_mod1\n'
235                          f'{CYAN}\tSource code path{END}\n'
236                          '\t\t[\'src/path/mod1\']\n'
237                          f'{CYAN}\tInstalled path{END}\n'
238                          '\t\tinstalled/path/mod1\n')
239        capture_output = StringIO()
240        sys.stdout = capture_output
241
242        atest_main._print_module_info_from_module_name(mod_info, 'mod1')
243
244        # Check the function correctly printed module_info in color to stdout
245        self.assertEqual(correct_output, capture_output.getvalue())
246
247    @mock.patch('atest.atest_utils._has_colors', return_value=True)
248    def test_print_test_info(self, _):
249        """Test _print_test_info method."""
250        modules = []
251        for index in {1, 2, 3}:
252            modules.append(
253                module(
254                    name=f'mod{index}',
255                    path=[f'path/mod{index}'],
256                    installed=[f'installed/mod{index}'],
257                    compatibility_suites=[f'suite_mod{index}']
258                )
259            )
260        mod_info = self.create_module_info(modules)
261        test_infos = {
262            self.create_test_info(
263                test_name='mod1',
264                test_runner='mock_runner',
265                build_targets={'mod1', 'mod2', 'mod3'},
266            ),
267        }
268        correct_output = (f'{GREEN}mod1{END}\n'
269                          f'{CYAN}\tCompatibility suite{END}\n'
270                          '\t\tsuite_mod1\n'
271                          f'{CYAN}\tSource code path{END}\n'
272                          '\t\t[\'path/mod1\']\n'
273                          f'{CYAN}\tInstalled path{END}\n'
274                          '\t\tinstalled/mod1\n'
275                          f'{MAGENTA}\tRelated build targets{END}\n'
276                          '\t\tmod1, mod2, mod3\n'
277                          f'{GREEN}mod2{END}\n'
278                          f'{CYAN}\tCompatibility suite{END}\n'
279                          '\t\tsuite_mod2\n'
280                          f'{CYAN}\tSource code path{END}\n'
281                          '\t\t[\'path/mod2\']\n'
282                          f'{CYAN}\tInstalled path{END}\n'
283                          '\t\tinstalled/mod2\n'
284                          f'{GREEN}mod3{END}\n'
285                          f'{CYAN}\tCompatibility suite{END}\n'
286                          '\t\tsuite_mod3\n'
287                          f'{CYAN}\tSource code path{END}\n'
288                          '\t\t[\'path/mod3\']\n'
289                          f'{CYAN}\tInstalled path{END}\n'
290                          '\t\tinstalled/mod3\n'
291                          f'\x1b[1;37m{END}\n')
292        capture_output = StringIO()
293        sys.stdout = capture_output
294
295        # The _print_test_info() will print the module_info of the test_info's
296        # test_name first. Then, print its related build targets. If the build
297        # target be printed before(e.g. build_target == test_info's test_name),
298        # it will skip it and print the next build_target.
299        # Since the build_targets of test_info are mod_one, mod_two, and
300        # mod_three, it will print mod_one first, then mod_two, and mod_three.
301        #
302        # _print_test_info() calls _print_module_info_from_module_name() to
303        # print the module_info. And _print_module_info_from_module_name()
304        # calls get_module_info() to get the module_info. So we can mock
305        # get_module_info() to achieve that.
306        atest_main._print_test_info(mod_info, test_infos)
307
308        self.assertEqual(correct_output, capture_output.getvalue())
309
310
311# pylint: disable=too-many-arguments
312def module(
313    name=None,
314    path=None,
315    installed=None,
316    classes=None,
317    auto_test_config=None,
318    test_config=None,
319    shared_libs=None,
320    dependencies=None,
321    runtime_dependencies=None,
322    data=None,
323    data_dependencies=None,
324    compatibility_suites=None,
325    host_dependencies=None,
326    srcs=None,
327):
328    name = name or 'libhello'
329
330    m = {}
331
332    m['module_name'] = name
333    m['class'] = classes
334    m['path'] = [path or '']
335    m['installed'] = installed or []
336    m['is_unit_test'] = 'false'
337    m['auto_test_config'] = auto_test_config or []
338    m['test_config'] = test_config or []
339    m['shared_libs'] = shared_libs or []
340    m['runtime_dependencies'] = runtime_dependencies or []
341    m['dependencies'] = dependencies or []
342    m['data'] = data or []
343    m['data_dependencies'] = data_dependencies or []
344    m['compatibility_suites'] = compatibility_suites or []
345    m['host_dependencies'] = host_dependencies or []
346    m['srcs'] = srcs or []
347    return m
348
349if __name__ == '__main__':
350    unittest.main()
351