OLD | NEW |
1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
2 # Copyright (c) 2011 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2011 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 datetime | 9 import datetime |
10 import errno | 10 import errno |
(...skipping 143 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
154 sock.connect((host, port)) | 154 sock.connect((host, port)) |
155 logging.debug('%d was bound, waiting to free' % port) | 155 logging.debug('%d was bound, waiting to free' % port) |
156 except (socket.error, EnvironmentError): | 156 except (socket.error, EnvironmentError): |
157 logging.debug('%d now free' % port) | 157 logging.debug('%d now free' % port) |
158 return | 158 return |
159 finally: | 159 finally: |
160 sock.close() | 160 sock.close() |
161 assert False, '%d is still bound' % port | 161 assert False, '%d is still bound' % port |
162 | 162 |
163 | 163 |
164 _FAKE_LOADED = False | |
165 | |
166 class FakeReposBase(object): | 164 class FakeReposBase(object): |
167 """Generate both svn and git repositories to test gclient functionality. | 165 """Generate both svn and git repositories to test gclient functionality. |
168 | 166 |
169 Many DEPS functionalities need to be tested: Var, File, From, deps_os, hooks, | 167 Many DEPS functionalities need to be tested: Var, File, From, deps_os, hooks, |
170 use_relative_paths. | 168 use_relative_paths. |
171 | 169 |
172 And types of dependencies: Relative urls, Full urls, both svn and git. | 170 And types of dependencies: Relative urls, Full urls, both svn and git. |
173 | 171 |
174 populateSvn() and populateGit() need to be implemented by the subclass. | 172 populateSvn() and populateGit() need to be implemented by the subclass. |
175 """ | 173 """ |
176 # Hostname | 174 # Hostname |
177 NB_GIT_REPOS = 1 | 175 NB_GIT_REPOS = 1 |
178 USERS = [ | 176 USERS = [ |
179 ('user1@example.com', 'foo'), | 177 ('user1@example.com', 'foo'), |
180 ('user2@example.com', 'bar'), | 178 ('user2@example.com', 'bar'), |
181 ] | 179 ] |
182 | 180 |
183 def __init__(self, host=None): | 181 def __init__(self, host=None): |
184 global _FAKE_LOADED | |
185 if _FAKE_LOADED: | |
186 raise Exception('You can only start one FakeRepos at a time.') | |
187 _FAKE_LOADED = True | |
188 | |
189 self.trial = trial_dir.TrialDir('repos') | 182 self.trial = trial_dir.TrialDir('repos') |
190 self.host = host or '127.0.0.1' | 183 self.host = host or '127.0.0.1' |
191 # Format is [ None, tree, tree, ...] | 184 # Format is [ None, tree, tree, ...] |
192 # i.e. revisions are 1-based. | 185 # i.e. revisions are 1-based. |
193 self.svn_revs = [None] | 186 self.svn_revs = [None] |
194 # Format is { repo: [ None, (hash, tree), (hash, tree), ... ], ... } | 187 # Format is { repo: [ None, (hash, tree), (hash, tree), ... ], ... } |
195 # so reference looks like self.git_hashes[repo][rev][0] for hash and | 188 # so reference looks like self.git_hashes[repo][rev][0] for hash and |
196 # self.git_hashes[repo][rev][1] for it's tree snapshot. | 189 # self.git_hashes[repo][rev][1] for it's tree snapshot. |
197 # For consistency with self.svn_revs, it is 1-based too. | 190 # For consistency with self.svn_revs, it is 1-based too. |
198 self.git_hashes = {} | 191 self.git_hashes = {} |
(...skipping 115 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
314 return False | 307 return False |
315 write(join(self.svn_repo, 'conf', 'svnserve.conf'), | 308 write(join(self.svn_repo, 'conf', 'svnserve.conf'), |
316 '[general]\n' | 309 '[general]\n' |
317 'anon-access = read\n' | 310 'anon-access = read\n' |
318 'auth-access = write\n' | 311 'auth-access = write\n' |
319 'password-db = passwd\n') | 312 'password-db = passwd\n') |
320 text = '[users]\n' | 313 text = '[users]\n' |
321 text += ''.join('%s = %s\n' % (usr, pwd) for usr, pwd in self.USERS) | 314 text += ''.join('%s = %s\n' % (usr, pwd) for usr, pwd in self.USERS) |
322 write(join(self.svn_repo, 'conf', 'passwd'), text) | 315 write(join(self.svn_repo, 'conf', 'passwd'), text) |
323 | 316 |
| 317 # Necessary to be able to change revision properties |
| 318 revprop_hook_filename = join(self.svn_repo, 'hooks', 'pre-revprop-change') |
| 319 if sys.platform == 'win32': |
| 320 # TODO(kustermann): Test on Windows one day. |
| 321 write("%s.bat" % revprop_hook_filename, "") |
| 322 else: |
| 323 write(revprop_hook_filename, |
| 324 '#!/bin/sh\n' |
| 325 'exit 0\n') |
| 326 os.chmod(revprop_hook_filename, 0755) |
| 327 |
324 # Mac 10.6 ships with a buggy subversion build and we need this line | 328 # Mac 10.6 ships with a buggy subversion build and we need this line |
325 # to work around the bug. | 329 # to work around the bug. |
326 write(join(self.svn_repo, 'db', 'fsfs.conf'), | 330 write(join(self.svn_repo, 'db', 'fsfs.conf'), |
327 '[rep-sharing]\n' | 331 '[rep-sharing]\n' |
328 'enable-rep-sharing = false\n') | 332 'enable-rep-sharing = false\n') |
329 | 333 |
330 # Start the daemon. | 334 # Start the daemon. |
331 self.svn_port = find_free_port(self.host, 10000) | 335 self.svn_port = find_free_port(self.host, 10000) |
332 logging.debug('Using port %d' % self.svn_port) | 336 logging.debug('Using port %d' % self.svn_port) |
333 cmd = ['svnserve', '-d', '--foreground', '-r', self.root_dir, | 337 cmd = ['svnserve', '-d', '--foreground', '-r', self.root_dir, |
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
385 def _commit_svn(self, tree): | 389 def _commit_svn(self, tree): |
386 self._genTree(self.svn_checkout, tree) | 390 self._genTree(self.svn_checkout, tree) |
387 commit_svn(self.svn_checkout, self.USERS[0][0], self.USERS[0][1]) | 391 commit_svn(self.svn_checkout, self.USERS[0][0], self.USERS[0][1]) |
388 if self.svn_revs and self.svn_revs[-1]: | 392 if self.svn_revs and self.svn_revs[-1]: |
389 new_tree = self.svn_revs[-1].copy() | 393 new_tree = self.svn_revs[-1].copy() |
390 new_tree.update(tree) | 394 new_tree.update(tree) |
391 else: | 395 else: |
392 new_tree = tree.copy() | 396 new_tree = tree.copy() |
393 self.svn_revs.append(new_tree) | 397 self.svn_revs.append(new_tree) |
394 | 398 |
| 399 def _set_svn_commit_date(self, revision, date): |
| 400 subprocess2.check_output( |
| 401 ['svn', 'propset', 'svn:date', '--revprop', '-r', revision, date, |
| 402 self.svn_base, |
| 403 '--username', self.USERS[0][0], |
| 404 '--password', self.USERS[0][1], |
| 405 '--non-interactive']) |
| 406 |
395 def _commit_git(self, repo, tree): | 407 def _commit_git(self, repo, tree): |
396 repo_root = join(self.git_root, repo) | 408 repo_root = join(self.git_root, repo) |
397 self._genTree(repo_root, tree) | 409 self._genTree(repo_root, tree) |
398 commit_hash = commit_git(repo_root) | 410 commit_hash = commit_git(repo_root) |
399 if self.git_hashes[repo][-1]: | 411 if self.git_hashes[repo][-1]: |
400 new_tree = self.git_hashes[repo][-1][1].copy() | 412 new_tree = self.git_hashes[repo][-1][1].copy() |
401 new_tree.update(tree) | 413 new_tree.update(tree) |
402 else: | 414 else: |
403 new_tree = tree.copy() | 415 new_tree = tree.copy() |
404 self.git_hashes[repo].append((commit_hash, new_tree)) | 416 self.git_hashes[repo].append((commit_hash, new_tree)) |
(...skipping 230 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
635 'git_base': self.git_base, | 647 'git_base': self.git_base, |
636 # See self.__init__() for the format. Grab's the hash of the first | 648 # See self.__init__() for the format. Grab's the hash of the first |
637 # commit in repo_2. Only keep the first 7 character because of: | 649 # commit in repo_2. Only keep the first 7 character because of: |
638 # TODO(maruel): http://crosbug.com/3591 We need to strip the hash.. duh. | 650 # TODO(maruel): http://crosbug.com/3591 We need to strip the hash.. duh. |
639 'hash': self.git_hashes['repo_2'][1][0][:7] | 651 'hash': self.git_hashes['repo_2'][1][0][:7] |
640 }, | 652 }, |
641 'origin': 'git/repo_1@2\n', | 653 'origin': 'git/repo_1@2\n', |
642 }) | 654 }) |
643 | 655 |
644 | 656 |
| 657 class FakeRepoTransitive(FakeReposBase): |
| 658 """Implements populateSvn()""" |
| 659 |
| 660 def populateSvn(self): |
| 661 """Creates a few revisions of changes including a DEPS file.""" |
| 662 # Repos |
| 663 subprocess2.check_call( |
| 664 ['svn', 'checkout', self.svn_base, self.svn_checkout, |
| 665 '-q', '--non-interactive', '--no-auth-cache', |
| 666 '--username', self.USERS[0][0], '--password', self.USERS[0][1]]) |
| 667 assert os.path.isdir(join(self.svn_checkout, '.svn')) |
| 668 |
| 669 def file_system(rev): |
| 670 DEPS = """deps = { |
| 671 'src/different_repo': '%(svn_base)strunk/third_party', |
| 672 'src/different_repo_fixed': '%(svn_base)strunk/third_party@1', |
| 673 'src/same_repo': '/trunk/third_party', |
| 674 'src/same_repo_fixed': '/trunk/third_party@1', |
| 675 }""" % { 'svn_base': self.svn_base } |
| 676 return { |
| 677 'trunk/src/DEPS': DEPS, |
| 678 'trunk/src/origin': 'svn/trunk/src@%(rev)d' % { 'rev': rev }, |
| 679 'trunk/third_party/origin': |
| 680 'svn/trunk/third_party@%(rev)d' % { 'rev': rev }, |
| 681 } |
| 682 |
| 683 # We make three commits. We use always the same DEPS contents but |
| 684 # - 'trunk/src/origin' contains 'svn/trunk/src/origin@rX' |
| 685 # - 'trunk/third_party/origin' contains 'svn/trunk/third_party/origin@rX' |
| 686 # where 'X' is the revision number. |
| 687 # So the 'origin' files will change in every commit. |
| 688 self._commit_svn(file_system(1)) |
| 689 self._commit_svn(file_system(2)) |
| 690 self._commit_svn(file_system(3)) |
| 691 # We rewrite the timestamps so we can test that '--transitive' will take the |
| 692 # parent timestamp on different repositories and the parent revision |
| 693 # otherwise. |
| 694 self._set_svn_commit_date('1', '2011-10-01T03:00:00.000000Z') |
| 695 self._set_svn_commit_date('2', '2011-10-09T03:00:00.000000Z') |
| 696 self._set_svn_commit_date('3', '2011-10-02T03:00:00.000000Z') |
| 697 |
| 698 def populateGit(self): |
| 699 pass |
| 700 |
| 701 |
645 class FakeReposTestBase(trial_dir.TestCase): | 702 class FakeReposTestBase(trial_dir.TestCase): |
646 """This is vaguely inspired by twisted.""" | 703 """This is vaguely inspired by twisted.""" |
647 # static FakeRepos instance. Lazy loaded. | 704 # Static FakeRepos instances. Lazy loaded. |
648 FAKE_REPOS = None | 705 CACHED_FAKE_REPOS = {} |
649 # Override if necessary. | 706 # Override if necessary. |
650 FAKE_REPOS_CLASS = FakeRepos | 707 FAKE_REPOS_CLASS = FakeRepos |
651 | 708 |
652 def setUp(self): | 709 def setUp(self): |
653 super(FakeReposTestBase, self).setUp() | 710 super(FakeReposTestBase, self).setUp() |
654 if not FakeReposTestBase.FAKE_REPOS: | 711 if not self.FAKE_REPOS_CLASS in self.CACHED_FAKE_REPOS: |
655 # Lazy create the global instance. | 712 self.CACHED_FAKE_REPOS[self.FAKE_REPOS_CLASS] = self.FAKE_REPOS_CLASS() |
656 FakeReposTestBase.FAKE_REPOS = self.FAKE_REPOS_CLASS() | 713 self.FAKE_REPOS = self.CACHED_FAKE_REPOS[self.FAKE_REPOS_CLASS] |
657 # No need to call self.FAKE_REPOS.setUp(), it will be called by the child | 714 # No need to call self.FAKE_REPOS.setUp(), it will be called by the child |
658 # class. | 715 # class. |
659 # Do not define tearDown(), since super's version does the right thing and | 716 # Do not define tearDown(), since super's version does the right thing and |
660 # FAKE_REPOS is kept across tests. | 717 # self.FAKE_REPOS is kept across tests. |
661 | 718 |
662 @property | 719 @property |
663 def svn_base(self): | 720 def svn_base(self): |
664 """Shortcut.""" | 721 """Shortcut.""" |
665 return self.FAKE_REPOS.svn_base | 722 return self.FAKE_REPOS.svn_base |
666 | 723 |
667 @property | 724 @property |
668 def git_base(self): | 725 def git_base(self): |
669 """Shortcut.""" | 726 """Shortcut.""" |
670 return self.FAKE_REPOS.git_base | 727 return self.FAKE_REPOS.git_base |
(...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
743 fake.set_up_git() | 800 fake.set_up_git() |
744 print('Fake setup, press enter to quit or Ctrl-C to keep the checkouts.') | 801 print('Fake setup, press enter to quit or Ctrl-C to keep the checkouts.') |
745 sys.stdin.readline() | 802 sys.stdin.readline() |
746 except KeyboardInterrupt: | 803 except KeyboardInterrupt: |
747 trial_dir.TrialDir.SHOULD_LEAK.leak = True | 804 trial_dir.TrialDir.SHOULD_LEAK.leak = True |
748 return 0 | 805 return 0 |
749 | 806 |
750 | 807 |
751 if __name__ == '__main__': | 808 if __name__ == '__main__': |
752 sys.exit(main(sys.argv)) | 809 sys.exit(main(sys.argv)) |
OLD | NEW |