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