OLD | NEW |
---|---|
1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 3 # Use of this source code is governed by a BSD-style license that can be |
4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
5 | 5 |
6 import cStringIO | 6 import cStringIO |
7 import hashlib | 7 import hashlib |
8 import json | 8 import json |
9 import logging | 9 import logging |
10 import os | 10 import os |
11 import re | 11 import re |
12 import shutil | 12 import shutil |
13 import subprocess | 13 import subprocess |
14 import sys | 14 import sys |
15 import tempfile | 15 import tempfile |
16 import unittest | 16 import unittest |
17 | 17 |
18 import isolate | 18 import isolate |
19 | 19 |
20 ROOT_DIR = os.path.dirname(os.path.abspath(__file__)) | 20 ROOT_DIR = os.path.dirname(os.path.abspath(__file__)) |
21 VERBOSE = False | 21 VERBOSE = False |
22 | 22 |
23 | 23 |
24 # Keep the list hard coded. | |
25 EXPECTED_MODES = ('check', 'hashtable', 'remap', 'run', 'trace') | |
26 # These are per test case, not per mode. | |
27 RELATIVE_CWD = { | |
28 'fail': '.', | |
29 'missing_trailing_slash': '.', | |
30 'no_run': '.', | |
31 'non_existent': '.', | |
32 'touch_root': 'data/isolate', | |
33 'with_flag': '.', | |
34 } | |
35 DEPENDENCIES = { | |
36 'fail': ['fail.py'], | |
37 'missing_trailing_slash': [], | |
38 'no_run': [ | |
39 'no_run.isolate', 'files1/test_file1.txt', 'files1/test_file2.txt', | |
40 ], | |
41 'non_existent': [], | |
42 'touch_root': ['data/isolate/touch_root.py', 'isolate.py'], | |
43 'with_flag': [ | |
44 'with_flag.py', 'files1/test_file1.txt', 'files1/test_file2.txt', | |
45 ], | |
46 } | |
24 | 47 |
25 class CalledProcessError(subprocess.CalledProcessError): | 48 class CalledProcessError(subprocess.CalledProcessError): |
26 """Makes 2.6 version act like 2.7""" | 49 """Makes 2.6 version act like 2.7""" |
27 def __init__(self, returncode, cmd, output, cwd): | 50 def __init__(self, returncode, cmd, output, cwd): |
28 super(CalledProcessError, self).__init__(returncode, cmd) | 51 super(CalledProcessError, self).__init__(returncode, cmd) |
29 self.output = output | 52 self.output = output |
30 self.cwd = cwd | 53 self.cwd = cwd |
31 | 54 |
32 def __str__(self): | 55 def __str__(self): |
33 return super(CalledProcessError, self).__str__() + ( | 56 return super(CalledProcessError, self).__str__() + ( |
34 '\n' | 57 '\n' |
35 'cwd=%s\n%s') % (self.cwd, self.output) | 58 'cwd=%s\n%s') % (self.cwd, self.output) |
36 | 59 |
37 | 60 |
38 class Isolate(unittest.TestCase): | 61 class IsolateBase(unittest.TestCase): |
62 # To be defined by the subclass, it defines the amount of meta data saved by | |
63 # isolate.py for each file. | |
64 LEVEL = None | |
65 | |
39 def setUp(self): | 66 def setUp(self): |
40 # The tests assume the current directory is the file's directory. | 67 # The tests assume the current directory is the file's directory. |
41 os.chdir(ROOT_DIR) | 68 os.chdir(ROOT_DIR) |
42 self.tempdir = tempfile.mkdtemp() | 69 self.tempdir = tempfile.mkdtemp() |
43 self.result = os.path.join(self.tempdir, 'result') | 70 self.result = os.path.join(self.tempdir, 'isolate_smoke_test.result') |
44 self.child = os.path.join('data', 'isolate', 'child.py') | 71 self.outdir = os.path.join(self.tempdir, 'isolated') |
45 if VERBOSE: | |
46 print | |
47 self.files = [ | |
48 self.child, | |
49 os.path.join('data', 'isolate', 'files1', 'test_file1.txt'), | |
50 os.path.join('data', 'isolate', 'files1', 'test_file2.txt'), | |
51 ] | |
52 | 72 |
53 def tearDown(self): | 73 def tearDown(self): |
54 shutil.rmtree(self.tempdir) | 74 shutil.rmtree(self.tempdir) |
55 | 75 |
56 def _expected_tree(self, files): | 76 def _expect_no_tree(self): |
57 self.assertEquals(sorted(files), sorted(os.listdir(self.tempdir))) | 77 self.assertFalse(os.path.exists(self.outdir)) |
58 | 78 |
59 def _expected_result(self, with_hash, files, args, read_only): | 79 def _result_tree(self): |
80 actual = [] | |
81 for root, _dirs, files in os.walk(self.outdir): | |
82 actual.extend(os.path.join(root, f)[len(self.outdir)+1:] for f in files) | |
83 return sorted(actual) | |
84 | |
85 def _expected_tree(self): | |
86 """Verifies the files written in the temporary directory.""" | |
87 self.assertEquals(sorted(DEPENDENCIES[self.case()]), self._result_tree()) | |
88 | |
89 @staticmethod | |
90 def _fix_file_mode(filename, read_only): | |
60 if sys.platform == 'win32': | 91 if sys.platform == 'win32': |
61 mode = lambda _: 420 | 92 # Deterministic file mode for a deterministic OS. |
93 return 420 | |
nsylvain
2012/04/13 15:49:58
do we really need to mess with modes on windows? I
M-A Ruel
2012/04/13 15:56:16
In fact, it's not used. Let's look at removing thi
| |
62 else: | 94 else: |
63 # 4 modes are supported, 0755 (rwx), 0644 (rw), 0555 (rx), 0444 (r) | 95 # 4 modes are supported, 0755 (rwx), 0644 (rw), 0555 (rx), 0444 (r) |
64 min_mode = 0444 | 96 min_mode = 0444 |
65 if not read_only: | 97 if not read_only: |
66 min_mode |= 0200 | 98 min_mode |= 0200 |
67 def mode(filename): | 99 return (min_mode | 0111) if filename.endswith('.py') else min_mode |
68 return (min_mode | 0111) if filename.endswith('.py') else min_mode | |
69 | 100 |
70 if not isinstance(files, dict): | 101 def _gen_files(self, read_only): |
71 # Update files to dict. | 102 root_dir = ROOT_DIR |
72 files = dict((unicode(f), {u'mode': mode(f)}) for f in files) | 103 if RELATIVE_CWD[self.case()] == '.': |
73 # Add size and timestamp. | 104 #root_dir = os.path.join(root_dir, RELATIVE_CWD[self.case()]) |
nsylvain
2012/04/13 15:49:58
remove?
M-A Ruel
2012/04/13 15:56:16
done
| |
74 files = files.copy() | 105 root_dir = os.path.join(root_dir, 'data', 'isolate') |
75 for k, v in files.iteritems(): | 106 |
76 if v: | 107 files = dict((unicode(f), {}) for f in DEPENDENCIES[self.case()]) |
77 filestats = os.stat(k) | 108 |
109 if self.LEVEL > 1: | |
110 for k, v in files.iteritems(): | |
111 v[u'mode'] = self._fix_file_mode(k, read_only) | |
112 filestats = os.stat(os.path.join(root_dir, k)) | |
78 v[u'size'] = filestats.st_size | 113 v[u'size'] = filestats.st_size |
79 # Used the skip recalculating the hash. Use the most recent update | 114 # Used the skip recalculating the hash. Use the most recent update |
80 # time. | 115 # time. |
81 v[u'timestamp'] = int(round( | 116 v[u'timestamp'] = int(round(filestats.st_mtime)) |
82 max(filestats.st_mtime, filestats.st_ctime))) | |
83 | 117 |
118 if self.LEVEL > 2: | |
119 for filename in files: | |
120 # Calculate our hash. | |
121 h = hashlib.sha1() | |
122 h.update(open(os.path.join(root_dir, filename), 'rb').read()) | |
123 files[filename][u'sha-1'] = unicode(h.hexdigest()) | |
124 return files | |
125 | |
126 def _expected_result(self, args, read_only): | |
127 """Verifies self.result contains the expected data.""" | |
84 expected = { | 128 expected = { |
85 u'files': files, | 129 u'files': self._gen_files(read_only), |
86 u'relative_cwd': u'data/isolate', | 130 u'relative_cwd': unicode(RELATIVE_CWD[self.case()]), |
87 u'read_only': None, | 131 u'read_only': read_only, |
88 } | 132 } |
89 if args: | 133 if args: |
90 expected[u'command'] = [u'python'] + [unicode(x) for x in args] | 134 expected[u'command'] = [u'python'] + [unicode(x) for x in args] |
91 else: | 135 else: |
92 expected[u'command'] = [] | 136 expected[u'command'] = [] |
93 if with_hash: | |
94 for filename in expected[u'files']: | |
95 # Calculate our hash. | |
96 h = hashlib.sha1() | |
97 h.update(open(os.path.join(ROOT_DIR, filename), 'rb').read()) | |
98 expected[u'files'][filename][u'sha-1'] = unicode(h.hexdigest()) | |
99 | 137 |
100 actual = json.load(open(self.result, 'rb')) | 138 self.assertEquals(expected, json.load(open(self.result, 'rb'))) |
101 self.assertEquals(expected, actual) | |
102 return expected | 139 return expected |
103 | 140 |
104 def _execute(self, filename, args, need_output=False): | 141 def _expect_no_result(self): |
142 self.assertFalse(os.path.exists(self.result)) | |
143 | |
144 def _execute_base(self, args, need_output): | |
145 """Executes isolate.py.""" | |
146 # TODO(maruel): This is going away, temporary until DEPTH support is | |
147 # removed. | |
148 depth = os.path.join('data', 'isolate') | |
149 if RELATIVE_CWD[self.case()] != '.': | |
150 depth = '.' | |
105 cmd = [ | 151 cmd = [ |
106 sys.executable, os.path.join(ROOT_DIR, 'isolate.py'), | 152 sys.executable, os.path.join(ROOT_DIR, 'isolate.py'), |
107 '--variable', 'DEPTH=%s' % ROOT_DIR, | |
108 '--result', self.result, | 153 '--result', self.result, |
109 os.path.join(ROOT_DIR, 'data', 'isolate', filename), | 154 '--outdir', self.outdir, |
155 '-V', 'DEPTH=%s' % depth, | |
110 ] + args | 156 ] + args |
157 | |
111 env = os.environ.copy() | 158 env = os.environ.copy() |
112 if 'ISOLATE_DEBUG' in env: | 159 if 'ISOLATE_DEBUG' in env: |
113 del env['ISOLATE_DEBUG'] | 160 del env['ISOLATE_DEBUG'] |
161 | |
114 if need_output or not VERBOSE: | 162 if need_output or not VERBOSE: |
115 stdout = subprocess.PIPE | 163 stdout = subprocess.PIPE |
116 stderr = subprocess.STDOUT | 164 stderr = subprocess.STDOUT |
117 else: | 165 else: |
118 cmd.extend(['-v'] * 3) | 166 cmd.extend(['-v'] * 3) |
119 stdout = None | 167 stdout = None |
120 stderr = None | 168 stderr = None |
169 | |
121 cwd = ROOT_DIR | 170 cwd = ROOT_DIR |
122 p = subprocess.Popen( | 171 p = subprocess.Popen( |
123 cmd + args, | 172 cmd, |
124 stdout=stdout, | 173 stdout=stdout, |
125 stderr=stderr, | 174 stderr=stderr, |
126 cwd=cwd, | 175 cwd=cwd, |
127 env=env, | 176 env=env, |
128 universal_newlines=True) | 177 universal_newlines=True) |
129 out = p.communicate()[0] | 178 out = p.communicate()[0] |
130 if p.returncode: | 179 if p.returncode: |
131 raise CalledProcessError(p.returncode, cmd, out, cwd) | 180 raise CalledProcessError(p.returncode, cmd, out, cwd) |
132 return out | 181 return out |
133 | 182 |
183 def mode(self): | |
184 """Returns the execution mode corresponding to this test case.""" | |
185 test_id = self.id().split('.') | |
186 self.assertEquals(3, len(test_id)) | |
187 self.assertEquals('__main__', test_id[0]) | |
188 return re.match('^Isolate_([a-z]+)$', test_id[1]).group(1) | |
189 | |
190 def case(self): | |
191 """Returns the filename corresponding to this test case.""" | |
192 test_id = self.id().split('.') | |
193 return re.match('^test_([a-z_]+)$', test_id[2]).group(1) | |
194 | |
195 def filename(self): | |
196 """Returns the filename corresponding to this test case.""" | |
197 filename = os.path.join( | |
198 ROOT_DIR, 'data', 'isolate', self.case() + '.isolate') | |
199 self.assertTrue(os.path.isfile(filename), filename) | |
200 return filename | |
201 | |
202 def _execute(self, args=None, need_output=False): | |
203 """Deduces the arguments based on the test case and function names.""" | |
204 return self._execute_base( | |
205 [self.filename(), '--mode', self.mode()] + (args or []), need_output) | |
206 | |
207 | |
208 class Isolate(unittest.TestCase): | |
134 def test_help_modes(self): | 209 def test_help_modes(self): |
135 # Check coherency in the help and implemented modes. | 210 # Check coherency in the help and implemented modes. |
136 p = subprocess.Popen( | 211 p = subprocess.Popen( |
137 [sys.executable, os.path.join(ROOT_DIR, 'isolate.py'), '--help'], | 212 [sys.executable, os.path.join(ROOT_DIR, 'isolate.py'), '--help'], |
138 stdout=subprocess.PIPE, | 213 stdout=subprocess.PIPE, |
139 stderr=subprocess.STDOUT, | 214 stderr=subprocess.STDOUT, |
140 cwd=ROOT_DIR) | 215 cwd=ROOT_DIR) |
141 out = p.communicate()[0].splitlines() | 216 out = p.communicate()[0].splitlines() |
142 self.assertEquals(0, p.returncode) | 217 self.assertEquals(0, p.returncode) |
143 out = out[out.index('') + 1:] | 218 out = out[out.index('') + 1:] |
144 out = out[:out.index('')] | 219 out = out[:out.index('')] |
145 modes = [re.match(r'^ (\w+) .+', l) for l in out] | 220 modes = [re.match(r'^ (\w+) .+', l) for l in out] |
146 modes = tuple(m.group(1) for m in modes if m) | 221 modes = tuple(m.group(1) for m in modes if m) |
147 # Keep the list hard coded. | 222 self.assertEquals(EXPECTED_MODES, modes) |
148 expected = ('check', 'hashtable', 'remap', 'run', 'trace') | 223 |
149 self.assertEquals(expected, modes) | 224 def test_modes(self): |
150 self.assertEquals(expected, modes) | 225 # This is a bit redundant but make sure all combinations are tested. |
151 for mode in modes: | 226 files = sorted( |
152 self.assertTrue(hasattr(self, 'test_%s' % mode), mode) | 227 i[:-len('.isolate')] |
153 self._expected_tree([]) | 228 for i in os.listdir(os.path.join(ROOT_DIR, 'data', 'isolate')) |
154 | 229 if i.endswith('.isolate') |
155 def test_check(self): | 230 ) |
156 self._execute('fail.isolate', ['--mode', 'check']) | 231 self.assertEquals(sorted(RELATIVE_CWD), files) |
157 self._expected_tree(['result']) | 232 self.assertEquals(sorted(DEPENDENCIES), files) |
158 self._expected_result( | 233 for mode in EXPECTED_MODES: |
159 False, dict((f, {}) for f in self.files), ['child.py', '--fail'], False) | 234 expected_cases = set('test_%s' % f for f in files) |
160 | 235 fixture_name = 'Isolate_%s' % mode |
161 def test_check_no_run(self): | 236 fixture = getattr(sys.modules[__name__], fixture_name) |
162 self._execute('no_run.isolate', ['--mode', 'check']) | 237 actual_cases = set(i for i in dir(fixture) if i.startswith('test_')) |
163 self._expected_tree(['result']) | 238 missing = expected_cases - actual_cases |
164 self._expected_result( | 239 self.assertFalse(missing, '%s.%s' % (fixture_name, missing)) |
165 False, dict((f, {}) for f in self.files), None, False) | 240 |
166 | 241 |
167 def test_check_non_existent(self): | 242 class Isolate_check(IsolateBase): |
168 try: | 243 # No meta-data |
169 self._execute('non_existent.isolate', ['--mode', 'check']) | 244 LEVEL = 1 |
nsylvain
2012/04/13 15:49:58
i dont get the LEVEL thing yet.
M-A Ruel
2012/04/13 15:56:16
Used constants instead to relate to process_inputs
| |
170 self.fail() | 245 |
171 except subprocess.CalledProcessError: | 246 def test_fail(self): |
172 pass | 247 self._execute() |
173 self._expected_tree([]) | 248 self._expect_no_tree() |
174 | 249 self._expected_result(['fail.py'], None) |
175 def test_check_directory_no_slash(self): | 250 |
176 try: | 251 def test_missing_trailing_slash(self): |
177 self._execute('missing_trailing_slash.isolate', ['--mode', 'check']) | 252 try: |
178 self.fail() | 253 self._execute() |
179 except subprocess.CalledProcessError: | 254 self.fail() |
180 pass | 255 except subprocess.CalledProcessError: |
181 self._expected_tree([]) | 256 pass |
nsylvain
2012/04/13 15:49:58
you expect it to go there right? Can't you make s
M-A Ruel
2012/04/13 15:56:16
See line 254, self.fail() would mark the test as f
| |
182 | 257 self._expect_no_tree() |
183 def test_hashtable(self): | 258 self._expect_no_result() |
184 cmd = [ | 259 |
185 '--mode', 'hashtable', | 260 def test_non_existent(self): |
186 '--outdir', self.tempdir, | 261 try: |
187 ] | 262 self._execute() |
188 self._execute('no_run.isolate', cmd) | 263 self.fail() |
189 data = self._expected_result(True, self.files, None, False) | 264 except subprocess.CalledProcessError: |
190 self._expected_tree( | 265 pass |
191 [f['sha-1'] for f in data['files'].itervalues()] + ['result']) | 266 self._expect_no_tree() |
192 | 267 self._expect_no_result() |
193 def test_remap(self): | 268 |
194 cmd = [ | 269 def test_no_run(self): |
195 '--mode', 'remap', | 270 self._execute() |
196 '--outdir', self.tempdir, | 271 self._expect_no_tree() |
197 ] | 272 self._expected_result([], None) |
198 self._execute('no_run.isolate', cmd) | 273 |
199 self._expected_tree(['data', 'result']) | 274 def test_touch_root(self): |
200 self._expected_result( | 275 self._execute() |
201 False, | 276 self._expect_no_tree() |
202 self.files, | 277 self._expected_result(['touch_root.py'], None) |
203 None, | 278 |
204 False) | 279 def test_with_flag(self): |
205 | 280 self._execute(['-V', 'FLAG=gyp']) |
206 def test_run(self): | 281 self._expect_no_tree() |
207 self._execute('ok.isolate', ['--mode', 'run']) | 282 self._expected_result(['with_flag.py', 'gyp'], None) |
208 self._expected_tree(['result']) | 283 |
209 # cmd[0] is not generated from infiles[0] so it's not using a relative path. | 284 |
210 self._expected_result( | 285 class Isolate_hashtable(IsolateBase): |
211 False, self.files, ['child.py', '--ok'], False) | 286 # Full meta-data |
212 | 287 LEVEL = 3 |
213 def test_run_fail(self): | 288 |
214 try: | 289 def _expected_hash_tree(self): |
215 self._execute('fail.isolate', ['--mode', 'run']) | 290 """Verifies the files written in the temporary directory.""" |
216 self.fail() | 291 expected = [v['sha-1'] for v in self._gen_files(False).itervalues()] |
217 except subprocess.CalledProcessError: | 292 self.assertEquals(sorted(expected), self._result_tree()) |
218 pass | 293 |
219 self._expected_tree(['result']) | 294 def test_fail(self): |
220 | 295 self._execute() |
221 def test_trace(self): | 296 self._expected_hash_tree() |
222 out = self._execute('ok.isolate', ['--mode', 'trace'], True) | 297 self._expected_result(['fail.py'], None) |
223 self._expected_tree(['result', 'result.log']) | 298 |
224 # The 'result.log' log is OS-specific so we can't read it but we can read | 299 def test_missing_trailing_slash(self): |
225 # the gyp result. | 300 try: |
226 # cmd[0] is not generated from infiles[0] so it's not using a relative path. | 301 self._execute() |
227 self._expected_result( | 302 self.fail() |
228 False, self.files, ['child.py', '--ok'], False) | 303 except subprocess.CalledProcessError: |
229 | 304 pass |
230 expected_value = { | 305 self._expect_no_tree() |
306 self._expect_no_result() | |
307 | |
308 def test_non_existent(self): | |
309 try: | |
310 self._execute() | |
311 self.fail() | |
312 except subprocess.CalledProcessError: | |
313 pass | |
314 self._expect_no_tree() | |
315 self._expect_no_result() | |
316 | |
317 def test_no_run(self): | |
318 self._execute() | |
319 self._expected_hash_tree() | |
320 self._expected_result([], None) | |
321 | |
322 def test_touch_root(self): | |
323 self._execute() | |
324 self._expected_hash_tree() | |
325 self._expected_result(['touch_root.py'], None) | |
326 | |
327 def test_with_flag(self): | |
328 self._execute(['-V', 'FLAG=gyp']) | |
329 self._expected_hash_tree() | |
330 self._expected_result(['with_flag.py', 'gyp'], None) | |
331 | |
332 | |
333 class Isolate_remap(IsolateBase): | |
334 # Basic meta-data | |
335 LEVEL = 2 | |
336 | |
337 def test_fail(self): | |
338 self._execute() | |
339 self._expected_tree() | |
340 self._expected_result(['fail.py'], None) | |
341 | |
342 def test_missing_trailing_slash(self): | |
343 try: | |
344 self._execute() | |
345 self.fail() | |
346 except subprocess.CalledProcessError: | |
347 pass | |
348 self._expect_no_tree() | |
349 self._expect_no_result() | |
350 | |
351 def test_non_existent(self): | |
352 try: | |
353 self._execute() | |
354 self.fail() | |
355 except subprocess.CalledProcessError: | |
356 pass | |
357 self._expect_no_tree() | |
358 self._expect_no_result() | |
359 | |
360 def test_no_run(self): | |
361 self._execute() | |
362 self._expected_tree() | |
363 self._expected_result([], None) | |
364 | |
365 def test_touch_root(self): | |
366 self._execute() | |
367 self._expected_tree() | |
368 self._expected_result(['touch_root.py'], None) | |
369 | |
370 def test_with_flag(self): | |
371 self._execute(['-V', 'FLAG=gyp']) | |
372 self._expected_tree() | |
373 self._expected_result(['with_flag.py', 'gyp'], None) | |
374 | |
375 | |
376 class Isolate_run(IsolateBase): | |
377 # Basic meta-data | |
378 LEVEL = 2 | |
nsylvain
2012/04/13 15:49:58
i have a hard time understanding the difference be
M-A Ruel
2012/04/13 15:56:16
Cleaned up.
nsylvain
2012/04/14 21:56:07
Still don't get it.
class Isolate_remap(IsolateBa
Marc-Antoine Ruel (Google)
2012/04/14 22:33:58
Should be much more explicit now.
| |
379 | |
380 def _expect_empty_tree(self): | |
381 self.assertEquals([], self._result_tree()) | |
382 | |
383 def test_fail(self): | |
384 try: | |
385 self._execute() | |
386 self.fail() | |
387 except subprocess.CalledProcessError: | |
388 pass | |
389 self._expect_empty_tree() | |
390 self._expected_result(['fail.py'], None) | |
391 | |
392 def test_missing_trailing_slash(self): | |
393 try: | |
394 self._execute() | |
395 self.fail() | |
396 except subprocess.CalledProcessError: | |
397 pass | |
398 self._expect_no_tree() | |
399 self._expect_no_result() | |
400 | |
401 def test_non_existent(self): | |
402 try: | |
403 self._execute() | |
404 self.fail() | |
405 except subprocess.CalledProcessError: | |
406 pass | |
407 self._expect_no_tree() | |
408 self._expect_no_result() | |
409 | |
410 def test_no_run(self): | |
411 try: | |
412 self._execute() | |
413 self.fail() | |
414 except subprocess.CalledProcessError: | |
415 pass | |
416 self._expect_empty_tree() | |
417 self._expected_result([], None) | |
418 | |
419 def test_touch_root(self): | |
420 self._execute() | |
421 self._expect_empty_tree() | |
422 self._expected_result(['touch_root.py'], None) | |
423 | |
424 def test_with_flag(self): | |
425 self._execute(['-V', 'FLAG=run']) | |
426 # Not sure about the empty tree, should be deleted. | |
427 self._expect_empty_tree() | |
428 self._expected_result(['with_flag.py', 'run'], None) | |
429 | |
430 | |
431 class Isolate_trace(IsolateBase): | |
432 # Basic meta-data | |
433 LEVEL = 2 | |
434 | |
435 @staticmethod | |
436 def _to_string(values): | |
437 buf = cStringIO.StringIO() | |
438 isolate.trace_inputs.pretty_print(values, buf) | |
439 return buf.getvalue() | |
440 | |
441 def test_fail(self): | |
442 try: | |
443 self._execute([], True) | |
444 self.fail() | |
445 except subprocess.CalledProcessError, e: | |
446 out = e.output | |
447 self._expect_no_tree() | |
448 self._expected_result(['fail.py'], None) | |
449 expected = 'Failure: 1\nFailing\n\n' | |
450 self.assertEquals(expected, out) | |
451 | |
452 def test_missing_trailing_slash(self): | |
453 try: | |
454 self._execute([], True) | |
455 self.fail() | |
456 except subprocess.CalledProcessError, e: | |
457 out = e.output | |
458 self._expect_no_tree() | |
459 self._expect_no_result() | |
460 expected = 'Input directory %s must have a trailing slash\n' % os.path.join( | |
461 ROOT_DIR, 'data', 'isolate', 'files1') | |
462 self.assertEquals(expected, out) | |
463 | |
464 def test_non_existent(self): | |
465 try: | |
466 self._execute([], True) | |
467 self.fail() | |
468 except subprocess.CalledProcessError, e: | |
469 out = e.output | |
470 self._expect_no_tree() | |
471 self._expect_no_result() | |
472 expected = 'Input file %s doesn\'t exist\n' % os.path.join( | |
473 ROOT_DIR, 'data', 'isolate', 'A_file_that_do_not_exist') | |
474 self.assertEquals(expected, out) | |
475 | |
476 def test_no_run(self): | |
477 try: | |
478 self._execute([], True) | |
479 self.fail() | |
480 except subprocess.CalledProcessError, e: | |
481 out = e.output | |
482 self._expect_no_tree() | |
483 self._expected_result([], None) | |
484 expected = 'No command to run\n' | |
485 self.assertEquals(expected, out) | |
486 | |
487 def test_touch_root(self): | |
488 out = self._execute([], True) | |
489 self._expect_no_tree() | |
490 self._expected_result(['touch_root.py'], None) | |
491 expected = { | |
231 'conditions': [ | 492 'conditions': [ |
232 ['OS=="%s"' % isolate.trace_inputs.get_flavor(), { | 493 ['OS=="%s"' % isolate.trace_inputs.get_flavor(), { |
233 'variables': { | 494 'variables': { |
234 isolate.trace_inputs.KEY_TRACKED: [ | 495 isolate.trace_inputs.KEY_TRACKED: [ |
235 'child.py', | 496 'touch_root.py', |
497 '../../isolate.py', | |
498 ], | |
499 }, | |
500 }], | |
501 ], | |
502 } | |
503 self.assertEquals(self._to_string(expected), out) | |
504 | |
505 def test_with_flag(self): | |
506 out = self._execute(['-V', 'FLAG=trace'], True) | |
507 self._expect_no_tree() | |
508 self._expected_result(['with_flag.py', 'trace'], None) | |
509 expected = { | |
510 'conditions': [ | |
511 ['OS=="%s"' % isolate.trace_inputs.get_flavor(), { | |
512 'variables': { | |
513 isolate.trace_inputs.KEY_TRACKED: [ | |
514 'with_flag.py', | |
236 ], | 515 ], |
237 isolate.trace_inputs.KEY_UNTRACKED: [ | 516 isolate.trace_inputs.KEY_UNTRACKED: [ |
238 'files1/', | 517 'files1/', |
239 ], | 518 ], |
240 }, | 519 }, |
241 }], | 520 }], |
242 ], | 521 ], |
243 } | 522 } |
244 expected_buffer = cStringIO.StringIO() | 523 self.assertEquals(self._to_string(expected), out) |
245 isolate.trace_inputs.pretty_print(expected_value, expected_buffer) | |
246 self.assertEquals(expected_buffer.getvalue(), out) | |
247 | 524 |
248 | 525 |
249 | 526 |
250 if __name__ == '__main__': | 527 if __name__ == '__main__': |
251 VERBOSE = '-v' in sys.argv | 528 VERBOSE = '-v' in sys.argv |
252 logging.basicConfig(level=logging.DEBUG if VERBOSE else logging.ERROR) | 529 logging.basicConfig(level=logging.DEBUG if VERBOSE else logging.ERROR) |
253 unittest.main() | 530 unittest.main() |
OLD | NEW |