• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/python2
2import logging, mox, os, shutil, tempfile, unittest, utils
3
4# This makes autotest_lib imports available.
5import common
6from autotest_lib.client.common_lib import revision_control
7
8
9class 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, main_repo=None):
20        """
21        Setup self.git_repo_manager.
22
23        If a main_repo is present clone it.
24        Otherwise create a directory in /tmp and init it.
25
26        @param main_repo: GitRepo representing main.
27        """
28        if main_repo is None:
29            self.repodir = tempfile.mktemp(suffix='main')
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                                      main_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@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_main_tot(self):
110        """
111        Get everything from mains TOT squashing local changes.
112        If the dependent repo is empty pull from main.
113        """
114        # TODO b:169251326 terms below are set outside of this codebase
115        # and should be updated when possible. ("master" -> "main")
116        # Currently (but I believe it will eventually) does not support
117        # `reset --hard origin/main` (must be origin/master).
118        self.git_repo_manager.reinit_repo_at('master')
119        self.commit_hash = self.git_repo_manager.get_latest_commit_hash()
120
121
122class RevisionControlUnittest(mox.MoxTestBase):
123    """
124    A unittest to exercise build_externals.py's usage
125    of revision_control.py's Git wrappers.
126    """
127    main_repo=None
128    dependent_repo=None
129
130    def setUp(self):
131        """
132        Create a main repo and clone it into a dependent repo.
133        """
134        super(RevisionControlUnittest, self).setUp()
135        self.main_repo = GitRepoManager()
136        self.dependent_repo = GitRepoManager(self.main_repo)
137
138
139    def tearDown(self):
140        """
141        Delete temporary directories.
142        """
143        shutil.rmtree(self.main_repo.repodir)
144        shutil.rmtree(self.dependent_repo.repodir)
145        super(RevisionControlUnittest, self).tearDown()
146
147
148    def testCommit(self):
149        """
150        Test add, commit, pull, clone.
151        """
152        self.main_repo._edit()
153        self.main_repo.add()
154        self.main_repo.commit()
155        self.dependent_repo.get_main_tot()
156        self.assertEquals(self.dependent_repo.commit_hash,
157            self.main_repo.commit_hash,
158            msg=(("hashes don't match after clone, main and dependent repo"
159                  "out of sync: %r != %r") %
160                  (self.dependent_repo.commit_hash,
161                   self.main_repo.commit_hash)))
162
163        self.main_repo._edit(msg='foobar')
164        self.main_repo.commit()
165        self.dependent_repo.get_main_tot()
166        self.assertEquals(self.dependent_repo.commit_hash,
167            self.main_repo.commit_hash,
168            msg=(("hashes don't match after pull, main and dependent repo"
169                  "out of sync: %r != %r") %
170                  (self.dependent_repo.commit_hash,
171                   self.main_repo.commit_hash)))
172
173
174    def testGitUrlClone(self):
175        """
176        Test that git clone raises a ValueError if giturl is unset.
177        """
178        self.dependent_repo.git_repo_manager._giturl = None
179        self.assertRaises(ValueError,
180                          self.dependent_repo.git_repo_manager.clone)
181
182
183    def testGitUrlPull(self):
184        """
185        Test that git pull raises a ValueError if giturl is unset.
186        """
187        self.dependent_repo.git_repo_manager._giturl = None
188        self.assertRaises(ValueError,
189                          self.dependent_repo.git_repo_manager.pull)
190
191
192    def testGitUrlFetch(self):
193        """
194        Test that git fetch raises a ValueError if giturl is unset.
195        """
196        self.dependent_repo.git_repo_manager._giturl = None
197        self.assertRaises(ValueError,
198                          self.dependent_repo.git_repo_manager.fetch_remote)
199
200
201if __name__ == '__main__':
202  unittest.main()
203