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 atexit |
9 import errno | 9 import errno |
10 import logging | 10 import logging |
11 import os | 11 import os |
12 import pprint | 12 import pprint |
13 import re | 13 import re |
14 import stat | 14 import stat |
15 import subprocess | 15 import subprocess |
16 import sys | 16 import sys |
17 import time | 17 import time |
18 import unittest | 18 import unittest |
19 | 19 |
| 20 sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) |
| 21 |
| 22 import scm |
20 | 23 |
21 ## Utility functions | 24 ## Utility functions |
22 | 25 |
23 | 26 |
24 def addKill(): | 27 def addKill(): |
25 """Add kill() method to subprocess.Popen for python <2.6""" | 28 """Add kill() method to subprocess.Popen for python <2.6""" |
26 if getattr(subprocess.Popen, 'kill', None): | 29 if getattr(subprocess.Popen, 'kill', None): |
27 return | 30 return |
28 if sys.platform == 'win32': | 31 if sys.platform == 'win32': |
29 def kill_win(process): | 32 def kill_win(process): |
(...skipping 112 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
142 if k not in dict2: | 145 if k not in dict2: |
143 diff[k] = v | 146 diff[k] = v |
144 elif v != dict2[k]: | 147 elif v != dict2[k]: |
145 diff[k] = (v, dict2[k]) | 148 diff[k] = (v, dict2[k]) |
146 for k, v in dict2.iteritems(): | 149 for k, v in dict2.iteritems(): |
147 if k not in dict1: | 150 if k not in dict1: |
148 diff[k] = v | 151 diff[k] = v |
149 return diff | 152 return diff |
150 | 153 |
151 | 154 |
152 def mangle_svn_tree(*args): | |
153 result = {} | |
154 for old_root, new_root, tree in args: | |
155 for k, v in tree.iteritems(): | |
156 if not k.startswith(old_root): | |
157 continue | |
158 result[join(new_root, k[len(old_root) + 1:]).replace(os.sep, '/')] = v | |
159 return result | |
160 | |
161 | |
162 def mangle_git_tree(*args): | |
163 result = {} | |
164 for new_root, tree in args: | |
165 for k, v in tree.iteritems(): | |
166 result[join(new_root, k)] = v | |
167 return result | |
168 | |
169 | |
170 def commit_svn(repo): | 155 def commit_svn(repo): |
171 """Commits the changes and returns the new revision number.""" | 156 """Commits the changes and returns the new revision number.""" |
172 # Basic parsing. | |
173 to_add = [] | 157 to_add = [] |
174 to_remove = [] | 158 to_remove = [] |
175 for item in Popen(['svn', 'status'], | 159 for status, filepath in scm.SVN.CaptureStatus(repo): |
176 cwd=repo).communicate()[0].splitlines(False): | 160 if status[0] == '?': |
177 if item[0] == '?': | 161 to_add.append(filepath) |
178 to_add.append(item[7:].strip()) | 162 elif status[0] == '!': |
179 elif item[0] == '!': | 163 to_remove.append(filepath) |
180 to_remove.append(item[7:].strip()) | |
181 if to_add: | 164 if to_add: |
182 check_call(['svn', 'add', '--no-auto-props', '-q'] + to_add, cwd=repo) | 165 check_call(['svn', 'add', '--no-auto-props', '-q'] + to_add, cwd=repo) |
183 if to_remove: | 166 if to_remove: |
184 check_call(['svn', 'remove', '-q'] + to_remove, cwd=repo) | 167 check_call(['svn', 'remove', '-q'] + to_remove, cwd=repo) |
185 proc = Popen(['svn', 'commit', repo, '-m', 'foo', '--non-interactive', | 168 proc = Popen(['svn', 'commit', repo, '-m', 'foo', '--non-interactive', |
186 '--no-auth-cache', '--username', 'user1', '--password', 'foo'], | 169 '--no-auth-cache', '--username', 'user1', '--password', 'foo'], |
187 cwd=repo) | 170 cwd=repo) |
188 out, err = proc.communicate() | 171 out, err = proc.communicate() |
189 match = re.search(r'revision (\d+).', out) | 172 match = re.search(r'revision (\d+).', out) |
190 if not match: | 173 if not match: |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
235 self.SHOULD_LEAK = True | 218 self.SHOULD_LEAK = True |
236 sys.argv.remove('-l') | 219 sys.argv.remove('-l') |
237 elif leak is not None: | 220 elif leak is not None: |
238 self.SHOULD_LEAK = leak | 221 self.SHOULD_LEAK = leak |
239 if host: | 222 if host: |
240 self.HOST = host | 223 self.HOST = host |
241 if trial_dir: | 224 if trial_dir: |
242 self.TRIAL_DIR = trial_dir | 225 self.TRIAL_DIR = trial_dir |
243 | 226 |
244 # Format is [ None, tree, tree, ...] | 227 # Format is [ None, tree, tree, ...] |
| 228 # i.e. revisions are 1-based. |
245 self.svn_revs = [None] | 229 self.svn_revs = [None] |
246 # Format is { repo: [ (hash, tree), (hash, tree), ... ], ... } | 230 # Format is { repo: [ None, (hash, tree), (hash, tree), ... ], ... } |
| 231 # so reference looks like self.git_hashes[repo][rev][0] for hash and |
| 232 # self.git_hashes[repo][rev][1] for it's tree snapshot. |
| 233 # For consistency with self.svn_revs, it is 1-based too. |
247 self.git_hashes = {} | 234 self.git_hashes = {} |
248 self.svnserve = None | 235 self.svnserve = None |
249 self.gitdaemon = None | 236 self.gitdaemon = None |
250 self.common_init = False | 237 self.common_init = False |
251 | 238 |
252 def trial_dir(self): | 239 def trial_dir(self): |
253 if not self.TRIAL_DIR: | 240 if not self.TRIAL_DIR: |
254 self.TRIAL_DIR = os.path.join( | 241 self.TRIAL_DIR = os.path.join( |
255 os.path.dirname(os.path.abspath(__file__)), '_trial') | 242 os.path.dirname(os.path.abspath(__file__)), '_trial') |
256 return self.TRIAL_DIR | 243 return self.TRIAL_DIR |
(...skipping 138 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
395 | 382 |
396 def setUpGIT(self): | 383 def setUpGIT(self): |
397 """Creates git repositories and start the servers.""" | 384 """Creates git repositories and start the servers.""" |
398 if self.gitdaemon: | 385 if self.gitdaemon: |
399 return True | 386 return True |
400 self.setUp() | 387 self.setUp() |
401 if sys.platform == 'win32': | 388 if sys.platform == 'win32': |
402 return False | 389 return False |
403 for repo in ['repo_%d' % r for r in range(1, 5)]: | 390 for repo in ['repo_%d' % r for r in range(1, 5)]: |
404 check_call(['git', 'init', '-q', join(self.git_root, repo)]) | 391 check_call(['git', 'init', '-q', join(self.git_root, repo)]) |
405 self.git_hashes[repo] = [] | 392 self.git_hashes[repo] = [None] |
406 | 393 |
407 # Testing: | 394 # Testing: |
408 # - dependency disapear | 395 # - dependency disapear |
409 # - dependency renamed | 396 # - dependency renamed |
410 # - versioned and unversioned reference | 397 # - versioned and unversioned reference |
411 # - relative and full reference | 398 # - relative and full reference |
412 # - deps_os | 399 # - deps_os |
413 # - var | 400 # - var |
414 # - hooks | 401 # - hooks |
415 # TODO(maruel): | 402 # TODO(maruel): |
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
472 'open(\\'src/git_hooked1\\', \\'w\\').write(\\'git_hooked1\\')'], | 459 'open(\\'src/git_hooked1\\', \\'w\\').write(\\'git_hooked1\\')'], |
473 }, | 460 }, |
474 { | 461 { |
475 # Should not be run. | 462 # Should not be run. |
476 'pattern': 'nonexistent', | 463 'pattern': 'nonexistent', |
477 'action': ['python', '-c', | 464 'action': ['python', '-c', |
478 'open(\\'src/git_hooked2\\', \\'w\\').write(\\'git_hooked2\\')'], | 465 'open(\\'src/git_hooked2\\', \\'w\\').write(\\'git_hooked2\\')'], |
479 }, | 466 }, |
480 ] | 467 ] |
481 """ % { | 468 """ % { |
| 469 'host': self.HOST, |
| 470 # See self.__init__() for the format. Grab's the hash of the first |
| 471 # commit in repo_2. Only keep the first 7 character because of: |
482 # TODO(maruel): http://crosbug.com/3591 We need to strip the hash.. duh. | 472 # TODO(maruel): http://crosbug.com/3591 We need to strip the hash.. duh. |
483 'host': self.HOST, | 473 'hash': self.git_hashes['repo_2'][1][0][:7] |
484 'hash': self.git_hashes['repo_2'][0][0][:7] | |
485 }, | 474 }, |
486 'origin': "git/repo_1@2\n" | 475 'origin': "git/repo_1@2\n" |
487 }) | 476 }) |
488 | 477 |
489 # Start the daemon. | 478 # Start the daemon. |
490 cmd = ['git', 'daemon', '--export-all', '--base-path=' + self.repos_dir] | 479 cmd = ['git', 'daemon', '--export-all', '--base-path=' + self.repos_dir] |
491 if self.HOST == '127.0.0.1': | 480 if self.HOST == '127.0.0.1': |
492 cmd.append('--listen=127.0.0.1') | 481 cmd.append('--listen=127.0.0.1') |
493 logging.debug(cmd) | 482 logging.debug(cmd) |
494 self.gitdaemon = Popen(cmd, cwd=self.repos_dir) | 483 self.gitdaemon = Popen(cmd, cwd=self.repos_dir) |
495 return True | 484 return True |
496 | 485 |
497 def _commit_svn(self, tree): | 486 def _commit_svn(self, tree): |
498 self._genTree(self.svn_root, tree) | 487 self._genTree(self.svn_root, tree) |
499 commit_svn(self.svn_root) | 488 commit_svn(self.svn_root) |
500 if self.svn_revs and self.svn_revs[-1]: | 489 if self.svn_revs and self.svn_revs[-1]: |
501 new_tree = self.svn_revs[-1].copy() | 490 new_tree = self.svn_revs[-1].copy() |
502 new_tree.update(tree) | 491 new_tree.update(tree) |
503 else: | 492 else: |
504 new_tree = tree.copy() | 493 new_tree = tree.copy() |
505 self.svn_revs.append(new_tree) | 494 self.svn_revs.append(new_tree) |
506 | 495 |
507 def _commit_git(self, repo, tree): | 496 def _commit_git(self, repo, tree): |
508 repo_root = join(self.git_root, repo) | 497 repo_root = join(self.git_root, repo) |
509 self._genTree(repo_root, tree) | 498 self._genTree(repo_root, tree) |
510 hash = commit_git(repo_root) | 499 hash = commit_git(repo_root) |
511 if self.git_hashes[repo]: | 500 if self.git_hashes[repo][-1]: |
512 new_tree = self.git_hashes[repo][-1][1].copy() | 501 new_tree = self.git_hashes[repo][-1][1].copy() |
513 new_tree.update(tree) | 502 new_tree.update(tree) |
514 else: | 503 else: |
515 new_tree = tree.copy() | 504 new_tree = tree.copy() |
516 self.git_hashes[repo].append((hash, new_tree)) | 505 self.git_hashes[repo].append((hash, new_tree)) |
517 | 506 |
518 | 507 |
519 class FakeReposTestBase(unittest.TestCase): | 508 class FakeReposTestBase(unittest.TestCase): |
520 """This is vaguely inspired by twisted.""" | 509 """This is vaguely inspired by twisted.""" |
521 | 510 |
(...skipping 13 matching lines...) Expand all Loading... |
535 self.root_dir = join(self.CLASS_ROOT_DIR, self.id()) | 524 self.root_dir = join(self.CLASS_ROOT_DIR, self.id()) |
536 rmtree(self.root_dir) | 525 rmtree(self.root_dir) |
537 os.makedirs(self.root_dir) | 526 os.makedirs(self.root_dir) |
538 self.svn_base = 'svn://%s/svn/' % self.FAKE_REPOS.HOST | 527 self.svn_base = 'svn://%s/svn/' % self.FAKE_REPOS.HOST |
539 self.git_base = 'git://%s/git/' % self.FAKE_REPOS.HOST | 528 self.git_base = 'git://%s/git/' % self.FAKE_REPOS.HOST |
540 | 529 |
541 def tearDown(self): | 530 def tearDown(self): |
542 if not self.FAKE_REPOS.SHOULD_LEAK: | 531 if not self.FAKE_REPOS.SHOULD_LEAK: |
543 rmtree(self.root_dir) | 532 rmtree(self.root_dir) |
544 | 533 |
545 def checkString(self, expected, result): | 534 def checkString(self, expected, result, msg=None): |
546 """Prints the diffs to ease debugging.""" | 535 """Prints the diffs to ease debugging.""" |
547 if expected != result: | 536 if expected != result: |
548 # Strip the begining | 537 # Strip the begining |
549 while expected and result and expected[0] == result[0]: | 538 while expected and result and expected[0] == result[0]: |
550 expected = expected[1:] | 539 expected = expected[1:] |
551 result = result[1:] | 540 result = result[1:] |
552 # The exception trace makes it hard to read so dump it too. | 541 # The exception trace makes it hard to read so dump it too. |
553 if '\n' in result: | 542 if '\n' in result: |
554 print result | 543 print result |
555 self.assertEquals(expected, result) | 544 self.assertEquals(expected, result, msg) |
556 | 545 |
557 def check(self, expected, results): | 546 def check(self, expected, results): |
558 """Checks stdout, stderr, retcode.""" | 547 """Checks stdout, stderr, retcode.""" |
559 self.checkString(expected[0], results[0]) | 548 self.checkString(expected[0], results[0]) |
560 self.checkString(expected[1], results[1]) | 549 self.checkString(expected[1], results[1]) |
561 self.assertEquals(expected[2], results[2]) | 550 self.assertEquals(expected[2], results[2]) |
562 | 551 |
563 def assertTree(self, tree, tree_root=None): | 552 def assertTree(self, tree, tree_root=None): |
564 """Diff the checkout tree with a dict.""" | 553 """Diff the checkout tree with a dict.""" |
565 if not tree_root: | 554 if not tree_root: |
566 tree_root = self.root_dir | 555 tree_root = self.root_dir |
567 actual = read_tree(tree_root) | 556 actual = read_tree(tree_root) |
568 diff = dict_diff(tree, actual) | 557 diff = dict_diff(tree, actual) |
569 if diff: | 558 if diff: |
570 logging.debug('Actual %s\n%s' % (tree_root, pprint.pformat(actual))) | 559 logging.debug('Actual %s\n%s' % (tree_root, pprint.pformat(actual))) |
571 logging.debug('Expected\n%s' % pprint.pformat(tree)) | 560 logging.debug('Expected\n%s' % pprint.pformat(tree)) |
572 logging.debug('Diff\n%s' % pprint.pformat(diff)) | 561 logging.debug('Diff\n%s' % pprint.pformat(diff)) |
573 self.assertEquals(diff, []) | 562 self.assertEquals(diff, []) |
574 | 563 |
| 564 def mangle_svn_tree(self, *args): |
| 565 """Creates a 'virtual directory snapshot' to compare with the actual result |
| 566 on disk.""" |
| 567 result = {} |
| 568 for item, new_root in args: |
| 569 old_root, rev = item.split('@', 1) |
| 570 tree = self.FAKE_REPOS.svn_revs[int(rev)] |
| 571 for k, v in tree.iteritems(): |
| 572 if not k.startswith(old_root): |
| 573 continue |
| 574 result[join(new_root, k[len(old_root) + 1:]).replace(os.sep, '/')] = v |
| 575 return result |
| 576 |
| 577 def mangle_git_tree(self, *args): |
| 578 """Creates a 'virtual directory snapshot' to compare with the actual result |
| 579 on disk.""" |
| 580 result = {} |
| 581 for item, new_root in args: |
| 582 repo, rev = item.split('@', 1) |
| 583 tree = self.gittree(repo, rev) |
| 584 for k, v in tree.iteritems(): |
| 585 result[join(new_root, k)] = v |
| 586 return result |
| 587 |
| 588 def githash(self, repo, rev): |
| 589 """Sort-hand: Returns the hash for a git 'revision'.""" |
| 590 return self.FAKE_REPOS.git_hashes[repo][int(rev)][0] |
| 591 |
| 592 def gittree(self, repo, rev): |
| 593 """Sort-hand: returns the directory tree for a git 'revision'.""" |
| 594 return self.FAKE_REPOS.git_hashes[repo][int(rev)][1] |
| 595 |
575 | 596 |
576 def main(argv): | 597 def main(argv): |
577 fake = FakeRepos() | 598 fake = FakeRepos() |
578 print 'Using %s' % fake.trial_dir() | 599 print 'Using %s' % fake.trial_dir() |
579 try: | 600 try: |
580 fake.setUp() | 601 fake.setUp() |
581 print('Fake setup, press enter to quit or Ctrl-C to keep the checkouts.') | 602 print('Fake setup, press enter to quit or Ctrl-C to keep the checkouts.') |
582 sys.stdin.readline() | 603 sys.stdin.readline() |
583 except KeyboardInterrupt: | 604 except KeyboardInterrupt: |
584 fake.SHOULD_LEAK = True | 605 fake.SHOULD_LEAK = True |
585 return 0 | 606 return 0 |
586 | 607 |
587 | 608 |
588 if __name__ == '__main__': | 609 if __name__ == '__main__': |
589 sys.exit(main(sys.argv)) | 610 sys.exit(main(sys.argv)) |
OLD | NEW |