1*3c875a21SAndroid Build Coastguard Worker# 2*3c875a21SAndroid Build Coastguard Worker# Copyright (C) 2023 The Android Open Source Project 3*3c875a21SAndroid Build Coastguard Worker# 4*3c875a21SAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License"); 5*3c875a21SAndroid Build Coastguard Worker# you may not use this file except in compliance with the License. 6*3c875a21SAndroid Build Coastguard Worker# You may obtain a copy of the License at 7*3c875a21SAndroid Build Coastguard Worker# 8*3c875a21SAndroid Build Coastguard Worker# http://www.apache.org/licenses/LICENSE-2.0 9*3c875a21SAndroid Build Coastguard Worker# 10*3c875a21SAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software 11*3c875a21SAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS, 12*3c875a21SAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13*3c875a21SAndroid Build Coastguard Worker# See the License for the specific language governing permissions and 14*3c875a21SAndroid Build Coastguard Worker# limitations under the License. 15*3c875a21SAndroid Build Coastguard Worker# 16*3c875a21SAndroid Build Coastguard Worker"""repo tree fakes for use in tests.""" 17*3c875a21SAndroid Build Coastguard Workerimport contextlib 18*3c875a21SAndroid Build Coastguard Workerimport subprocess 19*3c875a21SAndroid Build Coastguard Workerfrom pathlib import Path 20*3c875a21SAndroid Build Coastguard Workerfrom xml.etree import ElementTree 21*3c875a21SAndroid Build Coastguard Worker 22*3c875a21SAndroid Build Coastguard Workerfrom .fakeproject import FakeProject 23*3c875a21SAndroid Build Coastguard Worker 24*3c875a21SAndroid Build Coastguard Worker 25*3c875a21SAndroid Build Coastguard Workerclass FakeRepo: 26*3c875a21SAndroid Build Coastguard Worker """A repo tree for use in tests. 27*3c875a21SAndroid Build Coastguard Worker 28*3c875a21SAndroid Build Coastguard Worker This shouldn't be used directly. Use the tree_builder fixture. 29*3c875a21SAndroid Build Coastguard Worker """ 30*3c875a21SAndroid Build Coastguard Worker 31*3c875a21SAndroid Build Coastguard Worker def __init__(self, temp_dir: Path) -> None: 32*3c875a21SAndroid Build Coastguard Worker self.root = temp_dir / "tree" 33*3c875a21SAndroid Build Coastguard Worker self.mirror_dir = temp_dir / "mirrors" 34*3c875a21SAndroid Build Coastguard Worker self.upstream_dir = temp_dir / "upstreams" 35*3c875a21SAndroid Build Coastguard Worker self.manifest_repo = temp_dir / "manifest" 36*3c875a21SAndroid Build Coastguard Worker self.projects: list[FakeProject] = [] 37*3c875a21SAndroid Build Coastguard Worker self._used_git_subpaths: set[str] = set() 38*3c875a21SAndroid Build Coastguard Worker self._used_tree_subpaths: set[str] = set() 39*3c875a21SAndroid Build Coastguard Worker 40*3c875a21SAndroid Build Coastguard Worker def project(self, git_subpath: str, tree_subpath: str) -> FakeProject: 41*3c875a21SAndroid Build Coastguard Worker """Creates a new project in the repo.""" 42*3c875a21SAndroid Build Coastguard Worker if git_subpath in self._used_git_subpaths: 43*3c875a21SAndroid Build Coastguard Worker raise KeyError(f"A project with git path {git_subpath} already exists") 44*3c875a21SAndroid Build Coastguard Worker if tree_subpath in self._used_tree_subpaths: 45*3c875a21SAndroid Build Coastguard Worker raise KeyError(f"A project with tree path {tree_subpath} already exists") 46*3c875a21SAndroid Build Coastguard Worker project = FakeProject( 47*3c875a21SAndroid Build Coastguard Worker self.root / tree_subpath, 48*3c875a21SAndroid Build Coastguard Worker self.upstream_dir / tree_subpath, 49*3c875a21SAndroid Build Coastguard Worker self.mirror_dir / git_subpath, 50*3c875a21SAndroid Build Coastguard Worker ) 51*3c875a21SAndroid Build Coastguard Worker self.projects.append(project) 52*3c875a21SAndroid Build Coastguard Worker self._used_git_subpaths.add(git_subpath) 53*3c875a21SAndroid Build Coastguard Worker self._used_tree_subpaths.add(tree_subpath) 54*3c875a21SAndroid Build Coastguard Worker return project 55*3c875a21SAndroid Build Coastguard Worker 56*3c875a21SAndroid Build Coastguard Worker def init_and_sync(self) -> None: 57*3c875a21SAndroid Build Coastguard Worker """Runs repo init and repo sync to clone the repo tree.""" 58*3c875a21SAndroid Build Coastguard Worker self.root.mkdir(parents=True) 59*3c875a21SAndroid Build Coastguard Worker with contextlib.chdir(self.root): 60*3c875a21SAndroid Build Coastguard Worker subprocess.run( 61*3c875a21SAndroid Build Coastguard Worker ["repo", "init", "-c", "-u", str(self.manifest_repo), "-b", "main"], 62*3c875a21SAndroid Build Coastguard Worker check=True, 63*3c875a21SAndroid Build Coastguard Worker ) 64*3c875a21SAndroid Build Coastguard Worker subprocess.run(["repo", "sync", "-c"], check=True) 65*3c875a21SAndroid Build Coastguard Worker 66*3c875a21SAndroid Build Coastguard Worker def create_manifest_repo(self) -> None: 67*3c875a21SAndroid Build Coastguard Worker """Creates the git repo for the manifest, commits the manifest XML.""" 68*3c875a21SAndroid Build Coastguard Worker self.manifest_repo.mkdir(parents=True) 69*3c875a21SAndroid Build Coastguard Worker with contextlib.chdir(self.manifest_repo): 70*3c875a21SAndroid Build Coastguard Worker subprocess.run(["git", "init"], check=True) 71*3c875a21SAndroid Build Coastguard Worker Path("default.xml").write_bytes( 72*3c875a21SAndroid Build Coastguard Worker ElementTree.tostring(self._create_manifest_xml(), encoding="utf-8") 73*3c875a21SAndroid Build Coastguard Worker ) 74*3c875a21SAndroid Build Coastguard Worker subprocess.run(["git", "add", "default.xml"], check=True) 75*3c875a21SAndroid Build Coastguard Worker subprocess.run(["git", "commit", "-m", "Initial commit."], check=True) 76*3c875a21SAndroid Build Coastguard Worker 77*3c875a21SAndroid Build Coastguard Worker def _create_manifest_xml(self) -> ElementTree.Element: 78*3c875a21SAndroid Build Coastguard Worker # Example manifest: 79*3c875a21SAndroid Build Coastguard Worker # 80*3c875a21SAndroid Build Coastguard Worker # <manifest> 81*3c875a21SAndroid Build Coastguard Worker # <remote name="aosp" fetch="$URL" /> 82*3c875a21SAndroid Build Coastguard Worker # <default revision="main" remote="aosp" /> 83*3c875a21SAndroid Build Coastguard Worker # 84*3c875a21SAndroid Build Coastguard Worker # <project path="external/project" name="platform/external/project" 85*3c875a21SAndroid Build Coastguard Worker # revision="master" remote="goog" /> 86*3c875a21SAndroid Build Coastguard Worker # ... 87*3c875a21SAndroid Build Coastguard Worker # </manifest> 88*3c875a21SAndroid Build Coastguard Worker # 89*3c875a21SAndroid Build Coastguard Worker # The revision and remote attributes of project are optional. 90*3c875a21SAndroid Build Coastguard Worker root = ElementTree.Element("manifest") 91*3c875a21SAndroid Build Coastguard Worker ElementTree.SubElement( 92*3c875a21SAndroid Build Coastguard Worker root, 93*3c875a21SAndroid Build Coastguard Worker "remote", 94*3c875a21SAndroid Build Coastguard Worker {"name": "aosp", "fetch": self.mirror_dir.resolve().as_uri()}, 95*3c875a21SAndroid Build Coastguard Worker ) 96*3c875a21SAndroid Build Coastguard Worker ElementTree.SubElement(root, "default", {"revision": "main", "remote": "aosp"}) 97*3c875a21SAndroid Build Coastguard Worker for project in self.projects: 98*3c875a21SAndroid Build Coastguard Worker ElementTree.SubElement( 99*3c875a21SAndroid Build Coastguard Worker root, 100*3c875a21SAndroid Build Coastguard Worker "project", 101*3c875a21SAndroid Build Coastguard Worker { 102*3c875a21SAndroid Build Coastguard Worker "path": str(project.local.path.relative_to(self.root)), 103*3c875a21SAndroid Build Coastguard Worker "name": str( 104*3c875a21SAndroid Build Coastguard Worker project.android_mirror.path.relative_to(self.mirror_dir) 105*3c875a21SAndroid Build Coastguard Worker ), 106*3c875a21SAndroid Build Coastguard Worker }, 107*3c875a21SAndroid Build Coastguard Worker ) 108*3c875a21SAndroid Build Coastguard Worker return root 109