summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--docs/internal-fs-layout.md2
-rw-r--r--git_superproject.py154
-rwxr-xr-xrepo8
-rw-r--r--subcmds/init.py20
-rw-r--r--subcmds/sync.py22
-rw-r--r--tests/test_git_superproject.py115
6 files changed, 185 insertions, 136 deletions
diff --git a/docs/internal-fs-layout.md b/docs/internal-fs-layout.md
index 4d18bb31..53c42638 100644
--- a/docs/internal-fs-layout.md
+++ b/docs/internal-fs-layout.md
@@ -142,11 +142,13 @@ User controlled settings are initialized when running `repo init`.
142| repo.partialclone | `--partial-clone` | Create [partial git clones] | 142| repo.partialclone | `--partial-clone` | Create [partial git clones] |
143| repo.reference | `--reference` | Reference repo client checkout | 143| repo.reference | `--reference` | Reference repo client checkout |
144| repo.submodules | `--submodules` | Sync git submodules | 144| repo.submodules | `--submodules` | Sync git submodules |
145| repo.superproject | `--use-superproject` | Sync [superproject] |
145| repo.worktree | `--worktree` | Use `git worktree` for checkouts | 146| repo.worktree | `--worktree` | Use `git worktree` for checkouts |
146| user.email | `--config-name` | User's e-mail address; Copied into `.git/config` when checking out a new project | 147| user.email | `--config-name` | User's e-mail address; Copied into `.git/config` when checking out a new project |
147| user.name | `--config-name` | User's name; Copied into `.git/config` when checking out a new project | 148| user.name | `--config-name` | User's name; Copied into `.git/config` when checking out a new project |
148 149
149[partial git clones]: https://git-scm.com/docs/gitrepository-layout#_code_partialclone_code 150[partial git clones]: https://git-scm.com/docs/gitrepository-layout#_code_partialclone_code
151[superproject]: https://en.wikibooks.org/wiki/Git/Submodules_and_Superprojects
150 152
151### Repo hooks settings 153### Repo hooks settings
152 154
diff --git a/git_superproject.py b/git_superproject.py
index 378ede25..471dadc4 100644
--- a/git_superproject.py
+++ b/git_superproject.py
@@ -12,21 +12,22 @@
12# See the License for the specific language governing permissions and 12# See the License for the specific language governing permissions and
13# limitations under the License. 13# limitations under the License.
14 14
15"""Provide functionality to get all projects and their SHAs from Superproject. 15"""Provide functionality to get all projects and their commit ids from Superproject.
16 16
17For more information on superproject, check out: 17For more information on superproject, check out:
18https://en.wikibooks.org/wiki/Git/Submodules_and_Superprojects 18https://en.wikibooks.org/wiki/Git/Submodules_and_Superprojects
19 19
20Examples: 20Examples:
21 superproject = Superproject() 21 superproject = Superproject()
22 project_shas = superproject.GetAllProjectsSHAs() 22 project_commit_ids = superproject.UpdateProjectsRevisionId(projects)
23""" 23"""
24 24
25import os 25import os
26import sys 26import sys
27 27
28from error import BUG_REPORT_URL, GitError 28from error import BUG_REPORT_URL
29from git_command import GitCommand 29from git_command import GitCommand
30from git_refs import R_HEADS
30import platform_utils 31import platform_utils
31 32
32_SUPERPROJECT_GIT_NAME = 'superproject.git' 33_SUPERPROJECT_GIT_NAME = 'superproject.git'
@@ -34,19 +35,24 @@ _SUPERPROJECT_MANIFEST_NAME = 'superproject_override.xml'
34 35
35 36
36class Superproject(object): 37class Superproject(object):
37 """Get SHAs from superproject. 38 """Get commit ids from superproject.
38 39
39 It does a 'git clone' of superproject and 'git ls-tree' to get list of SHAs for all projects. 40 It does a 'git clone' of superproject and 'git ls-tree' to get list of commit ids
40 It contains project_shas which is a dictionary with project/sha entries. 41 for all projects. It contains project_commit_ids which is a dictionary with
42 project/commit id entries.
41 """ 43 """
42 def __init__(self, repodir, superproject_dir='exp-superproject'): 44 def __init__(self, manifest, repodir, superproject_dir='exp-superproject'):
43 """Initializes superproject. 45 """Initializes superproject.
44 46
45 Args: 47 Args:
48 manifest: A Manifest object that is to be written to a file.
46 repodir: Path to the .repo/ dir for holding all internal checkout state. 49 repodir: Path to the .repo/ dir for holding all internal checkout state.
50 It must be in the top directory of the repo client checkout.
47 superproject_dir: Relative path under |repodir| to checkout superproject. 51 superproject_dir: Relative path under |repodir| to checkout superproject.
48 """ 52 """
49 self._project_shas = None 53 self._project_commit_ids = None
54 self._manifest = manifest
55 self._branch = self._GetBranch()
50 self._repodir = os.path.abspath(repodir) 56 self._repodir = os.path.abspath(repodir)
51 self._superproject_dir = superproject_dir 57 self._superproject_dir = superproject_dir
52 self._superproject_path = os.path.join(self._repodir, superproject_dir) 58 self._superproject_path = os.path.join(self._repodir, superproject_dir)
@@ -56,25 +62,35 @@ class Superproject(object):
56 _SUPERPROJECT_GIT_NAME) 62 _SUPERPROJECT_GIT_NAME)
57 63
58 @property 64 @property
59 def project_shas(self): 65 def project_commit_ids(self):
60 """Returns a dictionary of projects and their SHAs.""" 66 """Returns a dictionary of projects and their commit ids."""
61 return self._project_shas 67 return self._project_commit_ids
68
69 def _GetBranch(self):
70 """Returns the branch name for getting the approved manifest."""
71 p = self._manifest.manifestProject
72 b = p.GetBranch(p.CurrentBranch)
73 if not b:
74 return None
75 branch = b.merge
76 if branch and branch.startswith(R_HEADS):
77 branch = branch[len(R_HEADS):]
78 return branch
62 79
63 def _Clone(self, url, branch=None): 80 def _Clone(self, url):
64 """Do a 'git clone' for the given url and branch. 81 """Do a 'git clone' for the given url.
65 82
66 Args: 83 Args:
67 url: superproject's url to be passed to git clone. 84 url: superproject's url to be passed to git clone.
68 branch: The branchname to be passed as argument to git clone.
69 85
70 Returns: 86 Returns:
71 True if 'git clone <url> <branch>' is successful, or False. 87 True if git clone is successful, or False.
72 """ 88 """
73 if not os.path.exists(self._superproject_path): 89 if not os.path.exists(self._superproject_path):
74 os.mkdir(self._superproject_path) 90 os.mkdir(self._superproject_path)
75 cmd = ['clone', url, '--filter', 'blob:none', '--bare'] 91 cmd = ['clone', url, '--filter', 'blob:none', '--bare']
76 if branch: 92 if self._branch:
77 cmd += ['--branch', branch] 93 cmd += ['--branch', self._branch]
78 p = GitCommand(None, 94 p = GitCommand(None,
79 cmd, 95 cmd,
80 cwd=self._superproject_path, 96 cwd=self._superproject_path,
@@ -112,22 +128,20 @@ class Superproject(object):
112 return False 128 return False
113 return True 129 return True
114 130
115 def _LsTree(self, branch='HEAD'): 131 def _LsTree(self):
116 """Returns the data from 'git ls-tree -r <branch>'. 132 """Returns the data from 'git ls-tree ...'.
117 133
118 Works only in git repositories. 134 Works only in git repositories.
119 135
120 Args:
121 branch: The branchname to be passed as argument to git ls-tree.
122
123 Returns: 136 Returns:
124 data: data returned from 'git ls-tree -r HEAD' instead of None. 137 data: data returned from 'git ls-tree ...' instead of None.
125 """ 138 """
126 if not os.path.exists(self._work_git): 139 if not os.path.exists(self._work_git):
127 print('git ls-tree missing drectory: %s' % self._work_git, 140 print('git ls-tree missing drectory: %s' % self._work_git,
128 file=sys.stderr) 141 file=sys.stderr)
129 return None 142 return None
130 data = None 143 data = None
144 branch = 'HEAD' if not self._branch else self._branch
131 cmd = ['ls-tree', '-z', '-r', branch] 145 cmd = ['ls-tree', '-z', '-r', branch]
132 146
133 p = GitCommand(None, 147 p = GitCommand(None,
@@ -145,18 +159,25 @@ class Superproject(object):
145 retval, p.stderr), file=sys.stderr) 159 retval, p.stderr), file=sys.stderr)
146 return data 160 return data
147 161
148 def _GetAllProjectsSHAs(self, url, branch=None): 162 def Sync(self):
149 """Get SHAs for all projects from superproject and save them in _project_shas. 163 """Sync superproject either by git clone/fetch.
150
151 Args:
152 url: superproject's url to be passed to git clone or fetch.
153 branch: The branchname to be passed as argument to git clone or fetch.
154 164
155 Returns: 165 Returns:
156 A dictionary with the projects/SHAs instead of None. 166 True if sync of superproject is successful, or False.
157 """ 167 """
168 print('WARNING: --use-superproject is experimental and not '
169 'for general use', file=sys.stderr)
170
171 if not self._manifest.superproject:
172 print('error: superproject tag is not defined in manifest',
173 file=sys.stderr)
174 return False
175
176 url = self._manifest.superproject['remote'].url
158 if not url: 177 if not url:
159 raise ValueError('url argument is not supplied.') 178 print('error: superproject URL is not defined in manifest',
179 file=sys.stderr)
180 return False
160 181
161 do_clone = True 182 do_clone = True
162 if os.path.exists(self._superproject_path): 183 if os.path.exists(self._superproject_path):
@@ -166,35 +187,44 @@ class Superproject(object):
166 else: 187 else:
167 do_clone = False 188 do_clone = False
168 if do_clone: 189 if do_clone:
169 if not self._Clone(url, branch): 190 if not self._Clone(url):
170 raise GitError('git clone failed for url: %s' % url) 191 print('error: git clone failed for url: %s' % url, file=sys.stderr)
192 return False
193 return True
171 194
172 data = self._LsTree(branch) 195 def _GetAllProjectsCommitIds(self):
196 """Get commit ids for all projects from superproject and save them in _project_commit_ids.
197
198 Returns:
199 A dictionary with the projects/commit ids on success, otherwise None.
200 """
201 if not self.Sync():
202 return None
203
204 data = self._LsTree()
173 if not data: 205 if not data:
174 raise GitError('git ls-tree failed for url: %s' % url) 206 print('error: git ls-tree failed for superproject', file=sys.stderr)
207 return None
175 208
176 # Parse lines like the following to select lines starting with '160000' and 209 # Parse lines like the following to select lines starting with '160000' and
177 # build a dictionary with project path (last element) and its SHA (3rd element). 210 # build a dictionary with project path (last element) and its commit id (3rd element).
178 # 211 #
179 # 160000 commit 2c2724cb36cd5a9cec6c852c681efc3b7c6b86ea\tart\x00 212 # 160000 commit 2c2724cb36cd5a9cec6c852c681efc3b7c6b86ea\tart\x00
180 # 120000 blob acc2cbdf438f9d2141f0ae424cec1d8fc4b5d97f\tbootstrap.bash\x00 213 # 120000 blob acc2cbdf438f9d2141f0ae424cec1d8fc4b5d97f\tbootstrap.bash\x00
181 shas = {} 214 commit_ids = {}
182 for line in data.split('\x00'): 215 for line in data.split('\x00'):
183 ls_data = line.split(None, 3) 216 ls_data = line.split(None, 3)
184 if not ls_data: 217 if not ls_data:
185 break 218 break
186 if ls_data[0] == '160000': 219 if ls_data[0] == '160000':
187 shas[ls_data[3]] = ls_data[2] 220 commit_ids[ls_data[3]] = ls_data[2]
188 221
189 self._project_shas = shas 222 self._project_commit_ids = commit_ids
190 return shas 223 return commit_ids
191 224
192 def _WriteManfiestFile(self, manifest): 225 def _WriteManfiestFile(self):
193 """Writes manifest to a file. 226 """Writes manifest to a file.
194 227
195 Args:
196 manifest: A Manifest object that is to be written to a file.
197
198 Returns: 228 Returns:
199 manifest_path: Path name of the file into which manifest is written instead of None. 229 manifest_path: Path name of the file into which manifest is written instead of None.
200 """ 230 """
@@ -203,7 +233,7 @@ class Superproject(object):
203 self._superproject_path, 233 self._superproject_path,
204 file=sys.stderr) 234 file=sys.stderr)
205 return None 235 return None
206 manifest_str = manifest.ToXml().toxml() 236 manifest_str = self._manifest.ToXml().toxml()
207 manifest_path = self._manifest_path 237 manifest_path = self._manifest_path
208 try: 238 try:
209 with open(manifest_path, 'w', encoding='utf-8') as fp: 239 with open(manifest_path, 'w', encoding='utf-8') as fp:
@@ -215,40 +245,34 @@ class Superproject(object):
215 return None 245 return None
216 return manifest_path 246 return manifest_path
217 247
218 def UpdateProjectsRevisionId(self, manifest, projects, url, branch=None): 248 def UpdateProjectsRevisionId(self, projects):
219 """Update revisionId of every project in projects with the SHA. 249 """Update revisionId of every project in projects with the commit id.
220 250
221 Args: 251 Args:
222 manifest: A Manifest object that is to be written to a file.
223 projects: List of projects whose revisionId needs to be updated. 252 projects: List of projects whose revisionId needs to be updated.
224 url: superproject's url to be passed to git clone or fetch.
225 branch: The branchname to be passed as argument to git clone or fetch.
226 253
227 Returns: 254 Returns:
228 manifest_path: Path name of the overriding manfiest file instead of None. 255 manifest_path: Path name of the overriding manfiest file instead of None.
229 """ 256 """
230 try: 257 commit_ids = self._GetAllProjectsCommitIds()
231 shas = self._GetAllProjectsSHAs(url=url, branch=branch) 258 if not commit_ids:
232 except Exception as e: 259 print('error: Cannot get project commit ids from manifest', file=sys.stderr)
233 print('error: Cannot get project SHAs for %s: %s: %s' %
234 (url, type(e).__name__, str(e)),
235 file=sys.stderr)
236 return None 260 return None
237 261
238 projects_missing_shas = [] 262 projects_missing_commit_ids = []
239 for project in projects: 263 for project in projects:
240 path = project.relpath 264 path = project.relpath
241 if not path: 265 if not path:
242 continue 266 continue
243 sha = shas.get(path) 267 commit_id = commit_ids.get(path)
244 if sha: 268 if commit_id:
245 project.SetRevisionId(sha) 269 project.SetRevisionId(commit_id)
246 else: 270 else:
247 projects_missing_shas.append(path) 271 projects_missing_commit_ids.append(path)
248 if projects_missing_shas: 272 if projects_missing_commit_ids:
249 print('error: please file a bug using %s to report missing shas for: %s' % 273 print('error: please file a bug using %s to report missing commit_ids for: %s' %
250 (BUG_REPORT_URL, projects_missing_shas), file=sys.stderr) 274 (BUG_REPORT_URL, projects_missing_commit_ids), file=sys.stderr)
251 return None 275 return None
252 276
253 manifest_path = self._WriteManfiestFile(manifest) 277 manifest_path = self._WriteManfiestFile()
254 return manifest_path 278 return manifest_path
diff --git a/repo b/repo
index 83933d7c..63cee031 100755
--- a/repo
+++ b/repo
@@ -324,6 +324,11 @@ def GetParser(gitc_init=False):
324 'each project. See git archive.') 324 'each project. See git archive.')
325 group.add_option('--submodules', action='store_true', 325 group.add_option('--submodules', action='store_true',
326 help='sync any submodules associated with the manifest repo') 326 help='sync any submodules associated with the manifest repo')
327 group.add_option('--use-superproject', action='store_true', default=None,
328 help='use the manifest superproject to sync projects')
329 group.add_option('--no-use-superproject', action='store_false',
330 dest='use_superproject',
331 help='disable use of manifest superprojects')
327 group.add_option('-g', '--groups', default='default', 332 group.add_option('-g', '--groups', default='default',
328 help='restrict manifest projects to ones with specified ' 333 help='restrict manifest projects to ones with specified '
329 'group(s) [default|all|G1,G2,G3|G4,-G5,-G6]', 334 'group(s) [default|all|G1,G2,G3|G4,-G5,-G6]',
@@ -333,7 +338,8 @@ def GetParser(gitc_init=False):
333 'platform group [auto|all|none|linux|darwin|...]', 338 'platform group [auto|all|none|linux|darwin|...]',
334 metavar='PLATFORM') 339 metavar='PLATFORM')
335 group.add_option('--clone-bundle', action='store_true', 340 group.add_option('--clone-bundle', action='store_true',
336 help='enable use of /clone.bundle on HTTP/HTTPS (default if not --partial-clone)') 341 help='enable use of /clone.bundle on HTTP/HTTPS '
342 '(default if not --partial-clone)')
337 group.add_option('--no-clone-bundle', 343 group.add_option('--no-clone-bundle',
338 dest='clone_bundle', action='store_false', 344 dest='clone_bundle', action='store_false',
339 help='disable use of /clone.bundle on HTTP/HTTPS (default if --partial-clone)') 345 help='disable use of /clone.bundle on HTTP/HTTPS (default if --partial-clone)')
diff --git a/subcmds/init.py b/subcmds/init.py
index fe3ebd2c..1d16c856 100644
--- a/subcmds/init.py
+++ b/subcmds/init.py
@@ -25,6 +25,7 @@ from error import ManifestParseError
25from project import SyncBuffer 25from project import SyncBuffer
26from git_config import GitConfig 26from git_config import GitConfig
27from git_command import git_require, MIN_GIT_VERSION_SOFT, MIN_GIT_VERSION_HARD 27from git_command import git_require, MIN_GIT_VERSION_SOFT, MIN_GIT_VERSION_HARD
28import git_superproject
28import platform_utils 29import platform_utils
29from wrapper import Wrapper 30from wrapper import Wrapper
30 31
@@ -134,6 +135,11 @@ to update the working directory files.
134 g.add_option('--submodules', 135 g.add_option('--submodules',
135 dest='submodules', action='store_true', 136 dest='submodules', action='store_true',
136 help='sync any submodules associated with the manifest repo') 137 help='sync any submodules associated with the manifest repo')
138 g.add_option('--use-superproject', action='store_true',
139 help='use the manifest superproject to sync projects')
140 g.add_option('--no-use-superproject', action='store_false',
141 dest='use_superproject',
142 help='disable use of manifest superprojects')
137 g.add_option('-g', '--groups', 143 g.add_option('-g', '--groups',
138 dest='groups', default='default', 144 dest='groups', default='default',
139 help='restrict manifest projects to ones with specified ' 145 help='restrict manifest projects to ones with specified '
@@ -176,6 +182,14 @@ to update the working directory files.
176 return {'REPO_MANIFEST_URL': 'manifest_url', 182 return {'REPO_MANIFEST_URL': 'manifest_url',
177 'REPO_MIRROR_LOCATION': 'reference'} 183 'REPO_MIRROR_LOCATION': 'reference'}
178 184
185 def _CloneSuperproject(self):
186 """Clone the superproject based on the superproject's url and branch."""
187 superproject = git_superproject.Superproject(self.manifest,
188 self.repodir)
189 if not superproject.Sync():
190 print('error: git update of superproject failed', file=sys.stderr)
191 sys.exit(1)
192
179 def _SyncManifest(self, opt): 193 def _SyncManifest(self, opt):
180 m = self.manifest.manifestProject 194 m = self.manifest.manifestProject
181 is_new = not m.Exists 195 is_new = not m.Exists
@@ -305,6 +319,9 @@ to update the working directory files.
305 if opt.submodules: 319 if opt.submodules:
306 m.config.SetBoolean('repo.submodules', opt.submodules) 320 m.config.SetBoolean('repo.submodules', opt.submodules)
307 321
322 if opt.use_superproject is not None:
323 m.config.SetBoolean('repo.superproject', opt.use_superproject)
324
308 if not m.Sync_NetworkHalf(is_new=is_new, quiet=opt.quiet, verbose=opt.verbose, 325 if not m.Sync_NetworkHalf(is_new=is_new, quiet=opt.quiet, verbose=opt.verbose,
309 clone_bundle=opt.clone_bundle, 326 clone_bundle=opt.clone_bundle,
310 current_branch_only=opt.current_branch_only, 327 current_branch_only=opt.current_branch_only,
@@ -519,6 +536,9 @@ to update the working directory files.
519 self._SyncManifest(opt) 536 self._SyncManifest(opt)
520 self._LinkManifest(opt.manifest_name) 537 self._LinkManifest(opt.manifest_name)
521 538
539 if self.manifest.manifestProject.config.GetBoolean('repo.superproject'):
540 self._CloneSuperproject()
541
522 if os.isatty(0) and os.isatty(1) and not self.manifest.IsMirror: 542 if os.isatty(0) and os.isatty(1) and not self.manifest.IsMirror:
523 if opt.config_name or self._ShouldConfigureUser(opt): 543 if opt.config_name or self._ShouldConfigureUser(opt):
524 self._ConfigureUser(opt) 544 self._ConfigureUser(opt)
diff --git a/subcmds/sync.py b/subcmds/sync.py
index 5020ea7a..eda95f96 100644
--- a/subcmds/sync.py
+++ b/subcmds/sync.py
@@ -294,28 +294,12 @@ later is required to fix a server side protocol bug.
294 Returns: 294 Returns:
295 Returns path to the overriding manifest file. 295 Returns path to the overriding manifest file.
296 """ 296 """
297 if not self.manifest.superproject: 297 superproject = git_superproject.Superproject(self.manifest,
298 print('error: superproject tag is not defined in manifest.xml', 298 self.repodir)
299 file=sys.stderr)
300 sys.exit(1)
301 print('WARNING: --use-superproject is experimental and not '
302 'for general use', file=sys.stderr)
303
304 superproject_url = self.manifest.superproject['remote'].url
305 if not superproject_url:
306 print('error: superproject URL is not defined in manifest.xml',
307 file=sys.stderr)
308 sys.exit(1)
309
310 superproject = git_superproject.Superproject(self.manifest.repodir)
311 all_projects = self.GetProjects(args, 299 all_projects = self.GetProjects(args,
312 missing_ok=True, 300 missing_ok=True,
313 submodules_ok=opt.fetch_submodules) 301 submodules_ok=opt.fetch_submodules)
314 branch = self._GetBranch() 302 manifest_path = superproject.UpdateProjectsRevisionId(all_projects)
315 manifest_path = superproject.UpdateProjectsRevisionId(self.manifest,
316 all_projects,
317 url=superproject_url,
318 branch=branch)
319 if not manifest_path: 303 if not manifest_path:
320 print('error: Update of revsionId from superproject has failed', 304 print('error: Update of revsionId from superproject has failed',
321 file=sys.stderr) 305 file=sys.stderr)
diff --git a/tests/test_git_superproject.py b/tests/test_git_superproject.py
index fc9101dd..d2ee9f4f 100644
--- a/tests/test_git_superproject.py
+++ b/tests/test_git_superproject.py
@@ -19,7 +19,6 @@ import tempfile
19import unittest 19import unittest
20from unittest import mock 20from unittest import mock
21 21
22from error import GitError
23import git_superproject 22import git_superproject
24import manifest_xml 23import manifest_xml
25import platform_utils 24import platform_utils
@@ -32,7 +31,6 @@ class SuperprojectTestCase(unittest.TestCase):
32 """Set up superproject every time.""" 31 """Set up superproject every time."""
33 self.tempdir = tempfile.mkdtemp(prefix='repo_tests') 32 self.tempdir = tempfile.mkdtemp(prefix='repo_tests')
34 self.repodir = os.path.join(self.tempdir, '.repo') 33 self.repodir = os.path.join(self.tempdir, '.repo')
35 self._superproject = git_superproject.Superproject(self.repodir)
36 self.manifest_file = os.path.join( 34 self.manifest_file = os.path.join(
37 self.repodir, manifest_xml.MANIFEST_FILE_NAME) 35 self.repodir, manifest_xml.MANIFEST_FILE_NAME)
38 os.mkdir(self.repodir) 36 os.mkdir(self.repodir)
@@ -45,6 +43,16 @@ class SuperprojectTestCase(unittest.TestCase):
45 url = https://localhost:0/manifest 43 url = https://localhost:0/manifest
46""") 44""")
47 45
46 manifest = self.getXmlManifest("""
47<manifest>
48 <remote name="default-remote" fetch="http://localhost" />
49 <default remote="default-remote" revision="refs/heads/main" />
50 <superproject name="superproject"/>
51 <project path="art" name="platform/art" />
52</manifest>
53""")
54 self._superproject = git_superproject.Superproject(manifest, self.repodir)
55
48 def tearDown(self): 56 def tearDown(self):
49 """Tear down superproject every time.""" 57 """Tear down superproject every time."""
50 platform_utils.rmtree(self.tempdir) 58 platform_utils.rmtree(self.tempdir)
@@ -55,37 +63,53 @@ class SuperprojectTestCase(unittest.TestCase):
55 fp.write(data) 63 fp.write(data)
56 return manifest_xml.XmlManifest(self.repodir, self.manifest_file) 64 return manifest_xml.XmlManifest(self.repodir, self.manifest_file)
57 65
58 def test_superproject_get_project_shas_no_url(self): 66 def test_superproject_get_superproject_no_superproject(self):
59 """Test with no url.""" 67 """Test with no url."""
60 with self.assertRaises(ValueError): 68 manifest = self.getXmlManifest("""
61 self._superproject._GetAllProjectsSHAs(url=None) 69<manifest>
70</manifest>
71""")
72 superproject = git_superproject.Superproject(manifest, self.repodir)
73 self.assertFalse(superproject.Sync())
62 74
63 def test_superproject_get_project_shas_invalid_url(self): 75 def test_superproject_get_superproject_invalid_url(self):
64 """Test with an invalid url.""" 76 """Test with an invalid url."""
65 with self.assertRaises(GitError): 77 manifest = self.getXmlManifest("""
66 self._superproject._GetAllProjectsSHAs(url='localhost') 78<manifest>
79 <remote name="test-remote" fetch="localhost" />
80 <default remote="test-remote" revision="refs/heads/main" />
81 <superproject name="superproject"/>
82</manifest>
83""")
84 superproject = git_superproject.Superproject(manifest, self.repodir)
85 self.assertFalse(superproject.Sync())
67 86
68 def test_superproject_get_project_shas_invalid_branch(self): 87 def test_superproject_get_superproject_invalid_branch(self):
69 """Test with an invalid branch.""" 88 """Test with an invalid branch."""
70 with self.assertRaises(GitError): 89 manifest = self.getXmlManifest("""
71 self._superproject._GetAllProjectsSHAs( 90<manifest>
72 url='sso://android/platform/superproject', 91 <remote name="test-remote" fetch="localhost" />
73 branch='junk') 92 <default remote="test-remote" revision="refs/heads/main" />
93 <superproject name="superproject"/>
94</manifest>
95""")
96 superproject = git_superproject.Superproject(manifest, self.repodir)
97 with mock.patch.object(self._superproject, '_GetBranch', return_value='junk'):
98 self.assertFalse(superproject.Sync())
74 99
75 def test_superproject_get_project_shas_mock_clone(self): 100 def test_superproject_get_superproject_mock_clone(self):
76 """Test with _Clone failing.""" 101 """Test with _Clone failing."""
77 with self.assertRaises(GitError): 102 with mock.patch.object(self._superproject, '_Clone', return_value=False):
78 with mock.patch.object(self._superproject, '_Clone', return_value=False): 103 self.assertFalse(self._superproject.Sync())
79 self._superproject._GetAllProjectsSHAs(url='localhost') 104
80 105 def test_superproject_get_superproject_mock_fetch(self):
81 def test_superproject_get_project_shas_mock_fetch(self): 106 """Test with _Fetch failing and _clone being called."""
82 """Test with _Fetch failing.""" 107 with mock.patch.object(self._superproject, '_Clone', return_value=True):
83 with self.assertRaises(GitError): 108 os.mkdir(self._superproject._superproject_path)
84 with mock.patch.object(self._superproject, '_Clone', return_value=True): 109 with mock.patch.object(self._superproject, '_Fetch', return_value=False):
85 with mock.patch.object(self._superproject, '_Fetch', return_value=False): 110 self.assertTrue(self._superproject.Sync())
86 self._superproject._GetAllProjectsSHAs(url='localhost') 111
87 112 def test_superproject_get_all_project_commit_ids_mock_ls_tree(self):
88 def test_superproject_get_project_shas_mock_ls_tree(self):
89 """Test with LsTree being a mock.""" 113 """Test with LsTree being a mock."""
90 data = ('120000 blob 158258bdf146f159218e2b90f8b699c4d85b5804\tAndroid.bp\x00' 114 data = ('120000 blob 158258bdf146f159218e2b90f8b699c4d85b5804\tAndroid.bp\x00'
91 '160000 commit 2c2724cb36cd5a9cec6c852c681efc3b7c6b86ea\tart\x00' 115 '160000 commit 2c2724cb36cd5a9cec6c852c681efc3b7c6b86ea\tart\x00'
@@ -94,8 +118,8 @@ class SuperprojectTestCase(unittest.TestCase):
94 '160000 commit ade9b7a0d874e25fff4bf2552488825c6f111928\tbuild/bazel\x00') 118 '160000 commit ade9b7a0d874e25fff4bf2552488825c6f111928\tbuild/bazel\x00')
95 with mock.patch.object(self._superproject, '_Clone', return_value=True): 119 with mock.patch.object(self._superproject, '_Clone', return_value=True):
96 with mock.patch.object(self._superproject, '_LsTree', return_value=data): 120 with mock.patch.object(self._superproject, '_LsTree', return_value=data):
97 shas = self._superproject._GetAllProjectsSHAs(url='localhost', branch='junk') 121 commit_ids = self._superproject._GetAllProjectsCommitIds()
98 self.assertEqual(shas, { 122 self.assertEqual(commit_ids, {
99 'art': '2c2724cb36cd5a9cec6c852c681efc3b7c6b86ea', 123 'art': '2c2724cb36cd5a9cec6c852c681efc3b7c6b86ea',
100 'bootable/recovery': 'e9d25da64d8d365dbba7c8ee00fe8c4473fe9a06', 124 'bootable/recovery': 'e9d25da64d8d365dbba7c8ee00fe8c4473fe9a06',
101 'build/bazel': 'ade9b7a0d874e25fff4bf2552488825c6f111928' 125 'build/bazel': 'ade9b7a0d874e25fff4bf2552488825c6f111928'
@@ -103,19 +127,12 @@ class SuperprojectTestCase(unittest.TestCase):
103 127
104 def test_superproject_write_manifest_file(self): 128 def test_superproject_write_manifest_file(self):
105 """Test with writing manifest to a file after setting revisionId.""" 129 """Test with writing manifest to a file after setting revisionId."""
106 manifest = self.getXmlManifest(""" 130 self.assertEqual(len(self._superproject._manifest.projects), 1)
107<manifest> 131 project = self._superproject._manifest.projects[0]
108 <remote name="default-remote" fetch="http://localhost" />
109 <default remote="default-remote" revision="refs/heads/main" />
110 <project name="test-name"/>
111</manifest>
112""")
113 self.assertEqual(len(manifest.projects), 1)
114 project = manifest.projects[0]
115 project.SetRevisionId('ABCDEF') 132 project.SetRevisionId('ABCDEF')
116 # Create temporary directory so that it can write the file. 133 # Create temporary directory so that it can write the file.
117 os.mkdir(self._superproject._superproject_path) 134 os.mkdir(self._superproject._superproject_path)
118 manifest_path = self._superproject._WriteManfiestFile(manifest) 135 manifest_path = self._superproject._WriteManfiestFile()
119 self.assertIsNotNone(manifest_path) 136 self.assertIsNotNone(manifest_path)
120 with open(manifest_path, 'r') as fp: 137 with open(manifest_path, 'r') as fp:
121 manifest_xml = fp.read() 138 manifest_xml = fp.read()
@@ -124,29 +141,24 @@ class SuperprojectTestCase(unittest.TestCase):
124 '<?xml version="1.0" ?><manifest>' + 141 '<?xml version="1.0" ?><manifest>' +
125 '<remote name="default-remote" fetch="http://localhost"/>' + 142 '<remote name="default-remote" fetch="http://localhost"/>' +
126 '<default remote="default-remote" revision="refs/heads/main"/>' + 143 '<default remote="default-remote" revision="refs/heads/main"/>' +
127 '<project name="test-name" revision="ABCDEF"/>' + 144 '<project name="platform/art" path="art" revision="ABCDEF"/>' +
145 '<superproject name="superproject"/>' +
128 '</manifest>') 146 '</manifest>')
129 147
130 def test_superproject_update_project_revision_id(self): 148 def test_superproject_update_project_revision_id(self):
131 """Test with LsTree being a mock.""" 149 """Test with LsTree being a mock."""
132 manifest = self.getXmlManifest(""" 150 self.assertEqual(len(self._superproject._manifest.projects), 1)
133<manifest> 151 projects = self._superproject._manifest.projects
134 <remote name="default-remote" fetch="http://localhost" />
135 <default remote="default-remote" revision="refs/heads/main" />
136 <project path="art" name="platform/art" />
137</manifest>
138""")
139 self.assertEqual(len(manifest.projects), 1)
140 projects = manifest.projects
141 data = ('160000 commit 2c2724cb36cd5a9cec6c852c681efc3b7c6b86ea\tart\x00' 152 data = ('160000 commit 2c2724cb36cd5a9cec6c852c681efc3b7c6b86ea\tart\x00'
142 '160000 commit e9d25da64d8d365dbba7c8ee00fe8c4473fe9a06\tbootable/recovery\x00') 153 '160000 commit e9d25da64d8d365dbba7c8ee00fe8c4473fe9a06\tbootable/recovery\x00')
143 with mock.patch.object(self._superproject, '_Clone', return_value=True): 154 with mock.patch.object(self._superproject, '_Clone', return_value=True):
144 with mock.patch.object(self._superproject, '_Fetch', return_value=True): 155 with mock.patch.object(self._superproject, '_Fetch', return_value=True):
145 with mock.patch.object(self._superproject, '_LsTree', return_value=data): 156 with mock.patch.object(self._superproject,
157 '_LsTree',
158 return_value=data):
146 # Create temporary directory so that it can write the file. 159 # Create temporary directory so that it can write the file.
147 os.mkdir(self._superproject._superproject_path) 160 os.mkdir(self._superproject._superproject_path)
148 manifest_path = self._superproject.UpdateProjectsRevisionId( 161 manifest_path = self._superproject.UpdateProjectsRevisionId(projects)
149 manifest, projects, url='localhost')
150 self.assertIsNotNone(manifest_path) 162 self.assertIsNotNone(manifest_path)
151 with open(manifest_path, 'r') as fp: 163 with open(manifest_path, 'r') as fp:
152 manifest_xml = fp.read() 164 manifest_xml = fp.read()
@@ -157,6 +169,7 @@ class SuperprojectTestCase(unittest.TestCase):
157 '<default remote="default-remote" revision="refs/heads/main"/>' + 169 '<default remote="default-remote" revision="refs/heads/main"/>' +
158 '<project name="platform/art" path="art" ' + 170 '<project name="platform/art" path="art" ' +
159 'revision="2c2724cb36cd5a9cec6c852c681efc3b7c6b86ea"/>' + 171 'revision="2c2724cb36cd5a9cec6c852c681efc3b7c6b86ea"/>' +
172 '<superproject name="superproject"/>' +
160 '</manifest>') 173 '</manifest>')
161 174
162 175