#!/usr/bin/python2 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, main_repo=None): """ Setup self.git_repo_manager. If a main_repo is present clone it. Otherwise create a directory in /tmp and init it. @param main_repo: GitRepo representing main. """ if main_repo is None: self.repodir = tempfile.mktemp(suffix='main') 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, main_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_main_tot(self): """ Get everything from mains TOT squashing local changes. If the dependent repo is empty pull from main. """ # TODO b:169251326 terms below are set outside of this codebase # and should be updated when possible. ("master" -> "main") # Currently (but I believe it will eventually) does not support # `reset --hard origin/main` (must be origin/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. """ main_repo=None dependent_repo=None def setUp(self): """ Create a main repo and clone it into a dependent repo. """ super(RevisionControlUnittest, self).setUp() self.main_repo = GitRepoManager() self.dependent_repo = GitRepoManager(self.main_repo) def tearDown(self): """ Delete temporary directories. """ shutil.rmtree(self.main_repo.repodir) shutil.rmtree(self.dependent_repo.repodir) super(RevisionControlUnittest, self).tearDown() def testCommit(self): """ Test add, commit, pull, clone. """ self.main_repo._edit() self.main_repo.add() self.main_repo.commit() self.dependent_repo.get_main_tot() self.assertEquals(self.dependent_repo.commit_hash, self.main_repo.commit_hash, msg=(("hashes don't match after clone, main and dependent repo" "out of sync: %r != %r") % (self.dependent_repo.commit_hash, self.main_repo.commit_hash))) self.main_repo._edit(msg='foobar') self.main_repo.commit() self.dependent_repo.get_main_tot() self.assertEquals(self.dependent_repo.commit_hash, self.main_repo.commit_hash, msg=(("hashes don't match after pull, main and dependent repo" "out of sync: %r != %r") % (self.dependent_repo.commit_hash, self.main_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()