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

Side by Side Diff: tools/isolate/tests/isolate_smoke_test.py

Issue 11045023: Move src/tools/isolate to src/tools/swarm_client as a DEPS. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Use r159961 Created 8 years, 2 months 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 | « tools/isolate/tests/isolate/with_flag.py ('k') | tools/isolate/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 run_test_from_archive
24
25 VERBOSE = False
26 SHA_1_NULL = hashlib.sha1().hexdigest()
27
28
29 # Keep the list hard coded.
30 EXPECTED_MODES = (
31 'check',
32 'hashtable',
33 'help',
34 'noop',
35 'merge',
36 'read',
37 'remap',
38 'run',
39 'trace',
40 )
41
42 # These are per test case, not per mode.
43 RELATIVE_CWD = {
44 'fail': '.',
45 'missing_trailing_slash': '.',
46 'no_run': '.',
47 'non_existent': '.',
48 'symlink_full': '.',
49 'symlink_partial': '.',
50 'touch_only': '.',
51 'touch_root': os.path.join('tests', 'isolate'),
52 'with_flag': '.',
53 }
54
55 DEPENDENCIES = {
56 'fail': ['fail.py'],
57 'missing_trailing_slash': [],
58 'no_run': [
59 'no_run.isolate',
60 os.path.join('files1', 'subdir', '42.txt'),
61 os.path.join('files1', 'test_file1.txt'),
62 os.path.join('files1', 'test_file2.txt'),
63 ],
64 'non_existent': [],
65 'symlink_full': [
66 os.path.join('files1', 'subdir', '42.txt'),
67 os.path.join('files1', 'test_file1.txt'),
68 os.path.join('files1', 'test_file2.txt'),
69 # files2 is a symlink to files1.
70 'files2',
71 'symlink_full.py',
72 ],
73 'symlink_partial': [
74 os.path.join('files1', 'test_file2.txt'),
75 # files2 is a symlink to files1.
76 'files2',
77 'symlink_partial.py',
78 ],
79 'touch_only': [
80 'touch_only.py',
81 os.path.join('files1', 'test_file1.txt'),
82 ],
83 'touch_root': [
84 os.path.join('tests', 'isolate', 'touch_root.py'),
85 'isolate.py',
86 ],
87 'with_flag': [
88 'with_flag.py',
89 os.path.join('files1', 'subdir', '42.txt'),
90 os.path.join('files1', 'test_file1.txt'),
91 os.path.join('files1', 'test_file2.txt'),
92 ],
93 }
94
95
96 class CalledProcessError(subprocess.CalledProcessError):
97 """Makes 2.6 version act like 2.7"""
98 def __init__(self, returncode, cmd, output, stderr, cwd):
99 super(CalledProcessError, self).__init__(returncode, cmd)
100 self.output = output
101 self.stderr = stderr
102 self.cwd = cwd
103
104 def __str__(self):
105 return super(CalledProcessError, self).__str__() + (
106 '\n'
107 'cwd=%s\n%s\n%s\n%s') % (
108 self.cwd,
109 self.output,
110 self.stderr,
111 ' '.join(self.cmd))
112
113
114 def list_files_tree(directory):
115 """Returns the list of all the files in a tree."""
116 actual = []
117 for root, dirnames, filenames in os.walk(directory):
118 actual.extend(os.path.join(root, f)[len(directory)+1:] for f in filenames)
119 for dirname in dirnames:
120 full = os.path.join(root, dirname)
121 # Manually include symlinks.
122 if os.path.islink(full):
123 actual.append(full[len(directory)+1:])
124 return sorted(actual)
125
126
127 def calc_sha1(filepath):
128 """Calculates the SHA-1 hash for a file."""
129 return hashlib.sha1(open(filepath, 'rb').read()).hexdigest()
130
131
132 class IsolateBase(unittest.TestCase):
133 # To be defined by the subclass, it defines the amount of meta data saved by
134 # isolate.py for each file. Should be one of (NO_INFO, STATS_ONLY, WITH_HASH).
135 LEVEL = None
136
137 def setUp(self):
138 # The tests assume the current directory is the file's directory.
139 os.chdir(ROOT_DIR)
140 self.tempdir = tempfile.mkdtemp()
141 self.result = os.path.join(self.tempdir, 'isolate_smoke_test.results')
142 self.outdir = os.path.join(self.tempdir, 'isolated')
143
144 def tearDown(self):
145 logging.debug(self.tempdir)
146 shutil.rmtree(self.tempdir)
147
148 @staticmethod
149 def _isolate_dict_to_string(values):
150 buf = cStringIO.StringIO()
151 isolate.pretty_print(values, buf)
152 return buf.getvalue()
153
154 @classmethod
155 def _wrap_in_condition(cls, variables):
156 """Wraps a variables dict inside the current OS condition.
157
158 Returns the equivalent string.
159 """
160 return cls._isolate_dict_to_string(
161 {
162 'conditions': [
163 ['OS=="%s"' % isolate.get_flavor(), {
164 'variables': variables
165 }],
166 ],
167 })
168
169
170 class IsolateModeBase(IsolateBase):
171 def _expect_no_tree(self):
172 self.assertFalse(os.path.exists(self.outdir))
173
174 def _result_tree(self):
175 return list_files_tree(self.outdir)
176
177 def _expected_tree(self):
178 """Verifies the files written in the temporary directory."""
179 self.assertEquals(sorted(DEPENDENCIES[self.case()]), self._result_tree())
180
181 @staticmethod
182 def _fix_file_mode(filename, read_only):
183 """4 modes are supported, 0750 (rwx), 0640 (rw), 0550 (rx), 0440 (r)."""
184 min_mode = 0440
185 if not read_only:
186 min_mode |= 0200
187 return (min_mode | 0110) if filename.endswith('.py') else min_mode
188
189 def _gen_files(self, read_only, empty_file):
190 """Returns a dict of files like calling isolate.process_input() on each
191 file.
192 """
193 root_dir = ROOT_DIR
194 if RELATIVE_CWD[self.case()] == '.':
195 root_dir = os.path.join(root_dir, 'tests', 'isolate')
196
197 files = dict((unicode(f), {}) for f in DEPENDENCIES[self.case()])
198
199 for relfile, v in files.iteritems():
200 filepath = os.path.join(root_dir, relfile)
201 if self.LEVEL >= isolate.STATS_ONLY:
202 filestats = os.lstat(filepath)
203 is_link = stat.S_ISLNK(filestats.st_mode)
204 if not is_link:
205 v[u'size'] = filestats.st_size
206 if isolate.get_flavor() != 'win':
207 v[u'mode'] = self._fix_file_mode(relfile, read_only)
208 else:
209 v[u'mode'] = 488
210 # Used the skip recalculating the hash. Use the most recent update
211 # time.
212 v[u'timestamp'] = int(round(filestats.st_mtime))
213 if is_link:
214 v['link'] = os.readlink(filepath)
215
216 if self.LEVEL >= isolate.WITH_HASH:
217 if not is_link:
218 # Upgrade the value to unicode so diffing the structure in case of
219 # test failure is easier, since the basestring type must match,
220 # str!=unicode.
221 v[u'sha-1'] = unicode(calc_sha1(filepath))
222
223 if empty_file:
224 item = files[empty_file]
225 item['sha-1'] = unicode(SHA_1_NULL)
226 if sys.platform != 'win32':
227 item['mode'] = 288
228 item['size'] = 0
229 item['touched_only'] = True
230 item.pop('timestamp', None)
231 return files
232
233 def _expected_result(self, args, read_only, empty_file):
234 """Verifies self.result contains the expected data."""
235 expected = {
236 u'files': self._gen_files(read_only, empty_file),
237 u'os': isolate.get_flavor(),
238 u'relative_cwd': unicode(RELATIVE_CWD[self.case()]),
239 }
240 if read_only is not None:
241 expected[u'read_only'] = read_only
242 if args:
243 expected[u'command'] = [u'python'] + [unicode(x) for x in args]
244 else:
245 expected[u'command'] = []
246 self.assertEquals(expected, json.load(open(self.result, 'r')))
247
248 def _expected_saved_state(self, extra_vars):
249 flavor = isolate.get_flavor()
250 expected = {
251 u'isolate_file': unicode(self.filename()),
252 u'variables': {
253 u'EXECUTABLE_SUFFIX': '.exe' if flavor == 'win' else '',
254 u'OS': unicode(flavor),
255 },
256 }
257 expected['variables'].update(extra_vars or {})
258 self.assertEquals(expected, json.load(open(self.saved_state(), 'r')))
259
260 def _expect_results(self, args, read_only, extra_vars, empty_file):
261 self._expected_result(args, read_only, empty_file)
262 self._expected_saved_state(extra_vars)
263 # Also verifies run_test_from_archive.py will be able to read it.
264 run_test_from_archive.load_manifest(open(self.result, 'r').read())
265
266 def _expect_no_result(self):
267 self.assertFalse(os.path.exists(self.result))
268
269 def _execute(self, mode, case, args, need_output):
270 """Executes isolate.py."""
271 self.assertEquals(
272 case,
273 self.case() + '.isolate',
274 'Rename the test case to test_%s()' % case)
275 cmd = [
276 sys.executable, os.path.join(ROOT_DIR, 'isolate.py'),
277 mode,
278 '--result', self.result,
279 '--outdir', self.outdir,
280 '--isolate', self.filename(),
281 ]
282 cmd.extend(args)
283
284 env = os.environ.copy()
285 if 'ISOLATE_DEBUG' in env:
286 del env['ISOLATE_DEBUG']
287
288 if need_output or not VERBOSE:
289 stdout = subprocess.PIPE
290 stderr = subprocess.PIPE
291 else:
292 cmd.extend(['-v'] * 3)
293 stdout = None
294 stderr = None
295
296 logging.debug(cmd)
297 cwd = ROOT_DIR
298 p = subprocess.Popen(
299 cmd,
300 stdout=stdout,
301 stderr=stderr,
302 cwd=cwd,
303 env=env,
304 universal_newlines=True)
305 out, err = p.communicate()
306 if p.returncode:
307 raise CalledProcessError(p.returncode, cmd, out, err, cwd)
308
309 # Do not check on Windows since a lot of spew is generated there.
310 if sys.platform != 'win32':
311 self.assertTrue(err in (None, ''), err)
312 return out
313
314 def case(self):
315 """Returns the filename corresponding to this test case."""
316 test_id = self.id().split('.')
317 return re.match('^test_([a-z_]+)$', test_id[2]).group(1)
318
319 def filename(self):
320 """Returns the filename corresponding to this test case."""
321 filename = os.path.join(
322 ROOT_DIR, 'tests', 'isolate', self.case() + '.isolate')
323 self.assertTrue(os.path.isfile(filename), filename)
324 return filename
325
326 def saved_state(self):
327 return isolate.result_to_state(self.result)
328
329
330 class Isolate(unittest.TestCase):
331 # Does not inherit from the other *Base classes.
332 def test_help_modes(self):
333 # Check coherency in the help and implemented modes.
334 p = subprocess.Popen(
335 [sys.executable, os.path.join(ROOT_DIR, 'isolate.py'), '--help'],
336 stdout=subprocess.PIPE,
337 stderr=subprocess.STDOUT,
338 cwd=ROOT_DIR)
339 out = p.communicate()[0].splitlines()
340 self.assertEquals(0, p.returncode)
341 out = out[out.index('') + 1:]
342 out = out[:out.index('')]
343 modes = [re.match(r'^ (\w+) .+', l) for l in out]
344 modes = tuple(m.group(1) for m in modes if m)
345 # noop doesn't do anything so no point in testing it.
346 self.assertEquals(sorted(EXPECTED_MODES), sorted(modes))
347
348 def test_modes(self):
349 # This is a bit redundant but make sure all combinations are tested.
350 files = sorted(
351 i[:-len('.isolate')]
352 for i in os.listdir(os.path.join(ROOT_DIR, 'tests', 'isolate'))
353 if i.endswith('.isolate')
354 )
355 self.assertEquals(sorted(RELATIVE_CWD), files)
356 self.assertEquals(sorted(DEPENDENCIES), files)
357
358 if sys.platform == 'win32':
359 # Symlink stuff is unsupported there, remove them from the list.
360 files = [f for f in files if not f.startswith('symlink_')]
361
362 # TODO(csharp): touched_only is disabled until crbug.com/150823 is fixed.
363 files.remove('touch_only')
364
365 # modes read and trace are tested together.
366 modes_to_check = list(EXPECTED_MODES)
367 modes_to_check.remove('help')
368 modes_to_check.remove('merge')
369 modes_to_check.remove('noop')
370 modes_to_check.remove('read')
371 modes_to_check.remove('trace')
372 modes_to_check.append('trace_read_merge')
373 for mode in modes_to_check:
374 expected_cases = set('test_%s' % f for f in files)
375 fixture_name = 'Isolate_%s' % mode
376 fixture = getattr(sys.modules[__name__], fixture_name)
377 actual_cases = set(i for i in dir(fixture) if i.startswith('test_'))
378 missing = expected_cases - actual_cases
379 self.assertFalse(missing, '%s.%s' % (fixture_name, missing))
380
381
382 class Isolate_check(IsolateModeBase):
383 LEVEL = isolate.NO_INFO
384
385 def test_fail(self):
386 self._execute('check', 'fail.isolate', [], False)
387 self._expect_no_tree()
388 self._expect_results(['fail.py'], None, None, None)
389
390 def test_missing_trailing_slash(self):
391 try:
392 self._execute('check', 'missing_trailing_slash.isolate', [], False)
393 self.fail()
394 except subprocess.CalledProcessError:
395 pass
396 self._expect_no_tree()
397 self._expect_no_result()
398
399 def test_non_existent(self):
400 try:
401 self._execute('check', 'non_existent.isolate', [], False)
402 self.fail()
403 except subprocess.CalledProcessError:
404 pass
405 self._expect_no_tree()
406 self._expect_no_result()
407
408 def test_no_run(self):
409 self._execute('check', 'no_run.isolate', [], False)
410 self._expect_no_tree()
411 self._expect_results([], None, None, None)
412
413 # TODO(csharp): Disabled until crbug.com/150823 is fixed.
414 def do_not_test_touch_only(self):
415 self._execute('check', 'touch_only.isolate', ['-V', 'FLAG', 'gyp'], False)
416 self._expect_no_tree()
417 empty = os.path.join('files1', 'test_file1.txt')
418 self._expected_result(['touch_only.py', 'gyp'], None, empty)
419
420 def test_touch_root(self):
421 self._execute('check', 'touch_root.isolate', [], False)
422 self._expect_no_tree()
423 self._expect_results(['touch_root.py'], None, None, None)
424
425 def test_with_flag(self):
426 self._execute('check', 'with_flag.isolate', ['-V', 'FLAG', 'gyp'], False)
427 self._expect_no_tree()
428 self._expect_results(
429 ['with_flag.py', 'gyp'], None, {u'FLAG': u'gyp'}, None)
430
431 if sys.platform != 'win32':
432 def test_symlink_full(self):
433 self._execute('check', 'symlink_full.isolate', [], False)
434 self._expect_no_tree()
435 self._expect_results(['symlink_full.py'], None, None, None)
436
437 def test_symlink_partial(self):
438 self._execute('check', 'symlink_partial.isolate', [], False)
439 self._expect_no_tree()
440 self._expect_results(['symlink_partial.py'], None, None, None)
441
442
443 class Isolate_hashtable(IsolateModeBase):
444 LEVEL = isolate.WITH_HASH
445
446 def _gen_expected_tree(self, empty_file):
447 expected = [
448 v['sha-1'] for v in self._gen_files(False, empty_file).itervalues()
449 ]
450 expected.append(calc_sha1(self.result))
451 return expected
452
453 def _expected_hash_tree(self, empty_file):
454 """Verifies the files written in the temporary directory."""
455 self.assertEquals(
456 sorted(self._gen_expected_tree(empty_file)), self._result_tree())
457
458 def test_fail(self):
459 self._execute('hashtable', 'fail.isolate', [], False)
460 self._expected_hash_tree(None)
461 self._expect_results(['fail.py'], None, None, None)
462
463 def test_missing_trailing_slash(self):
464 try:
465 self._execute('hashtable', 'missing_trailing_slash.isolate', [], False)
466 self.fail()
467 except subprocess.CalledProcessError:
468 pass
469 self._expect_no_tree()
470 self._expect_no_result()
471
472 def test_non_existent(self):
473 try:
474 self._execute('hashtable', 'non_existent.isolate', [], False)
475 self.fail()
476 except subprocess.CalledProcessError:
477 pass
478 self._expect_no_tree()
479 self._expect_no_result()
480
481 def test_no_run(self):
482 self._execute('hashtable', 'no_run.isolate', [], False)
483 self._expected_hash_tree(None)
484 self._expect_results([], None, None, None)
485
486 # TODO(csharp): Disabled until crbug.com/150823 is fixed.
487 def do_not_test_touch_only(self):
488 self._execute(
489 'hashtable', 'touch_only.isolate', ['-V', 'FLAG', 'gyp'], False)
490 empty = os.path.join('files1', 'test_file1.txt')
491 self._expected_hash_tree(empty)
492 self._expected_result(['touch_only.py', 'gyp'], None, empty)
493
494 def test_touch_root(self):
495 self._execute('hashtable', 'touch_root.isolate', [], False)
496 self._expected_hash_tree(None)
497 self._expect_results(['touch_root.py'], None, None, None)
498
499 def test_with_flag(self):
500 self._execute(
501 'hashtable', 'with_flag.isolate', ['-V', 'FLAG', 'gyp'], False)
502 self._expected_hash_tree(None)
503 self._expect_results(
504 ['with_flag.py', 'gyp'], None, {u'FLAG': u'gyp'}, None)
505
506 if sys.platform != 'win32':
507 def test_symlink_full(self):
508 self._execute('hashtable', 'symlink_full.isolate', [], False)
509 # Construct our own tree.
510 expected = [
511 str(v['sha-1'])
512 for v in self._gen_files(False, None).itervalues() if 'sha-1' in v
513 ]
514 expected.append(calc_sha1(self.result))
515 self.assertEquals(sorted(expected), self._result_tree())
516 self._expect_results(['symlink_full.py'], None, None, None)
517
518 def test_symlink_partial(self):
519 self._execute('hashtable', 'symlink_partial.isolate', [], False)
520 # Construct our own tree.
521 expected = [
522 str(v['sha-1'])
523 for v in self._gen_files(False, None).itervalues() if 'sha-1' in v
524 ]
525 expected.append(calc_sha1(self.result))
526 self.assertEquals(sorted(expected), self._result_tree())
527 self._expect_results(['symlink_partial.py'], None, None, None)
528
529
530
531 class Isolate_remap(IsolateModeBase):
532 LEVEL = isolate.STATS_ONLY
533
534 def test_fail(self):
535 self._execute('remap', 'fail.isolate', [], False)
536 self._expected_tree()
537 self._expect_results(['fail.py'], None, None, None)
538
539 def test_missing_trailing_slash(self):
540 try:
541 self._execute('remap', 'missing_trailing_slash.isolate', [], False)
542 self.fail()
543 except subprocess.CalledProcessError:
544 pass
545 self._expect_no_tree()
546 self._expect_no_result()
547
548 def test_non_existent(self):
549 try:
550 self._execute('remap', 'non_existent.isolate', [], False)
551 self.fail()
552 except subprocess.CalledProcessError:
553 pass
554 self._expect_no_tree()
555 self._expect_no_result()
556
557 def test_no_run(self):
558 self._execute('remap', 'no_run.isolate', [], False)
559 self._expected_tree()
560 self._expect_results([], None, None, None)
561
562 # TODO(csharp): Disabled until crbug.com/150823 is fixed.
563 def do_not_test_touch_only(self):
564 self._execute('remap', 'touch_only.isolate', ['-V', 'FLAG', 'gyp'], False)
565 self._expected_tree()
566 empty = os.path.join('files1', 'test_file1.txt')
567 self._expect_results(
568 ['touch_only.py', 'gyp'], None, {u'FLAG': u'gyp'}, empty)
569
570 def test_touch_root(self):
571 self._execute('remap', 'touch_root.isolate', [], False)
572 self._expected_tree()
573 self._expect_results(['touch_root.py'], None, None, None)
574
575 def test_with_flag(self):
576 self._execute('remap', 'with_flag.isolate', ['-V', 'FLAG', 'gyp'], False)
577 self._expected_tree()
578 self._expect_results(
579 ['with_flag.py', 'gyp'], None, {u'FLAG': u'gyp'}, None)
580
581 if sys.platform != 'win32':
582 def test_symlink_full(self):
583 self._execute('remap', 'symlink_full.isolate', [], False)
584 self._expected_tree()
585 self._expect_results(['symlink_full.py'], None, None, None)
586
587 def test_symlink_partial(self):
588 self._execute('remap', 'symlink_partial.isolate', [], False)
589 self._expected_tree()
590 self._expect_results(['symlink_partial.py'], None, None, None)
591
592
593 class Isolate_run(IsolateModeBase):
594 LEVEL = isolate.STATS_ONLY
595
596 def _expect_empty_tree(self):
597 self.assertEquals([], self._result_tree())
598
599 def test_fail(self):
600 try:
601 self._execute('run', 'fail.isolate', [], False)
602 self.fail()
603 except subprocess.CalledProcessError:
604 pass
605 self._expect_empty_tree()
606 self._expect_results(['fail.py'], None, None, None)
607
608 def test_missing_trailing_slash(self):
609 try:
610 self._execute('run', 'missing_trailing_slash.isolate', [], False)
611 self.fail()
612 except subprocess.CalledProcessError:
613 pass
614 self._expect_no_tree()
615 self._expect_no_result()
616
617 def test_non_existent(self):
618 try:
619 self._execute('run', 'non_existent.isolate', [], False)
620 self.fail()
621 except subprocess.CalledProcessError:
622 pass
623 self._expect_no_tree()
624 self._expect_no_result()
625
626 def test_no_run(self):
627 try:
628 self._execute('run', 'no_run.isolate', [], False)
629 self.fail()
630 except subprocess.CalledProcessError:
631 pass
632 self._expect_empty_tree()
633 self._expect_no_result()
634
635 # TODO(csharp): Disabled until crbug.com/150823 is fixed.
636 def do_not_test_touch_only(self):
637 self._execute('run', 'touch_only.isolate', ['-V', 'FLAG', 'run'], False)
638 self._expect_empty_tree()
639 empty = os.path.join('files1', 'test_file1.txt')
640 self._expect_results(
641 ['touch_only.py', 'run'], None, {u'FLAG': u'run'}, empty)
642
643 def test_touch_root(self):
644 self._execute('run', 'touch_root.isolate', [], False)
645 self._expect_empty_tree()
646 self._expect_results(['touch_root.py'], None, None, None)
647
648 def test_with_flag(self):
649 self._execute('run', 'with_flag.isolate', ['-V', 'FLAG', 'run'], False)
650 # Not sure about the empty tree, should be deleted.
651 self._expect_empty_tree()
652 self._expect_results(
653 ['with_flag.py', 'run'], None, {u'FLAG': u'run'}, None)
654
655 if sys.platform != 'win32':
656 def test_symlink_full(self):
657 self._execute('run', 'symlink_full.isolate', [], False)
658 self._expect_empty_tree()
659 self._expect_results(['symlink_full.py'], None, None, None)
660
661 def test_symlink_partial(self):
662 self._execute('run', 'symlink_partial.isolate', [], False)
663 self._expect_empty_tree()
664 self._expect_results(['symlink_partial.py'], None, None, None)
665
666
667 class Isolate_trace_read_merge(IsolateModeBase):
668 # Tests both trace, read and merge.
669 # Warning: merge updates .isolate files. But they are currently in their
670 # canonical format so they shouldn't be changed.
671 LEVEL = isolate.STATS_ONLY
672
673 def _check_merge(self, filename):
674 filepath = isolate.trace_inputs.get_native_path_case(
675 os.path.join(ROOT_DIR, 'tests', 'isolate', filename))
676 expected = 'Updating %s\n' % filepath
677 with open(filepath, 'rb') as f:
678 old_content = f.read()
679 out = self._execute('merge', filename, [], True) or ''
680 self.assertEquals(expected, out)
681 with open(filepath, 'rb') as f:
682 new_content = f.read()
683 self.assertEquals(old_content, new_content)
684
685 def test_fail(self):
686 # Even if the process returns non-zero, the trace will still be good.
687 try:
688 self._execute('trace', 'fail.isolate', ['-v'], True)
689 self.fail()
690 except subprocess.CalledProcessError, e:
691 self.assertEquals('', e.output)
692 self._expect_no_tree()
693 self._expect_results(['fail.py'], None, None, None)
694 expected = self._wrap_in_condition(
695 {
696 isolate.KEY_TRACKED: [
697 'fail.py',
698 ],
699 })
700 out = self._execute('read', 'fail.isolate', [], True) or ''
701 self.assertEquals(expected.splitlines(), out.splitlines())
702 self._check_merge('fail.isolate')
703
704 def test_missing_trailing_slash(self):
705 try:
706 self._execute('trace', 'missing_trailing_slash.isolate', [], True)
707 self.fail()
708 except subprocess.CalledProcessError, e:
709 self.assertEquals('', e.output)
710 out = e.stderr
711 self._expect_no_tree()
712 self._expect_no_result()
713 expected = (
714 '\n'
715 'Error: Input directory %s must have a trailing slash\n' %
716 os.path.join(ROOT_DIR, 'tests', 'isolate', 'files1')
717 )
718 self.assertEquals(expected, out)
719
720 def test_non_existent(self):
721 try:
722 self._execute('trace', 'non_existent.isolate', [], True)
723 self.fail()
724 except subprocess.CalledProcessError, e:
725 self.assertEquals('', e.output)
726 out = e.stderr
727 self._expect_no_tree()
728 self._expect_no_result()
729 expected = (
730 '\n'
731 'Error: Input file %s doesn\'t exist\n' %
732 os.path.join(ROOT_DIR, 'tests', 'isolate', 'A_file_that_do_not_exist')
733 )
734 self.assertEquals(expected, out)
735
736 def test_no_run(self):
737 try:
738 self._execute('trace', 'no_run.isolate', [], True)
739 self.fail()
740 except subprocess.CalledProcessError, e:
741 out = e.output
742 err = e.stderr
743 self._expect_no_tree()
744 self._expect_no_result()
745 expected = '\nError: No command to run\n'
746 self.assertEquals('', out)
747 self.assertEquals(expected, err)
748
749 # TODO(csharp): Disabled until crbug.com/150823 is fixed.
750 def do_not_test_touch_only(self):
751 out = self._execute(
752 'trace', 'touch_only.isolate', ['-V', 'FLAG', 'trace'], True)
753 self.assertEquals('', out)
754 self._expect_no_tree()
755 empty = os.path.join('files1', 'test_file1.txt')
756 self._expect_results(
757 ['touch_only.py', 'trace'], None, {u'FLAG': u'trace'}, empty)
758 expected = {
759 isolate.KEY_TRACKED: [
760 'touch_only.py',
761 ],
762 isolate.KEY_TOUCHED: [
763 # Note that .isolate format mandates / and not os.path.sep.
764 'files1/test_file1.txt',
765 ],
766 }
767 if sys.platform != 'linux2':
768 # TODO(maruel): Implement touch-only tracing on non-linux.
769 del expected[isolate.KEY_TOUCHED]
770
771 out = self._execute('read', 'touch_only.isolate', [], True)
772 self.assertEquals(self._wrap_in_condition(expected), out)
773 self._check_merge('touch_only.isolate')
774
775 def test_touch_root(self):
776 out = self._execute('trace', 'touch_root.isolate', [], True)
777 self.assertEquals('', out)
778 self._expect_no_tree()
779 self._expect_results(['touch_root.py'], None, None, None)
780 expected = self._wrap_in_condition(
781 {
782 isolate.KEY_TRACKED: [
783 '../../isolate.py',
784 'touch_root.py',
785 ],
786 })
787 out = self._execute('read', 'touch_root.isolate', [], True)
788 self.assertEquals(expected, out)
789 self._check_merge('touch_root.isolate')
790
791 def test_with_flag(self):
792 out = self._execute(
793 'trace', 'with_flag.isolate', ['-V', 'FLAG', 'trace'], True)
794 self.assertEquals('', out)
795 self._expect_no_tree()
796 self._expect_results(
797 ['with_flag.py', 'trace'], None, {u'FLAG': u'trace'}, None)
798 expected = {
799 isolate.KEY_TRACKED: [
800 'with_flag.py',
801 ],
802 isolate.KEY_UNTRACKED: [
803 # Note that .isolate format mandates / and not os.path.sep.
804 'files1/',
805 ],
806 }
807 out = self._execute('read', 'with_flag.isolate', [], True)
808 self.assertEquals(self._wrap_in_condition(expected), out)
809 self._check_merge('with_flag.isolate')
810
811 if sys.platform != 'win32':
812 def test_symlink_full(self):
813 out = self._execute(
814 'trace', 'symlink_full.isolate', [], True)
815 self.assertEquals('', out)
816 self._expect_no_tree()
817 self._expect_results(['symlink_full.py'], None, None, None)
818 expected = {
819 isolate.KEY_TRACKED: [
820 'symlink_full.py',
821 ],
822 isolate.KEY_UNTRACKED: [
823 # Note that .isolate format mandates / and not os.path.sep.
824 'files2/',
825 ],
826 }
827 out = self._execute('read', 'symlink_full.isolate', [], True)
828 self.assertEquals(self._wrap_in_condition(expected), out)
829 self._check_merge('symlink_full.isolate')
830
831 def test_symlink_partial(self):
832 out = self._execute(
833 'trace', 'symlink_partial.isolate', [], True)
834 self.assertEquals('', out)
835 self._expect_no_tree()
836 self._expect_results(['symlink_partial.py'], None, None, None)
837 expected = {
838 isolate.KEY_TRACKED: [
839 'symlink_partial.py',
840 ],
841 isolate.KEY_UNTRACKED: [
842 'files2/test_file2.txt',
843 ],
844 }
845 out = self._execute('read', 'symlink_partial.isolate', [], True)
846 self.assertEquals(self._wrap_in_condition(expected), out)
847 self._check_merge('symlink_partial.isolate')
848
849
850 class IsolateNoOutdir(IsolateBase):
851 # Test without the --outdir flag.
852 # So all the files are first copied in the tempdir and the test is run from
853 # there.
854 def setUp(self):
855 super(IsolateNoOutdir, self).setUp()
856 self.root = os.path.join(self.tempdir, 'root')
857 os.makedirs(os.path.join(self.root, 'tests', 'isolate'))
858 for i in ('touch_root.isolate', 'touch_root.py'):
859 shutil.copy(
860 os.path.join(ROOT_DIR, 'tests', 'isolate', i),
861 os.path.join(self.root, 'tests', 'isolate', i))
862 shutil.copy(
863 os.path.join(ROOT_DIR, 'isolate.py'),
864 os.path.join(self.root, 'isolate.py'))
865
866 def _execute(self, mode, args, need_output):
867 """Executes isolate.py."""
868 cmd = [
869 sys.executable, os.path.join(ROOT_DIR, 'isolate.py'),
870 mode,
871 '--result', self.result,
872 ]
873 cmd.extend(args)
874
875 env = os.environ.copy()
876 if 'ISOLATE_DEBUG' in env:
877 del env['ISOLATE_DEBUG']
878
879 if need_output or not VERBOSE:
880 stdout = subprocess.PIPE
881 stderr = subprocess.STDOUT
882 else:
883 cmd.extend(['-v'] * 3)
884 stdout = None
885 stderr = None
886
887 logging.debug(cmd)
888 cwd = self.tempdir
889 p = subprocess.Popen(
890 cmd,
891 stdout=stdout,
892 stderr=stderr,
893 cwd=cwd,
894 env=env,
895 universal_newlines=True)
896 out, err = p.communicate()
897 if p.returncode:
898 raise CalledProcessError(p.returncode, cmd, out, err, cwd)
899 return out
900
901 def mode(self):
902 """Returns the execution mode corresponding to this test case."""
903 test_id = self.id().split('.')
904 self.assertEquals(3, len(test_id))
905 self.assertEquals('__main__', test_id[0])
906 return re.match('^test_([a-z]+)$', test_id[2]).group(1)
907
908 def filename(self):
909 """Returns the filename corresponding to this test case."""
910 filename = os.path.join(self.root, 'tests', 'isolate', 'touch_root.isolate')
911 self.assertTrue(os.path.isfile(filename), filename)
912 return filename
913
914 def test_check(self):
915 self._execute('check', ['--isolate', self.filename()], False)
916 files = sorted([
917 'isolate_smoke_test.results',
918 'isolate_smoke_test.state',
919 os.path.join('root', 'tests', 'isolate', 'touch_root.isolate'),
920 os.path.join('root', 'tests', 'isolate', 'touch_root.py'),
921 os.path.join('root', 'isolate.py'),
922 ])
923 self.assertEquals(files, list_files_tree(self.tempdir))
924
925 def test_hashtable(self):
926 self._execute('hashtable', ['--isolate', self.filename()], False)
927 files = sorted([
928 os.path.join(
929 'hashtable', calc_sha1(os.path.join(ROOT_DIR, 'isolate.py'))),
930 os.path.join(
931 'hashtable',
932 calc_sha1(
933 os.path.join(ROOT_DIR, 'tests', 'isolate', 'touch_root.py'))),
934 os.path.join('hashtable', calc_sha1(os.path.join(self.result))),
935 'isolate_smoke_test.results',
936 'isolate_smoke_test.state',
937 os.path.join('root', 'tests', 'isolate', 'touch_root.isolate'),
938 os.path.join('root', 'tests', 'isolate', 'touch_root.py'),
939 os.path.join('root', 'isolate.py'),
940 ])
941 self.assertEquals(files, list_files_tree(self.tempdir))
942
943 def test_remap(self):
944 self._execute('remap', ['--isolate', self.filename()], False)
945 files = sorted([
946 'isolate_smoke_test.results',
947 'isolate_smoke_test.state',
948 os.path.join('root', 'tests', 'isolate', 'touch_root.isolate'),
949 os.path.join('root', 'tests', 'isolate', 'touch_root.py'),
950 os.path.join('root', 'isolate.py'),
951 ])
952 self.assertEquals(files, list_files_tree(self.tempdir))
953
954 def test_run(self):
955 self._execute('run', ['--isolate', self.filename()], False)
956 files = sorted([
957 'isolate_smoke_test.results',
958 'isolate_smoke_test.state',
959 os.path.join('root', 'tests', 'isolate', 'touch_root.isolate'),
960 os.path.join('root', 'tests', 'isolate', 'touch_root.py'),
961 os.path.join('root', 'isolate.py'),
962 ])
963 self.assertEquals(files, list_files_tree(self.tempdir))
964
965 def test_trace_read_merge(self):
966 self._execute('trace', ['--isolate', self.filename()], False)
967 # Read the trace before cleaning up. No need to specify self.filename()
968 # because add the needed information is in the .state file.
969 output = self._execute('read', [], True)
970 expected = {
971 isolate.KEY_TRACKED: [
972 '../../isolate.py',
973 'touch_root.py',
974 ],
975 }
976 self.assertEquals(self._wrap_in_condition(expected), output)
977
978 output = self._execute('merge', [], True)
979 expected = 'Updating %s\n' % isolate.trace_inputs.get_native_path_case(
980 os.path.join(self.root, 'tests', 'isolate', 'touch_root.isolate'))
981 self.assertEquals(expected, output)
982 # In theory the file is going to be updated but in practice its content
983 # won't change.
984
985 # Clean the directory from the logs, which are OS-specific.
986 isolate.trace_inputs.get_api().clean_trace(
987 os.path.join(self.tempdir, 'isolate_smoke_test.results.log'))
988 files = sorted([
989 'isolate_smoke_test.results',
990 'isolate_smoke_test.state',
991 os.path.join('root', 'tests', 'isolate', 'touch_root.isolate'),
992 os.path.join('root', 'tests', 'isolate', 'touch_root.py'),
993 os.path.join('root', 'isolate.py'),
994 ])
995 self.assertEquals(files, list_files_tree(self.tempdir))
996
997
998 if __name__ == '__main__':
999 VERBOSE = '-v' in sys.argv
1000 logging.basicConfig(level=logging.DEBUG if VERBOSE else logging.ERROR)
1001 unittest.main()
OLDNEW
« no previous file with comments | « tools/isolate/tests/isolate/with_flag.py ('k') | tools/isolate/tests/isolate_test.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698