OLD | NEW |
| (Empty) |
1 #!/usr/bin/env python | |
2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
3 # Use of this source code is governed by a BSD-style license that can be | |
4 # found in the LICENSE file. | |
5 | |
6 import cStringIO | |
7 import hashlib | |
8 import json | |
9 import logging | |
10 import os | |
11 import re | |
12 import shutil | |
13 import subprocess | |
14 import sys | |
15 import tempfile | |
16 import unittest | |
17 | |
18 ROOT_DIR = os.path.dirname(os.path.abspath(__file__)) | |
19 VERBOSE = False | |
20 | |
21 | |
22 class CalledProcessError(subprocess.CalledProcessError): | |
23 """Makes 2.6 version act like 2.7""" | |
24 def __init__(self, returncode, cmd, output, cwd): | |
25 super(CalledProcessError, self).__init__(returncode, cmd) | |
26 self.output = output | |
27 self.cwd = cwd | |
28 | |
29 def __str__(self): | |
30 return super(CalledProcessError, self).__str__() + ( | |
31 '\n' | |
32 'cwd=%s\n%s') % (self.cwd, self.output) | |
33 | |
34 | |
35 class Isolate(unittest.TestCase): | |
36 def setUp(self): | |
37 # The reason is that isolate_test.py --ok is run in a temporary directory | |
38 # without access to isolate.py | |
39 import isolate | |
40 self.isolate = isolate | |
41 self.tempdir = tempfile.mkdtemp() | |
42 self.result = os.path.join(self.tempdir, 'result') | |
43 if VERBOSE: | |
44 print | |
45 | |
46 def tearDown(self): | |
47 shutil.rmtree(self.tempdir) | |
48 | |
49 def _expected_tree(self, files): | |
50 self.assertEquals(sorted(files), sorted(os.listdir(self.tempdir))) | |
51 | |
52 def _expected_result(self, with_hash, files, args, read_only): | |
53 if sys.platform == 'win32': | |
54 mode = lambda _: 420 | |
55 else: | |
56 # 4 modes are supported, 0755 (rwx), 0644 (rw), 0555 (rx), 0444 (r) | |
57 min_mode = 0444 | |
58 if not read_only: | |
59 min_mode |= 0200 | |
60 def mode(filename): | |
61 return (min_mode | 0111) if filename.endswith('.py') else min_mode | |
62 expected = { | |
63 u'command': | |
64 [unicode(sys.executable)] + | |
65 [unicode(x) for x in args], | |
66 u'files': dict((unicode(f), {u'mode': mode(f)}) for f in files), | |
67 u'relative_cwd': u'.', | |
68 u'read_only': False, | |
69 } | |
70 if with_hash: | |
71 for filename in expected[u'files']: | |
72 # Calculate our hash. | |
73 h = hashlib.sha1() | |
74 h.update(open(os.path.join(ROOT_DIR, filename), 'rb').read()) | |
75 expected[u'files'][filename][u'sha-1'] = h.hexdigest() | |
76 | |
77 actual = json.load(open(self.result, 'rb')) | |
78 self.assertEquals(expected, actual) | |
79 return expected | |
80 | |
81 def _execute(self, args, need_output=False): | |
82 cmd = [ | |
83 sys.executable, os.path.join(ROOT_DIR, 'isolate.py'), | |
84 '--root', ROOT_DIR, | |
85 '--result', self.result, | |
86 ] | |
87 if need_output or not VERBOSE: | |
88 stdout = subprocess.PIPE | |
89 stderr = subprocess.STDOUT | |
90 else: | |
91 cmd.extend(['-v'] * 3) | |
92 stdout = None | |
93 stderr = None | |
94 cwd = ROOT_DIR | |
95 p = subprocess.Popen( | |
96 cmd + args, | |
97 stdout=stdout, | |
98 stderr=stderr, | |
99 cwd=cwd, | |
100 universal_newlines=True) | |
101 out = p.communicate()[0] | |
102 if p.returncode: | |
103 raise CalledProcessError(p.returncode, cmd, out, cwd) | |
104 return out | |
105 | |
106 def test_help_modes(self): | |
107 # Check coherency in the help and implemented modes. | |
108 p = subprocess.Popen( | |
109 [sys.executable, os.path.join(ROOT_DIR, 'isolate.py'), '--help'], | |
110 stdout=subprocess.PIPE, | |
111 stderr=subprocess.STDOUT, | |
112 cwd=ROOT_DIR) | |
113 out = p.communicate()[0].splitlines() | |
114 self.assertEquals(0, p.returncode) | |
115 out = out[out.index('') + 1:] | |
116 out = out[:out.index('')] | |
117 modes = [re.match(r'^ (\w+) .+', l) for l in out] | |
118 modes = tuple(m.group(1) for m in modes if m) | |
119 # Keep the list hard coded. | |
120 expected = ('check', 'hashtable', 'remap', 'run', 'trace') | |
121 self.assertEquals(expected, modes) | |
122 self.assertEquals(expected, modes) | |
123 for mode in modes: | |
124 self.assertTrue(hasattr(self, 'test_%s' % mode), mode) | |
125 self._expected_tree([]) | |
126 | |
127 def test_check(self): | |
128 cmd = [ | |
129 '--mode', 'check', | |
130 'isolate_test.py', | |
131 ] | |
132 self._execute(cmd) | |
133 self._expected_tree(['result']) | |
134 self._expected_result( | |
135 False, | |
136 ['isolate_test.py'], | |
137 [os.path.join('.', 'isolate_test.py')], | |
138 False) | |
139 | |
140 def test_check_non_existant(self): | |
141 cmd = [ | |
142 '--mode', 'check', | |
143 'NonExistentFile', | |
144 ] | |
145 try: | |
146 self._execute(cmd) | |
147 self.fail() | |
148 except subprocess.CalledProcessError: | |
149 pass | |
150 self._expected_tree([]) | |
151 | |
152 def test_check_directory_no_slash(self): | |
153 cmd = [ | |
154 '--mode', 'check', | |
155 # Trailing slash missing. | |
156 os.path.join('data', 'isolate'), | |
157 ] | |
158 try: | |
159 self._execute(cmd) | |
160 self.fail() | |
161 except subprocess.CalledProcessError: | |
162 pass | |
163 self._expected_tree([]) | |
164 | |
165 def test_check_abs_path(self): | |
166 cmd = [ | |
167 '--mode', 'check', | |
168 'isolate_test.py', | |
169 '--', | |
170 os.path.join(ROOT_DIR, 'isolate_test.py'), | |
171 ] | |
172 self._execute(cmd) | |
173 self._expected_tree(['result']) | |
174 self._expected_result( | |
175 False, ['isolate_test.py'], ['isolate_test.py'], False) | |
176 | |
177 def test_hashtable(self): | |
178 cmd = [ | |
179 '--mode', 'hashtable', | |
180 '--outdir', self.tempdir, | |
181 'isolate_test.py', | |
182 os.path.join('data', 'isolate') + os.path.sep, | |
183 ] | |
184 self._execute(cmd) | |
185 files = [ | |
186 'isolate_test.py', | |
187 os.path.join('data', 'isolate', 'test_file1.txt'), | |
188 os.path.join('data', 'isolate', 'test_file2.txt'), | |
189 ] | |
190 data = self._expected_result( | |
191 True, files, [os.path.join('.', 'isolate_test.py')], False) | |
192 self._expected_tree( | |
193 [f['sha-1'] for f in data['files'].itervalues()] + ['result']) | |
194 | |
195 def test_remap(self): | |
196 cmd = [ | |
197 '--mode', 'remap', | |
198 '--outdir', self.tempdir, | |
199 'isolate_test.py', | |
200 ] | |
201 self._execute(cmd) | |
202 self._expected_tree(['isolate_test.py', 'result']) | |
203 self._expected_result( | |
204 False, | |
205 ['isolate_test.py'], | |
206 [os.path.join('.', 'isolate_test.py')], | |
207 False) | |
208 | |
209 def test_run(self): | |
210 cmd = [ | |
211 '--mode', 'run', | |
212 'isolate_test.py', | |
213 '--', | |
214 sys.executable, 'isolate_test.py', '--ok', | |
215 ] | |
216 self._execute(cmd) | |
217 self._expected_tree(['result']) | |
218 # cmd[0] is not generated from infiles[0] so it's not using a relative path. | |
219 self._expected_result( | |
220 False, ['isolate_test.py'], ['isolate_test.py', '--ok'], False) | |
221 | |
222 def test_run_fail(self): | |
223 cmd = [ | |
224 '--mode', 'run', | |
225 'isolate_test.py', | |
226 '--', | |
227 sys.executable, 'isolate_test.py', '--fail', | |
228 ] | |
229 try: | |
230 self._execute(cmd) | |
231 self.fail() | |
232 except subprocess.CalledProcessError: | |
233 pass | |
234 self._expected_tree([]) | |
235 | |
236 def test_trace(self): | |
237 cmd = [ | |
238 '--mode', 'trace', | |
239 'isolate_test.py', | |
240 '--', | |
241 sys.executable, os.path.join(ROOT_DIR, 'isolate_test.py'), '--ok', | |
242 ] | |
243 out = self._execute(cmd, True) | |
244 expected_tree = ['result', 'result.log'] | |
245 if sys.platform == 'win32': | |
246 expected_tree.append('result.log.etl') | |
247 self._expected_tree(expected_tree) | |
248 # The 'result.log' log is OS-specific so we can't read it but we can read | |
249 # the gyp result. | |
250 # cmd[0] is not generated from infiles[0] so it's not using a relative path. | |
251 self._expected_result( | |
252 False, ['isolate_test.py'], ['isolate_test.py', '--ok'], False) | |
253 | |
254 expected_value = { | |
255 'conditions': [ | |
256 ['OS=="%s"' % self.isolate.trace_inputs.get_flavor(), { | |
257 'variables': { | |
258 'isolate_files': [ | |
259 '<(DEPTH)/isolate_test.py', | |
260 ], | |
261 }, | |
262 }], | |
263 ], | |
264 } | |
265 expected_buffer = cStringIO.StringIO() | |
266 self.isolate.trace_inputs.pretty_print(expected_value, expected_buffer) | |
267 self.assertEquals(expected_buffer.getvalue(), out) | |
268 | |
269 | |
270 def main(): | |
271 global VERBOSE | |
272 VERBOSE = '-v' in sys.argv | |
273 level = logging.DEBUG if VERBOSE else logging.ERROR | |
274 logging.basicConfig(level=level) | |
275 if len(sys.argv) == 1: | |
276 unittest.main() | |
277 if sys.argv[1] == '--ok': | |
278 return 0 | |
279 if sys.argv[1] == '--fail': | |
280 return 1 | |
281 | |
282 unittest.main() | |
283 | |
284 | |
285 if __name__ == '__main__': | |
286 sys.exit(main()) | |
OLD | NEW |