#! /usr/bin/python
import logging, mox, os, shutil, tempfile, unittest, utils
# This makes autotest_lib imports available.
import common
from autotest_lib.client.common_lib import revision_control
class GitRepoManager(object):
"""
A wrapper for GitRepo.
"""
commit_hash = None
commit_msg = None
repodir = None
git_repo_manager = None
def __init__(self, master_repo=None):
"""
Setup self.git_repo_manager.
If a master_repo is present clone it.
Otherwise create a directory in /tmp and init it.
@param master_repo: GitRepo representing master.
"""
if master_repo is None:
self.repodir = tempfile.mktemp(suffix='master')
self._create_git_repo(self.repodir)
self.git_repo_manager = revision_control.GitRepo(
self.repodir,
self.repodir,
abs_work_tree=self.repodir)
self._setup_git_environment()
# Create an initial commit. We really care about the common case
# where there exists a commit in the upstream repo.
self._edit('initial_commit_file', 'is_non_empty')
self.add()
self.commit('initial_commit')
else:
self.repodir = tempfile.mktemp(suffix='dependent')
self.git_repo_manager = revision_control.GitRepo(
self.repodir,
master_repo.repodir,
abs_work_tree=self.repodir)
self.git_repo_manager.clone()
self._setup_git_environment()
def _setup_git_environment(self):
"""
Mock out basic git environment to keep tests deterministic.
"""
# Set user and email for the test git checkout.
self.git_repo_manager.gitcmd('config user.name Unittests')
self.git_repo_manager.gitcmd('config user.email utests@chromium.org')
def _edit(self, filename='foo', msg='bar'):
"""
Write msg into a file in the repodir.
@param filename: Name of the file in current repo.
If none exists one will be created.
@param msg: A message to write into the file.
"""
local_file_name = os.path.join(self.git_repo_manager.repodir,
filename)
with open(local_file_name, 'w') as f:
f.write(msg)
def _create_git_repo(self, repodir):
"""
Init a new git repository.
@param repodir: directory for repo.
"""
logging.info('initializing git repo in: %s', repodir)
gitcmd = 'git init %s' % repodir
rv = utils.run(gitcmd)
if rv.exit_status != 0:
logging.error(rv.stderr)
raise revision_control.revision_control.GitError(gitcmd + 'failed')
def add(self):
"""
Add all unadded files in repodir to repo.
"""
rv = self.git_repo_manager.gitcmd('add .')
if rv.exit_status != 0:
logging.error(rv.stderr)
raise revision_control.GitError('Unable to add files to repo', rv)
def commit(self, msg='default'):
"""
Commit changes to repo with the supplied commit msg.
Also updates commit_hash with the hash for this commit.
@param msg: A message that goes with the commit.
"""
self.git_repo_manager.commit(msg)
self.commit_hash = self.git_repo_manager.get_latest_commit_hash()
def get_master_tot(self):
"""
Get everything from masters TOT squashing local changes.
If the dependent repo is empty pull from master.
"""
self.git_repo_manager.reinit_repo_at('master')
self.commit_hash = self.git_repo_manager.get_latest_commit_hash()
class RevisionControlUnittest(mox.MoxTestBase):
"""
A unittest to exercise build_externals.py's usage
of revision_control.py's Git wrappers.
"""
master_repo=None
dependent_repo=None
def setUp(self):
"""
Create a master repo and clone it into a dependent repo.
"""
super(RevisionControlUnittest, self).setUp()
self.master_repo = GitRepoManager()
self.dependent_repo = GitRepoManager(self.master_repo)
def tearDown(self):
"""
Delete temporary directories.
"""
shutil.rmtree(self.master_repo.repodir)
shutil.rmtree(self.dependent_repo.repodir)
super(RevisionControlUnittest, self).tearDown()
def testCommit(self):
"""
Test add, commit, pull, clone.
"""
self.master_repo._edit()
self.master_repo.add()
self.master_repo.commit()
self.dependent_repo.get_master_tot()
self.assertEquals(self.dependent_repo.commit_hash,
self.master_repo.commit_hash,
msg=(("hashes don't match after clone, master and dependent repo"
"out of sync: %r != %r") %
(self.dependent_repo.commit_hash,
self.master_repo.commit_hash)))
self.master_repo._edit(msg='foobar')
self.master_repo.commit()
self.dependent_repo.get_master_tot()
self.assertEquals(self.dependent_repo.commit_hash,
self.master_repo.commit_hash,
msg=(("hashes don't match after pull, master and dependent repo"
"out of sync: %r != %r") %
(self.dependent_repo.commit_hash,
self.master_repo.commit_hash)))
def testGitUrlClone(self):
"""
Test that git clone raises a ValueError if giturl is unset.
"""
self.dependent_repo.git_repo_manager._giturl = None
self.assertRaises(ValueError,
self.dependent_repo.git_repo_manager.clone)
def testGitUrlPull(self):
"""
Test that git pull raises a ValueError if giturl is unset.
"""
self.dependent_repo.git_repo_manager._giturl = None
self.assertRaises(ValueError,
self.dependent_repo.git_repo_manager.pull)
def testGitUrlFetch(self):
"""
Test that git fetch raises a ValueError if giturl is unset.
"""
self.dependent_repo.git_repo_manager._giturl = None
self.assertRaises(ValueError,
self.dependent_repo.git_repo_manager.fetch_remote)
if __name__ == '__main__':
unittest.main()