OLD | NEW |
1 #!/usr/bin/python | 1 #!/usr/bin/python |
2 # Copyright (c) 2010 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2010 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 """Generate fake repositories for testing.""" | 6 """Generate fake repositories for testing.""" |
7 | 7 |
| 8 import atexit |
8 import logging | 9 import logging |
9 import os | 10 import os |
10 import re | 11 import re |
11 import shutil | 12 import shutil |
12 import subprocess | 13 import subprocess |
13 import sys | 14 import sys |
| 15 import unittest |
| 16 |
| 17 |
| 18 ## Utility functions |
14 | 19 |
15 | 20 |
16 def addKill(): | 21 def addKill(): |
17 """Add kill() method to subprocess.Popen for python <2.6""" | 22 """Add kill() method to subprocess.Popen for python <2.6""" |
18 if getattr(subprocess.Popen, 'kill', None): | 23 if getattr(subprocess.Popen, 'kill', None): |
19 return | 24 return |
20 if sys.platform.startswith('win'): | 25 if sys.platform.startswith('win'): |
21 def kill_win(process): | 26 def kill_win(process): |
22 import win32process | 27 import win32process |
23 return win32process.TerminateProcess(process._handle, -1) | 28 return win32process.TerminateProcess(process._handle, -1) |
(...skipping 13 matching lines...) Expand all Loading... |
37 | 42 |
38 def write(path, content): | 43 def write(path, content): |
39 f = open(path, 'wb') | 44 f = open(path, 'wb') |
40 f.write(content) | 45 f.write(content) |
41 f.close() | 46 f.close() |
42 | 47 |
43 | 48 |
44 join = os.path.join | 49 join = os.path.join |
45 | 50 |
46 | 51 |
47 def call(*args, **kwargs): | |
48 logging.debug(args[0]) | |
49 subprocess.call(*args, **kwargs) | |
50 | |
51 | |
52 def check_call(*args, **kwargs): | 52 def check_call(*args, **kwargs): |
53 logging.debug(args[0]) | 53 logging.debug(args[0]) |
54 subprocess.check_call(*args, **kwargs) | 54 subprocess.check_call(*args, **kwargs) |
55 | 55 |
56 | 56 |
57 def Popen(*args, **kwargs): | 57 def Popen(*args, **kwargs): |
58 kwargs.setdefault('stdout', subprocess.PIPE) | 58 kwargs.setdefault('stdout', subprocess.PIPE) |
59 kwargs.setdefault('stderr', subprocess.STDOUT) | 59 kwargs.setdefault('stderr', subprocess.STDOUT) |
60 logging.debug(args[0]) | 60 logging.debug(args[0]) |
61 return subprocess.Popen(*args, **kwargs) | 61 return subprocess.Popen(*args, **kwargs) |
62 | 62 |
63 | 63 |
| 64 def read_tree(tree_root): |
| 65 """Returns a dict of all the files in a tree. Defaults to self.root_dir.""" |
| 66 tree = {} |
| 67 for root, dirs, files in os.walk(tree_root): |
| 68 for d in filter(lambda x: x.startswith('.'), dirs): |
| 69 dirs.remove(d) |
| 70 for f in [join(root, f) for f in files if not f.startswith('.')]: |
| 71 tree[f[len(tree_root) + 1:]] = open(join(root, f), 'rb').read() |
| 72 return tree |
| 73 |
| 74 |
| 75 def dict_diff(dict1, dict2): |
| 76 diff = {} |
| 77 for k, v in dict1.iteritems(): |
| 78 if k not in dict2: |
| 79 diff[k] = v |
| 80 elif v != dict2[k]: |
| 81 diff[k] = (v, dict2[k]) |
| 82 for k, v in dict2.iteritems(): |
| 83 if k not in dict1: |
| 84 diff[k] = v |
| 85 return diff |
| 86 |
| 87 |
| 88 def mangle_svn_tree(*args): |
| 89 result = {} |
| 90 for old_root, new_root, tree in args: |
| 91 for k, v in tree.iteritems(): |
| 92 if not k.startswith(old_root): |
| 93 continue |
| 94 result[join(new_root, k[len(old_root) + 1:])] = v |
| 95 return result |
| 96 |
| 97 |
| 98 def mangle_git_tree(*args): |
| 99 result = {} |
| 100 for new_root, tree in args: |
| 101 for k, v in tree.iteritems(): |
| 102 result[join(new_root, k)] = v |
| 103 return result |
| 104 |
| 105 |
64 def commit_svn(repo): | 106 def commit_svn(repo): |
65 """Commits the changes and returns the new revision number.""" | 107 """Commits the changes and returns the new revision number.""" |
66 # Basic parsing. | 108 # Basic parsing. |
67 to_add = [] | 109 to_add = [] |
68 to_remove = [] | 110 to_remove = [] |
69 for item in Popen(['svn', 'status'], | 111 for item in Popen(['svn', 'status'], |
70 cwd=repo).communicate()[0].splitlines(False): | 112 cwd=repo).communicate()[0].splitlines(False): |
71 if item[0] == '?': | 113 if item[0] == '?': |
72 to_add.append(item[8:]) | 114 to_add.append(item[8:]) |
73 elif item[0] == '!': | 115 elif item[0] == '!': |
(...skipping 15 matching lines...) Expand all Loading... |
89 def commit_git(repo): | 131 def commit_git(repo): |
90 """Commits the changes and returns the new hash.""" | 132 """Commits the changes and returns the new hash.""" |
91 check_call(['git', 'add', '-A', '-f'], cwd=repo) | 133 check_call(['git', 'add', '-A', '-f'], cwd=repo) |
92 check_call(['git', 'commit', '-q', '--message', 'foo'], cwd=repo) | 134 check_call(['git', 'commit', '-q', '--message', 'foo'], cwd=repo) |
93 rev = Popen(['git', 'show-ref', '--head', 'HEAD'], | 135 rev = Popen(['git', 'show-ref', '--head', 'HEAD'], |
94 cwd=repo).communicate()[0].split(' ', 1)[0] | 136 cwd=repo).communicate()[0].split(' ', 1)[0] |
95 logging.debug('At revision %s' % rev) | 137 logging.debug('At revision %s' % rev) |
96 return rev | 138 return rev |
97 | 139 |
98 | 140 |
| 141 _FAKE_LOADED = False |
| 142 |
99 class FakeRepos(object): | 143 class FakeRepos(object): |
100 """Generate both svn and git repositories to test gclient functionality. | 144 """Generate both svn and git repositories to test gclient functionality. |
101 | 145 |
102 Many DEPS functionalities need to be tested: Var, File, From, deps_os, hooks, | 146 Many DEPS functionalities need to be tested: Var, File, From, deps_os, hooks, |
103 use_relative_paths. | 147 use_relative_paths. |
104 | 148 |
105 And types of dependencies: Relative urls, Full urls, both svn and git.""" | 149 And types of dependencies: Relative urls, Full urls, both svn and git.""" |
106 | 150 |
107 def __init__(self, trial_dir, leak, local_only): | 151 # Should leak the repositories. |
108 self.trial_dir = trial_dir | 152 SHOULD_LEAK = False |
109 self.repos_dir = os.path.join(self.trial_dir, 'repos') | 153 # Override if unhappy. |
110 self.git_root = join(self.repos_dir, 'git') | 154 TRIAL_DIR = None |
111 self.svn_root = join(self.repos_dir, 'svn_checkout') | 155 # Hostname |
112 self.leak = leak | 156 HOST = '127.0.0.1' |
113 self.local_only = local_only | 157 |
114 self.svnserve = [] | 158 def __init__(self, trial_dir=None, leak=None, host=None): |
115 self.gitdaemon = [] | 159 global _FAKE_LOADED |
116 addKill() | 160 if _FAKE_LOADED: |
117 rmtree(self.trial_dir) | 161 raise Exception('You can only start one FakeRepos at a time.') |
118 os.mkdir(self.trial_dir) | 162 _FAKE_LOADED = True |
119 os.mkdir(self.repos_dir) | 163 # Quick hack. |
| 164 if '-v' in sys.argv: |
| 165 logging.basicConfig(level=logging.DEBUG) |
| 166 if '-l' in sys.argv: |
| 167 self.SHOULD_LEAK = True |
| 168 sys.argv.remove('-l') |
| 169 elif leak is not None: |
| 170 self.SHOULD_LEAK = leak |
| 171 if host: |
| 172 self.HOST = host |
| 173 if trial_dir: |
| 174 self.TRIAL_DIR = trial_dir |
| 175 |
120 # Format is [ None, tree, tree, ...] | 176 # Format is [ None, tree, tree, ...] |
121 self.svn_revs = [None] | 177 self.svn_revs = [None] |
122 # Format is { repo: [ (hash, tree), (hash, tree), ... ], ... } | 178 # Format is { repo: [ (hash, tree), (hash, tree), ... ], ... } |
123 self.git_hashes = {} | 179 self.git_hashes = {} |
| 180 self.svnserve = None |
| 181 self.gitdaemon = None |
| 182 self.common_init = False |
| 183 |
| 184 def trial_dir(self): |
| 185 if not self.TRIAL_DIR: |
| 186 self.TRIAL_DIR = os.path.join( |
| 187 os.path.dirname(os.path.abspath(__file__)), '_trial') |
| 188 return self.TRIAL_DIR |
124 | 189 |
125 def setUp(self): | 190 def setUp(self): |
126 self.setUpSVN() | 191 """All late initialization comes here. |
127 self.setUpGIT() | 192 |
| 193 Note that it deletes all trial_dir() and not only repos_dir.""" |
| 194 if not self.common_init: |
| 195 self.common_init = True |
| 196 self.repos_dir = os.path.join(self.trial_dir(), 'repos') |
| 197 self.git_root = join(self.repos_dir, 'git') |
| 198 self.svn_root = join(self.repos_dir, 'svn_checkout') |
| 199 addKill() |
| 200 rmtree(self.trial_dir()) |
| 201 os.makedirs(self.repos_dir) |
| 202 atexit.register(self.tearDown) |
128 | 203 |
129 def tearDown(self): | 204 def tearDown(self): |
130 for i in self.svnserve: | 205 if self.svnserve: |
131 logging.debug('Killing svnserve pid %s' % i.pid) | 206 logging.debug('Killing svnserve pid %s' % self.svnserve.pid) |
132 i.kill() | 207 self.svnserve.kill() |
133 for i in self.gitdaemon: | 208 self.svnserve = None |
134 logging.debug('Killing git-daemon pid %s' % i.pid) | 209 if self.gitdaemon: |
135 i.kill() | 210 logging.debug('Killing git-daemon pid %s' % self.gitdaemon.pid) |
136 if not self.leak: | 211 self.gitdaemon.kill() |
137 logging.debug('Removing %s' % self.trial_dir) | 212 self.gitdaemon = None |
138 rmtree(self.trial_dir) | 213 if not self.SHOULD_LEAK: |
| 214 logging.debug('Removing %s' % self.trial_dir()) |
| 215 rmtree(self.trial_dir()) |
139 | 216 |
140 def _genTree(self, root, tree_dict): | 217 def _genTree(self, root, tree_dict): |
141 """For a dictionary of file contents, generate a filesystem.""" | 218 """For a dictionary of file contents, generate a filesystem.""" |
142 if not os.path.isdir(root): | 219 if not os.path.isdir(root): |
143 os.makedirs(root) | 220 os.makedirs(root) |
144 for (k, v) in tree_dict.iteritems(): | 221 for (k, v) in tree_dict.iteritems(): |
145 k_os = k.replace('/', os.sep) | 222 k_os = k.replace('/', os.sep) |
146 k_arr = k_os.split(os.sep) | 223 k_arr = k_os.split(os.sep) |
147 if len(k_arr) > 1: | 224 if len(k_arr) > 1: |
148 p = os.sep.join([root] + k_arr[:-1]) | 225 p = os.sep.join([root] + k_arr[:-1]) |
149 if not os.path.isdir(p): | 226 if not os.path.isdir(p): |
150 os.makedirs(p) | 227 os.makedirs(p) |
151 if v is None: | 228 if v is None: |
152 os.remove(join(root, k)) | 229 os.remove(join(root, k)) |
153 else: | 230 else: |
154 write(join(root, k), v) | 231 write(join(root, k), v) |
155 | 232 |
156 def setUpSVN(self): | 233 def setUpSVN(self): |
157 """Creates subversion repositories and start the servers.""" | 234 """Creates subversion repositories and start the servers.""" |
158 assert not self.svnserve | 235 if self.svnserve: |
| 236 return |
| 237 self.setUp() |
159 root = join(self.repos_dir, 'svn') | 238 root = join(self.repos_dir, 'svn') |
160 rmtree(root) | |
161 check_call(['svnadmin', 'create', root]) | 239 check_call(['svnadmin', 'create', root]) |
162 write(join(root, 'conf', 'svnserve.conf'), | 240 write(join(root, 'conf', 'svnserve.conf'), |
163 '[general]\n' | 241 '[general]\n' |
164 'anon-access = read\n' | 242 'anon-access = read\n' |
165 'auth-access = write\n' | 243 'auth-access = write\n' |
166 'password-db = passwd\n') | 244 'password-db = passwd\n') |
167 write(join(root, 'conf', 'passwd'), | 245 write(join(root, 'conf', 'passwd'), |
168 '[users]\n' | 246 '[users]\n' |
169 'user1 = foo\n' | 247 'user1 = foo\n' |
170 'user2 = bar\n') | 248 'user2 = bar\n') |
171 | 249 |
172 # Start the daemon. | 250 # Start the daemon. |
173 cmd = ['svnserve', '-d', '--foreground', '-r', self.repos_dir] | 251 cmd = ['svnserve', '-d', '--foreground', '-r', self.repos_dir] |
174 if self.local_only: | 252 if self.HOST == '127.0.0.1': |
175 cmd.append('--listen-host=127.0.0.1') | 253 cmd.append('--listen-host=127.0.0.1') |
176 logging.debug(cmd) | 254 logging.debug(cmd) |
177 self.svnserve.append(Popen(cmd, cwd=root)) | 255 self.svnserve = Popen(cmd, cwd=root) |
178 self.populateSvn() | 256 self.populateSvn() |
179 | 257 |
180 def populateSvn(self): | 258 def populateSvn(self): |
181 """Creates a few revisions of changes including DEPS files.""" | 259 """Creates a few revisions of changes including DEPS files.""" |
182 # Repos | 260 # Repos |
183 check_call(['svn', 'checkout', 'svn://127.0.0.1/svn', self.svn_root, '-q', | 261 check_call(['svn', 'checkout', 'svn://127.0.0.1/svn', self.svn_root, '-q', |
184 '--non-interactive', '--no-auth-cache', | 262 '--non-interactive', '--no-auth-cache', |
185 '--username', 'user1', '--password', 'foo']) | 263 '--username', 'user1', '--password', 'foo']) |
186 assert os.path.isdir(join(self.svn_root, '.svn')) | 264 assert os.path.isdir(join(self.svn_root, '.svn')) |
187 def file_system(rev, DEPS): | 265 def file_system(rev, DEPS): |
(...skipping 29 matching lines...) Expand all Loading... |
217 'DummyVariable': 'third_party', | 295 'DummyVariable': 'third_party', |
218 } | 296 } |
219 deps = { | 297 deps = { |
220 'src/other': 'svn://%(host)s/svn/trunk/other', | 298 'src/other': 'svn://%(host)s/svn/trunk/other', |
221 'src/third_party/fpp': '/trunk/' + Var('DummyVariable') + '/foo', | 299 'src/third_party/fpp': '/trunk/' + Var('DummyVariable') + '/foo', |
222 } | 300 } |
223 deps_os = { | 301 deps_os = { |
224 'mac': { | 302 'mac': { |
225 'src/third_party/prout': '/trunk/third_party/prout', | 303 'src/third_party/prout': '/trunk/third_party/prout', |
226 }, | 304 }, |
227 }""" % { 'host': '127.0.0.1' })) | 305 }""" % { 'host': self.HOST })) |
228 | 306 |
229 self._commit_svn(file_system(2, """ | 307 self._commit_svn(file_system(2, """ |
230 deps = { | 308 deps = { |
231 'src/other': 'svn://%(host)s/svn/trunk/other', | 309 'src/other': 'svn://%(host)s/svn/trunk/other', |
232 'src/third_party/foo': '/trunk/third_party/foo@1', | 310 'src/third_party/foo': '/trunk/third_party/foo@1', |
233 } | 311 } |
234 # I think this is wrong to have the hooks run from the base of the gclient | 312 # I think this is wrong to have the hooks run from the base of the gclient |
235 # checkout. It's maybe a bit too late to change that behavior. | 313 # checkout. It's maybe a bit too late to change that behavior. |
236 hooks = [ | 314 hooks = [ |
237 { | 315 { |
238 'pattern': '.', | 316 'pattern': '.', |
239 'action': ['python', '-c', | 317 'action': ['python', '-c', |
240 'open(\\'src/hooked1\\', \\'w\\').write(\\'hooked1\\')'], | 318 'open(\\'src/hooked1\\', \\'w\\').write(\\'hooked1\\')'], |
241 }, | 319 }, |
242 { | 320 { |
243 # Should not be run. | 321 # Should not be run. |
244 'pattern': 'nonexistent', | 322 'pattern': 'nonexistent', |
245 'action': ['python', '-c', | 323 'action': ['python', '-c', |
246 'open(\\'src/hooked2\\', \\'w\\').write(\\'hooked2\\')'], | 324 'open(\\'src/hooked2\\', \\'w\\').write(\\'hooked2\\')'], |
247 }, | 325 }, |
248 ] | 326 ] |
249 """ % { 'host': '127.0.0.1' })) | 327 """ % { 'host': self.HOST })) |
250 | 328 |
251 def setUpGIT(self): | 329 def setUpGIT(self): |
252 """Creates git repositories and start the servers.""" | 330 """Creates git repositories and start the servers.""" |
253 assert not self.gitdaemon | 331 if self.gitdaemon: |
254 rmtree(self.git_root) | 332 return |
| 333 self.setUp() |
255 for repo in ['repo_%d' % r for r in range(1, 5)]: | 334 for repo in ['repo_%d' % r for r in range(1, 5)]: |
256 check_call(['git', 'init', '-q', join(self.git_root, repo)]) | 335 check_call(['git', 'init', '-q', join(self.git_root, repo)]) |
257 self.git_hashes[repo] = [] | 336 self.git_hashes[repo] = [] |
258 | 337 |
259 # Testing: | 338 # Testing: |
260 # - dependency disapear | 339 # - dependency disapear |
261 # - dependency renamed | 340 # - dependency renamed |
262 # - versioned and unversioned reference | 341 # - versioned and unversioned reference |
263 # - relative and full reference | 342 # - relative and full reference |
264 # - deps_os | 343 # - deps_os |
265 # - var | 344 # - var |
266 # - hooks | 345 # - hooks |
267 # TODO(maruel): | 346 # TODO(maruel): |
268 # - File | 347 # - File |
269 # - $matching_files | 348 # - $matching_files |
270 # - use_relative_paths | 349 # - use_relative_paths |
271 self._commit_git('repo_1', { | 350 self._commit_git('repo_1', { |
272 'DEPS': """ | 351 'DEPS': """ |
273 vars = { | 352 vars = { |
274 'DummyVariable': 'repo', | 353 'DummyVariable': 'repo', |
275 } | 354 } |
276 deps = { | 355 deps = { |
277 'src/repo2': 'git://%(host)s/git/repo_2', | 356 'src/repo2': 'git://%(host)s/git/repo_2', |
278 'src/repo2/repo3': '/' + Var('DummyVariable') + '_3', | 357 'src/repo2/repo3': '/' + Var('DummyVariable') + '_3', |
279 } | 358 } |
280 deps_os = { | 359 deps_os = { |
281 'mac': { | 360 'mac': { |
282 'src/repo4': '/repo_4', | 361 'src/repo4': '/repo_4', |
283 }, | 362 }, |
284 }""" % { 'host': '127.0.0.1' }, | 363 }""" % { 'host': self.HOST }, |
285 'origin': 'git/repo_1@1\n', | 364 'origin': 'git/repo_1@1\n', |
286 }) | 365 }) |
287 | 366 |
288 self._commit_git('repo_2', { | 367 self._commit_git('repo_2', { |
289 'origin': "git/repo_2@1\n" | 368 'origin': "git/repo_2@1\n" |
290 }) | 369 }) |
291 | 370 |
292 self._commit_git('repo_2', { | 371 self._commit_git('repo_2', { |
293 'origin': "git/repo_2@2\n" | 372 'origin': "git/repo_2@2\n" |
294 }) | 373 }) |
(...skipping 29 matching lines...) Expand all Loading... |
324 'open(\\'src/hooked1\\', \\'w\\').write(\\'hooked1\\')'], | 403 'open(\\'src/hooked1\\', \\'w\\').write(\\'hooked1\\')'], |
325 }, | 404 }, |
326 { | 405 { |
327 # Should not be run. | 406 # Should not be run. |
328 'pattern': 'nonexistent', | 407 'pattern': 'nonexistent', |
329 'action': ['python', '-c', | 408 'action': ['python', '-c', |
330 'open(\\'src/hooked2\\', \\'w\\').write(\\'hooked2\\')'], | 409 'open(\\'src/hooked2\\', \\'w\\').write(\\'hooked2\\')'], |
331 }, | 410 }, |
332 ] | 411 ] |
333 """ % { | 412 """ % { |
334 # TODO(maruel): http://crosbug.com/3591 We need to strip the hash.. duh. | 413 # TODO(maruel): http://crosbug.com/3591 We need to strip the hash.. duh. |
335 'host': '127.0.0.1', 'hash': self.git_hashes['repo_2'][0][0][:7] }, | 414 'host': self.HOST, |
| 415 'hash': self.git_hashes['repo_2'][0][0][:7] |
| 416 }, |
336 'origin': "git/repo_1@2\n" | 417 'origin': "git/repo_1@2\n" |
337 }) | 418 }) |
338 | 419 |
339 # Start the daemon. | 420 # Start the daemon. |
340 cmd = ['git', 'daemon', '--export-all', '--base-path=' + self.repos_dir] | 421 cmd = ['git', 'daemon', '--export-all', '--base-path=' + self.repos_dir] |
341 if self.local_only: | 422 if self.HOST == '127.0.0.1': |
342 cmd.append('--listen=127.0.0.1') | 423 cmd.append('--listen=127.0.0.1') |
343 logging.debug(cmd) | 424 logging.debug(cmd) |
344 self.gitdaemon.append(Popen(cmd, cwd=self.repos_dir)) | 425 self.gitdaemon = Popen(cmd, cwd=self.repos_dir) |
345 | 426 |
346 def _commit_svn(self, tree): | 427 def _commit_svn(self, tree): |
347 self._genTree(self.svn_root, tree) | 428 self._genTree(self.svn_root, tree) |
348 commit_svn(self.svn_root) | 429 commit_svn(self.svn_root) |
349 if self.svn_revs and self.svn_revs[-1]: | 430 if self.svn_revs and self.svn_revs[-1]: |
350 new_tree = self.svn_revs[-1].copy() | 431 new_tree = self.svn_revs[-1].copy() |
351 new_tree.update(tree) | 432 new_tree.update(tree) |
352 else: | 433 else: |
353 new_tree = tree.copy() | 434 new_tree = tree.copy() |
354 self.svn_revs.append(new_tree) | 435 self.svn_revs.append(new_tree) |
355 | 436 |
356 def _commit_git(self, repo, tree): | 437 def _commit_git(self, repo, tree): |
357 repo_root = join(self.git_root, repo) | 438 repo_root = join(self.git_root, repo) |
358 self._genTree(repo_root, tree) | 439 self._genTree(repo_root, tree) |
359 hash = commit_git(repo_root) | 440 hash = commit_git(repo_root) |
360 if self.git_hashes[repo]: | 441 if self.git_hashes[repo]: |
361 new_tree = self.git_hashes[repo][-1][1].copy() | 442 new_tree = self.git_hashes[repo][-1][1].copy() |
362 new_tree.update(tree) | 443 new_tree.update(tree) |
363 else: | 444 else: |
364 new_tree = tree.copy() | 445 new_tree = tree.copy() |
365 self.git_hashes[repo].append((hash, new_tree)) | 446 self.git_hashes[repo].append((hash, new_tree)) |
366 | 447 |
367 | 448 |
| 449 class FakeReposTestBase(unittest.TestCase): |
| 450 """This is vaguely inspired by twisted.""" |
| 451 |
| 452 # Replace this in your subclass. |
| 453 CLASS_ROOT_DIR = None |
| 454 |
| 455 # static FakeRepos instance. |
| 456 FAKE_REPOS = FakeRepos() |
| 457 |
| 458 def setUp(self): |
| 459 unittest.TestCase.setUp(self) |
| 460 self.FAKE_REPOS.setUp() |
| 461 |
| 462 # Remove left overs and start fresh. |
| 463 if not self.CLASS_ROOT_DIR: |
| 464 self.CLASS_ROOT_DIR = join(self.FAKE_REPOS.trial_dir(), 'smoke') |
| 465 self.root_dir = join(self.CLASS_ROOT_DIR, self.id()) |
| 466 rmtree(self.root_dir) |
| 467 os.makedirs(self.root_dir) |
| 468 self.svn_base = 'svn://%s/svn/' % self.FAKE_REPOS.HOST |
| 469 self.git_base = 'git://%s/git/' % self.FAKE_REPOS.HOST |
| 470 |
| 471 def tearDown(self): |
| 472 if not self.FAKE_REPOS.SHOULD_LEAK: |
| 473 rmtree(self.root_dir) |
| 474 |
| 475 def checkString(self, expected, result): |
| 476 """Prints the diffs to ease debugging.""" |
| 477 if expected != result: |
| 478 # Strip the begining |
| 479 while expected and result and expected[0] == result[0]: |
| 480 expected = expected[1:] |
| 481 result = result[1:] |
| 482 # The exception trace makes it hard to read so dump it too. |
| 483 if '\n' in result: |
| 484 print result |
| 485 self.assertEquals(expected, result) |
| 486 |
| 487 def check(self, expected, results): |
| 488 """Checks stdout, stderr, retcode.""" |
| 489 self.checkString(expected[0], results[0]) |
| 490 self.checkString(expected[1], results[1]) |
| 491 self.assertEquals(expected[2], results[2]) |
| 492 |
| 493 def assertTree(self, tree, tree_root=None): |
| 494 """Diff the checkout tree with a dict.""" |
| 495 if not tree_root: |
| 496 tree_root = self.root_dir |
| 497 actual = read_tree(tree_root) |
| 498 diff = dict_diff(tree, actual) |
| 499 if diff: |
| 500 logging.debug('Actual %s\n%s' % (tree_root, pprint.pformat(actual))) |
| 501 logging.debug('Expected\n%s' % pprint.pformat(tree)) |
| 502 logging.debug('Diff\n%s' % pprint.pformat(diff)) |
| 503 |
| 504 |
368 def main(argv): | 505 def main(argv): |
369 leak = '-l' in argv | 506 fake = FakeRepos() |
370 if leak: | 507 print 'Using %s' % fake.trial_dir() |
371 argv.remove('-l') | |
372 verbose = '-v' in argv | |
373 if verbose: | |
374 logging.basicConfig(level=logging.DEBUG) | |
375 argv.remove('-v') | |
376 assert len(argv) == 1, argv | |
377 trial_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), | |
378 '_trial') | |
379 print 'Using %s' % trial_dir | |
380 fake = FakeRepos(trial_dir, leak, True) | |
381 try: | 508 try: |
382 fake.setUp() | 509 fake.setUp() |
383 print('Fake setup, press enter to quit or Ctrl-C to keep the checkouts.') | 510 print('Fake setup, press enter to quit or Ctrl-C to keep the checkouts.') |
384 sys.stdin.readline() | 511 sys.stdin.readline() |
385 except KeyboardInterrupt: | 512 except KeyboardInterrupt: |
386 fake.leak = True | 513 fake.SHOULD_LEAK = True |
387 finally: | |
388 fake.tearDown() | |
389 return 0 | 514 return 0 |
390 | 515 |
391 | 516 |
392 if __name__ == '__main__': | 517 if __name__ == '__main__': |
393 sys.exit(main(sys.argv)) | 518 sys.exit(main(sys.argv)) |
OLD | NEW |