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 hashlib | |
7 import json | |
8 import logging | |
9 import os | |
10 import shutil | |
11 import subprocess | |
12 import sys | |
13 import tempfile | |
14 import unittest | |
15 | |
16 ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) | |
17 sys.path.insert(0, ROOT_DIR) | |
18 | |
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, stderr, cwd): | |
25 super(CalledProcessError, self).__init__(returncode, cmd) | |
26 self.output = output | |
27 self.stderr = stderr | |
28 self.cwd = cwd | |
29 | |
30 def __str__(self): | |
31 return super(CalledProcessError, self).__str__() + ( | |
32 '\n' | |
33 'cwd=%s\n%s\n%s\n%s') % ( | |
34 self.cwd, | |
35 self.output, | |
36 self.stderr, | |
37 ' '.join(self.cmd)) | |
38 | |
39 | |
40 def list_files_tree(directory): | |
41 """Returns the list of all the files in a tree.""" | |
42 actual = [] | |
43 for root, _dirs, files in os.walk(directory): | |
44 actual.extend(os.path.join(root, f)[len(directory)+1:] for f in files) | |
45 return sorted(actual) | |
46 | |
47 | |
48 def calc_sha1(filepath): | |
49 """Calculates the SHA-1 hash for a file.""" | |
50 return hashlib.sha1(open(filepath, 'rb').read()).hexdigest() | |
51 | |
52 | |
53 def write_content(filepath, content): | |
54 with open(filepath, 'wb') as f: | |
55 f.write(content) | |
56 | |
57 | |
58 def write_json(filepath, data): | |
59 with open(filepath, 'wb') as f: | |
60 json.dump(data, f, sort_keys=True, indent=2) | |
61 | |
62 | |
63 class RunTestFromArchive(unittest.TestCase): | |
64 def setUp(self): | |
65 self.tempdir = tempfile.mkdtemp(prefix='run_test_from_archive_smoke_test') | |
66 logging.debug(self.tempdir) | |
67 # The "source" hash table. | |
68 self.table = os.path.join(self.tempdir, 'table') | |
69 os.mkdir(self.table) | |
70 # The slave-side cache. | |
71 self.cache = os.path.join(self.tempdir, 'cache') | |
72 | |
73 self.data_dir = os.path.join(ROOT_DIR, 'tests', 'run_test_from_archive') | |
74 | |
75 def tearDown(self): | |
76 shutil.rmtree(self.tempdir) | |
77 | |
78 def _result_tree(self): | |
79 return list_files_tree(self.tempdir) | |
80 | |
81 @staticmethod | |
82 def _run(args): | |
83 cmd = [sys.executable, os.path.join(ROOT_DIR, 'run_test_from_archive.py')] | |
84 cmd.extend(args) | |
85 if VERBOSE: | |
86 cmd.extend(['-v'] * 2) | |
87 pipe = None | |
88 else: | |
89 pipe = subprocess.PIPE | |
90 logging.debug(' '.join(cmd)) | |
91 proc = subprocess.Popen( | |
92 cmd, stdout=pipe, stderr=pipe, universal_newlines=True) | |
93 out, err = proc.communicate() | |
94 return out, err, proc.returncode | |
95 | |
96 def _store_result(self, result_data): | |
97 """Stores a .results file in the hash table.""" | |
98 result_text = json.dumps(result_data, sort_keys=True, indent=2) | |
99 result_sha1 = hashlib.sha1(result_text).hexdigest() | |
100 write_content(os.path.join(self.table, result_sha1), result_text) | |
101 return result_sha1 | |
102 | |
103 def _store(self, filename): | |
104 """Stores a test data file in the table. | |
105 | |
106 Returns its sha-1 hash. | |
107 """ | |
108 filepath = os.path.join(self.data_dir, filename) | |
109 h = calc_sha1(filepath) | |
110 shutil.copyfile(filepath, os.path.join(self.table, h)) | |
111 return h | |
112 | |
113 def _generate_args(self, sha1_hash): | |
114 """Generates the standard arguments used with sha1_hash as the hash. | |
115 | |
116 Returns a list of the required arguments. | |
117 """ | |
118 return [ | |
119 '--hash', sha1_hash, | |
120 '--cache', self.cache, | |
121 '--remote', self.table, | |
122 ] | |
123 | |
124 def test_result(self): | |
125 # Loads an arbitrary manifest on the file system. | |
126 manifest = os.path.join(self.data_dir, 'gtest_fake.results') | |
127 expected = [ | |
128 'state.json', | |
129 self._store('gtest_fake.py'), | |
130 calc_sha1(manifest), | |
131 ] | |
132 args = [ | |
133 '--manifest', manifest, | |
134 '--cache', self.cache, | |
135 '--remote', self.table, | |
136 ] | |
137 out, err, returncode = self._run(args) | |
138 if not VERBOSE: | |
139 self.assertEquals('', err) | |
140 self.assertEquals(1070, len(out), out) | |
141 self.assertEquals(6, returncode) | |
142 actual = list_files_tree(self.cache) | |
143 self.assertEquals(sorted(expected), actual) | |
144 | |
145 def test_hash(self): | |
146 # Loads the manifest from the store as a hash. | |
147 result_sha1 = self._store('gtest_fake.results') | |
148 expected = [ | |
149 'state.json', | |
150 self._store('gtest_fake.py'), | |
151 result_sha1, | |
152 ] | |
153 args = [ | |
154 '--hash', result_sha1, | |
155 '--cache', self.cache, | |
156 '--remote', self.table, | |
157 ] | |
158 out, err, returncode = self._run(args) | |
159 if not VERBOSE: | |
160 self.assertEquals('', err) | |
161 self.assertEquals(1070, len(out), out) | |
162 self.assertEquals(6, returncode) | |
163 actual = list_files_tree(self.cache) | |
164 self.assertEquals(sorted(expected), actual) | |
165 | |
166 def test_fail_empty_manifest(self): | |
167 result_sha1 = self._store_result({}) | |
168 expected = [ | |
169 'state.json', | |
170 result_sha1, | |
171 ] | |
172 out, err, returncode = self._run(self._generate_args(result_sha1)) | |
173 if not VERBOSE: | |
174 self.assertEquals('', out) | |
175 self.assertEquals('No command to run\n', err) | |
176 self.assertEquals(1, returncode) | |
177 actual = list_files_tree(self.cache) | |
178 self.assertEquals(sorted(expected), actual) | |
179 | |
180 def test_includes(self): | |
181 # Loads a manifest that includes another one. | |
182 | |
183 # References manifest1.results and gtest_fake.results. Maps file3.txt as | |
184 # file2.txt. | |
185 result_sha1 = self._store('check_files.results') | |
186 expected = [ | |
187 'state.json', | |
188 self._store('check_files.py'), | |
189 self._store('gtest_fake.py'), | |
190 self._store('gtest_fake.results'), | |
191 self._store('file1.txt'), | |
192 self._store('file3.txt'), | |
193 # Maps file1.txt. | |
194 self._store('manifest1.results'), | |
195 # References manifest1.results. Maps file2.txt but it is overriden. | |
196 self._store('manifest2.results'), | |
197 result_sha1, | |
198 ] | |
199 out, err, returncode = self._run(self._generate_args(result_sha1)) | |
200 if not VERBOSE: | |
201 self.assertEquals('', err) | |
202 self.assertEquals('Success\n', out) | |
203 self.assertEquals(0, returncode) | |
204 actual = list_files_tree(self.cache) | |
205 self.assertEquals(sorted(expected), actual) | |
206 | |
207 def test_link_all_hash_instances(self): | |
208 # Load a manifest file with the same file (same sha-1 hash), listed under | |
209 # two different names and ensure both are created. | |
210 result_sha1 = self._store('repeated_files.results') | |
211 expected = [ | |
212 'state.json', | |
213 result_sha1, | |
214 self._store('file1.txt'), | |
215 self._store('repeated_files.py') | |
216 ] | |
217 | |
218 out, err, returncode = self._run(self._generate_args(result_sha1)) | |
219 if not VERBOSE: | |
220 self.assertEquals('', err) | |
221 self.assertEquals('Success\n', out) | |
222 self.assertEquals(0, returncode) | |
223 actual = list_files_tree(self.cache) | |
224 self.assertEquals(sorted(expected), actual) | |
225 | |
226 | |
227 if __name__ == '__main__': | |
228 VERBOSE = '-v' in sys.argv | |
229 logging.basicConfig(level=logging.DEBUG if VERBOSE else logging.ERROR) | |
230 unittest.main() | |
OLD | NEW |