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