Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(433)

Side by Side Diff: swarm_client/tests/isolate_smoke_test.py

Issue 69143004: Delete swarm_client. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/
Patch Set: Created 7 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « swarm_client/tests/isolate/with_flag.py ('k') | swarm_client/tests/isolate_test.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(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()
OLDNEW
« no previous file with comments | « swarm_client/tests/isolate/with_flag.py ('k') | swarm_client/tests/isolate_test.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698