| 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 |