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