| OLD | NEW |
| (Empty) |
| 1 #!/usr/bin/env python | |
| 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
| 3 # Use of this source code is governed by a BSD-style license that can be | |
| 4 # found in the LICENSE file. | |
| 5 | |
| 6 import cStringIO | |
| 7 import hashlib | |
| 8 import json | |
| 9 import logging | |
| 10 import os | |
| 11 import re | |
| 12 import shutil | |
| 13 import stat | |
| 14 import subprocess | |
| 15 import sys | |
| 16 import tempfile | |
| 17 import unittest | |
| 18 | |
| 19 ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) | |
| 20 sys.path.insert(0, ROOT_DIR) | |
| 21 | |
| 22 import isolate | |
| 23 import isolateserver | |
| 24 from utils import file_path | |
| 25 | |
| 26 | |
| 27 VERBOSE = False | |
| 28 | |
| 29 ALGO = hashlib.sha1 | |
| 30 HASH_NULL = ALGO().hexdigest() | |
| 31 | |
| 32 | |
| 33 # Keep the list hard coded. | |
| 34 EXPECTED_MODES = ( | |
| 35 'archive', | |
| 36 'check', | |
| 37 'help', | |
| 38 'merge', | |
| 39 'read', | |
| 40 'remap', | |
| 41 'rewrite', | |
| 42 'run', | |
| 43 'trace', | |
| 44 ) | |
| 45 | |
| 46 # These are per test case, not per mode. | |
| 47 RELATIVE_CWD = { | |
| 48 'all_items_invalid': '.', | |
| 49 'fail': '.', | |
| 50 'missing_trailing_slash': '.', | |
| 51 'no_run': '.', | |
| 52 'non_existent': '.', | |
| 53 'split': '.', | |
| 54 'symlink_full': '.', | |
| 55 'symlink_partial': '.', | |
| 56 'symlink_outside_build_root': '.', | |
| 57 'touch_only': '.', | |
| 58 'touch_root': os.path.join('tests', 'isolate'), | |
| 59 'with_flag': '.', | |
| 60 } | |
| 61 | |
| 62 DEPENDENCIES = { | |
| 63 'all_items_invalid' : ['empty.py'], | |
| 64 'fail': ['fail.py'], | |
| 65 'missing_trailing_slash': [], | |
| 66 'no_run': [ | |
| 67 'no_run.isolate', | |
| 68 os.path.join('files1', 'subdir', '42.txt'), | |
| 69 os.path.join('files1', 'test_file1.txt'), | |
| 70 os.path.join('files1', 'test_file2.txt'), | |
| 71 ], | |
| 72 'non_existent': [], | |
| 73 'split': [ | |
| 74 os.path.join('files1', 'subdir', '42.txt'), | |
| 75 os.path.join('test', 'data', 'foo.txt'), | |
| 76 'split.py', | |
| 77 ], | |
| 78 'symlink_full': [ | |
| 79 os.path.join('files1', 'subdir', '42.txt'), | |
| 80 os.path.join('files1', 'test_file1.txt'), | |
| 81 os.path.join('files1', 'test_file2.txt'), | |
| 82 # files2 is a symlink to files1. | |
| 83 'files2', | |
| 84 'symlink_full.py', | |
| 85 ], | |
| 86 'symlink_partial': [ | |
| 87 os.path.join('files1', 'test_file2.txt'), | |
| 88 # files2 is a symlink to files1. | |
| 89 'files2', | |
| 90 'symlink_partial.py', | |
| 91 ], | |
| 92 'symlink_outside_build_root': [ | |
| 93 os.path.join('link_outside_build_root', 'test_file3.txt'), | |
| 94 'symlink_outside_build_root.py', | |
| 95 ], | |
| 96 'touch_only': [ | |
| 97 'touch_only.py', | |
| 98 os.path.join('files1', 'test_file1.txt'), | |
| 99 ], | |
| 100 'touch_root': [ | |
| 101 os.path.join('tests', 'isolate', 'touch_root.py'), | |
| 102 'isolate.py', | |
| 103 ], | |
| 104 'with_flag': [ | |
| 105 'with_flag.py', | |
| 106 os.path.join('files1', 'subdir', '42.txt'), | |
| 107 os.path.join('files1', 'test_file1.txt'), | |
| 108 os.path.join('files1', 'test_file2.txt'), | |
| 109 ], | |
| 110 } | |
| 111 | |
| 112 | |
| 113 class CalledProcessError(subprocess.CalledProcessError): | |
| 114 """Makes 2.6 version act like 2.7""" | |
| 115 def __init__(self, returncode, cmd, output, stderr, cwd): | |
| 116 super(CalledProcessError, self).__init__(returncode, cmd) | |
| 117 self.output = output | |
| 118 self.stderr = stderr | |
| 119 self.cwd = cwd | |
| 120 | |
| 121 def __str__(self): | |
| 122 return super(CalledProcessError, self).__str__() + ( | |
| 123 '\n' | |
| 124 'cwd=%s\n%s\n%s\n%s') % ( | |
| 125 self.cwd, | |
| 126 self.output, | |
| 127 self.stderr, | |
| 128 ' '.join(self.cmd)) | |
| 129 | |
| 130 | |
| 131 def list_files_tree(directory): | |
| 132 """Returns the list of all the files in a tree.""" | |
| 133 actual = [] | |
| 134 for root, dirnames, filenames in os.walk(directory): | |
| 135 actual.extend(os.path.join(root, f)[len(directory)+1:] for f in filenames) | |
| 136 for dirname in dirnames: | |
| 137 full = os.path.join(root, dirname) | |
| 138 # Manually include symlinks. | |
| 139 if os.path.islink(full): | |
| 140 actual.append(full[len(directory)+1:]) | |
| 141 return sorted(actual) | |
| 142 | |
| 143 | |
| 144 class IsolateBase(unittest.TestCase): | |
| 145 def setUp(self): | |
| 146 # The tests assume the current directory is the file's directory. | |
| 147 os.chdir(ROOT_DIR) | |
| 148 self.tempdir = tempfile.mkdtemp() | |
| 149 self.isolated = os.path.join(self.tempdir, 'isolate_smoke_test.isolated') | |
| 150 self.outdir = os.path.join(self.tempdir, 'isolated') | |
| 151 | |
| 152 def tearDown(self): | |
| 153 logging.debug(self.tempdir) | |
| 154 shutil.rmtree(self.tempdir) | |
| 155 | |
| 156 @staticmethod | |
| 157 def _isolate_dict_to_string(values): | |
| 158 buf = cStringIO.StringIO() | |
| 159 isolate.pretty_print(values, buf) | |
| 160 return buf.getvalue() | |
| 161 | |
| 162 @classmethod | |
| 163 def _wrap_in_condition(cls, variables): | |
| 164 """Wraps a variables dict inside the current OS condition. | |
| 165 | |
| 166 Returns the equivalent string. | |
| 167 """ | |
| 168 flavor = isolate.get_flavor() | |
| 169 chromeos_value = int(flavor == 'linux') | |
| 170 return cls._isolate_dict_to_string( | |
| 171 { | |
| 172 'conditions': [ | |
| 173 ['OS=="%s" and chromeos==%d' % (flavor, chromeos_value), { | |
| 174 'variables': variables | |
| 175 }], | |
| 176 ], | |
| 177 }) | |
| 178 | |
| 179 | |
| 180 class IsolateModeBase(IsolateBase): | |
| 181 def _expect_no_tree(self): | |
| 182 self.assertFalse(os.path.exists(self.outdir)) | |
| 183 | |
| 184 def _result_tree(self): | |
| 185 return list_files_tree(self.outdir) | |
| 186 | |
| 187 def _expected_tree(self): | |
| 188 """Verifies the files written in the temporary directory.""" | |
| 189 self.assertEqual(sorted(DEPENDENCIES[self.case()]), self._result_tree()) | |
| 190 | |
| 191 @staticmethod | |
| 192 def _fix_file_mode(filename, read_only): | |
| 193 """4 modes are supported, 0750 (rwx), 0640 (rw), 0550 (rx), 0440 (r).""" | |
| 194 min_mode = 0440 | |
| 195 if not read_only: | |
| 196 min_mode |= 0200 | |
| 197 return (min_mode | 0110) if filename.endswith('.py') else min_mode | |
| 198 | |
| 199 def _gen_files(self, read_only, empty_file, with_time): | |
| 200 """Returns a dict of files like calling isolate.process_input() on each | |
| 201 file. | |
| 202 | |
| 203 Arguments: | |
| 204 - read_only: Mark all the 'm' modes without the writeable bit. | |
| 205 - empty_file: Add a specific empty file (size 0). | |
| 206 - with_time: Include 't' timestamps. For saved state .state files. | |
| 207 """ | |
| 208 root_dir = ROOT_DIR | |
| 209 if RELATIVE_CWD[self.case()] == '.': | |
| 210 root_dir = os.path.join(root_dir, 'tests', 'isolate') | |
| 211 | |
| 212 files = dict((unicode(f), {}) for f in DEPENDENCIES[self.case()]) | |
| 213 | |
| 214 for relfile, v in files.iteritems(): | |
| 215 filepath = os.path.join(root_dir, relfile) | |
| 216 filestats = os.lstat(filepath) | |
| 217 is_link = stat.S_ISLNK(filestats.st_mode) | |
| 218 if not is_link: | |
| 219 v[u's'] = filestats.st_size | |
| 220 if isolate.get_flavor() != 'win': | |
| 221 v[u'm'] = self._fix_file_mode(relfile, read_only) | |
| 222 if with_time: | |
| 223 # Used to skip recalculating the hash. Use the most recent update | |
| 224 # time. | |
| 225 v[u't'] = int(round(filestats.st_mtime)) | |
| 226 if is_link: | |
| 227 v[u'l'] = os.readlink(filepath) # pylint: disable=E1101 | |
| 228 else: | |
| 229 # Upgrade the value to unicode so diffing the structure in case of | |
| 230 # test failure is easier, since the basestring type must match, | |
| 231 # str!=unicode. | |
| 232 v[u'h'] = unicode(isolateserver.hash_file(filepath, ALGO)) | |
| 233 | |
| 234 if empty_file: | |
| 235 item = files[empty_file] | |
| 236 item['h'] = unicode(HASH_NULL) | |
| 237 if sys.platform != 'win32': | |
| 238 item['m'] = 288 | |
| 239 item['s'] = 0 | |
| 240 if with_time: | |
| 241 item['T'] = True | |
| 242 item.pop('t', None) | |
| 243 return files | |
| 244 | |
| 245 def _expected_isolated(self, args, read_only, empty_file): | |
| 246 """Verifies self.isolated contains the expected data.""" | |
| 247 expected = { | |
| 248 u'algo': u'sha-1', | |
| 249 u'files': self._gen_files(read_only, empty_file, False), | |
| 250 u'os': unicode(isolate.get_flavor()), | |
| 251 u'relative_cwd': unicode(RELATIVE_CWD[self.case()]), | |
| 252 u'version': u'1.0', | |
| 253 } | |
| 254 if read_only is not None: | |
| 255 expected[u'read_only'] = read_only | |
| 256 if args: | |
| 257 expected[u'command'] = [u'python'] + [unicode(x) for x in args] | |
| 258 self.assertEqual(expected, json.load(open(self.isolated, 'r'))) | |
| 259 | |
| 260 def _expected_saved_state(self, args, read_only, empty_file, extra_vars): | |
| 261 flavor = isolate.get_flavor() | |
| 262 chromeos_value = int(flavor == 'linux') | |
| 263 expected = { | |
| 264 u'algo': u'sha-1', | |
| 265 u'child_isolated_files': [], | |
| 266 u'command': [], | |
| 267 u'files': self._gen_files(read_only, empty_file, True), | |
| 268 u'isolate_file': isolate.safe_relpath( | |
| 269 file_path.get_native_path_case(unicode(self.filename())), | |
| 270 unicode(os.path.dirname(self.isolated))), | |
| 271 u'relative_cwd': unicode(RELATIVE_CWD[self.case()]), | |
| 272 u'variables': { | |
| 273 u'EXECUTABLE_SUFFIX': u'.exe' if flavor == 'win' else u'', | |
| 274 u'OS': unicode(flavor), | |
| 275 u'chromeos': chromeos_value, | |
| 276 }, | |
| 277 u'version': u'1.0', | |
| 278 } | |
| 279 if args: | |
| 280 expected[u'command'] = [u'python'] + [unicode(x) for x in args] | |
| 281 expected['variables'].update(extra_vars or {}) | |
| 282 self.assertEqual(expected, json.load(open(self.saved_state(), 'r'))) | |
| 283 | |
| 284 def _expect_results(self, args, read_only, extra_vars, empty_file): | |
| 285 self._expected_isolated(args, read_only, empty_file) | |
| 286 self._expected_saved_state(args, read_only, empty_file, extra_vars) | |
| 287 # Also verifies run_isolated.py will be able to read it. | |
| 288 isolate.isolateserver.load_isolated( | |
| 289 open(self.isolated, 'r').read(), | |
| 290 isolate.run_isolated.get_flavor(), ALGO) | |
| 291 | |
| 292 def _expect_no_result(self): | |
| 293 self.assertFalse(os.path.exists(self.isolated)) | |
| 294 | |
| 295 def _execute(self, mode, case, args, need_output, cwd=ROOT_DIR): | |
| 296 """Executes isolate.py.""" | |
| 297 self.assertEqual( | |
| 298 case, | |
| 299 self.case() + '.isolate', | |
| 300 'Rename the test case to test_%s()' % case) | |
| 301 chromeos_value = int(isolate.get_flavor() == 'linux') | |
| 302 cmd = [ | |
| 303 sys.executable, os.path.join(ROOT_DIR, 'isolate.py'), | |
| 304 mode, | |
| 305 '--isolated', self.isolated, | |
| 306 '--outdir', self.outdir, | |
| 307 '--isolate', self.filename(), | |
| 308 '-V', 'chromeos', str(chromeos_value), | |
| 309 ] | |
| 310 cmd.extend(args) | |
| 311 | |
| 312 env = os.environ.copy() | |
| 313 if 'ISOLATE_DEBUG' in env: | |
| 314 del env['ISOLATE_DEBUG'] | |
| 315 | |
| 316 if need_output or not VERBOSE: | |
| 317 stdout = subprocess.PIPE | |
| 318 stderr = subprocess.PIPE | |
| 319 else: | |
| 320 cmd.extend(['-v'] * 3) | |
| 321 stdout = None | |
| 322 stderr = None | |
| 323 | |
| 324 logging.debug(cmd) | |
| 325 p = subprocess.Popen( | |
| 326 cmd, | |
| 327 stdout=stdout, | |
| 328 stderr=stderr, | |
| 329 cwd=cwd, | |
| 330 env=env, | |
| 331 universal_newlines=True) | |
| 332 out, err = p.communicate() | |
| 333 if p.returncode: | |
| 334 raise CalledProcessError(p.returncode, cmd, out, err, cwd) | |
| 335 | |
| 336 # Do not check on Windows since a lot of spew is generated there. | |
| 337 if sys.platform != 'win32': | |
| 338 self.assertTrue(err in (None, ''), err) | |
| 339 return out | |
| 340 | |
| 341 def case(self): | |
| 342 """Returns the filename corresponding to this test case.""" | |
| 343 test_id = self.id().split('.') | |
| 344 return re.match('^test_([a-z_]+)$', test_id[2]).group(1) | |
| 345 | |
| 346 def filename(self): | |
| 347 """Returns the filename corresponding to this test case.""" | |
| 348 filename = os.path.join( | |
| 349 ROOT_DIR, 'tests', 'isolate', self.case() + '.isolate') | |
| 350 self.assertTrue(os.path.isfile(filename), filename) | |
| 351 return filename | |
| 352 | |
| 353 def saved_state(self): | |
| 354 return isolate.isolatedfile_to_state(self.isolated) | |
| 355 | |
| 356 def _test_missing_trailing_slash(self, mode): | |
| 357 try: | |
| 358 self._execute(mode, 'missing_trailing_slash.isolate', [], True) | |
| 359 self.fail() | |
| 360 except subprocess.CalledProcessError as e: | |
| 361 self.assertEqual('', e.output) | |
| 362 out = e.stderr | |
| 363 self._expect_no_tree() | |
| 364 self._expect_no_result() | |
| 365 root = file_path.get_native_path_case(unicode(ROOT_DIR)) | |
| 366 expected = ( | |
| 367 'Input directory %s must have a trailing slash' % | |
| 368 os.path.join(root, 'tests', 'isolate', 'files1') | |
| 369 ) | |
| 370 self.assertIn(expected, out) | |
| 371 | |
| 372 def _test_non_existent(self, mode): | |
| 373 try: | |
| 374 self._execute(mode, 'non_existent.isolate', [], True) | |
| 375 self.fail() | |
| 376 except subprocess.CalledProcessError as e: | |
| 377 self.assertEqual('', e.output) | |
| 378 out = e.stderr | |
| 379 self._expect_no_tree() | |
| 380 self._expect_no_result() | |
| 381 root = file_path.get_native_path_case(unicode(ROOT_DIR)) | |
| 382 expected = ( | |
| 383 'Input file %s doesn\'t exist' % | |
| 384 os.path.join(root, 'tests', 'isolate', 'A_file_that_do_not_exist') | |
| 385 ) | |
| 386 self.assertIn(expected, out) | |
| 387 | |
| 388 def _test_all_items_invalid(self, mode): | |
| 389 out = self._execute(mode, 'all_items_invalid.isolate', | |
| 390 ['--ignore_broken_item'], True) | |
| 391 self._expect_results(['empty.py'], None, None, None) | |
| 392 | |
| 393 return out or '' | |
| 394 | |
| 395 | |
| 396 class Isolate(unittest.TestCase): | |
| 397 # Does not inherit from the other *Base classes. | |
| 398 def test_help_modes(self): | |
| 399 # Check coherency in the help and implemented modes. | |
| 400 p = subprocess.Popen( | |
| 401 [sys.executable, os.path.join(ROOT_DIR, 'isolate.py'), '--help'], | |
| 402 stdout=subprocess.PIPE, | |
| 403 stderr=subprocess.STDOUT, | |
| 404 cwd=ROOT_DIR) | |
| 405 out = p.communicate()[0].splitlines() | |
| 406 self.assertEqual(0, p.returncode) | |
| 407 out = out[out.index('Commands are:') + 1:] | |
| 408 out = out[:out.index('')] | |
| 409 regexp = '^ (?:\x1b\\[\\d\\dm)(\\w+)\s*(:?\x1b\\[\\d\\dm) .+' | |
| 410 modes = [re.match(regexp, l) for l in out] | |
| 411 modes = [m.group(1) for m in modes if m] | |
| 412 self.assertEqual(sorted(EXPECTED_MODES), sorted(modes)) | |
| 413 | |
| 414 def test_modes(self): | |
| 415 # This is a bit redundant but make sure all combinations are tested. | |
| 416 files = sorted( | |
| 417 i[:-len('.isolate')] | |
| 418 for i in os.listdir(os.path.join(ROOT_DIR, 'tests', 'isolate')) | |
| 419 if i.endswith('.isolate') | |
| 420 ) | |
| 421 files.remove('simple') | |
| 422 self.assertEqual(sorted(RELATIVE_CWD), files) | |
| 423 self.assertEqual(sorted(DEPENDENCIES), files) | |
| 424 | |
| 425 if sys.platform == 'win32': | |
| 426 # Symlink stuff is unsupported there, remove them from the list. | |
| 427 files = [f for f in files if not f.startswith('symlink_')] | |
| 428 | |
| 429 files.remove('split') | |
| 430 # TODO(csharp): touch_only is disabled until crbug.com/150823 is fixed. | |
| 431 files.remove('touch_only') | |
| 432 | |
| 433 # modes read and trace are tested together. | |
| 434 modes_to_check = list(EXPECTED_MODES) | |
| 435 # Tested with test_help_modes. | |
| 436 modes_to_check.remove('help') | |
| 437 # Tested with trace_read_merge. | |
| 438 modes_to_check.remove('merge') | |
| 439 # Tested with trace_read_merge. | |
| 440 modes_to_check.remove('read') | |
| 441 # Tested in isolate_test.py. | |
| 442 modes_to_check.remove('rewrite') | |
| 443 # Tested with trace_read_merge. | |
| 444 modes_to_check.remove('trace') | |
| 445 modes_to_check.append('trace_read_merge') | |
| 446 for mode in modes_to_check: | |
| 447 expected_cases = set('test_%s' % f for f in files) | |
| 448 fixture_name = 'Isolate_%s' % mode | |
| 449 fixture = getattr(sys.modules[__name__], fixture_name) | |
| 450 actual_cases = set(i for i in dir(fixture) if i.startswith('test_')) | |
| 451 actual_cases.discard('split') | |
| 452 missing = expected_cases - actual_cases | |
| 453 self.assertFalse(missing, '%s.%s' % (fixture_name, missing)) | |
| 454 | |
| 455 | |
| 456 class Isolate_check(IsolateModeBase): | |
| 457 def test_fail(self): | |
| 458 self._execute('check', 'fail.isolate', [], False) | |
| 459 self._expect_no_tree() | |
| 460 self._expect_results(['fail.py'], None, None, None) | |
| 461 | |
| 462 def test_missing_trailing_slash(self): | |
| 463 self._test_missing_trailing_slash('check') | |
| 464 | |
| 465 def test_non_existent(self): | |
| 466 self._test_non_existent('check') | |
| 467 | |
| 468 def test_all_items_invalid(self): | |
| 469 out = self._test_all_items_invalid('check') | |
| 470 self.assertEqual('', out) | |
| 471 self._expect_no_tree() | |
| 472 | |
| 473 def test_no_run(self): | |
| 474 self._execute('check', 'no_run.isolate', [], False) | |
| 475 self._expect_no_tree() | |
| 476 self._expect_results([], None, None, None) | |
| 477 | |
| 478 # TODO(csharp): Disabled until crbug.com/150823 is fixed. | |
| 479 def do_not_test_touch_only(self): | |
| 480 self._execute('check', 'touch_only.isolate', ['-V', 'FLAG', 'gyp'], False) | |
| 481 self._expect_no_tree() | |
| 482 empty = os.path.join('files1', 'test_file1.txt') | |
| 483 self._expected_isolated(['touch_only.py', 'gyp'], None, empty) | |
| 484 | |
| 485 def test_touch_root(self): | |
| 486 self._execute('check', 'touch_root.isolate', [], False) | |
| 487 self._expect_no_tree() | |
| 488 self._expect_results(['touch_root.py'], None, None, None) | |
| 489 | |
| 490 def test_with_flag(self): | |
| 491 self._execute('check', 'with_flag.isolate', ['-V', 'FLAG', 'gyp'], False) | |
| 492 self._expect_no_tree() | |
| 493 self._expect_results( | |
| 494 ['with_flag.py', 'gyp'], None, {u'FLAG': u'gyp'}, None) | |
| 495 | |
| 496 if sys.platform != 'win32': | |
| 497 def test_symlink_full(self): | |
| 498 self._execute('check', 'symlink_full.isolate', [], False) | |
| 499 self._expect_no_tree() | |
| 500 self._expect_results(['symlink_full.py'], None, None, None) | |
| 501 | |
| 502 def test_symlink_partial(self): | |
| 503 self._execute('check', 'symlink_partial.isolate', [], False) | |
| 504 self._expect_no_tree() | |
| 505 self._expect_results(['symlink_partial.py'], None, None, None) | |
| 506 | |
| 507 def test_symlink_outside_build_root(self): | |
| 508 self._execute('check', 'symlink_outside_build_root.isolate', [], False) | |
| 509 self._expect_no_tree() | |
| 510 self._expect_results(['symlink_outside_build_root.py'], None, None, None) | |
| 511 | |
| 512 | |
| 513 class Isolate_archive(IsolateModeBase): | |
| 514 def _gen_expected_tree(self, empty_file): | |
| 515 expected = [ | |
| 516 unicode(v['h']) | |
| 517 for v in self._gen_files(False, empty_file, False).itervalues() | |
| 518 ] | |
| 519 expected.append( | |
| 520 unicode(isolateserver.hash_file(self.isolated, ALGO))) | |
| 521 return expected | |
| 522 | |
| 523 def _expected_hash_tree(self, empty_file): | |
| 524 """Verifies the files written in the temporary directory.""" | |
| 525 self.assertEqual( | |
| 526 sorted(self._gen_expected_tree(empty_file)), | |
| 527 map(unicode, self._result_tree())) | |
| 528 | |
| 529 def test_fail(self): | |
| 530 self._execute('archive', 'fail.isolate', [], False) | |
| 531 self._expected_hash_tree(None) | |
| 532 self._expect_results(['fail.py'], None, None, None) | |
| 533 | |
| 534 def test_missing_trailing_slash(self): | |
| 535 self._test_missing_trailing_slash('archive') | |
| 536 | |
| 537 def test_non_existent(self): | |
| 538 self._test_non_existent('archive') | |
| 539 | |
| 540 def test_all_items_invalid(self): | |
| 541 out = self._test_all_items_invalid('archive') | |
| 542 expected = ( | |
| 543 '%s isolate_smoke_test.isolated\n' % | |
| 544 isolateserver.hash_file(self.isolated, ALGO)) | |
| 545 self.assertEqual(expected, out) | |
| 546 self._expected_hash_tree(None) | |
| 547 | |
| 548 def test_no_run(self): | |
| 549 self._execute('archive', 'no_run.isolate', [], False) | |
| 550 self._expected_hash_tree(None) | |
| 551 self._expect_results([], None, None, None) | |
| 552 | |
| 553 def test_split(self): | |
| 554 self._execute( | |
| 555 'archive', | |
| 556 'split.isolate', | |
| 557 ['-V', 'DEPTH', '.', '-V', 'PRODUCT_DIR', 'files1'], | |
| 558 False, | |
| 559 cwd=os.path.join(ROOT_DIR, 'tests', 'isolate')) | |
| 560 # Reimplement _expected_hash_tree(): | |
| 561 tree = self._gen_expected_tree(None) | |
| 562 isolated_base = self.isolated[:-len('.isolated')] | |
| 563 isolated_hashes = [ | |
| 564 unicode(isolateserver.hash_file(isolated_base + '.0.isolated', ALGO)), | |
| 565 unicode(isolateserver.hash_file(isolated_base + '.1.isolated', ALGO)), | |
| 566 ] | |
| 567 tree.extend(isolated_hashes) | |
| 568 self.assertEqual(sorted(tree), map(unicode, self._result_tree())) | |
| 569 | |
| 570 # Reimplement _expected_isolated(): | |
| 571 files = self._gen_files(None, None, False) | |
| 572 expected = { | |
| 573 u'algo': u'sha-1', | |
| 574 u'command': [u'python', u'split.py'], | |
| 575 u'files': {u'split.py': files['split.py']}, | |
| 576 u'includes': isolated_hashes, | |
| 577 u'os': unicode(isolate.get_flavor()), | |
| 578 u'relative_cwd': unicode(RELATIVE_CWD[self.case()]), | |
| 579 u'version': u'1.0', | |
| 580 } | |
| 581 self.assertEqual(expected, json.load(open(self.isolated, 'r'))) | |
| 582 | |
| 583 key = os.path.join(u'test', 'data', 'foo.txt') | |
| 584 expected = { | |
| 585 u'algo': u'sha-1', | |
| 586 u'files': {key: files[key]}, | |
| 587 u'os': unicode(isolate.get_flavor()), | |
| 588 u'version': u'1.0', | |
| 589 } | |
| 590 self.assertEqual( | |
| 591 expected, json.load(open(isolated_base + '.0.isolated', 'r'))) | |
| 592 | |
| 593 key = os.path.join(u'files1', 'subdir', '42.txt') | |
| 594 expected = { | |
| 595 u'algo': u'sha-1', | |
| 596 u'files': {key: files[key]}, | |
| 597 u'os': unicode(isolate.get_flavor()), | |
| 598 u'version': u'1.0', | |
| 599 } | |
| 600 self.assertEqual( | |
| 601 expected, json.load(open(isolated_base + '.1.isolated', 'r'))) | |
| 602 | |
| 603 | |
| 604 # TODO(csharp): Disabled until crbug.com/150823 is fixed. | |
| 605 def do_not_test_touch_only(self): | |
| 606 self._execute( | |
| 607 'archive', 'touch_only.isolate', ['-V', 'FLAG', 'gyp'], False) | |
| 608 empty = os.path.join('files1', 'test_file1.txt') | |
| 609 self._expected_hash_tree(empty) | |
| 610 self._expected_isolated(['touch_only.py', 'gyp'], None, empty) | |
| 611 | |
| 612 def test_touch_root(self): | |
| 613 self._execute('archive', 'touch_root.isolate', [], False) | |
| 614 self._expected_hash_tree(None) | |
| 615 self._expect_results(['touch_root.py'], None, None, None) | |
| 616 | |
| 617 def test_with_flag(self): | |
| 618 self._execute( | |
| 619 'archive', 'with_flag.isolate', ['-V', 'FLAG', 'gyp'], False) | |
| 620 self._expected_hash_tree(None) | |
| 621 self._expect_results( | |
| 622 ['with_flag.py', 'gyp'], None, {u'FLAG': u'gyp'}, None) | |
| 623 | |
| 624 if sys.platform != 'win32': | |
| 625 def test_symlink_full(self): | |
| 626 self._execute('archive', 'symlink_full.isolate', [], False) | |
| 627 # Construct our own tree. | |
| 628 expected = [ | |
| 629 str(v['h']) | |
| 630 for v in self._gen_files(False, None, False).itervalues() if 'h' in v | |
| 631 ] | |
| 632 expected.append(isolateserver.hash_file(self.isolated, ALGO)) | |
| 633 self.assertEqual(sorted(expected), self._result_tree()) | |
| 634 self._expect_results(['symlink_full.py'], None, None, None) | |
| 635 | |
| 636 def test_symlink_partial(self): | |
| 637 self._execute('archive', 'symlink_partial.isolate', [], False) | |
| 638 # Construct our own tree. | |
| 639 expected = [ | |
| 640 str(v['h']) | |
| 641 for v in self._gen_files(False, None, False).itervalues() if 'h' in v | |
| 642 ] | |
| 643 expected.append(isolateserver.hash_file(self.isolated, ALGO)) | |
| 644 self.assertEqual(sorted(expected), self._result_tree()) | |
| 645 self._expect_results(['symlink_partial.py'], None, None, None) | |
| 646 | |
| 647 def test_symlink_outside_build_root(self): | |
| 648 self._execute( | |
| 649 'archive', 'symlink_outside_build_root.isolate', [], False) | |
| 650 # Construct our own tree. | |
| 651 expected = [ | |
| 652 str(v['h']) | |
| 653 for v in self._gen_files(False, None, False).itervalues() if 'h' in v | |
| 654 ] | |
| 655 expected.append(isolateserver.hash_file(self.isolated, ALGO)) | |
| 656 self.assertEqual(sorted(expected), self._result_tree()) | |
| 657 self._expect_results(['symlink_outside_build_root.py'], None, None, None) | |
| 658 | |
| 659 | |
| 660 class Isolate_remap(IsolateModeBase): | |
| 661 def test_fail(self): | |
| 662 self._execute('remap', 'fail.isolate', [], False) | |
| 663 self._expected_tree() | |
| 664 self._expect_results(['fail.py'], None, None, None) | |
| 665 | |
| 666 def test_missing_trailing_slash(self): | |
| 667 self._test_missing_trailing_slash('remap') | |
| 668 | |
| 669 def test_non_existent(self): | |
| 670 self._test_non_existent('remap') | |
| 671 | |
| 672 def test_all_items_invalid(self): | |
| 673 out = self._test_all_items_invalid('remap') | |
| 674 self.assertTrue(out.startswith('Remapping')) | |
| 675 self._expected_tree() | |
| 676 | |
| 677 def test_no_run(self): | |
| 678 self._execute('remap', 'no_run.isolate', [], False) | |
| 679 self._expected_tree() | |
| 680 self._expect_results([], None, None, None) | |
| 681 | |
| 682 # TODO(csharp): Disabled until crbug.com/150823 is fixed. | |
| 683 def do_not_test_touch_only(self): | |
| 684 self._execute('remap', 'touch_only.isolate', ['-V', 'FLAG', 'gyp'], False) | |
| 685 self._expected_tree() | |
| 686 empty = os.path.join('files1', 'test_file1.txt') | |
| 687 self._expect_results( | |
| 688 ['touch_only.py', 'gyp'], None, {u'FLAG': u'gyp'}, empty) | |
| 689 | |
| 690 def test_touch_root(self): | |
| 691 self._execute('remap', 'touch_root.isolate', [], False) | |
| 692 self._expected_tree() | |
| 693 self._expect_results(['touch_root.py'], None, None, None) | |
| 694 | |
| 695 def test_with_flag(self): | |
| 696 self._execute('remap', 'with_flag.isolate', ['-V', 'FLAG', 'gyp'], False) | |
| 697 self._expected_tree() | |
| 698 self._expect_results( | |
| 699 ['with_flag.py', 'gyp'], None, {u'FLAG': u'gyp'}, None) | |
| 700 | |
| 701 if sys.platform != 'win32': | |
| 702 def test_symlink_full(self): | |
| 703 self._execute('remap', 'symlink_full.isolate', [], False) | |
| 704 self._expected_tree() | |
| 705 self._expect_results(['symlink_full.py'], None, None, None) | |
| 706 | |
| 707 def test_symlink_partial(self): | |
| 708 self._execute('remap', 'symlink_partial.isolate', [], False) | |
| 709 self._expected_tree() | |
| 710 self._expect_results(['symlink_partial.py'], None, None, None) | |
| 711 | |
| 712 def test_symlink_outside_build_root(self): | |
| 713 self._execute('remap', 'symlink_outside_build_root.isolate', [], False) | |
| 714 self._expected_tree() | |
| 715 self._expect_results(['symlink_outside_build_root.py'], None, None, None) | |
| 716 | |
| 717 | |
| 718 class Isolate_run(IsolateModeBase): | |
| 719 def _expect_empty_tree(self): | |
| 720 self.assertEqual([], self._result_tree()) | |
| 721 | |
| 722 def test_fail(self): | |
| 723 try: | |
| 724 self._execute('run', 'fail.isolate', [], False) | |
| 725 self.fail() | |
| 726 except subprocess.CalledProcessError: | |
| 727 pass | |
| 728 self._expect_empty_tree() | |
| 729 self._expect_results(['fail.py'], None, None, None) | |
| 730 | |
| 731 def test_missing_trailing_slash(self): | |
| 732 self._test_missing_trailing_slash('run') | |
| 733 | |
| 734 def test_non_existent(self): | |
| 735 self._test_non_existent('run') | |
| 736 | |
| 737 def test_all_items_invalid(self): | |
| 738 out = self._test_all_items_invalid('run') | |
| 739 self.assertEqual('', out) | |
| 740 self._expect_no_tree() | |
| 741 | |
| 742 def test_no_run(self): | |
| 743 try: | |
| 744 self._execute('run', 'no_run.isolate', [], False) | |
| 745 self.fail() | |
| 746 except subprocess.CalledProcessError: | |
| 747 pass | |
| 748 self._expect_empty_tree() | |
| 749 self._expect_no_result() | |
| 750 | |
| 751 # TODO(csharp): Disabled until crbug.com/150823 is fixed. | |
| 752 def do_not_test_touch_only(self): | |
| 753 self._execute('run', 'touch_only.isolate', ['-V', 'FLAG', 'run'], False) | |
| 754 self._expect_empty_tree() | |
| 755 empty = os.path.join('files1', 'test_file1.txt') | |
| 756 self._expect_results( | |
| 757 ['touch_only.py', 'run'], None, {u'FLAG': u'run'}, empty) | |
| 758 | |
| 759 def test_touch_root(self): | |
| 760 self._execute('run', 'touch_root.isolate', [], False) | |
| 761 self._expect_empty_tree() | |
| 762 self._expect_results(['touch_root.py'], None, None, None) | |
| 763 | |
| 764 def test_with_flag(self): | |
| 765 self._execute('run', 'with_flag.isolate', ['-V', 'FLAG', 'run'], False) | |
| 766 # Not sure about the empty tree, should be deleted. | |
| 767 self._expect_empty_tree() | |
| 768 self._expect_results( | |
| 769 ['with_flag.py', 'run'], None, {u'FLAG': u'run'}, None) | |
| 770 | |
| 771 if sys.platform != 'win32': | |
| 772 def test_symlink_full(self): | |
| 773 self._execute('run', 'symlink_full.isolate', [], False) | |
| 774 self._expect_empty_tree() | |
| 775 self._expect_results(['symlink_full.py'], None, None, None) | |
| 776 | |
| 777 def test_symlink_partial(self): | |
| 778 self._execute('run', 'symlink_partial.isolate', [], False) | |
| 779 self._expect_empty_tree() | |
| 780 self._expect_results(['symlink_partial.py'], None, None, None) | |
| 781 | |
| 782 def test_symlink_outside_build_root(self): | |
| 783 self._execute('run', 'symlink_outside_build_root.isolate', [], False) | |
| 784 self._expect_empty_tree() | |
| 785 self._expect_results(['symlink_outside_build_root.py'], None, None, None) | |
| 786 | |
| 787 | |
| 788 class Isolate_trace_read_merge(IsolateModeBase): | |
| 789 # Tests both trace, read and merge. | |
| 790 # Warning: merge updates .isolate files. But they are currently in their | |
| 791 # canonical format so they shouldn't be changed. | |
| 792 def _check_merge(self, filename): | |
| 793 filepath = file_path.get_native_path_case( | |
| 794 os.path.join(unicode(ROOT_DIR), 'tests', 'isolate', filename)) | |
| 795 expected = 'Updating %s\n' % isolate.safe_relpath(filepath, self.tempdir) | |
| 796 with open(filepath, 'rb') as f: | |
| 797 old_content = f.read() | |
| 798 out = self._execute('merge', filename, [], True) or '' | |
| 799 self.assertEqual(expected, out) | |
| 800 with open(filepath, 'rb') as f: | |
| 801 new_content = f.read() | |
| 802 self.assertEqual(old_content, new_content) | |
| 803 | |
| 804 def test_fail(self): | |
| 805 # Even if the process returns non-zero, the trace will still be good. | |
| 806 try: | |
| 807 self._execute('trace', 'fail.isolate', ['-v'], True) | |
| 808 self.fail() | |
| 809 except subprocess.CalledProcessError, e: | |
| 810 self.assertEqual('', e.output) | |
| 811 self._expect_no_tree() | |
| 812 self._expect_results(['fail.py'], None, None, None) | |
| 813 expected = self._wrap_in_condition( | |
| 814 { | |
| 815 isolate.KEY_TRACKED: [ | |
| 816 'fail.py', | |
| 817 ], | |
| 818 }) | |
| 819 out = self._execute('read', 'fail.isolate', [], True) or '' | |
| 820 self.assertEqual(expected.splitlines(), out.splitlines()) | |
| 821 self._check_merge('fail.isolate') | |
| 822 | |
| 823 def test_missing_trailing_slash(self): | |
| 824 self._test_missing_trailing_slash('trace') | |
| 825 | |
| 826 def test_non_existent(self): | |
| 827 self._test_non_existent('trace') | |
| 828 | |
| 829 def test_all_items_invalid(self): | |
| 830 out = self._test_all_items_invalid('trace') | |
| 831 self.assertEqual('', out) | |
| 832 self._expect_no_tree() | |
| 833 | |
| 834 def test_no_run(self): | |
| 835 try: | |
| 836 self._execute('trace', 'no_run.isolate', [], True) | |
| 837 self.fail() | |
| 838 except subprocess.CalledProcessError, e: | |
| 839 out = e.output | |
| 840 err = e.stderr | |
| 841 self._expect_no_tree() | |
| 842 self._expect_no_result() | |
| 843 expected = 'No command to run.' | |
| 844 self.assertEqual('', out) | |
| 845 self.assertIn(expected, err) | |
| 846 | |
| 847 # TODO(csharp): Disabled until crbug.com/150823 is fixed. | |
| 848 def do_not_test_touch_only(self): | |
| 849 out = self._execute( | |
| 850 'trace', 'touch_only.isolate', ['-V', 'FLAG', 'trace'], True) | |
| 851 self.assertEqual('', out) | |
| 852 self._expect_no_tree() | |
| 853 empty = os.path.join('files1', 'test_file1.txt') | |
| 854 self._expect_results( | |
| 855 ['touch_only.py', 'trace'], None, {u'FLAG': u'trace'}, empty) | |
| 856 expected = { | |
| 857 isolate.KEY_TRACKED: [ | |
| 858 'touch_only.py', | |
| 859 ], | |
| 860 isolate.KEY_TOUCHED: [ | |
| 861 # Note that .isolate format mandates / and not os.path.sep. | |
| 862 'files1/test_file1.txt', | |
| 863 ], | |
| 864 } | |
| 865 if sys.platform != 'linux2': | |
| 866 # TODO(maruel): Implement touch-only tracing on non-linux. | |
| 867 del expected[isolate.KEY_TOUCHED] | |
| 868 | |
| 869 out = self._execute('read', 'touch_only.isolate', [], True) | |
| 870 self.assertEqual(self._wrap_in_condition(expected), out) | |
| 871 self._check_merge('touch_only.isolate') | |
| 872 | |
| 873 def test_touch_root(self): | |
| 874 out = self._execute('trace', 'touch_root.isolate', [], True) | |
| 875 self.assertEqual('', out) | |
| 876 self._expect_no_tree() | |
| 877 self._expect_results(['touch_root.py'], None, None, None) | |
| 878 expected = self._wrap_in_condition( | |
| 879 { | |
| 880 isolate.KEY_TRACKED: [ | |
| 881 '../../isolate.py', | |
| 882 'touch_root.py', | |
| 883 ], | |
| 884 }) | |
| 885 out = self._execute('read', 'touch_root.isolate', [], True) | |
| 886 self.assertEqual(expected, out) | |
| 887 self._check_merge('touch_root.isolate') | |
| 888 | |
| 889 def test_with_flag(self): | |
| 890 out = self._execute( | |
| 891 'trace', 'with_flag.isolate', ['-V', 'FLAG', 'trace'], True) | |
| 892 self.assertEqual('', out) | |
| 893 self._expect_no_tree() | |
| 894 self._expect_results( | |
| 895 ['with_flag.py', 'trace'], None, {u'FLAG': u'trace'}, None) | |
| 896 expected = { | |
| 897 isolate.KEY_TRACKED: [ | |
| 898 'with_flag.py', | |
| 899 ], | |
| 900 isolate.KEY_UNTRACKED: [ | |
| 901 # Note that .isolate format mandates / and not os.path.sep. | |
| 902 'files1/', | |
| 903 ], | |
| 904 } | |
| 905 out = self._execute('read', 'with_flag.isolate', [], True) | |
| 906 self.assertEqual(self._wrap_in_condition(expected), out) | |
| 907 self._check_merge('with_flag.isolate') | |
| 908 | |
| 909 if sys.platform != 'win32': | |
| 910 def test_symlink_full(self): | |
| 911 out = self._execute( | |
| 912 'trace', 'symlink_full.isolate', [], True) | |
| 913 self.assertEqual('', out) | |
| 914 self._expect_no_tree() | |
| 915 self._expect_results(['symlink_full.py'], None, None, None) | |
| 916 expected = { | |
| 917 isolate.KEY_TRACKED: [ | |
| 918 'symlink_full.py', | |
| 919 ], | |
| 920 isolate.KEY_UNTRACKED: [ | |
| 921 # Note that .isolate format mandates / and not os.path.sep. | |
| 922 'files2/', | |
| 923 ], | |
| 924 } | |
| 925 out = self._execute('read', 'symlink_full.isolate', [], True) | |
| 926 self.assertEqual(self._wrap_in_condition(expected), out) | |
| 927 self._check_merge('symlink_full.isolate') | |
| 928 | |
| 929 def test_symlink_partial(self): | |
| 930 out = self._execute( | |
| 931 'trace', 'symlink_partial.isolate', [], True) | |
| 932 self.assertEqual('', out) | |
| 933 self._expect_no_tree() | |
| 934 self._expect_results(['symlink_partial.py'], None, None, None) | |
| 935 expected = { | |
| 936 isolate.KEY_TRACKED: [ | |
| 937 'symlink_partial.py', | |
| 938 ], | |
| 939 isolate.KEY_UNTRACKED: [ | |
| 940 'files2/test_file2.txt', | |
| 941 ], | |
| 942 } | |
| 943 out = self._execute('read', 'symlink_partial.isolate', [], True) | |
| 944 self.assertEqual(self._wrap_in_condition(expected), out) | |
| 945 self._check_merge('symlink_partial.isolate') | |
| 946 | |
| 947 def test_symlink_outside_build_root(self): | |
| 948 out = self._execute( | |
| 949 'trace', 'symlink_outside_build_root.isolate', [], True) | |
| 950 self.assertEqual('', out) | |
| 951 self._expect_no_tree() | |
| 952 self._expect_results(['symlink_outside_build_root.py'], None, None, None) | |
| 953 expected = { | |
| 954 isolate.KEY_TRACKED: [ | |
| 955 'symlink_outside_build_root.py', | |
| 956 ], | |
| 957 isolate.KEY_UNTRACKED: [ | |
| 958 'link_outside_build_root/', | |
| 959 ], | |
| 960 } | |
| 961 out = self._execute( | |
| 962 'read', 'symlink_outside_build_root.isolate', [], True) | |
| 963 self.assertEqual(self._wrap_in_condition(expected), out) | |
| 964 self._check_merge('symlink_outside_build_root.isolate') | |
| 965 | |
| 966 | |
| 967 class IsolateNoOutdir(IsolateBase): | |
| 968 # Test without the --outdir flag. | |
| 969 # So all the files are first copied in the tempdir and the test is run from | |
| 970 # there. | |
| 971 def setUp(self): | |
| 972 super(IsolateNoOutdir, self).setUp() | |
| 973 self.root = os.path.join(self.tempdir, 'root') | |
| 974 os.makedirs(os.path.join(self.root, 'tests', 'isolate')) | |
| 975 for i in ('touch_root.isolate', 'touch_root.py'): | |
| 976 shutil.copy( | |
| 977 os.path.join(ROOT_DIR, 'tests', 'isolate', i), | |
| 978 os.path.join(self.root, 'tests', 'isolate', i)) | |
| 979 shutil.copy( | |
| 980 os.path.join(ROOT_DIR, 'isolate.py'), | |
| 981 os.path.join(self.root, 'isolate.py')) | |
| 982 | |
| 983 def _execute(self, mode, args, need_output): | |
| 984 """Executes isolate.py.""" | |
| 985 chromeos_value = int(isolate.get_flavor() == 'linux') | |
| 986 cmd = [ | |
| 987 sys.executable, os.path.join(ROOT_DIR, 'isolate.py'), | |
| 988 mode, | |
| 989 '--isolated', self.isolated, | |
| 990 '-V', 'chromeos', str(chromeos_value), | |
| 991 ] | |
| 992 cmd.extend(args) | |
| 993 | |
| 994 env = os.environ.copy() | |
| 995 if 'ISOLATE_DEBUG' in env: | |
| 996 del env['ISOLATE_DEBUG'] | |
| 997 | |
| 998 if need_output or not VERBOSE: | |
| 999 stdout = subprocess.PIPE | |
| 1000 stderr = subprocess.STDOUT | |
| 1001 else: | |
| 1002 cmd.extend(['-v'] * 3) | |
| 1003 stdout = None | |
| 1004 stderr = None | |
| 1005 | |
| 1006 logging.debug(cmd) | |
| 1007 cwd = self.tempdir | |
| 1008 p = subprocess.Popen( | |
| 1009 cmd, | |
| 1010 stdout=stdout, | |
| 1011 stderr=stderr, | |
| 1012 cwd=cwd, | |
| 1013 env=env, | |
| 1014 universal_newlines=True) | |
| 1015 out, err = p.communicate() | |
| 1016 if p.returncode: | |
| 1017 raise CalledProcessError(p.returncode, cmd, out, err, cwd) | |
| 1018 return out | |
| 1019 | |
| 1020 def mode(self): | |
| 1021 """Returns the execution mode corresponding to this test case.""" | |
| 1022 test_id = self.id().split('.') | |
| 1023 self.assertEqual(3, len(test_id)) | |
| 1024 self.assertEqual('__main__', test_id[0]) | |
| 1025 return re.match('^test_([a-z]+)$', test_id[2]).group(1) | |
| 1026 | |
| 1027 def filename(self): | |
| 1028 """Returns the filename corresponding to this test case.""" | |
| 1029 filename = os.path.join(self.root, 'tests', 'isolate', 'touch_root.isolate') | |
| 1030 self.assertTrue(os.path.isfile(filename), filename) | |
| 1031 return filename | |
| 1032 | |
| 1033 def test_check(self): | |
| 1034 self._execute('check', ['--isolate', self.filename()], False) | |
| 1035 files = sorted([ | |
| 1036 'isolate_smoke_test.isolated', | |
| 1037 'isolate_smoke_test.isolated.state', | |
| 1038 os.path.join('root', 'tests', 'isolate', 'touch_root.isolate'), | |
| 1039 os.path.join('root', 'tests', 'isolate', 'touch_root.py'), | |
| 1040 os.path.join('root', 'isolate.py'), | |
| 1041 ]) | |
| 1042 self.assertEqual(files, list_files_tree(self.tempdir)) | |
| 1043 | |
| 1044 def test_archive(self): | |
| 1045 self._execute('archive', ['--isolate', self.filename()], False) | |
| 1046 files = sorted([ | |
| 1047 os.path.join( | |
| 1048 'hashtable', | |
| 1049 isolateserver.hash_file(os.path.join(ROOT_DIR, 'isolate.py'), ALGO)), | |
| 1050 os.path.join( | |
| 1051 'hashtable', | |
| 1052 isolateserver.hash_file( | |
| 1053 os.path.join(ROOT_DIR, 'tests', 'isolate', 'touch_root.py'), | |
| 1054 ALGO)), | |
| 1055 os.path.join( | |
| 1056 'hashtable', | |
| 1057 isolateserver.hash_file(os.path.join(self.isolated), ALGO)), | |
| 1058 'isolate_smoke_test.isolated', | |
| 1059 'isolate_smoke_test.isolated.state', | |
| 1060 os.path.join('root', 'tests', 'isolate', 'touch_root.isolate'), | |
| 1061 os.path.join('root', 'tests', 'isolate', 'touch_root.py'), | |
| 1062 os.path.join('root', 'isolate.py'), | |
| 1063 ]) | |
| 1064 self.assertEqual(files, list_files_tree(self.tempdir)) | |
| 1065 | |
| 1066 def test_remap(self): | |
| 1067 self._execute('remap', ['--isolate', self.filename()], False) | |
| 1068 files = sorted([ | |
| 1069 'isolate_smoke_test.isolated', | |
| 1070 'isolate_smoke_test.isolated.state', | |
| 1071 os.path.join('root', 'tests', 'isolate', 'touch_root.isolate'), | |
| 1072 os.path.join('root', 'tests', 'isolate', 'touch_root.py'), | |
| 1073 os.path.join('root', 'isolate.py'), | |
| 1074 ]) | |
| 1075 self.assertEqual(files, list_files_tree(self.tempdir)) | |
| 1076 | |
| 1077 def test_run(self): | |
| 1078 self._execute('run', ['--isolate', self.filename()], False) | |
| 1079 files = sorted([ | |
| 1080 'isolate_smoke_test.isolated', | |
| 1081 'isolate_smoke_test.isolated.state', | |
| 1082 os.path.join('root', 'tests', 'isolate', 'touch_root.isolate'), | |
| 1083 os.path.join('root', 'tests', 'isolate', 'touch_root.py'), | |
| 1084 os.path.join('root', 'isolate.py'), | |
| 1085 ]) | |
| 1086 self.assertEqual(files, list_files_tree(self.tempdir)) | |
| 1087 | |
| 1088 def test_trace_read_merge(self): | |
| 1089 self._execute('trace', ['--isolate', self.filename()], False) | |
| 1090 # Read the trace before cleaning up. No need to specify self.filename() | |
| 1091 # because add the needed information is in the .state file. | |
| 1092 output = self._execute('read', [], True) | |
| 1093 expected = { | |
| 1094 isolate.KEY_TRACKED: [ | |
| 1095 '../../isolate.py', | |
| 1096 'touch_root.py', | |
| 1097 ], | |
| 1098 } | |
| 1099 self.assertEqual(self._wrap_in_condition(expected), output) | |
| 1100 | |
| 1101 output = self._execute('merge', [], True) | |
| 1102 expected = 'Updating %s\n' % os.path.join( | |
| 1103 'root', 'tests', 'isolate', 'touch_root.isolate') | |
| 1104 self.assertEqual(expected, output) | |
| 1105 # In theory the file is going to be updated but in practice its content | |
| 1106 # won't change. | |
| 1107 | |
| 1108 # Clean the directory from the logs, which are OS-specific. | |
| 1109 isolate.trace_inputs.get_api().clean_trace( | |
| 1110 os.path.join(self.tempdir, 'isolate_smoke_test.isolated.log')) | |
| 1111 files = sorted([ | |
| 1112 'isolate_smoke_test.isolated', | |
| 1113 'isolate_smoke_test.isolated.state', | |
| 1114 os.path.join('root', 'tests', 'isolate', 'touch_root.isolate'), | |
| 1115 os.path.join('root', 'tests', 'isolate', 'touch_root.py'), | |
| 1116 os.path.join('root', 'isolate.py'), | |
| 1117 ]) | |
| 1118 self.assertEqual(files, list_files_tree(self.tempdir)) | |
| 1119 | |
| 1120 | |
| 1121 class IsolateOther(IsolateBase): | |
| 1122 def test_run_mixed(self): | |
| 1123 # Test when a user mapped from a directory and then replay from another | |
| 1124 # directory. This is a very rare corner case. | |
| 1125 indir = os.path.join(self.tempdir, 'input') | |
| 1126 os.mkdir(indir) | |
| 1127 for i in ('simple.py', 'simple.isolate'): | |
| 1128 shutil.copy( | |
| 1129 os.path.join(ROOT_DIR, 'tests', 'isolate', i), | |
| 1130 os.path.join(indir, i)) | |
| 1131 proc = subprocess.Popen( | |
| 1132 [ | |
| 1133 sys.executable, 'isolate.py', | |
| 1134 'check', | |
| 1135 '-i', os.path.join(indir, 'simple.isolate'), | |
| 1136 '-s', os.path.join(indir, 'simple.isolated'), | |
| 1137 ], | |
| 1138 stdout=subprocess.PIPE, | |
| 1139 stderr=subprocess.STDOUT, | |
| 1140 cwd=ROOT_DIR) | |
| 1141 stdout = proc.communicate()[0] | |
| 1142 self.assertEqual('', stdout) | |
| 1143 self.assertEqual(0, proc.returncode) | |
| 1144 expected = [ | |
| 1145 'simple.isolate', 'simple.isolated', 'simple.isolated.state', 'simple.py', | |
| 1146 ] | |
| 1147 self.assertEqual(expected, sorted(os.listdir(indir))) | |
| 1148 | |
| 1149 # Remove the original directory. | |
| 1150 indir2 = indir + '2' | |
| 1151 os.rename(indir, indir2) | |
| 1152 | |
| 1153 # simple.isolated.state is required; it contains the variables. | |
| 1154 # This should still work. | |
| 1155 proc = subprocess.Popen( | |
| 1156 [ | |
| 1157 sys.executable, 'isolate.py', 'run', | |
| 1158 '-s', os.path.join(indir2, 'simple.isolated'), | |
| 1159 '--skip-refresh', | |
| 1160 ], | |
| 1161 stdout=subprocess.PIPE, | |
| 1162 stderr=subprocess.STDOUT, | |
| 1163 cwd=ROOT_DIR, | |
| 1164 universal_newlines=True) | |
| 1165 stdout = proc.communicate()[0] | |
| 1166 self.assertEqual('Simply works.\n', stdout) | |
| 1167 self.assertEqual(0, proc.returncode) | |
| 1168 | |
| 1169 | |
| 1170 if __name__ == '__main__': | |
| 1171 VERBOSE = '-v' in sys.argv | |
| 1172 logging.basicConfig(level=logging.DEBUG if VERBOSE else logging.ERROR) | |
| 1173 if VERBOSE: | |
| 1174 unittest.TestCase.maxDiff = None | |
| 1175 unittest.main() | |
| OLD | NEW |