2 # Copyright 2012 The Swarming Authors. All rights reserved.
3 # Use of this source code is governed under the Apache License, Version 2.0 that
4 # can be found in the LICENSE file.
16 ROOT_DIR = unicode(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
17 sys.path.insert(0, ROOT_DIR)
18 sys.path.insert(0, os.path.join(ROOT_DIR, 'third_party'))
20 from depot_tools import auto_stub
22 from utils import file_path
23 from utils import tools
30 return os.stat(os.path.join(ROOT_DIR, *args)).st_size
34 return isolate.isolateserver.hash_file(os.path.join(ROOT_DIR, *args), ALGO)
37 class IsolateBase(auto_stub.TestCase):
39 super(IsolateBase, self).setUp()
40 self.mock(isolate.auth, 'ensure_logged_in', lambda _: None)
41 self.old_cwd = os.getcwd()
42 self.cwd = tempfile.mkdtemp(prefix='isolate_')
43 # Everything should work even from another directory.
48 os.chdir(self.old_cwd)
49 isolate.run_isolated.rmtree(self.cwd)
51 super(IsolateBase, self).tearDown()
54 class IsolateTest(IsolateBase):
55 def test_savedstate_load_minimal(self):
56 # The file referenced by 'isolate_file' must exist even if its content is
58 open(os.path.join(self.cwd, 'fake.isolate'), 'wb').close()
62 'isolate_file': 'fake.isolate',
67 'child_isolated_files': [],
68 'config_variables': {},
70 'extra_variables': {},
72 'isolate_file': 'fake.isolate',
74 'version': isolate.SavedState.EXPECTED_VERSION,
76 saved_state = isolate.SavedState.load(values, self.cwd)
77 self.assertEqual(expected, saved_state.flatten())
79 def test_savedstate_load(self):
80 # The file referenced by 'isolate_file' must exist even if its content is
82 open(os.path.join(self.cwd, 'fake.isolate'), 'wb').close()
86 'config_variables': {},
90 'isolate_file': 'fake.isolate',
95 'child_isolated_files': [],
97 'config_variables': {},
102 'isolate_file': 'fake.isolate',
103 'path_variables': {},
104 'version': isolate.SavedState.EXPECTED_VERSION,
106 saved_state = isolate.SavedState.load(values, self.cwd)
107 self.assertEqual(expected, saved_state.flatten())
109 def test_variable_arg(self):
110 parser = isolate.OptionParserIsolate()
111 parser.require_isolated = False
120 'EXECUTABLE_SUFFIX': '.exe' if sys.platform == 'win32' else '',
123 options, args = parser.parse_args(
124 ['--config-variable', 'Foo', 'bar',
125 '--path-variable', 'Baz=sub=string',
126 '--extra-variable', 'biz', 'b uz=a'])
127 self.assertEqual(expected_path, options.path_variables)
128 self.assertEqual(expected_config, options.config_variables)
129 self.assertEqual(expected_extra, options.extra_variables)
130 self.assertEqual([], args)
132 def test_variable_arg_fail(self):
133 parser = isolate.OptionParserIsolate()
134 self.mock(sys, 'stderr', cStringIO.StringIO())
135 with self.assertRaises(SystemExit):
136 parser.parse_args(['--config-variable', 'Foo'])
138 def test_blacklist(self):
148 os.path.join('foo', '.git'),
152 blacklist = tools.gen_blacklist(isolate.isolateserver.DEFAULT_BLACKLIST)
154 self.assertFalse(blacklist(i), i)
156 self.assertTrue(blacklist(i), i)
158 def test_blacklist_chromium(self):
164 'foo.run_test_cases',
166 os.path.join('foo', 'testserver.log'),
168 blacklist = tools.gen_blacklist(isolate.isolateserver.DEFAULT_BLACKLIST)
170 self.assertFalse(blacklist(i), i)
172 self.assertTrue(blacklist(i), i)
175 class IsolateLoad(IsolateBase):
177 super(IsolateLoad, self).setUp()
178 self.directory = tempfile.mkdtemp(prefix='isolate_')
182 isolate.run_isolated.rmtree(self.directory)
184 super(IsolateLoad, self).tearDown()
186 def _get_option(self, isolate_file):
187 class Options(object):
188 isolated = os.path.join(self.directory, 'foo.isolated')
189 outdir = os.path.join(self.directory, 'outdir')
190 isolate = isolate_file
196 extra_variables = {'foo': 'bar'}
197 ignore_broken_items = False
200 def _cleanup_isolated(self, expected_isolated):
201 """Modifies isolated to remove the non-deterministic parts."""
202 if sys.platform == 'win32':
203 # 'm' are not saved in windows.
204 for values in expected_isolated['files'].itervalues():
205 self.assertTrue(values.pop('m'))
207 def _cleanup_saved_state(self, actual_saved_state):
208 for item in actual_saved_state['files'].itervalues():
209 self.assertTrue(item.pop('t'))
211 def test_load_stale_isolated(self):
212 isolate_file = os.path.join(
213 ROOT_DIR, 'tests', 'isolate', 'touch_root.isolate')
215 # Data to be loaded in the .isolated file. Do not create a .state file.
217 'command': ['python'],
225 os.path.join('tests', 'isolate', 'touch_root.py'): {
233 options = self._get_option(isolate_file)
234 tools.write_json(options.isolated, input_data, False)
236 # A CompleteState object contains two parts:
237 # - Result instance stored in complete_state.isolated, corresponding to the
238 # .isolated file, is what is read by run_test_from_archive.py.
239 # - SavedState instance stored in compelte_state.saved_state,
240 # corresponding to the .state file, which is simply to aid the developer
241 # when re-running the same command multiple times and contain
242 # discardable information.
243 complete_state = isolate.load_complete_state(options, self.cwd, None, False)
244 actual_isolated = complete_state.saved_state.to_isolated()
245 actual_saved_state = complete_state.saved_state.flatten()
247 expected_isolated = {
249 'command': ['python', 'touch_root.py'],
251 os.path.join(u'tests', 'isolate', 'touch_root.py'): {
253 'h': hash_file('tests', 'isolate', 'touch_root.py'),
254 's': _size('tests', 'isolate', 'touch_root.py'),
258 'h': hash_file('isolate.py'),
259 's': _size('isolate.py'),
262 'relative_cwd': os.path.join(u'tests', 'isolate'),
263 'version': isolate.isolateserver.ISOLATED_FILE_VERSION,
265 self._cleanup_isolated(expected_isolated)
266 self.assertEqual(expected_isolated, actual_isolated)
268 expected_saved_state = {
271 'child_isolated_files': [],
272 'command': ['python', 'touch_root.py'],
273 'config_variables': {
275 'chromeos': options.config_variables['chromeos'],
281 os.path.join(u'tests', 'isolate', 'touch_root.py'): {
283 'h': hash_file('tests', 'isolate', 'touch_root.py'),
284 's': _size('tests', 'isolate', 'touch_root.py'),
288 'h': hash_file('isolate.py'),
289 's': _size('isolate.py'),
292 'isolate_file': file_path.safe_relpath(
293 file_path.get_native_path_case(isolate_file),
294 os.path.dirname(options.isolated)),
295 'path_variables': {},
296 'relative_cwd': os.path.join(u'tests', 'isolate'),
297 'root_dir': file_path.get_native_path_case(ROOT_DIR),
298 'version': isolate.SavedState.EXPECTED_VERSION,
300 self._cleanup_isolated(expected_saved_state)
301 self._cleanup_saved_state(actual_saved_state)
302 self.assertEqual(expected_saved_state, actual_saved_state)
304 def test_subdir(self):
305 # The resulting .isolated file will be missing ../../isolate.py. It is
306 # because this file is outside the --subdir parameter.
307 isolate_file = os.path.join(
308 ROOT_DIR, 'tests', 'isolate', 'touch_root.isolate')
309 options = self._get_option(isolate_file)
310 complete_state = isolate.load_complete_state(
311 options, self.cwd, os.path.join('tests', 'isolate'), False)
312 actual_isolated = complete_state.saved_state.to_isolated()
313 actual_saved_state = complete_state.saved_state.flatten()
315 expected_isolated = {
317 'command': ['python', 'touch_root.py'],
319 os.path.join(u'tests', 'isolate', 'touch_root.py'): {
321 'h': hash_file('tests', 'isolate', 'touch_root.py'),
322 's': _size('tests', 'isolate', 'touch_root.py'),
325 'relative_cwd': os.path.join(u'tests', 'isolate'),
326 'version': isolate.isolateserver.ISOLATED_FILE_VERSION,
328 self._cleanup_isolated(expected_isolated)
329 self.assertEqual(expected_isolated, actual_isolated)
331 expected_saved_state = {
334 'child_isolated_files': [],
335 'command': ['python', 'touch_root.py'],
336 'config_variables': {
344 os.path.join(u'tests', 'isolate', 'touch_root.py'): {
346 'h': hash_file('tests', 'isolate', 'touch_root.py'),
347 's': _size('tests', 'isolate', 'touch_root.py'),
350 'isolate_file': file_path.safe_relpath(
351 file_path.get_native_path_case(isolate_file),
352 os.path.dirname(options.isolated)),
353 'path_variables': {},
354 'relative_cwd': os.path.join(u'tests', 'isolate'),
355 'root_dir': file_path.get_native_path_case(ROOT_DIR),
356 'version': isolate.SavedState.EXPECTED_VERSION,
358 self._cleanup_isolated(expected_saved_state)
359 self._cleanup_saved_state(actual_saved_state)
360 self.assertEqual(expected_saved_state, actual_saved_state)
362 def test_subdir_variable(self):
363 # the resulting .isolated file will be missing ../../isolate.py. it is
364 # because this file is outside the --subdir parameter.
365 isolate_file = os.path.join(
366 ROOT_DIR, 'tests', 'isolate', 'touch_root.isolate')
367 options = self._get_option(isolate_file)
368 # Path variables are keyed on the directory containing the .isolate file.
369 options.path_variables['TEST_ISOLATE'] = '.'
370 # Note that options.isolated is in self.directory, which is a temporary
372 complete_state = isolate.load_complete_state(
373 options, os.path.join(ROOT_DIR, 'tests', 'isolate'),
374 '<(TEST_ISOLATE)', False)
375 actual_isolated = complete_state.saved_state.to_isolated()
376 actual_saved_state = complete_state.saved_state.flatten()
378 expected_isolated = {
380 'command': ['python', 'touch_root.py'],
382 os.path.join('tests', 'isolate', 'touch_root.py'): {
384 'h': hash_file('tests', 'isolate', 'touch_root.py'),
385 's': _size('tests', 'isolate', 'touch_root.py'),
388 'relative_cwd': os.path.join(u'tests', 'isolate'),
389 'version': isolate.isolateserver.ISOLATED_FILE_VERSION,
391 self._cleanup_isolated(expected_isolated)
392 self.assertEqual(expected_isolated, actual_isolated)
394 # It is important to note:
395 # - the root directory is ROOT_DIR.
396 # - relative_cwd is tests/isolate.
397 # - TEST_ISOLATE is based of relative_cwd, so it represents tests/isolate.
398 # - anything outside TEST_ISOLATE was not included in the 'files' section.
399 expected_saved_state = {
402 'child_isolated_files': [],
403 'command': ['python', 'touch_root.py'],
404 'config_variables': {
412 os.path.join(u'tests', 'isolate', 'touch_root.py'): {
414 'h': hash_file('tests', 'isolate', 'touch_root.py'),
415 's': _size('tests', 'isolate', 'touch_root.py'),
418 'isolate_file': file_path.safe_relpath(
419 file_path.get_native_path_case(isolate_file),
420 os.path.dirname(options.isolated)),
424 'relative_cwd': os.path.join(u'tests', 'isolate'),
425 'root_dir': file_path.get_native_path_case(ROOT_DIR),
426 'version': isolate.SavedState.EXPECTED_VERSION,
428 self._cleanup_isolated(expected_saved_state)
429 self._cleanup_saved_state(actual_saved_state)
430 self.assertEqual(expected_saved_state, actual_saved_state)
432 def test_variable_not_exist(self):
433 isolate_file = os.path.join(
434 ROOT_DIR, 'tests', 'isolate', 'touch_root.isolate')
435 options = self._get_option(isolate_file)
436 options.path_variables['PRODUCT_DIR'] = os.path.join(u'tests', u'isolate')
437 native_cwd = file_path.get_native_path_case(unicode(self.cwd))
439 isolate.load_complete_state(options, self.cwd, None, False)
441 except isolate.ExecutionError, e:
443 'PRODUCT_DIR=%s is not a directory' %
444 os.path.join(native_cwd, 'tests', 'isolate'),
447 def test_variable(self):
448 isolate_file = os.path.join(
449 ROOT_DIR, 'tests', 'isolate', 'touch_root.isolate')
450 options = self._get_option(isolate_file)
451 options.path_variables['PRODUCT_DIR'] = os.path.join('tests', 'isolate')
452 complete_state = isolate.load_complete_state(options, ROOT_DIR, None, False)
453 actual_isolated = complete_state.saved_state.to_isolated()
454 actual_saved_state = complete_state.saved_state.flatten()
456 expected_isolated = {
458 'command': ['python', 'touch_root.py'],
462 'h': hash_file('isolate.py'),
463 's': _size('isolate.py'),
465 os.path.join(u'tests', 'isolate', 'touch_root.py'): {
467 'h': hash_file('tests', 'isolate', 'touch_root.py'),
468 's': _size('tests', 'isolate', 'touch_root.py'),
471 'relative_cwd': os.path.join(u'tests', 'isolate'),
472 'version': isolate.isolateserver.ISOLATED_FILE_VERSION,
474 self._cleanup_isolated(expected_isolated)
475 self.assertEqual(expected_isolated, actual_isolated)
477 expected_saved_state = {
480 'child_isolated_files': [],
481 'command': ['python', 'touch_root.py'],
482 'config_variables': {
492 'h': hash_file('isolate.py'),
493 's': _size('isolate.py'),
495 os.path.join(u'tests', 'isolate', 'touch_root.py'): {
497 'h': hash_file('tests', 'isolate', 'touch_root.py'),
498 's': _size('tests', 'isolate', 'touch_root.py'),
501 'isolate_file': file_path.safe_relpath(
502 file_path.get_native_path_case(isolate_file),
503 os.path.dirname(options.isolated)),
507 'relative_cwd': os.path.join(u'tests', 'isolate'),
508 'root_dir': file_path.get_native_path_case(ROOT_DIR),
509 'version': isolate.SavedState.EXPECTED_VERSION,
511 self._cleanup_isolated(expected_saved_state)
512 self._cleanup_saved_state(actual_saved_state)
513 self.assertEqual(expected_saved_state, actual_saved_state)
514 self.assertEqual([], os.listdir(self.directory))
516 def test_root_dir_because_of_variable(self):
517 # Ensures that load_isolate() works even when path variables have deep root
518 # dirs. The end result is similar to touch_root.isolate, except that
519 # no_run.isolate doesn't reference '..' at all.
521 # A real world example would be PRODUCT_DIR=../../out/Release but nothing in
522 # this directory is mapped.
524 # Imagine base/base_unittests.isolate would not map anything in
525 # PRODUCT_DIR. In that case, the automatically determined root dir is
526 # src/base, since nothing outside this directory is mapped.
527 isolate_file = os.path.join(
528 ROOT_DIR, 'tests', 'isolate', 'no_run.isolate')
529 options = self._get_option(isolate_file)
530 # Any directory outside ROOT_DIR/tests/isolate.
531 options.path_variables['PRODUCT_DIR'] = os.path.join('third_party')
532 complete_state = isolate.load_complete_state(options, ROOT_DIR, None, False)
533 actual_isolated = complete_state.saved_state.to_isolated()
534 actual_saved_state = complete_state.saved_state.flatten()
536 expected_isolated = {
539 os.path.join(u'tests', 'isolate', 'files1', 'subdir', '42.txt'): {
541 'h': hash_file('tests', 'isolate', 'files1', 'subdir', '42.txt'),
542 's': _size('tests', 'isolate', 'files1', 'subdir', '42.txt'),
544 os.path.join(u'tests', 'isolate', 'files1', 'test_file1.txt'): {
546 'h': hash_file('tests', 'isolate', 'files1', 'test_file1.txt'),
547 's': _size('tests', 'isolate', 'files1', 'test_file1.txt'),
549 os.path.join(u'tests', 'isolate', 'files1', 'test_file2.txt'): {
551 'h': hash_file('tests', 'isolate', 'files1', 'test_file2.txt'),
552 's': _size('tests', 'isolate', 'files1', 'test_file2.txt'),
554 os.path.join(u'tests', 'isolate', 'no_run.isolate'): {
556 'h': hash_file('tests', 'isolate', 'no_run.isolate'),
557 's': _size('tests', 'isolate', 'no_run.isolate'),
560 'relative_cwd': os.path.join(u'tests', 'isolate'),
561 'version': isolate.isolateserver.ISOLATED_FILE_VERSION,
563 self._cleanup_isolated(expected_isolated)
564 self.assertEqual(expected_isolated, actual_isolated)
566 expected_saved_state = {
569 'child_isolated_files': [],
571 'config_variables': {
579 os.path.join(u'tests', 'isolate', 'files1', 'subdir', '42.txt'): {
581 'h': hash_file('tests', 'isolate', 'files1', 'subdir', '42.txt'),
582 's': _size('tests', 'isolate', 'files1', 'subdir', '42.txt'),
584 os.path.join(u'tests', 'isolate', 'files1', 'test_file1.txt'): {
586 'h': hash_file('tests', 'isolate', 'files1', 'test_file1.txt'),
587 's': _size('tests', 'isolate', 'files1', 'test_file1.txt'),
589 os.path.join(u'tests', 'isolate', 'files1', 'test_file2.txt'): {
591 'h': hash_file('tests', 'isolate', 'files1', 'test_file2.txt'),
592 's': _size('tests', 'isolate', 'files1', 'test_file2.txt'),
594 os.path.join(u'tests', 'isolate', 'no_run.isolate'): {
596 'h': hash_file('tests', 'isolate', 'no_run.isolate'),
597 's': _size('tests', 'isolate', 'no_run.isolate'),
600 'isolate_file': file_path.safe_relpath(
601 file_path.get_native_path_case(isolate_file),
602 os.path.dirname(options.isolated)),
604 'PRODUCT_DIR': os.path.join(u'..', '..', 'third_party'),
606 'relative_cwd': os.path.join(u'tests', 'isolate'),
607 'root_dir': file_path.get_native_path_case(ROOT_DIR),
608 'version': isolate.SavedState.EXPECTED_VERSION,
610 self._cleanup_isolated(expected_saved_state)
611 self._cleanup_saved_state(actual_saved_state)
612 self.assertEqual(expected_saved_state, actual_saved_state)
613 self.assertEqual([], os.listdir(self.directory))
615 def test_chromium_split(self):
616 # Create an .isolate file and a tree of random stuff.
617 isolate_file = os.path.join(
618 ROOT_DIR, 'tests', 'isolate', 'split.isolate')
619 options = self._get_option(isolate_file)
620 options.path_variables = {
622 'PRODUCT_DIR': os.path.join('files1'),
624 options.config_variables = {
627 complete_state = isolate.load_complete_state(
628 options, os.path.join(ROOT_DIR, 'tests', 'isolate'), None, False)
629 # By saving the files, it forces splitting the data up.
630 complete_state.save_files()
632 actual_isolated_master = tools.read_json(
633 os.path.join(self.directory, 'foo.isolated'))
634 expected_isolated_master = {
636 u'command': [u'python', u'split.py'],
640 u'h': unicode(hash_file('tests', 'isolate', 'split.py')),
641 u's': _size('tests', 'isolate', 'split.py'),
645 unicode(hash_file(os.path.join(self.directory, 'foo.0.isolated'))),
646 unicode(hash_file(os.path.join(self.directory, 'foo.1.isolated'))),
648 u'relative_cwd': u'.',
649 u'version': unicode(isolate.isolateserver.ISOLATED_FILE_VERSION),
651 self._cleanup_isolated(expected_isolated_master)
652 self.assertEqual(expected_isolated_master, actual_isolated_master)
654 actual_isolated_0 = tools.read_json(
655 os.path.join(self.directory, 'foo.0.isolated'))
656 expected_isolated_0 = {
659 os.path.join(u'test', 'data', 'foo.txt'): {
662 hash_file('tests', 'isolate', 'test', 'data', 'foo.txt')),
663 u's': _size('tests', 'isolate', 'test', 'data', 'foo.txt'),
666 u'version': unicode(isolate.isolateserver.ISOLATED_FILE_VERSION),
668 self._cleanup_isolated(expected_isolated_0)
669 self.assertEqual(expected_isolated_0, actual_isolated_0)
671 actual_isolated_1 = tools.read_json(
672 os.path.join(self.directory, 'foo.1.isolated'))
673 expected_isolated_1 = {
676 os.path.join(u'files1', 'subdir', '42.txt'): {
679 hash_file('tests', 'isolate', 'files1', 'subdir', '42.txt')),
680 u's': _size('tests', 'isolate', 'files1', 'subdir', '42.txt'),
683 u'version': unicode(isolate.isolateserver.ISOLATED_FILE_VERSION),
685 self._cleanup_isolated(expected_isolated_1)
686 self.assertEqual(expected_isolated_1, actual_isolated_1)
688 actual_saved_state = tools.read_json(
689 isolate.isolatedfile_to_state(options.isolated))
690 isolated_base = unicode(os.path.basename(options.isolated))
691 expected_saved_state = {
692 u'OS': unicode(sys.platform),
694 u'child_isolated_files': [
695 isolated_base[:-len('.isolated')] + '.0.isolated',
696 isolated_base[:-len('.isolated')] + '.1.isolated',
698 u'command': [u'python', u'split.py'],
699 u'config_variables': {
702 u'extra_variables': {
706 os.path.join(u'files1', 'subdir', '42.txt'): {
709 hash_file('tests', 'isolate', 'files1', 'subdir', '42.txt')),
710 u's': _size('tests', 'isolate', 'files1', 'subdir', '42.txt'),
714 u'h': unicode(hash_file('tests', 'isolate', 'split.py')),
715 u's': _size('tests', 'isolate', 'split.py'),
717 os.path.join(u'test', 'data', 'foo.txt'): {
720 hash_file('tests', 'isolate', 'test', 'data', 'foo.txt')),
721 u's': _size('tests', 'isolate', 'test', 'data', 'foo.txt'),
724 u'isolate_file': file_path.safe_relpath(
725 file_path.get_native_path_case(isolate_file),
726 unicode(os.path.dirname(options.isolated))),
729 u'PRODUCT_DIR': u'files1',
731 u'relative_cwd': u'.',
732 u'root_dir': file_path.get_native_path_case(
733 os.path.dirname(isolate_file)),
734 u'version': unicode(isolate.SavedState.EXPECTED_VERSION),
736 self._cleanup_isolated(expected_saved_state)
737 self._cleanup_saved_state(actual_saved_state)
738 self.assertEqual(expected_saved_state, actual_saved_state)
741 'foo.0.isolated', 'foo.1.isolated',
742 'foo.isolated', 'foo.isolated.state',
744 sorted(os.listdir(self.directory)))
746 def test_load_isolate_include_command(self):
747 # Ensure that using a .isolate that includes another one in a different
748 # directory will lead to the proper relative directory. See
749 # test_load_with_includes_with_commands in isolate_format_test.py as
752 # Exactly the same thing as in isolate_format_test.py
755 ['OS=="amiga" or OS=="win"', {
758 'foo', 'amiga_or_win',
767 'isolate_dependency_tracked': [
772 ['OS=="mac" or OS=="win"', {
774 'isolate_dependency_tracked': [
783 ['OS=="linux" or OS=="mac"', {
786 'foo', 'linux_or_mac',
788 'isolate_dependency_tracked': [
797 '../1/isolate1.isolate',
798 '2/isolate2.isolate',
803 'isolate_dependency_tracked': [
813 'isolate_dependency_tracked': [
822 config_os, files_to_create, expected_files, command, relative_cwd):
823 """Creates a tree of files in a subdirectory for testing and test this
826 directory = os.path.join(unicode(self.directory), config_os)
828 isolate_dir = os.path.join(directory, u'isolate')
829 isolate_dir_1 = os.path.join(isolate_dir, u'1')
830 isolate_dir_3 = os.path.join(isolate_dir, u'3')
831 isolate_dir_3_2 = os.path.join(isolate_dir_3, u'2')
832 isolated_dir = os.path.join(directory, u'isolated')
833 os.mkdir(isolated_dir)
834 os.mkdir(isolate_dir)
835 os.mkdir(isolate_dir_1)
836 os.mkdir(isolate_dir_3)
837 os.mkdir(isolate_dir_3_2)
838 isolated = os.path.join(isolated_dir, u'foo.isolated')
840 with open(os.path.join(isolate_dir_1, 'isolate1.isolate'), 'wb') as f:
841 isolate.isolate_format.pretty_print(isolate1, f)
842 with open(os.path.join(isolate_dir_3_2, 'isolate2.isolate'), 'wb') as f:
843 isolate.isolate_format.pretty_print(isolate2, f)
844 root_isolate = os.path.join(isolate_dir_3, 'isolate3.isolate')
845 with open(root_isolate, 'wb') as f:
846 isolate.isolate_format.pretty_print(isolate3, f)
848 # Make all the touched files.
849 mapping = {1: isolate_dir_1, 2: isolate_dir_3_2, 3: isolate_dir_3}
850 for k, v in files_to_create.iteritems():
851 f = os.path.join(mapping[k], v)
852 base = os.path.dirname(f)
853 if not os.path.isdir(base):
855 open(f, 'wb').close()
857 c = isolate.CompleteState(isolated, isolate.SavedState(isolated_dir))
861 c.load_isolate(unicode(self.cwd), root_isolate, {}, config, {}, False)
862 # Note that load_isolate() doesn't retrieve the meta data about each file.
866 'files': {unicode(f):{} for f in expected_files},
867 'relative_cwd': relative_cwd,
868 'version': isolate.isolateserver.ISOLATED_FILE_VERSION,
870 self.assertEqual(expected, c.saved_state.to_isolated())
872 # root is .../isolate/.
881 ['foo', 'amiga_or_win'],
883 # root is .../isolate/.
894 ['foo', 'linux_or_mac'],
896 # root is .../isolate/.
911 # root is .../isolate/1/.
920 ['foo', 'amiga_or_win'],
923 def test_load_isolate_include_command_and_variables(self):
924 # Ensure that using a .isolate that includes another one in a different
925 # directory will lead to the proper relative directory when using variables.
926 # See test_load_with_includes_with_commands_and_variables in
927 # isolate_format_test.py as reference.
929 # With path variables, 'cwd' is used instead of the path to the .isolate
930 # file. So the files have to be set towards the cwd accordingly. While this
931 # may seem surprising, this makes the whole thing work in the first place.
933 # Almost exactly the same thing as in isolate_format_test.py plus the EXTRA
934 # for better testing with variable replacement.
937 ['OS=="amiga" or OS=="win"', {
940 'foo', 'amiga_or_win', '<(PATH)', '<(EXTRA)',
947 'foo', 'linux', '<(PATH)', '<(EXTRA)',
949 'isolate_dependency_tracked': [
950 '<(PATH)/file_linux',
954 ['OS=="mac" or OS=="win"', {
956 'isolate_dependency_tracked': [
957 '<(PATH)/file_non_linux',
965 ['OS=="linux" or OS=="mac"', {
968 'foo', 'linux_or_mac', '<(PATH)', '<(EXTRA)',
970 'isolate_dependency_tracked': [
971 '<(PATH)/other/file',
979 '../1/isolate1.isolate',
980 '2/isolate2.isolate',
985 'isolate_dependency_tracked': [
986 '<(PATH)/file_amiga',
993 'foo', 'mac', '<(PATH)', '<(EXTRA)',
995 'isolate_dependency_tracked': [
1003 def test_with_os(config_os, expected_files, command, relative_cwd):
1004 """Creates a tree of files in a subdirectory for testing and test this
1007 directory = os.path.join(unicode(self.directory), config_os)
1009 cwd = os.path.join(unicode(self.cwd), config_os)
1011 isolate_dir = os.path.join(directory, u'isolate')
1012 isolate_dir_1 = os.path.join(isolate_dir, u'1')
1013 isolate_dir_3 = os.path.join(isolate_dir, u'3')
1014 isolate_dir_3_2 = os.path.join(isolate_dir_3, u'2')
1015 isolated_dir = os.path.join(directory, u'isolated')
1016 os.mkdir(isolated_dir)
1017 os.mkdir(isolate_dir)
1018 os.mkdir(isolate_dir_1)
1019 os.mkdir(isolate_dir_3)
1020 os.mkdir(isolate_dir_3_2)
1021 isolated = os.path.join(isolated_dir, u'foo.isolated')
1023 with open(os.path.join(isolate_dir_1, 'isolate1.isolate'), 'wb') as f:
1024 isolate.isolate_format.pretty_print(isolate1, f)
1025 with open(os.path.join(isolate_dir_3_2, 'isolate2.isolate'), 'wb') as f:
1026 isolate.isolate_format.pretty_print(isolate2, f)
1027 root_isolate = os.path.join(isolate_dir_3, 'isolate3.isolate')
1028 with open(root_isolate, 'wb') as f:
1029 isolate.isolate_format.pretty_print(isolate3, f)
1031 # Make all the touched files.
1032 path_dir = os.path.join(cwd, 'path')
1034 for v in expected_files:
1035 f = os.path.join(path_dir, v)
1036 base = os.path.dirname(f)
1037 if not os.path.isdir(base):
1040 open(f, 'wb').close()
1042 c = isolate.CompleteState(isolated, isolate.SavedState(isolated_dir))
1053 unicode(cwd), root_isolate, paths, config, extra, False)
1054 # Note that load_isolate() doesn't retrieve the meta data about each file.
1059 unicode(os.path.join(cwd_name, config_os, 'path', f)): {}
1060 for f in expected_files
1062 'relative_cwd': relative_cwd,
1063 'version': isolate.isolateserver.ISOLATED_FILE_VERSION,
1065 self.assertEqual(expected, c.saved_state.to_isolated())
1067 cwd_name = os.path.basename(self.cwd)
1068 dir_name = os.path.basename(self.directory)
1077 u'../../../../%s/amiga/path' % cwd_name,
1080 u'%s/amiga/isolate/1' % dir_name)
1090 u'../../../../../%s/linux/path' % cwd_name,
1093 u'%s/linux/isolate/3/2' % dir_name)
1104 u'../../../../%s/mac/path' % cwd_name,
1107 u'%s/mac/isolate/3' % dir_name)
1116 u'../../../../%s/win/path' % cwd_name,
1119 u'%s/win/isolate/1' % dir_name)
1122 class IsolateCommand(IsolateBase):
1123 def load_complete_state(self, *_):
1124 """Creates a minimalist CompleteState instance without an .isolated
1127 out = isolate.CompleteState(None, isolate.SavedState(self.cwd))
1128 out.saved_state.isolate_file = u'blah.isolate'
1129 out.saved_state.relative_cwd = u''
1130 out.saved_state.root_dir = ROOT_DIR
1133 def test_CMDarchive(self):
1136 isolate.isolateserver, 'upload_tree',
1137 lambda **kwargs: actual.append(kwargs))
1139 isolate_file = os.path.join(self.cwd, 'x.isolate')
1140 isolated_file = os.path.join(self.cwd, 'x.isolated')
1141 with open(isolate_file, 'wb') as f:
1146 ' [\'OS=="dendy"\', {'
1148 ' \'isolate_dependency_tracked\': [\'foo\'],'
1153 with open(os.path.join(self.cwd, 'foo'), 'wb') as f:
1156 self.mock(sys, 'stdout', cStringIO.StringIO())
1159 '-s', isolated_file,
1160 '--isolate-server', 'http://localhost:1',
1161 '--config-variable', 'OS', 'dendy',
1163 self.assertEqual(0, isolate.CMDarchive(isolate.OptionParserIsolate(), cmd))
1166 'base_url': 'http://localhost:1',
1173 'h': '520d41b29f891bbaccf31d9fcfa72e82ea20fcf0',
1177 'namespace': 'default-gzip',
1180 # These always change.
1181 actual[0]['infiles'][isolated_file].pop('h')
1182 actual[0]['infiles'][isolated_file].pop('s')
1183 actual[0]['infiles']['foo'].pop('m')
1184 actual[0]['infiles']['foo'].pop('t')
1185 self.assertEqual(expected, actual)
1187 def test_CMDcheck_empty(self):
1188 isolate_file = os.path.join(self.cwd, 'x.isolate')
1189 isolated_file = os.path.join(self.cwd, 'x.isolated')
1190 with open(isolate_file, 'wb') as f:
1191 f.write('# Foo\n{\n}')
1193 self.mock(sys, 'stdout', cStringIO.StringIO())
1194 cmd = ['-i', isolate_file, '-s', isolated_file]
1195 isolate.CMDcheck(isolate.OptionParserIsolate(), cmd)
1197 def test_CMDcheck_stale_version(self):
1198 isolate_file = os.path.join(self.cwd, 'x.isolate')
1199 isolated_file = os.path.join(self.cwd, 'x.isolated')
1200 with open(isolate_file, 'wb') as f:
1205 ' [\'OS=="dendy"\', {'
1207 ' \'command\': [\'foo\'],'
1213 self.mock(sys, 'stdout', cStringIO.StringIO())
1216 '-s', isolated_file,
1217 '--config-variable', 'OS=dendy',
1219 self.assertEqual(0, isolate.CMDcheck(isolate.OptionParserIsolate(), cmd))
1221 with open(isolate_file, 'rb') as f:
1224 '# Foo\n{ \'conditions\':[ [\'OS=="dendy"\', { '
1225 '\'variables\': { \'command\': [\'foo\'], }, }], ],}')
1226 self.assertEqual(expected, actual)
1228 with open(isolated_file, 'rb') as f:
1229 actual_isolated = f.read()
1230 expected_isolated = (
1231 '{"algo":"sha-1","command":["foo"],"files":{},'
1232 '"relative_cwd":".","version":"%s"}'
1233 ) % isolate.isolateserver.ISOLATED_FILE_VERSION
1234 self.assertEqual(expected_isolated, actual_isolated)
1235 isolated_data = json.loads(actual_isolated)
1237 with open(isolated_file + '.state', 'rb') as f:
1238 actual_isolated_state = f.read()
1239 expected_isolated_state = (
1240 '{"OS":"%s","algo":"sha-1","child_isolated_files":[],"command":["foo"],'
1241 '"config_variables":{"OS":"dendy"},'
1242 '"extra_variables":{"EXECUTABLE_SUFFIX":""},"files":{},'
1243 '"isolate_file":"x.isolate","path_variables":{},'
1244 '"relative_cwd":".","root_dir":"%s","version":"%s"}'
1245 ) % (sys.platform, self.cwd, isolate.SavedState.EXPECTED_VERSION)
1246 self.assertEqual(expected_isolated_state, actual_isolated_state)
1247 isolated_state_data = json.loads(actual_isolated_state)
1249 # Now edit the .isolated.state file to break the version number and make
1250 # sure it doesn't crash.
1251 with open(isolated_file + '.state', 'wb') as f:
1252 isolated_state_data['version'] = '100.42'
1253 json.dump(isolated_state_data, f)
1254 self.assertEqual(0, isolate.CMDcheck(isolate.OptionParserIsolate(), cmd))
1256 # Now edit the .isolated file to break the version number and make
1257 # sure it doesn't crash.
1258 with open(isolated_file, 'wb') as f:
1259 isolated_data['version'] = '100.42'
1260 json.dump(isolated_data, f)
1261 self.assertEqual(0, isolate.CMDcheck(isolate.OptionParserIsolate(), cmd))
1263 # Make sure the files were regenerated.
1264 with open(isolated_file, 'rb') as f:
1265 actual_isolated = f.read()
1266 self.assertEqual(expected_isolated, actual_isolated)
1267 with open(isolated_file + '.state', 'rb') as f:
1268 actual_isolated_state = f.read()
1269 self.assertEqual(expected_isolated_state, actual_isolated_state)
1271 def test_CMDcheck_new_variables(self):
1273 isolate_file = os.path.join(self.cwd, 'x.isolate')
1274 isolated_file = os.path.join(self.cwd, 'x.isolated')
1277 '-s', isolated_file,
1278 '--config-variable', 'OS=dendy',
1280 with open(isolate_file, 'wb') as f:
1285 ' [\'OS=="dendy"\', {'
1287 ' \'command\': [\'foo\'],'
1288 ' \'isolate_dependency_tracked\': [\'foo\'],'
1293 with open(os.path.join(self.cwd, 'foo'), 'wb') as f:
1296 self.mock(sys, 'stdout', cStringIO.StringIO())
1297 self.assertEqual(0, isolate.CMDcheck(isolate.OptionParserIsolate(), cmd))
1299 # Now add a new config variable.
1300 with open(isolate_file, 'wb') as f:
1305 ' [\'OS=="dendy"\', {'
1307 ' \'command\': [\'foo\'],'
1308 ' \'isolate_dependency_tracked\': [\'foo\'],'
1311 ' [\'foo=="baz"\', {'
1313 ' \'isolate_dependency_tracked\': [\'bar\'],'
1318 with open(os.path.join(self.cwd, 'bar'), 'wb') as f:
1319 f.write('yeah right!')
1321 # The configuration is OS=dendy and foo=bar. So it should load both
1326 isolate.OptionParserIsolate(),
1327 cmd + ['--config-variable', 'foo=bar']))
1329 def test_CMDcheck_isolate_copied(self):
1330 # Note that moving the .isolate file is a different code path, this is about
1331 # copying the .isolate file to a new place and specifying the new location
1332 # on a subsequent execution.
1333 x_isolate_file = os.path.join(self.cwd, 'x.isolate')
1334 isolated_file = os.path.join(self.cwd, 'x.isolated')
1335 cmd = ['-i', x_isolate_file, '-s', isolated_file]
1336 with open(x_isolate_file, 'wb') as f:
1338 self.assertEqual(0, isolate.CMDcheck(isolate.OptionParserIsolate(), cmd))
1339 self.assertTrue(os.path.isfile(isolated_file + '.state'))
1340 with open(isolated_file + '.state', 'rb') as f:
1341 self.assertEqual(json.load(f)['isolate_file'], 'x.isolate')
1343 # Move the .isolate file.
1344 y_isolate_file = os.path.join(self.cwd, 'Y.isolate')
1345 shutil.copyfile(x_isolate_file, y_isolate_file)
1346 cmd = ['-i', y_isolate_file, '-s', isolated_file]
1347 self.assertEqual(0, isolate.CMDcheck(isolate.OptionParserIsolate(), cmd))
1348 with open(isolated_file + '.state', 'rb') as f:
1349 self.assertEqual(json.load(f)['isolate_file'], 'Y.isolate')
1351 def test_CMDrewrite(self):
1352 isolate_file = os.path.join(self.cwd, 'x.isolate')
1358 with open(isolate_file, 'wb') as f:
1359 f.write('\n'.join(data))
1361 self.mock(sys, 'stdout', cStringIO.StringIO())
1362 cmd = ['-i', isolate_file]
1363 self.assertEqual(0, isolate.CMDrewrite(isolate.OptionParserIsolate(), cmd))
1364 with open(isolate_file, 'rb') as f:
1367 expected = "# Foo\n{\n 'conditions': [\n ],\n}\n"
1368 self.assertEqual(expected, actual)
1370 def test_CMDrun_extra_args(self):
1373 '--isolate', 'blah.isolate',
1376 self.mock(isolate, 'load_complete_state', self.load_complete_state)
1377 self.mock(isolate.subprocess, 'call', lambda *_, **_kwargs: 0)
1378 self.assertEqual(0, isolate.CMDrun(isolate.OptionParserIsolate(), cmd))
1380 def test_CMDrun_no_isolated(self):
1381 isolate_file = os.path.join(self.cwd, 'x.isolate')
1382 with open(isolate_file, 'wb') as f:
1383 f.write('{"variables": {"command": ["python", "-c", "print(\'hi\')"]} }')
1385 def expect_call(cmd, cwd):
1386 self.assertEqual([sys.executable, '-c', "print('hi')", 'run'], cmd)
1387 self.assertTrue(os.path.isdir(cwd))
1389 self.mock(isolate.subprocess, 'call', expect_call)
1391 cmd = ['run', '--isolate', isolate_file]
1392 self.assertEqual(0, isolate.CMDrun(isolate.OptionParserIsolate(), cmd))
1395 def clear_env_vars():
1396 for e in ('ISOLATE_DEBUG', 'ISOLATE_SERVER'):
1397 os.environ.pop(e, None)
1400 if __name__ == '__main__':
1401 logging.basicConfig(
1402 level=logging.DEBUG if '-v' in sys.argv else logging.ERROR,
1403 format='%(levelname)5s %(filename)15s(%(lineno)3d): %(message)s')
1404 if '-v' in sys.argv:
1405 unittest.TestCase.maxDiff = None