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 30 31import atest 32import constants 33import module_info 34 35from metrics import metrics_utils 36from test_finders import test_info 37 38#pylint: disable=protected-access 39class AtestUnittests(unittest.TestCase): 40 """Unit tests for atest.py""" 41 42 @mock.patch('os.environ.get', return_value=None) 43 def test_missing_environment_variables_uninitialized(self, _): 44 """Test _has_environment_variables when no env vars.""" 45 self.assertTrue(atest._missing_environment_variables()) 46 47 @mock.patch('os.environ.get', return_value='out/testcases/') 48 def test_missing_environment_variables_initialized(self, _): 49 """Test _has_environment_variables when env vars.""" 50 self.assertFalse(atest._missing_environment_variables()) 51 52 def test_parse_args(self): 53 """Test _parse_args parses command line args.""" 54 test_one = 'test_name_one' 55 test_two = 'test_name_two' 56 custom_arg = '--custom_arg' 57 custom_arg_val = 'custom_arg_val' 58 pos_custom_arg = 'pos_custom_arg' 59 60 # Test out test and custom args are properly retrieved. 61 args = [test_one, test_two, '--', custom_arg, custom_arg_val] 62 parsed_args = atest._parse_args(args) 63 self.assertEqual(parsed_args.tests, [test_one, test_two]) 64 self.assertEqual(parsed_args.custom_args, [custom_arg, custom_arg_val]) 65 66 # Test out custom positional args with no test args. 67 args = ['--', pos_custom_arg, custom_arg_val] 68 parsed_args = atest._parse_args(args) 69 self.assertEqual(parsed_args.tests, []) 70 self.assertEqual(parsed_args.custom_args, [pos_custom_arg, 71 custom_arg_val]) 72 73 def test_has_valid_test_mapping_args(self): 74 """Test _has_valid_test_mapping_args method.""" 75 # Test test mapping related args are not mixed with incompatible args. 76 options_no_tm_support = [ 77 ('--generate-baseline', '5'), 78 ('--detect-regression', 'path'), 79 ('--generate-new-metrics', '5') 80 ] 81 tm_options = [ 82 '--test-mapping', 83 '--include-subdirs' 84 ] 85 86 for tm_option in tm_options: 87 for no_tm_option, no_tm_option_value in options_no_tm_support: 88 args = [tm_option, no_tm_option] 89 if no_tm_option_value is not None: 90 args.append(no_tm_option_value) 91 parsed_args = atest._parse_args(args) 92 self.assertFalse( 93 atest._has_valid_test_mapping_args(parsed_args), 94 'Failed to validate: %s' % args) 95 96 @mock.patch('json.load', return_value={}) 97 @mock.patch('builtins.open', new_callable=mock.mock_open) 98 @mock.patch('os.path.isfile', return_value=True) 99 @mock.patch('atest_utils._has_colors', return_value=True) 100 @mock.patch.object(module_info.ModuleInfo, 'get_module_info',) 101 def test_print_module_info_from_module_name(self, mock_get_module_info, 102 _mock_has_colors, _isfile, 103 _open, _json): 104 """Test _print_module_info_from_module_name method.""" 105 mod_one_name = 'mod1' 106 mod_one_path = ['src/path/mod1'] 107 mod_one_installed = ['installed/path/mod1'] 108 mod_one_suites = ['device_test_mod1', 'native_test_mod1'] 109 mod_one = {constants.MODULE_NAME: mod_one_name, 110 constants.MODULE_PATH: mod_one_path, 111 constants.MODULE_INSTALLED: mod_one_installed, 112 constants.MODULE_COMPATIBILITY_SUITES: mod_one_suites} 113 114 # Case 1: The testing_module('mod_one') can be found in module_info. 115 mock_get_module_info.return_value = mod_one 116 capture_output = StringIO() 117 sys.stdout = capture_output 118 mod_info = module_info.ModuleInfo() 119 # Check return value = True, since 'mod_one' can be found. 120 self.assertTrue( 121 atest._print_module_info_from_module_name(mod_info, mod_one_name)) 122 # Assign sys.stdout back to default. 123 sys.stdout = sys.__stdout__ 124 correct_output = ('\x1b[1;32mmod1\x1b[0m\n' 125 '\x1b[1;36m\tCompatibility suite\x1b[0m\n' 126 '\t\tdevice_test_mod1\n' 127 '\t\tnative_test_mod1\n' 128 '\x1b[1;36m\tSource code path\x1b[0m\n' 129 '\t\tsrc/path/mod1\n' 130 '\x1b[1;36m\tInstalled path\x1b[0m\n' 131 '\t\tinstalled/path/mod1\n') 132 # Check the function correctly printed module_info in color to stdout 133 self.assertEqual(capture_output.getvalue(), correct_output) 134 135 # Case 2: The testing_module('mod_one') can NOT be found in module_info. 136 mock_get_module_info.return_value = None 137 capture_output = StringIO() 138 sys.stdout = capture_output 139 # Check return value = False, since 'mod_one' can NOT be found. 140 self.assertFalse( 141 atest._print_module_info_from_module_name(mod_info, mod_one_name)) 142 # Assign sys.stdout back to default. 143 sys.stdout = sys.__stdout__ 144 null_output = '' 145 # Check if no module_info, then nothing printed to screen. 146 self.assertEqual(capture_output.getvalue(), null_output) 147 148 @mock.patch('json.load', return_value={}) 149 @mock.patch('builtins.open', new_callable=mock.mock_open) 150 @mock.patch('os.path.isfile', return_value=True) 151 @mock.patch('atest_utils._has_colors', return_value=True) 152 @mock.patch.object(module_info.ModuleInfo, 'get_module_info',) 153 def test_print_test_info(self, mock_get_module_info, _mock_has_colors, 154 _isfile, _open, _json): 155 """Test _print_test_info method.""" 156 mod_one_name = 'mod1' 157 mod_one = {constants.MODULE_NAME: mod_one_name, 158 constants.MODULE_PATH: ['path/mod1'], 159 constants.MODULE_INSTALLED: ['installed/mod1'], 160 constants.MODULE_COMPATIBILITY_SUITES: ['suite_mod1']} 161 mod_two_name = 'mod2' 162 mod_two = {constants.MODULE_NAME: mod_two_name, 163 constants.MODULE_PATH: ['path/mod2'], 164 constants.MODULE_INSTALLED: ['installed/mod2'], 165 constants.MODULE_COMPATIBILITY_SUITES: ['suite_mod2']} 166 mod_three_name = 'mod3' 167 mod_three = {constants.MODULE_NAME: mod_two_name, 168 constants.MODULE_PATH: ['path/mod3'], 169 constants.MODULE_INSTALLED: ['installed/mod3'], 170 constants.MODULE_COMPATIBILITY_SUITES: ['suite_mod3']} 171 test_name = mod_one_name 172 build_targets = set([mod_one_name, mod_two_name, mod_three_name]) 173 t_info = test_info.TestInfo(test_name, 'mock_runner', build_targets) 174 test_infos = set([t_info]) 175 176 # The _print_test_info() will print the module_info of the test_info's 177 # test_name first. Then, print its related build targets. If the build 178 # target be printed before(e.g. build_target == test_info's test_name), 179 # it will skip it and print the next build_target. 180 # Since the build_targets of test_info are mod_one, mod_two, and 181 # mod_three, it will print mod_one first, then mod_two, and mod_three. 182 # 183 # _print_test_info() calls _print_module_info_from_module_name() to 184 # print the module_info. And _print_module_info_from_module_name() 185 # calls get_module_info() to get the module_info. So we can mock 186 # get_module_info() to achieve that. 187 mock_get_module_info.side_effect = [mod_one, mod_two, mod_three] 188 189 capture_output = StringIO() 190 sys.stdout = capture_output 191 mod_info = module_info.ModuleInfo() 192 atest._print_test_info(mod_info, test_infos) 193 # Assign sys.stdout back to default. 194 sys.stdout = sys.__stdout__ 195 correct_output = ('\x1b[1;32mmod1\x1b[0m\n' 196 '\x1b[1;36m\tCompatibility suite\x1b[0m\n' 197 '\t\tsuite_mod1\n' 198 '\x1b[1;36m\tSource code path\x1b[0m\n' 199 '\t\tpath/mod1\n' 200 '\x1b[1;36m\tInstalled path\x1b[0m\n' 201 '\t\tinstalled/mod1\n' 202 '\x1b[1;35m\tRelated build targets\x1b[0m\n' 203 '\t\tmod1, mod2, mod3\n' 204 '\x1b[1;32mmod2\x1b[0m\n' 205 '\x1b[1;36m\tCompatibility suite\x1b[0m\n' 206 '\t\tsuite_mod2\n' 207 '\x1b[1;36m\tSource code path\x1b[0m\n' 208 '\t\tpath/mod2\n' 209 '\x1b[1;36m\tInstalled path\x1b[0m\n' 210 '\t\tinstalled/mod2\n' 211 '\x1b[1;32mmod3\x1b[0m\n' 212 '\x1b[1;36m\tCompatibility suite\x1b[0m\n' 213 '\t\tsuite_mod3\n' 214 '\x1b[1;36m\tSource code path\x1b[0m\n' 215 '\t\tpath/mod3\n' 216 '\x1b[1;36m\tInstalled path\x1b[0m\n' 217 '\t\tinstalled/mod3\n' 218 '\x1b[1;37m\x1b[0m\n') 219 self.assertEqual(capture_output.getvalue(), correct_output) 220 221 @mock.patch.object(metrics_utils, 'send_exit_event') 222 def test_validate_exec_mode(self, _send_exit): 223 """Test _validate_exec_mode.""" 224 args = [] 225 parsed_args = atest._parse_args(args) 226 no_install_test_info = test_info.TestInfo( 227 'mod', '', set(), data={}, module_class=["JAVA_LIBRARIES"], 228 install_locations=set(['device'])) 229 host_test_info = test_info.TestInfo( 230 'mod', '', set(), data={}, module_class=["NATIVE_TESTS"], 231 install_locations=set(['host'])) 232 device_test_info = test_info.TestInfo( 233 'mod', '', set(), data={}, module_class=["NATIVE_TESTS"], 234 install_locations=set(['device'])) 235 both_test_info = test_info.TestInfo( 236 'mod', '', set(), data={}, module_class=["NATIVE_TESTS"], 237 install_locations=set(['host', 'device'])) 238 239 # $atest <Both-support> 240 test_infos = [host_test_info] 241 atest._validate_exec_mode(parsed_args, test_infos) 242 self.assertFalse(parsed_args.host) 243 244 # $atest <Both-support> with host_tests set to True 245 parsed_args = atest._parse_args([]) 246 test_infos = [host_test_info] 247 atest._validate_exec_mode(parsed_args, test_infos, host_tests=True) 248 # Make sure the host option is not set. 249 self.assertFalse(parsed_args.host) 250 251 # $atest <Both-support> with host_tests set to False 252 parsed_args = atest._parse_args([]) 253 test_infos = [host_test_info] 254 atest._validate_exec_mode(parsed_args, test_infos, host_tests=False) 255 self.assertFalse(parsed_args.host) 256 257 # $atest <device-only> with host_tests set to False 258 parsed_args = atest._parse_args([]) 259 test_infos = [device_test_info] 260 atest._validate_exec_mode(parsed_args, test_infos, host_tests=False) 261 # Make sure the host option is not set. 262 self.assertFalse(parsed_args.host) 263 264 # $atest <device-only> with host_tests set to True 265 parsed_args = atest._parse_args([]) 266 test_infos = [device_test_info] 267 self.assertRaises(SystemExit, atest._validate_exec_mode, 268 parsed_args, test_infos, host_tests=True) 269 270 # $atest <Both-support> 271 parsed_args = atest._parse_args([]) 272 test_infos = [both_test_info] 273 atest._validate_exec_mode(parsed_args, test_infos) 274 self.assertFalse(parsed_args.host) 275 276 # $atest <no_install_test_info> 277 parsed_args = atest._parse_args([]) 278 test_infos = [no_install_test_info] 279 atest._validate_exec_mode(parsed_args, test_infos) 280 self.assertFalse(parsed_args.host) 281 282 def test_make_test_run_dir(self): 283 """Test make_test_run_dir.""" 284 tmp_dir = tempfile.mkdtemp() 285 constants.ATEST_RESULT_ROOT = tmp_dir 286 date_time = None 287 288 work_dir = atest.make_test_run_dir() 289 folder_name = os.path.basename(work_dir) 290 date_time = datetime.datetime.strptime('_'.join(folder_name.split('_')[0:2]), 291 atest.TEST_RUN_DIR_PREFIX) 292 reload(constants) 293 self.assertTrue(date_time) 294 295 296if __name__ == '__main__': 297 unittest.main() 298