1 #! /usr/bin/python 2 import logging, mox, os, shutil, tempfile, unittest, utils 3 4 # This makes autotest_lib imports available. 5 import common 6 from autotest_lib.client.common_lib import revision_control 7 8 9 class GitRepoManager(object): 10 """ 11 A wrapper for GitRepo. 12 """ 13 commit_hash = None 14 commit_msg = None 15 repodir = None 16 git_repo_manager = None 17 18 19 def __init__(self, master_repo=None): 20 """ 21 Setup self.git_repo_manager. 22 23 If a master_repo is present clone it. 24 Otherwise create a directory in /tmp and init it. 25 26 @param master_repo: GitRepo representing master. 27 """ 28 if master_repo is None: 29 self.repodir = tempfile.mktemp(suffix='master') 30 self._create_git_repo(self.repodir) 31 self.git_repo_manager = revision_control.GitRepo( 32 self.repodir, 33 self.repodir, 34 abs_work_tree=self.repodir) 35 self._setup_git_environment() 36 # Create an initial commit. We really care about the common case 37 # where there exists a commit in the upstream repo. 38 self._edit('initial_commit_file', 'is_non_empty') 39 self.add() 40 self.commit('initial_commit') 41 else: 42 self.repodir = tempfile.mktemp(suffix='dependent') 43 self.git_repo_manager = revision_control.GitRepo( 44 self.repodir, 45 master_repo.repodir, 46 abs_work_tree=self.repodir) 47 self.git_repo_manager.clone() 48 self._setup_git_environment() 49 50 51 def _setup_git_environment(self): 52 """ 53 Mock out basic git environment to keep tests deterministic. 54 """ 55 # Set user and email for the test git checkout. 56 self.git_repo_manager.gitcmd('config user.name Unittests') 57 self.git_repo_manager.gitcmd('config user.email utests (at] chromium.org') 58 59 60 def _edit(self, filename='foo', msg='bar'): 61 """ 62 Write msg into a file in the repodir. 63 64 @param filename: Name of the file in current repo. 65 If none exists one will be created. 66 @param msg: A message to write into the file. 67 """ 68 local_file_name = os.path.join(self.git_repo_manager.repodir, 69 filename) 70 with open(local_file_name, 'w') as f: 71 f.write(msg) 72 73 74 def _create_git_repo(self, repodir): 75 """ 76 Init a new git repository. 77 78 @param repodir: directory for repo. 79 """ 80 logging.info('initializing git repo in: %s', repodir) 81 gitcmd = 'git init %s' % repodir 82 rv = utils.run(gitcmd) 83 if rv.exit_status != 0: 84 logging.error(rv.stderr) 85 raise revision_control.revision_control.GitError(gitcmd + 'failed') 86 87 88 def add(self): 89 """ 90 Add all unadded files in repodir to repo. 91 """ 92 rv = self.git_repo_manager.gitcmd('add .') 93 if rv.exit_status != 0: 94 logging.error(rv.stderr) 95 raise revision_control.GitError('Unable to add files to repo', rv) 96 97 98 def commit(self, msg='default'): 99 """ 100 Commit changes to repo with the supplied commit msg. 101 Also updates commit_hash with the hash for this commit. 102 103 @param msg: A message that goes with the commit. 104 """ 105 self.git_repo_manager.commit(msg) 106 self.commit_hash = self.git_repo_manager.get_latest_commit_hash() 107 108 109 def get_master_tot(self): 110 """ 111 Get everything from masters TOT squashing local changes. 112 If the dependent repo is empty pull from master. 113 """ 114 self.git_repo_manager.reinit_repo_at('master') 115 self.commit_hash = self.git_repo_manager.get_latest_commit_hash() 116 117 118 class RevisionControlUnittest(mox.MoxTestBase): 119 """ 120 A unittest to exercise build_externals.py's usage 121 of revision_control.py's Git wrappers. 122 """ 123 master_repo=None 124 dependent_repo=None 125 126 def setUp(self): 127 """ 128 Create a master repo and clone it into a dependent repo. 129 """ 130 super(RevisionControlUnittest, self).setUp() 131 self.master_repo = GitRepoManager() 132 self.dependent_repo = GitRepoManager(self.master_repo) 133 134 135 def tearDown(self): 136 """ 137 Delete temporary directories. 138 """ 139 shutil.rmtree(self.master_repo.repodir) 140 shutil.rmtree(self.dependent_repo.repodir) 141 super(RevisionControlUnittest, self).tearDown() 142 143 144 def testCommit(self): 145 """ 146 Test add, commit, pull, clone. 147 """ 148 self.master_repo._edit() 149 self.master_repo.add() 150 self.master_repo.commit() 151 self.dependent_repo.get_master_tot() 152 self.assertEquals(self.dependent_repo.commit_hash, 153 self.master_repo.commit_hash, 154 msg=(("hashes don't match after clone, master and dependent repo" 155 "out of sync: %r != %r") % 156 (self.dependent_repo.commit_hash, 157 self.master_repo.commit_hash))) 158 159 self.master_repo._edit(msg='foobar') 160 self.master_repo.commit() 161 self.dependent_repo.get_master_tot() 162 self.assertEquals(self.dependent_repo.commit_hash, 163 self.master_repo.commit_hash, 164 msg=(("hashes don't match after pull, master and dependent repo" 165 "out of sync: %r != %r") % 166 (self.dependent_repo.commit_hash, 167 self.master_repo.commit_hash))) 168 169 170 def testGitUrlClone(self): 171 """ 172 Test that git clone raises a ValueError if giturl is unset. 173 """ 174 self.dependent_repo.git_repo_manager._giturl = None 175 self.assertRaises(ValueError, 176 self.dependent_repo.git_repo_manager.clone) 177 178 179 def testGitUrlPull(self): 180 """ 181 Test that git pull raises a ValueError if giturl is unset. 182 """ 183 self.dependent_repo.git_repo_manager._giturl = None 184 self.assertRaises(ValueError, 185 self.dependent_repo.git_repo_manager.pull) 186 187 188 def testGitUrlFetch(self): 189 """ 190 Test that git fetch raises a ValueError if giturl is unset. 191 """ 192 self.dependent_repo.git_repo_manager._giturl = None 193 self.assertRaises(ValueError, 194 self.dependent_repo.git_repo_manager.fetch_remote) 195 196 197 if __name__ == '__main__': 198 unittest.main() 199