Index: tests/fake_repos.py |
diff --git a/tests/fake_repos.py b/tests/fake_repos.py |
index 307a3b790121a1bb5c6ef828c1d730d24e92b151..852f995523caa25524763d502e57622816e40840 100755 |
--- a/tests/fake_repos.py |
+++ b/tests/fake_repos.py |
@@ -5,7 +5,9 @@ |
"""Generate fake repositories for testing.""" |
+import logging |
import os |
+import re |
import shutil |
import subprocess |
import sys |
@@ -39,10 +41,73 @@ def write(path, content): |
f.close() |
+join = os.path.join |
+ |
+ |
+def call(*args, **kwargs): |
+ logging.debug(args[0]) |
+ subprocess.call(*args, **kwargs) |
+ |
+ |
+def check_call(*args, **kwargs): |
+ logging.debug(args[0]) |
+ subprocess.check_call(*args, **kwargs) |
+ |
+ |
+def Popen(*args, **kwargs): |
+ kwargs.setdefault('stdout', subprocess.PIPE) |
+ kwargs.setdefault('stderr', subprocess.STDOUT) |
+ logging.debug(args[0]) |
+ return subprocess.Popen(*args, **kwargs) |
+ |
+ |
+def commit_svn(repo): |
+ """Commits the changes and returns the new revision number.""" |
+ # Basic parsing. |
+ to_add = [] |
+ to_remove = [] |
+ for item in Popen(['svn', 'status'], |
+ cwd=repo).communicate()[0].splitlines(False): |
+ if item[0] == '?': |
+ to_add.append(item[8:]) |
+ elif item[0] == '!': |
+ to_remove.append(item[8:]) |
+ if to_add: |
+ check_call(['svn', 'add', '--no-auto-props', '-q'] + to_add, cwd=repo) |
+ if to_remove: |
+ check_call(['svn', 'remove', '-q'] + to_remove, cwd=repo) |
+ out = Popen(['svn', 'commit', repo, '-m', 'foo', '--non-interactive', |
+ '--no-auth-cache', '--username', 'user1', '--password', 'foo'], |
+ cwd=repo).communicate()[0] |
+ rev = re.search(r'revision (\d+).', out).group(1) |
+ st = Popen(['svn', 'status'], cwd=repo).communicate()[0] |
+ assert len(st) == 0, st |
+ logging.debug('At revision %s' % rev) |
+ return rev |
+ |
+ |
+def commit_git(repo): |
+ """Commits the changes and returns the new hash.""" |
+ check_call(['git', 'add', '-A', '-f'], cwd=repo) |
+ out = Popen(['git', 'commit', '-m', 'foo'], cwd=repo).communicate()[0] |
+ rev = re.search(r'^\[.*? ([a-f\d]+)\] ', out).group(1) |
+ logging.debug('At revision %s' % rev) |
+ return rev |
+ |
+ |
class FakeRepos(object): |
+ """Generate both svn and git repositories to test gclient functionality. |
+ |
+ Many DEPS functionalities need to be tested: Var, File, From, deps_os, hooks, |
+ use_relative_paths. |
+ |
+ And types of dependencies: Relative urls, Full urls, both svn and git.""" |
+ |
def __init__(self, trial_dir, leak, local_only): |
self.trial_dir = trial_dir |
self.repos_dir = os.path.join(self.trial_dir, 'repos') |
+ self.git_root = join(self.repos_dir, 'git') |
+ self.svn_root = join(self.repos_dir, 'svn_checkout') |
self.leak = leak |
self.local_only = local_only |
self.svnserve = [] |
@@ -51,6 +116,10 @@ class FakeRepos(object): |
rmtree(self.trial_dir) |
os.mkdir(self.trial_dir) |
os.mkdir(self.repos_dir) |
+ # Format is [ None, tree, tree, ...] |
+ self.svn_revs = [None] |
+ # Format is { repo: [ (hash, tree), (hash, tree), ... ], ... } |
+ self.git_hashes = {} |
def setUp(self): |
self.setUpSVN() |
@@ -58,19 +127,37 @@ class FakeRepos(object): |
def tearDown(self): |
for i in self.svnserve: |
+ logging.debug('Killing svnserve pid %s' % i.pid) |
i.kill() |
for i in self.gitdaemon: |
+ logging.debug('Killing git-daemon pid %s' % i.pid) |
i.kill() |
if not self.leak: |
+ logging.debug('Removing %s' % self.trial_dir) |
rmtree(self.trial_dir) |
+ def _genTree(self, root, tree_dict): |
+ """For a dictionary of file contents, generate a filesystem.""" |
+ if not os.path.isdir(root): |
+ os.makedirs(root) |
+ for (k, v) in tree_dict.iteritems(): |
+ k_os = k.replace('/', os.sep) |
+ k_arr = k_os.split(os.sep) |
+ if len(k_arr) > 1: |
+ p = os.sep.join([root] + k_arr[:-1]) |
+ if not os.path.isdir(p): |
+ os.makedirs(p) |
+ if v is None: |
+ os.remove(join(root, k)) |
+ else: |
+ write(join(root, k), v) |
+ |
def setUpSVN(self): |
"""Creates subversion repositories and start the servers.""" |
assert not self.svnserve |
- join = os.path.join |
root = join(self.repos_dir, 'svn') |
rmtree(root) |
- subprocess.check_call(['svnadmin', 'create', root]) |
+ check_call(['svnadmin', 'create', root]) |
write(join(root, 'conf', 'svnserve.conf'), |
'[general]\n' |
'anon-access = read\n' |
@@ -81,123 +168,183 @@ class FakeRepos(object): |
'user1 = foo\n' |
'user2 = bar\n') |
+ # Start the daemon. |
+ cmd = ['svnserve', '-d', '--foreground', '-r', self.repos_dir] |
+ if self.local_only: |
+ cmd.append('--listen-host=127.0.0.1') |
+ logging.debug(cmd) |
+ self.svnserve.append(Popen(cmd, cwd=root)) |
+ self.populateSvn() |
+ |
+ def populateSvn(self): |
+ """Creates a few revisions of changes including DEPS files.""" |
# Repos |
- repo = join(self.repos_dir, 'svn_import') |
- rmtree(repo) |
- os.mkdir(repo) |
- os.mkdir(join(repo, 'trunk')) |
- os.mkdir(join(repo, 'trunk', 'src')) |
- write(join(repo, 'trunk', 'src', 'DEPS'), """ |
-# Smoke test DEPS file. |
-# Many DEPS functionalities need to be tested: |
-# Var |
-# File |
-# From |
-# deps_os |
-# hooks |
-# use_relative_paths |
-# |
-# Types of dependencies: |
-# Relative urls |
-# Full urls |
-# svn |
-# git |
+ check_call(['svn', 'checkout', 'svn://127.0.0.1/svn', self.svn_root, '-q', |
+ '--non-interactive', '--no-auth-cache', |
+ '--username', 'user1', '--password', 'foo']) |
+ assert os.path.isdir(join(self.svn_root, '.svn')) |
+ def file_system(rev, DEPS): |
+ fs = { |
+ 'origin': 'svn@%(rev)d\n', |
+ 'trunk/origin': 'svn/trunk@%(rev)d\n', |
+ 'trunk/src/origin': 'svn/trunk/src@%(rev)d\n', |
+ 'trunk/src/third_party/origin': 'svn/trunk/src/third_party@%(rev)d\n', |
+ 'trunk/other/origin': 'src/trunk/other@%(rev)d\n', |
+ 'trunk/third_party/origin': 'svn/trunk/third_party@%(rev)d\n', |
+ 'trunk/third_party/foo/origin': 'svn/trunk/third_party/foo@%(rev)d\n', |
+ 'trunk/third_party/prout/origin': 'svn/trunk/third_party/foo@%(rev)d\n', |
+ } |
+ for k in fs.iterkeys(): |
+ fs[k] = fs[k] % { 'rev': rev } |
+ fs['trunk/src/DEPS'] = DEPS |
+ return fs |
+ # Testing: |
+ # - dependency disapear |
+ # - dependency renamed |
+ # - versioned and unversioned reference |
+ # - relative and full reference |
+ # - deps_os |
+ # TODO(maruel): |
+ # - var |
+ # - hooks |
+ # - File |
+ self._commit_svn(file_system(1, """ |
deps = { |
- 'src/other': 'svn://%(host)s/svn/trunk/other', |
- 'src/third_party': '/trunk/third_party', |
+ 'src/other': 'svn://%(host)s/svn/trunk/other', |
+ 'src/third_party/fpp': '/trunk/third_party/foo', |
} |
- |
deps_os = { |
- 'mac': 'repo_4' |
-} |
-""" % { |
- 'host': 'localhost', |
-}) |
- write(join(repo, 'trunk', 'src', 'origin'), "svn/trunk/src") |
- os.mkdir(join(repo, 'trunk', 'other')) |
- write(join(repo, 'trunk', 'other', 'origin'), "svn/trunk/other") |
- os.mkdir(join(repo, 'trunk', 'third_party')) |
- write(join(repo, 'trunk', 'third_party', 'origin'), "svn/trunk/third_party") |
+ 'mac': { |
+ 'src/third_party/prout': '/trunk/third_party/prout', |
+ }, |
+}""" % { 'host': '127.0.0.1' })) |
- # Start the daemon. |
- cmd = ['svnserve', '-d', '--foreground', '-r', self.repos_dir] |
- if self.local_only: |
- cmd.append('--listen-host=127.0.0.1') |
- self.svnserve.append(subprocess.Popen(cmd, cwd=root)) |
- |
- # Import the repo. |
- subprocess.check_call(['svn', 'import', repo, |
- 'svn://127.0.0.1/svn', '-m', 'foo', '-q', |
- '--no-auto-props', '--non-interactive', '--no-auth-cache', |
- '--username', 'user1', '--password', 'foo']) |
+ self._commit_svn(file_system(2, """ |
+deps = { |
+ 'src/other': 'svn://%(host)s/svn/trunk/other', |
+ 'src/third_party/foo': '/trunk/third_party/foo@1', |
+} |
+""" % { 'host': '127.0.0.1' })) |
def setUpGIT(self): |
"""Creates git repositories and start the servers.""" |
assert not self.gitdaemon |
- join = os.path.join |
- root = join(self.repos_dir, 'git') |
- rmtree(root) |
- os.mkdir(root) |
- # Repo 1 |
- repo = join(root, 'repo_1') |
- subprocess.check_call(['git', 'init', '-q', repo]) |
- write(join(repo, 'DEPS'), """ |
-# Smoke test DEPS file. |
-# Many DEPS functionalities need to be tested: |
-# Var |
-# File |
-# From |
-# deps_os |
-# hooks |
-# use_relative_paths |
-# |
-# Types of dependencies: |
-# Relative urls |
-# Full urls |
-# svn |
-# git |
+ rmtree(self.git_root) |
+ for repo in ['repo_%d' % r for r in range(1, 5)]: |
+ check_call(['git', 'init', '-q', join(self.git_root, repo)]) |
+ self.git_hashes[repo] = [] |
+ # Testing: |
+ # - dependency disapear |
+ # - dependency renamed |
+ # - versioned and unversioned reference |
+ # - relative and full reference |
+ # - deps_os |
+ # TODO(maruel): |
+ # - var |
+ # - hooks |
+ # - File |
+ self._commit_git('repo_1', { |
+ 'DEPS': """ |
deps = { |
- 'repo2': 'git://%(host)s/git/repo_2', |
- 'repo2/repo3': '/repo_3', |
+ 'src/repo2': 'git://%(host)s/git/repo_2', |
+ 'src/repo2/repo3': '/repo_3', |
} |
- |
deps_os = { |
- 'mac': 'repo_4' |
+ 'mac': { |
+ 'src/repo4': '/repo_4', |
+ }, |
+}""" % { 'host': '127.0.0.1' }, |
+ 'origin': 'git/repo_1@1\n', |
+ }) |
+ |
+ self._commit_git('repo_2', { |
+ 'origin': "git/repo_2@1\n" |
+ }) |
+ |
+ self._commit_git('repo_2', { |
+ 'origin': "git/repo_2@2\n" |
+ }) |
+ |
+ self._commit_git('repo_3', { |
+ 'origin': "git/repo_3@1\n" |
+ }) |
+ |
+ self._commit_git('repo_3', { |
+ 'origin': "git/repo_3@2\n" |
+ }) |
+ |
+ self._commit_git('repo_4', { |
+ 'origin': "git/repo_4@1\n" |
+ }) |
+ |
+ self._commit_git('repo_4', { |
+ 'origin': "git/repo_4@2\n" |
+ }) |
+ |
+ self._commit_git('repo_1', { |
+ 'DEPS': """ |
+deps = { |
+ 'src/repo2': 'git://%(host)s/git/repo_2@%(hash)s', |
+ 'src/repo2/repo_renamed': '/repo_3', |
} |
-""" % { |
- 'host': 'localhost', |
-}) |
- write(join(repo, 'origin'), "git/repo_1") |
- subprocess.check_call(['git', 'add', '-A', '-f'], cwd=repo) |
- subprocess.check_call(['git', 'commit', '-q', '-m', 'foo'], cwd=repo) |
- |
- # Repo 2 |
- repo = join(root, 'repo_2') |
- subprocess.check_call(['git', 'init', '-q', repo]) |
- write(join(repo, 'origin'), "git/repo_2") |
- subprocess.check_call(['git', 'add', '-A', '-f'], cwd=repo) |
- subprocess.check_call(['git', 'commit', '-q', '-m', 'foo'], cwd=repo) |
- |
- # Repo 3 |
- repo = join(root, 'repo_3') |
- subprocess.check_call(['git', 'init', '-q', repo]) |
- write(join(repo, 'origin'), "git/repo_3") |
- subprocess.check_call(['git', 'add', '-A', '-f'], cwd=repo) |
- subprocess.check_call(['git', 'commit', '-q', '-m', 'foo'], cwd=repo) |
+""" % { 'host': '127.0.0.1', 'hash': self.git_hashes['repo_2'][0][0] }, |
+ 'origin': "git/repo_1@2\n" |
+ }) |
# Start the daemon. |
cmd = ['git', 'daemon', '--export-all', '--base-path=' + self.repos_dir] |
if self.local_only: |
cmd.append('--listen=127.0.0.1') |
- self.gitdaemon.append(subprocess.Popen(cmd, cwd=self.repos_dir, |
- stderr=subprocess.PIPE)) |
+ logging.debug(cmd) |
+ self.gitdaemon.append(Popen(cmd, cwd=self.repos_dir)) |
-if __name__ == '__main__': |
- fake = FakeRepos(os.path.dirname(os.path.abspath(__file__)), False) |
+ def _commit_svn(self, tree): |
+ self._genTree(self.svn_root, tree) |
+ commit_svn(self.svn_root) |
+ if self.svn_revs and self.svn_revs[-1]: |
+ new_tree = self.svn_revs[-1].copy() |
+ new_tree.update(tree) |
+ else: |
+ new_tree = tree.copy() |
+ self.svn_revs.append(new_tree) |
+ |
+ def _commit_git(self, repo, tree): |
+ repo_root = join(self.git_root, repo) |
+ self._genTree(repo_root, tree) |
+ hash = commit_git(repo_root) |
+ if self.git_hashes[repo]: |
+ new_tree = self.git_hashes[repo][-1][1].copy() |
+ new_tree.update(tree) |
+ else: |
+ new_tree = tree.copy() |
+ self.git_hashes[repo].append((hash, new_tree)) |
+ |
+ |
+def main(argv): |
+ leak = '-l' in argv |
+ if leak: |
+ argv.remove('-l') |
+ verbose = '-v' in argv |
+ if verbose: |
+ logging.basicConfig(level=logging.DEBUG) |
+ argv.remove('-v') |
+ assert len(argv) == 1, argv |
+ trial_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), |
+ '_trial') |
+ print 'Using %s' % trial_dir |
+ fake = FakeRepos(trial_dir, leak, True) |
try: |
fake.setUp() |
+ print('Fake setup, press enter to quit or Ctrl-C to keep the checkouts.') |
sys.stdin.readline() |
+ except KeyboardInterrupt: |
+ fake.leak = True |
finally: |
fake.tearDown() |
+ return 0 |
+ |
+ |
+if __name__ == '__main__': |
+ sys.exit(main(sys.argv)) |