| Index: tests/fake_repos.py
|
| diff --git a/tests/fake_repos.py b/tests/fake_repos.py
|
| index 85746cf3ab0faf23401d01df8ae6486e50414d53..6d2d0d5ca5c776c25620177437aa57971319b761 100755
|
| --- a/tests/fake_repos.py
|
| +++ b/tests/fake_repos.py
|
| @@ -5,12 +5,17 @@
|
|
|
| """Generate fake repositories for testing."""
|
|
|
| +import atexit
|
| import logging
|
| import os
|
| import re
|
| import shutil
|
| import subprocess
|
| import sys
|
| +import unittest
|
| +
|
| +
|
| +## Utility functions
|
|
|
|
|
| def addKill():
|
| @@ -44,11 +49,6 @@ def write(path, content):
|
| 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)
|
| @@ -61,6 +61,48 @@ def Popen(*args, **kwargs):
|
| return subprocess.Popen(*args, **kwargs)
|
|
|
|
|
| +def read_tree(tree_root):
|
| + """Returns a dict of all the files in a tree. Defaults to self.root_dir."""
|
| + tree = {}
|
| + for root, dirs, files in os.walk(tree_root):
|
| + for d in filter(lambda x: x.startswith('.'), dirs):
|
| + dirs.remove(d)
|
| + for f in [join(root, f) for f in files if not f.startswith('.')]:
|
| + tree[f[len(tree_root) + 1:]] = open(join(root, f), 'rb').read()
|
| + return tree
|
| +
|
| +
|
| +def dict_diff(dict1, dict2):
|
| + diff = {}
|
| + for k, v in dict1.iteritems():
|
| + if k not in dict2:
|
| + diff[k] = v
|
| + elif v != dict2[k]:
|
| + diff[k] = (v, dict2[k])
|
| + for k, v in dict2.iteritems():
|
| + if k not in dict1:
|
| + diff[k] = v
|
| + return diff
|
| +
|
| +
|
| +def mangle_svn_tree(*args):
|
| + result = {}
|
| + for old_root, new_root, tree in args:
|
| + for k, v in tree.iteritems():
|
| + if not k.startswith(old_root):
|
| + continue
|
| + result[join(new_root, k[len(old_root) + 1:])] = v
|
| + return result
|
| +
|
| +
|
| +def mangle_git_tree(*args):
|
| + result = {}
|
| + for new_root, tree in args:
|
| + for k, v in tree.iteritems():
|
| + result[join(new_root, k)] = v
|
| + return result
|
| +
|
| +
|
| def commit_svn(repo):
|
| """Commits the changes and returns the new revision number."""
|
| # Basic parsing.
|
| @@ -96,6 +138,8 @@ def commit_git(repo):
|
| return rev
|
|
|
|
|
| +_FAKE_LOADED = False
|
| +
|
| class FakeRepos(object):
|
| """Generate both svn and git repositories to test gclient functionality.
|
|
|
| @@ -104,38 +148,71 @@ class FakeRepos(object):
|
|
|
| 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 = []
|
| - self.gitdaemon = []
|
| - addKill()
|
| - rmtree(self.trial_dir)
|
| - os.mkdir(self.trial_dir)
|
| - os.mkdir(self.repos_dir)
|
| + # Should leak the repositories.
|
| + SHOULD_LEAK = False
|
| + # Override if unhappy.
|
| + TRIAL_DIR = None
|
| + # Hostname
|
| + HOST = '127.0.0.1'
|
| +
|
| + def __init__(self, trial_dir=None, leak=None, host=None):
|
| + global _FAKE_LOADED
|
| + if _FAKE_LOADED:
|
| + raise Exception('You can only start one FakeRepos at a time.')
|
| + _FAKE_LOADED = True
|
| + # Quick hack.
|
| + if '-v' in sys.argv:
|
| + logging.basicConfig(level=logging.DEBUG)
|
| + if '-l' in sys.argv:
|
| + self.SHOULD_LEAK = True
|
| + sys.argv.remove('-l')
|
| + elif leak is not None:
|
| + self.SHOULD_LEAK = leak
|
| + if host:
|
| + self.HOST = host
|
| + if trial_dir:
|
| + self.TRIAL_DIR = trial_dir
|
| +
|
| # Format is [ None, tree, tree, ...]
|
| self.svn_revs = [None]
|
| # Format is { repo: [ (hash, tree), (hash, tree), ... ], ... }
|
| self.git_hashes = {}
|
| + self.svnserve = None
|
| + self.gitdaemon = None
|
| + self.common_init = False
|
| +
|
| + def trial_dir(self):
|
| + if not self.TRIAL_DIR:
|
| + self.TRIAL_DIR = os.path.join(
|
| + os.path.dirname(os.path.abspath(__file__)), '_trial')
|
| + return self.TRIAL_DIR
|
|
|
| def setUp(self):
|
| - self.setUpSVN()
|
| - self.setUpGIT()
|
| + """All late initialization comes here.
|
| +
|
| + Note that it deletes all trial_dir() and not only repos_dir."""
|
| + if not self.common_init:
|
| + self.common_init = True
|
| + 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')
|
| + addKill()
|
| + rmtree(self.trial_dir())
|
| + os.makedirs(self.repos_dir)
|
| + atexit.register(self.tearDown)
|
|
|
| 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)
|
| + if self.svnserve:
|
| + logging.debug('Killing svnserve pid %s' % self.svnserve.pid)
|
| + self.svnserve.kill()
|
| + self.svnserve = None
|
| + if self.gitdaemon:
|
| + logging.debug('Killing git-daemon pid %s' % self.gitdaemon.pid)
|
| + self.gitdaemon.kill()
|
| + self.gitdaemon = None
|
| + if not self.SHOULD_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."""
|
| @@ -155,9 +232,10 @@ class FakeRepos(object):
|
|
|
| def setUpSVN(self):
|
| """Creates subversion repositories and start the servers."""
|
| - assert not self.svnserve
|
| + if self.svnserve:
|
| + return
|
| + self.setUp()
|
| root = join(self.repos_dir, 'svn')
|
| - rmtree(root)
|
| check_call(['svnadmin', 'create', root])
|
| write(join(root, 'conf', 'svnserve.conf'),
|
| '[general]\n'
|
| @@ -171,10 +249,10 @@ class FakeRepos(object):
|
|
|
| # Start the daemon.
|
| cmd = ['svnserve', '-d', '--foreground', '-r', self.repos_dir]
|
| - if self.local_only:
|
| + if self.HOST == '127.0.0.1':
|
| cmd.append('--listen-host=127.0.0.1')
|
| logging.debug(cmd)
|
| - self.svnserve.append(Popen(cmd, cwd=root))
|
| + self.svnserve = Popen(cmd, cwd=root)
|
| self.populateSvn()
|
|
|
| def populateSvn(self):
|
| @@ -224,7 +302,7 @@ deps_os = {
|
| 'mac': {
|
| 'src/third_party/prout': '/trunk/third_party/prout',
|
| },
|
| -}""" % { 'host': '127.0.0.1' }))
|
| +}""" % { 'host': self.HOST }))
|
|
|
| self._commit_svn(file_system(2, """
|
| deps = {
|
| @@ -246,12 +324,13 @@ hooks = [
|
| 'open(\\'src/hooked2\\', \\'w\\').write(\\'hooked2\\')'],
|
| },
|
| ]
|
| -""" % { 'host': '127.0.0.1' }))
|
| +""" % { 'host': self.HOST }))
|
|
|
| def setUpGIT(self):
|
| """Creates git repositories and start the servers."""
|
| - assert not self.gitdaemon
|
| - rmtree(self.git_root)
|
| + if self.gitdaemon:
|
| + return
|
| + self.setUp()
|
| 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] = []
|
| @@ -281,7 +360,7 @@ deps_os = {
|
| 'mac': {
|
| 'src/repo4': '/repo_4',
|
| },
|
| -}""" % { 'host': '127.0.0.1' },
|
| +}""" % { 'host': self.HOST },
|
| 'origin': 'git/repo_1@1\n',
|
| })
|
|
|
| @@ -331,17 +410,19 @@ hooks = [
|
| },
|
| ]
|
| """ % {
|
| - # TODO(maruel): http://crosbug.com/3591 We need to strip the hash.. duh.
|
| - 'host': '127.0.0.1', 'hash': self.git_hashes['repo_2'][0][0][:7] },
|
| + # TODO(maruel): http://crosbug.com/3591 We need to strip the hash.. duh.
|
| + 'host': self.HOST,
|
| + 'hash': self.git_hashes['repo_2'][0][0][:7]
|
| + },
|
| 'origin': "git/repo_1@2\n"
|
| })
|
|
|
| # Start the daemon.
|
| cmd = ['git', 'daemon', '--export-all', '--base-path=' + self.repos_dir]
|
| - if self.local_only:
|
| + if self.HOST == '127.0.0.1':
|
| cmd.append('--listen=127.0.0.1')
|
| logging.debug(cmd)
|
| - self.gitdaemon.append(Popen(cmd, cwd=self.repos_dir))
|
| + self.gitdaemon = Popen(cmd, cwd=self.repos_dir)
|
|
|
| def _commit_svn(self, tree):
|
| self._genTree(self.svn_root, tree)
|
| @@ -365,27 +446,71 @@ hooks = [
|
| self.git_hashes[repo].append((hash, new_tree))
|
|
|
|
|
| +class FakeReposTestBase(unittest.TestCase):
|
| + """This is vaguely inspired by twisted."""
|
| +
|
| + # Replace this in your subclass.
|
| + CLASS_ROOT_DIR = None
|
| +
|
| + # static FakeRepos instance.
|
| + FAKE_REPOS = FakeRepos()
|
| +
|
| + def setUp(self):
|
| + unittest.TestCase.setUp(self)
|
| + self.FAKE_REPOS.setUp()
|
| +
|
| + # Remove left overs and start fresh.
|
| + if not self.CLASS_ROOT_DIR:
|
| + self.CLASS_ROOT_DIR = join(self.FAKE_REPOS.trial_dir(), 'smoke')
|
| + self.root_dir = join(self.CLASS_ROOT_DIR, self.id())
|
| + rmtree(self.root_dir)
|
| + os.makedirs(self.root_dir)
|
| + self.svn_base = 'svn://%s/svn/' % self.FAKE_REPOS.HOST
|
| + self.git_base = 'git://%s/git/' % self.FAKE_REPOS.HOST
|
| +
|
| + def tearDown(self):
|
| + if not self.FAKE_REPOS.SHOULD_LEAK:
|
| + rmtree(self.root_dir)
|
| +
|
| + def checkString(self, expected, result):
|
| + """Prints the diffs to ease debugging."""
|
| + if expected != result:
|
| + # Strip the begining
|
| + while expected and result and expected[0] == result[0]:
|
| + expected = expected[1:]
|
| + result = result[1:]
|
| + # The exception trace makes it hard to read so dump it too.
|
| + if '\n' in result:
|
| + print result
|
| + self.assertEquals(expected, result)
|
| +
|
| + def check(self, expected, results):
|
| + """Checks stdout, stderr, retcode."""
|
| + self.checkString(expected[0], results[0])
|
| + self.checkString(expected[1], results[1])
|
| + self.assertEquals(expected[2], results[2])
|
| +
|
| + def assertTree(self, tree, tree_root=None):
|
| + """Diff the checkout tree with a dict."""
|
| + if not tree_root:
|
| + tree_root = self.root_dir
|
| + actual = read_tree(tree_root)
|
| + diff = dict_diff(tree, actual)
|
| + if diff:
|
| + logging.debug('Actual %s\n%s' % (tree_root, pprint.pformat(actual)))
|
| + logging.debug('Expected\n%s' % pprint.pformat(tree))
|
| + logging.debug('Diff\n%s' % pprint.pformat(diff))
|
| +
|
| +
|
| 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)
|
| + fake = FakeRepos()
|
| + print 'Using %s' % fake.trial_dir()
|
| 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()
|
| + fake.SHOULD_LEAK = True
|
| return 0
|
|
|
|
|
|
|