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 write("%s.bat" % revprop_hook_filename, "") | |
M-A Ruel
2013/04/17 14:54:09
Add a # TODO(kustermann): Test on Windows one day.
kustermann
2013/04/18 08:28:40
Done :-)
| |
321 else: | |
322 write(revprop_hook_filename, | |
323 '#!/bin/sh\n' | |
324 'exit 0\n') | |
325 os.chmod(revprop_hook_filename, 0755) | |
326 | |
324 # Mac 10.6 ships with a buggy subversion build and we need this line | 327 # Mac 10.6 ships with a buggy subversion build and we need this line |
325 # to work around the bug. | 328 # to work around the bug. |
326 write(join(self.svn_repo, 'db', 'fsfs.conf'), | 329 write(join(self.svn_repo, 'db', 'fsfs.conf'), |
327 '[rep-sharing]\n' | 330 '[rep-sharing]\n' |
328 'enable-rep-sharing = false\n') | 331 'enable-rep-sharing = false\n') |
329 | 332 |
330 # Start the daemon. | 333 # Start the daemon. |
331 self.svn_port = find_free_port(self.host, 10000) | 334 self.svn_port = find_free_port(self.host, 10000) |
332 logging.debug('Using port %d' % self.svn_port) | 335 logging.debug('Using port %d' % self.svn_port) |
333 cmd = ['svnserve', '-d', '--foreground', '-r', self.root_dir, | 336 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): | 388 def _commit_svn(self, tree): |
386 self._genTree(self.svn_checkout, tree) | 389 self._genTree(self.svn_checkout, tree) |
387 commit_svn(self.svn_checkout, self.USERS[0][0], self.USERS[0][1]) | 390 commit_svn(self.svn_checkout, self.USERS[0][0], self.USERS[0][1]) |
388 if self.svn_revs and self.svn_revs[-1]: | 391 if self.svn_revs and self.svn_revs[-1]: |
389 new_tree = self.svn_revs[-1].copy() | 392 new_tree = self.svn_revs[-1].copy() |
390 new_tree.update(tree) | 393 new_tree.update(tree) |
391 else: | 394 else: |
392 new_tree = tree.copy() | 395 new_tree = tree.copy() |
393 self.svn_revs.append(new_tree) | 396 self.svn_revs.append(new_tree) |
394 | 397 |
398 def _set_svn_commit_date(self, revision, date): | |
399 subprocess2.check_output( | |
400 ['svn', 'propset', 'svn:date', '--revprop', '-r', revision, date, | |
401 self.svn_base, | |
402 '--username', self.USERS[0][0], | |
403 '--password', self.USERS[0][1], | |
404 '--non-interactive']) | |
405 | |
395 def _commit_git(self, repo, tree): | 406 def _commit_git(self, repo, tree): |
396 repo_root = join(self.git_root, repo) | 407 repo_root = join(self.git_root, repo) |
397 self._genTree(repo_root, tree) | 408 self._genTree(repo_root, tree) |
398 commit_hash = commit_git(repo_root) | 409 commit_hash = commit_git(repo_root) |
399 if self.git_hashes[repo][-1]: | 410 if self.git_hashes[repo][-1]: |
400 new_tree = self.git_hashes[repo][-1][1].copy() | 411 new_tree = self.git_hashes[repo][-1][1].copy() |
401 new_tree.update(tree) | 412 new_tree.update(tree) |
402 else: | 413 else: |
403 new_tree = tree.copy() | 414 new_tree = tree.copy() |
404 self.git_hashes[repo].append((commit_hash, new_tree)) | 415 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, | 646 'git_base': self.git_base, |
636 # See self.__init__() for the format. Grab's the hash of the first | 647 # 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: | 648 # 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. | 649 # TODO(maruel): http://crosbug.com/3591 We need to strip the hash.. duh. |
639 'hash': self.git_hashes['repo_2'][1][0][:7] | 650 'hash': self.git_hashes['repo_2'][1][0][:7] |
640 }, | 651 }, |
641 'origin': 'git/repo_1@2\n', | 652 'origin': 'git/repo_1@2\n', |
642 }) | 653 }) |
643 | 654 |
644 | 655 |
656 class FakeRepoTransitive(FakeReposBase): | |
657 """Implements populateSvn()""" | |
658 | |
659 def populateSvn(self): | |
660 """Creates a few revisions of changes including a DEPS file.""" | |
661 # Repos | |
662 subprocess2.check_call( | |
663 ['svn', 'checkout', self.svn_base, self.svn_checkout, | |
664 '-q', '--non-interactive', '--no-auth-cache', | |
665 '--username', self.USERS[0][0], '--password', self.USERS[0][1]]) | |
666 assert os.path.isdir(join(self.svn_checkout, '.svn')) | |
667 | |
668 def file_system(rev): | |
669 DEPS = """deps = { | |
670 'src/different_repo': '%(svn_base)strunk/third_party', | |
671 'src/different_repo_fixed': '%(svn_base)strunk/third_party@1', | |
672 'src/same_repo': '/trunk/third_party', | |
673 'src/same_repo_fixed': '/trunk/third_party@1', | |
674 }""" % { 'svn_base': self.svn_base } | |
675 return { | |
676 'trunk/src/DEPS': DEPS, | |
677 'trunk/src/origin': 'svn/trunk/src@%(rev)d' % { 'rev': rev }, | |
678 'trunk/third_party/origin': | |
679 'svn/trunk/third_party@%(rev)d' % { 'rev': rev }, | |
680 } | |
681 | |
682 # We make three commits. We use always the same DEPS contents but | |
683 # - 'trunk/src/origin' contains 'svn/trunk/src/origin@rX' | |
684 # - 'trunk/third_party/origin' contains 'svn/trunk/third_party/origin@rX' | |
685 # where 'X' is the revision number. | |
686 # So the 'origin' files will change in every commit. | |
687 self._commit_svn(file_system(1)) | |
688 self._commit_svn(file_system(2)) | |
689 self._commit_svn(file_system(3)) | |
690 # We rewrite the timestamps so we can test that '--transitive' will take the | |
691 # parent timestamp on different repositories and the parent revision | |
692 # otherwise. | |
693 self._set_svn_commit_date('1', '2011-10-01T03:00:00.000000Z') | |
694 self._set_svn_commit_date('2', '2011-10-09T03:00:00.000000Z') | |
695 self._set_svn_commit_date('3', '2011-10-02T03:00:00.000000Z') | |
696 | |
697 def populateGit(self): | |
698 pass | |
699 | |
700 | |
645 class FakeReposTestBase(trial_dir.TestCase): | 701 class FakeReposTestBase(trial_dir.TestCase): |
646 """This is vaguely inspired by twisted.""" | 702 """This is vaguely inspired by twisted.""" |
647 # static FakeRepos instance. Lazy loaded. | 703 # Static FakeRepos instances. Lazy loaded. |
648 FAKE_REPOS = None | 704 CACHED_FAKE_REPOS = {} |
649 # Override if necessary. | 705 # Override if necessary. |
650 FAKE_REPOS_CLASS = FakeRepos | 706 FAKE_REPOS_CLASS = FakeRepos |
651 | 707 |
652 def setUp(self): | 708 def setUp(self): |
653 super(FakeReposTestBase, self).setUp() | 709 super(FakeReposTestBase, self).setUp() |
654 if not FakeReposTestBase.FAKE_REPOS: | 710 if not self.FAKE_REPOS_CLASS in self.CACHED_FAKE_REPOS: |
M-A Ruel
2013/04/17 14:54:09
Thanks, that's a good idea.
| |
655 # Lazy create the global instance. | 711 self.CACHED_FAKE_REPOS[self.FAKE_REPOS_CLASS] = self.FAKE_REPOS_CLASS() |
656 FakeReposTestBase.FAKE_REPOS = self.FAKE_REPOS_CLASS() | 712 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 | 713 # No need to call self.FAKE_REPOS.setUp(), it will be called by the child |
658 # class. | 714 # class. |
659 # Do not define tearDown(), since super's version does the right thing and | 715 # Do not define tearDown(), since super's version does the right thing and |
660 # FAKE_REPOS is kept across tests. | 716 # self.FAKE_REPOS is kept across tests. |
661 | 717 |
662 @property | 718 @property |
663 def svn_base(self): | 719 def svn_base(self): |
664 """Shortcut.""" | 720 """Shortcut.""" |
665 return self.FAKE_REPOS.svn_base | 721 return self.FAKE_REPOS.svn_base |
666 | 722 |
667 @property | 723 @property |
668 def git_base(self): | 724 def git_base(self): |
669 """Shortcut.""" | 725 """Shortcut.""" |
670 return self.FAKE_REPOS.git_base | 726 return self.FAKE_REPOS.git_base |
(...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
743 fake.set_up_git() | 799 fake.set_up_git() |
744 print('Fake setup, press enter to quit or Ctrl-C to keep the checkouts.') | 800 print('Fake setup, press enter to quit or Ctrl-C to keep the checkouts.') |
745 sys.stdin.readline() | 801 sys.stdin.readline() |
746 except KeyboardInterrupt: | 802 except KeyboardInterrupt: |
747 trial_dir.TrialDir.SHOULD_LEAK.leak = True | 803 trial_dir.TrialDir.SHOULD_LEAK.leak = True |
748 return 0 | 804 return 0 |
749 | 805 |
750 | 806 |
751 if __name__ == '__main__': | 807 if __name__ == '__main__': |
752 sys.exit(main(sys.argv)) | 808 sys.exit(main(sys.argv)) |
OLD | NEW |