1#!/usr/bin/python2 2 3# Copyright 2012 Google Inc. All Rights Reserved. 4"""Tests for bisecting tool.""" 5 6from __future__ import print_function 7 8__author__ = 'shenhan@google.com (Han Shen)' 9 10import os 11import random 12import sys 13import unittest 14 15from cros_utils import command_executer 16from binary_search_tool import binary_search_state 17from binary_search_tool import bisect 18 19import common 20import gen_obj 21 22 23def GenObj(): 24 obj_num = random.randint(100, 1000) 25 bad_obj_num = random.randint(obj_num / 100, obj_num / 20) 26 if bad_obj_num == 0: 27 bad_obj_num = 1 28 gen_obj.Main(['--obj_num', str(obj_num), '--bad_obj_num', str(bad_obj_num)]) 29 30 31def CleanObj(): 32 os.remove(common.OBJECTS_FILE) 33 os.remove(common.WORKING_SET_FILE) 34 print('Deleted "{0}" and "{1}"'.format(common.OBJECTS_FILE, 35 common.WORKING_SET_FILE)) 36 37 38class BisectTest(unittest.TestCase): 39 """Tests for bisect.py""" 40 41 def setUp(self): 42 with open('./is_setup', 'w'): 43 pass 44 45 try: 46 os.remove(binary_search_state.STATE_FILE) 47 except OSError: 48 pass 49 50 def tearDown(self): 51 try: 52 os.remove('./is_setup') 53 os.remove(os.readlink(binary_search_state.STATE_FILE)) 54 os.remove(binary_search_state.STATE_FILE) 55 except OSError: 56 pass 57 58 class FullBisector(bisect.Bisector): 59 """Test bisector to test bisect.py with""" 60 61 def __init__(self, options, overrides): 62 super(BisectTest.FullBisector, self).__init__(options, overrides) 63 64 def PreRun(self): 65 GenObj() 66 return 0 67 68 def Run(self): 69 return binary_search_state.Run(get_initial_items='./gen_init_list.py', 70 switch_to_good='./switch_to_good.py', 71 switch_to_bad='./switch_to_bad.py', 72 test_script='./is_good.py', 73 prune=True, 74 file_args=True) 75 76 def PostRun(self): 77 CleanObj() 78 return 0 79 80 def test_full_bisector(self): 81 ret = bisect.Run(self.FullBisector({}, {})) 82 self.assertEquals(ret, 0) 83 self.assertFalse(os.path.exists(common.OBJECTS_FILE)) 84 self.assertFalse(os.path.exists(common.WORKING_SET_FILE)) 85 86 def check_output(self): 87 _, out, _ = command_executer.GetCommandExecuter().RunCommandWOutput( 88 ('grep "Bad items are: " logs/binary_search_tool_tester.py.out | ' 89 'tail -n1')) 90 ls = out.splitlines() 91 self.assertEqual(len(ls), 1) 92 line = ls[0] 93 94 _, _, bad_ones = line.partition('Bad items are: ') 95 bad_ones = bad_ones.split() 96 expected_result = common.ReadObjectsFile() 97 98 # Reconstruct objects file from bad_ones and compare 99 actual_result = [0] * len(expected_result) 100 for bad_obj in bad_ones: 101 actual_result[int(bad_obj)] = 1 102 103 self.assertEqual(actual_result, expected_result) 104 105 106class BisectingUtilsTest(unittest.TestCase): 107 """Tests for bisecting tool.""" 108 109 def setUp(self): 110 """Generate [100-1000] object files, and 1-5% of which are bad ones.""" 111 GenObj() 112 113 with open('./is_setup', 'w'): 114 pass 115 116 try: 117 os.remove(binary_search_state.STATE_FILE) 118 except OSError: 119 pass 120 121 def tearDown(self): 122 """Cleanup temp files.""" 123 CleanObj() 124 125 try: 126 os.remove(os.readlink(binary_search_state.STATE_FILE)) 127 except OSError: 128 pass 129 130 cleanup_list = ['./is_setup', binary_search_state.STATE_FILE, 131 'noinc_prune_bad', 'noinc_prune_good'] 132 for f in cleanup_list: 133 if os.path.exists(f): 134 os.remove(f) 135 136 def runTest(self): 137 ret = binary_search_state.Run(get_initial_items='./gen_init_list.py', 138 switch_to_good='./switch_to_good.py', 139 switch_to_bad='./switch_to_bad.py', 140 test_script='./is_good.py', 141 prune=True, 142 file_args=True) 143 self.assertEquals(ret, 0) 144 self.check_output() 145 146 def test_arg_parse(self): 147 args = ['--get_initial_items', './gen_init_list.py', '--switch_to_good', 148 './switch_to_good.py', '--switch_to_bad', './switch_to_bad.py', 149 '--test_script', './is_good.py', '--prune', '--file_args'] 150 ret = binary_search_state.Main(args) 151 self.assertEquals(ret, 0) 152 self.check_output() 153 154 def test_test_setup_script(self): 155 os.remove('./is_setup') 156 with self.assertRaises(AssertionError): 157 ret = binary_search_state.Run(get_initial_items='./gen_init_list.py', 158 switch_to_good='./switch_to_good.py', 159 switch_to_bad='./switch_to_bad.py', 160 test_script='./is_good.py', 161 prune=True, 162 file_args=True) 163 164 ret = binary_search_state.Run(get_initial_items='./gen_init_list.py', 165 switch_to_good='./switch_to_good.py', 166 switch_to_bad='./switch_to_bad.py', 167 test_script='./is_good.py', 168 test_setup_script='./test_setup.py', 169 prune=True, 170 file_args=True) 171 self.assertEquals(ret, 0) 172 self.check_output() 173 174 def test_bad_test_setup_script(self): 175 with self.assertRaises(AssertionError): 176 binary_search_state.Run(get_initial_items='./gen_init_list.py', 177 switch_to_good='./switch_to_good.py', 178 switch_to_bad='./switch_to_bad.py', 179 test_script='./is_good.py', 180 test_setup_script='./test_setup_bad.py', 181 prune=True, 182 file_args=True) 183 184 def test_bad_save_state(self): 185 state_file = binary_search_state.STATE_FILE 186 hidden_state_file = os.path.basename(binary_search_state.HIDDEN_STATE_FILE) 187 188 with open(state_file, 'w') as f: 189 f.write('test123') 190 191 bss = binary_search_state.MockBinarySearchState() 192 with self.assertRaises(binary_search_state.Error): 193 bss.SaveState() 194 195 with open(state_file, 'r') as f: 196 self.assertEquals(f.read(), 'test123') 197 198 os.remove(state_file) 199 200 # Cleanup generated save state that has no symlink 201 files = os.listdir(os.getcwd()) 202 save_states = [x for x in files if x.startswith(hidden_state_file)] 203 _ = [os.remove(x) for x in save_states] 204 205 def test_save_state(self): 206 state_file = binary_search_state.STATE_FILE 207 208 bss = binary_search_state.MockBinarySearchState() 209 bss.SaveState() 210 self.assertTrue(os.path.exists(state_file)) 211 first_state = os.readlink(state_file) 212 213 bss.SaveState() 214 second_state = os.readlink(state_file) 215 self.assertTrue(os.path.exists(state_file)) 216 self.assertTrue(second_state != first_state) 217 self.assertFalse(os.path.exists(first_state)) 218 219 bss.RemoveState() 220 self.assertFalse(os.path.islink(state_file)) 221 self.assertFalse(os.path.exists(second_state)) 222 223 def test_load_state(self): 224 test_items = [1, 2, 3, 4, 5] 225 226 bss = binary_search_state.MockBinarySearchState() 227 bss.all_items = test_items 228 bss.currently_good_items = set([1, 2, 3]) 229 bss.currently_bad_items = set([4, 5]) 230 bss.SaveState() 231 232 bss = None 233 234 bss2 = binary_search_state.MockBinarySearchState.LoadState() 235 self.assertEquals(bss2.all_items, test_items) 236 self.assertEquals(bss2.currently_good_items, set([])) 237 self.assertEquals(bss2.currently_bad_items, set([])) 238 239 def test_tmp_cleanup(self): 240 bss = binary_search_state.MockBinarySearchState( 241 get_initial_items='echo "0\n1\n2\n3"', 242 switch_to_good='./switch_tmp.py', 243 file_args=True) 244 bss.SwitchToGood(['0', '1', '2', '3']) 245 246 tmp_file = None 247 with open('tmp_file', 'r') as f: 248 tmp_file = f.read() 249 os.remove('tmp_file') 250 251 self.assertFalse(os.path.exists(tmp_file)) 252 ws = common.ReadWorkingSet() 253 for i in range(3): 254 self.assertEquals(ws[i], 42) 255 256 def test_verify_fail(self): 257 bss = binary_search_state.MockBinarySearchState( 258 get_initial_items='./gen_init_list.py', 259 switch_to_good='./switch_to_bad.py', 260 switch_to_bad='./switch_to_good.py', 261 test_script='./is_good.py', 262 prune=True, 263 file_args=True, 264 verify=True) 265 with self.assertRaises(AssertionError): 266 bss.DoVerify() 267 268 def test_early_terminate(self): 269 bss = binary_search_state.MockBinarySearchState( 270 get_initial_items='./gen_init_list.py', 271 switch_to_good='./switch_to_good.py', 272 switch_to_bad='./switch_to_bad.py', 273 test_script='./is_good.py', 274 prune=True, 275 file_args=True, 276 iterations=1) 277 bss.DoSearch() 278 self.assertFalse(bss.found_items) 279 280 def test_no_prune(self): 281 bss = binary_search_state.MockBinarySearchState( 282 get_initial_items='./gen_init_list.py', 283 switch_to_good='./switch_to_good.py', 284 switch_to_bad='./switch_to_bad.py', 285 test_script='./is_good.py', 286 test_setup_script='./test_setup.py', 287 prune=False, 288 file_args=True) 289 bss.DoSearch() 290 self.assertEquals(len(bss.found_items), 1) 291 292 bad_objs = common.ReadObjectsFile() 293 found_obj = int(bss.found_items.pop()) 294 self.assertEquals(bad_objs[found_obj], 1) 295 296 def test_set_file(self): 297 binary_search_state.Run(get_initial_items='./gen_init_list.py', 298 switch_to_good='./switch_to_good_set_file.py', 299 switch_to_bad='./switch_to_bad_set_file.py', 300 test_script='./is_good.py', 301 prune=True, 302 file_args=True, 303 verify=True) 304 self.check_output() 305 306 def test_noincremental_prune(self): 307 ret = binary_search_state.Run( 308 get_initial_items='./gen_init_list.py', 309 switch_to_good='./switch_to_good_noinc_prune.py', 310 switch_to_bad='./switch_to_bad_noinc_prune.py', 311 test_script='./is_good_noinc_prune.py', 312 test_setup_script='./test_setup.py', 313 prune=True, 314 noincremental=True, 315 file_args=True, 316 verify=False) 317 self.assertEquals(ret, 0) 318 self.check_output() 319 320 def check_output(self): 321 _, out, _ = command_executer.GetCommandExecuter().RunCommandWOutput( 322 ('grep "Bad items are: " logs/binary_search_tool_tester.py.out | ' 323 'tail -n1')) 324 ls = out.splitlines() 325 self.assertEqual(len(ls), 1) 326 line = ls[0] 327 328 _, _, bad_ones = line.partition('Bad items are: ') 329 bad_ones = bad_ones.split() 330 expected_result = common.ReadObjectsFile() 331 332 # Reconstruct objects file from bad_ones and compare 333 actual_result = [0] * len(expected_result) 334 for bad_obj in bad_ones: 335 actual_result[int(bad_obj)] = 1 336 337 self.assertEqual(actual_result, expected_result) 338 339 340class BisectStressTest(unittest.TestCase): 341 """Stress tests for bisecting tool.""" 342 343 def test_every_obj_bad(self): 344 amt = 25 345 gen_obj.Main(['--obj_num', str(amt), '--bad_obj_num', str(amt)]) 346 ret = binary_search_state.Run(get_initial_items='./gen_init_list.py', 347 switch_to_good='./switch_to_good.py', 348 switch_to_bad='./switch_to_bad.py', 349 test_script='./is_good.py', 350 prune=True, 351 file_args=True, 352 verify=False) 353 self.assertEquals(ret, 0) 354 self.check_output() 355 356 def test_every_index_is_bad(self): 357 amt = 25 358 for i in range(amt): 359 obj_list = ['0'] * amt 360 obj_list[i] = '1' 361 obj_list = ','.join(obj_list) 362 gen_obj.Main(['--obj_list', obj_list]) 363 ret = binary_search_state.Run(get_initial_items='./gen_init_list.py', 364 switch_to_good='./switch_to_good.py', 365 switch_to_bad='./switch_to_bad.py', 366 test_setup_script='./test_setup.py', 367 test_script='./is_good.py', 368 prune=True, 369 file_args=True) 370 self.assertEquals(ret, 0) 371 self.check_output() 372 373 def check_output(self): 374 _, out, _ = command_executer.GetCommandExecuter().RunCommandWOutput( 375 ('grep "Bad items are: " logs/binary_search_tool_tester.py.out | ' 376 'tail -n1')) 377 ls = out.splitlines() 378 self.assertEqual(len(ls), 1) 379 line = ls[0] 380 381 _, _, bad_ones = line.partition('Bad items are: ') 382 bad_ones = bad_ones.split() 383 expected_result = common.ReadObjectsFile() 384 385 # Reconstruct objects file from bad_ones and compare 386 actual_result = [0] * len(expected_result) 387 for bad_obj in bad_ones: 388 actual_result[int(bad_obj)] = 1 389 390 self.assertEqual(actual_result, expected_result) 391 392 393def Main(argv): 394 num_tests = 2 395 if len(argv) > 1: 396 num_tests = int(argv[1]) 397 398 suite = unittest.TestSuite() 399 for _ in range(0, num_tests): 400 suite.addTest(BisectingUtilsTest()) 401 suite.addTest(BisectingUtilsTest('test_arg_parse')) 402 suite.addTest(BisectingUtilsTest('test_test_setup_script')) 403 suite.addTest(BisectingUtilsTest('test_bad_test_setup_script')) 404 suite.addTest(BisectingUtilsTest('test_bad_save_state')) 405 suite.addTest(BisectingUtilsTest('test_save_state')) 406 suite.addTest(BisectingUtilsTest('test_load_state')) 407 suite.addTest(BisectingUtilsTest('test_tmp_cleanup')) 408 suite.addTest(BisectingUtilsTest('test_verify_fail')) 409 suite.addTest(BisectingUtilsTest('test_early_terminate')) 410 suite.addTest(BisectingUtilsTest('test_no_prune')) 411 suite.addTest(BisectingUtilsTest('test_set_file')) 412 suite.addTest(BisectingUtilsTest('test_noincremental_prune')) 413 suite.addTest(BisectTest('test_full_bisector')) 414 suite.addTest(BisectStressTest('test_every_obj_bad')) 415 suite.addTest(BisectStressTest('test_every_index_is_bad')) 416 runner = unittest.TextTestRunner() 417 runner.run(suite) 418 419 420if __name__ == '__main__': 421 Main(sys.argv) 422