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 |