xref: /aosp_15_r20/tools/external_updater/test_manifest.py (revision 3c875a214f382db1236d28570d1304ce57138f32)
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"""Tests for manifest.py."""
17*3c875a21SAndroid Build Coastguard Workerimport textwrap
18*3c875a21SAndroid Build Coastguard Workerfrom pathlib import Path
19*3c875a21SAndroid Build Coastguard Worker
20*3c875a21SAndroid Build Coastguard Workerimport pytest
21*3c875a21SAndroid Build Coastguard Worker
22*3c875a21SAndroid Build Coastguard Workerfrom manifest import Manifest, ManifestParser, find_manifest_xml_for_tree
23*3c875a21SAndroid Build Coastguard Worker
24*3c875a21SAndroid Build Coastguard Worker
25*3c875a21SAndroid Build Coastguard Workerclass TestFindManifestXmlForTree:
26*3c875a21SAndroid Build Coastguard Worker    """Tests for find_manifest_xml_for_tree."""
27*3c875a21SAndroid Build Coastguard Worker
28*3c875a21SAndroid Build Coastguard Worker    def test_repo_tree(self, repo_tree: Path) -> None:
29*3c875a21SAndroid Build Coastguard Worker        """Tests that the correct manifest file is found in a repo tree."""
30*3c875a21SAndroid Build Coastguard Worker        manifest_dir = Path(repo_tree / ".repo/manifests")
31*3c875a21SAndroid Build Coastguard Worker        manifest_dir.mkdir()
32*3c875a21SAndroid Build Coastguard Worker        manifest_path = manifest_dir / "default.xml"
33*3c875a21SAndroid Build Coastguard Worker        manifest_path.touch()
34*3c875a21SAndroid Build Coastguard Worker        assert find_manifest_xml_for_tree(repo_tree) == manifest_path
35*3c875a21SAndroid Build Coastguard Worker
36*3c875a21SAndroid Build Coastguard Worker    def test_no_manifest(self, tmp_path: Path) -> None:
37*3c875a21SAndroid Build Coastguard Worker        """Tests that an error is raised when no manifest is found."""
38*3c875a21SAndroid Build Coastguard Worker        with pytest.raises(FileNotFoundError):
39*3c875a21SAndroid Build Coastguard Worker            find_manifest_xml_for_tree(tmp_path)
40*3c875a21SAndroid Build Coastguard Worker
41*3c875a21SAndroid Build Coastguard Worker
42*3c875a21SAndroid Build Coastguard Workerclass TestManifestParser:
43*3c875a21SAndroid Build Coastguard Worker    """Tests for ManifestParser."""
44*3c875a21SAndroid Build Coastguard Worker
45*3c875a21SAndroid Build Coastguard Worker    def test_default_missing(self, tmp_path: Path) -> None:
46*3c875a21SAndroid Build Coastguard Worker        """Tests that an error is raised when the default node is missing."""
47*3c875a21SAndroid Build Coastguard Worker        manifest_path = tmp_path / "manifest.xml"
48*3c875a21SAndroid Build Coastguard Worker        manifest_path.write_text(
49*3c875a21SAndroid Build Coastguard Worker            textwrap.dedent(
50*3c875a21SAndroid Build Coastguard Worker                """\
51*3c875a21SAndroid Build Coastguard Worker                <?xml version="1.0" encoding="UTF-8"?>
52*3c875a21SAndroid Build Coastguard Worker                <manifest>
53*3c875a21SAndroid Build Coastguard Worker                    <project path="external/project" revision="master" />
54*3c875a21SAndroid Build Coastguard Worker                </manifest>
55*3c875a21SAndroid Build Coastguard Worker                """
56*3c875a21SAndroid Build Coastguard Worker            )
57*3c875a21SAndroid Build Coastguard Worker        )
58*3c875a21SAndroid Build Coastguard Worker        with pytest.raises(RuntimeError):
59*3c875a21SAndroid Build Coastguard Worker            ManifestParser(manifest_path).parse()
60*3c875a21SAndroid Build Coastguard Worker
61*3c875a21SAndroid Build Coastguard Worker    def test_name_missing(self, tmp_path: Path) -> None:
62*3c875a21SAndroid Build Coastguard Worker        """Tests that an error is raised when neither name nor path is defined for a project."""
63*3c875a21SAndroid Build Coastguard Worker        manifest_path = tmp_path / "manifest.xml"
64*3c875a21SAndroid Build Coastguard Worker        manifest_path.write_text(
65*3c875a21SAndroid Build Coastguard Worker            textwrap.dedent(
66*3c875a21SAndroid Build Coastguard Worker                """\
67*3c875a21SAndroid Build Coastguard Worker                <?xml version="1.0" encoding="UTF-8"?>
68*3c875a21SAndroid Build Coastguard Worker                <manifest>
69*3c875a21SAndroid Build Coastguard Worker                    <default revision="main" remote="aosp" />
70*3c875a21SAndroid Build Coastguard Worker
71*3c875a21SAndroid Build Coastguard Worker                    <project />
72*3c875a21SAndroid Build Coastguard Worker                </manifest>
73*3c875a21SAndroid Build Coastguard Worker                """
74*3c875a21SAndroid Build Coastguard Worker            )
75*3c875a21SAndroid Build Coastguard Worker        )
76*3c875a21SAndroid Build Coastguard Worker        with pytest.raises(RuntimeError):
77*3c875a21SAndroid Build Coastguard Worker            ManifestParser(manifest_path).parse()
78*3c875a21SAndroid Build Coastguard Worker
79*3c875a21SAndroid Build Coastguard Worker
80*3c875a21SAndroid Build Coastguard Worker    def test_multiple_default(self, tmp_path: Path) -> None:
81*3c875a21SAndroid Build Coastguard Worker        """Tests that an error is raised when there is more than one default node."""
82*3c875a21SAndroid Build Coastguard Worker        manifest = tmp_path / "manifest.xml"
83*3c875a21SAndroid Build Coastguard Worker        manifest.write_text(
84*3c875a21SAndroid Build Coastguard Worker            textwrap.dedent(
85*3c875a21SAndroid Build Coastguard Worker                """\
86*3c875a21SAndroid Build Coastguard Worker                <?xml version="1.0" encoding="UTF-8"?>
87*3c875a21SAndroid Build Coastguard Worker                <manifest>
88*3c875a21SAndroid Build Coastguard Worker                    <default revision="main" remote="aosp" />
89*3c875a21SAndroid Build Coastguard Worker                    <default revision="main" remote="aosp" />
90*3c875a21SAndroid Build Coastguard Worker
91*3c875a21SAndroid Build Coastguard Worker                    <project path="external/project" revision="master" />
92*3c875a21SAndroid Build Coastguard Worker                </manifest>
93*3c875a21SAndroid Build Coastguard Worker                """
94*3c875a21SAndroid Build Coastguard Worker            )
95*3c875a21SAndroid Build Coastguard Worker        )
96*3c875a21SAndroid Build Coastguard Worker        with pytest.raises(RuntimeError):
97*3c875a21SAndroid Build Coastguard Worker            ManifestParser(manifest).parse()
98*3c875a21SAndroid Build Coastguard Worker
99*3c875a21SAndroid Build Coastguard Worker    def test_remote_default(self, tmp_path: Path) -> None:
100*3c875a21SAndroid Build Coastguard Worker        """Tests that the default remote is used when not defined by the project."""
101*3c875a21SAndroid Build Coastguard Worker        manifest_path = tmp_path / "manifest.xml"
102*3c875a21SAndroid Build Coastguard Worker        manifest_path.write_text(
103*3c875a21SAndroid Build Coastguard Worker            textwrap.dedent(
104*3c875a21SAndroid Build Coastguard Worker                """\
105*3c875a21SAndroid Build Coastguard Worker                <?xml version="1.0" encoding="UTF-8"?>
106*3c875a21SAndroid Build Coastguard Worker                <manifest>
107*3c875a21SAndroid Build Coastguard Worker                    <remote name="aosp" />
108*3c875a21SAndroid Build Coastguard Worker                    <default revision="main" remote="aosp" />
109*3c875a21SAndroid Build Coastguard Worker
110*3c875a21SAndroid Build Coastguard Worker                    <project path="external/project" />
111*3c875a21SAndroid Build Coastguard Worker                </manifest>
112*3c875a21SAndroid Build Coastguard Worker                """
113*3c875a21SAndroid Build Coastguard Worker            )
114*3c875a21SAndroid Build Coastguard Worker        )
115*3c875a21SAndroid Build Coastguard Worker        manifest = ManifestParser(manifest_path).parse()
116*3c875a21SAndroid Build Coastguard Worker        assert manifest.project_with_path("external/project").remote == "aosp"
117*3c875a21SAndroid Build Coastguard Worker
118*3c875a21SAndroid Build Coastguard Worker    def test_revision_default(self, tmp_path: Path) -> None:
119*3c875a21SAndroid Build Coastguard Worker        """Tests that the default revision is used when not defined by the project."""
120*3c875a21SAndroid Build Coastguard Worker        manifest_path = tmp_path / "manifest.xml"
121*3c875a21SAndroid Build Coastguard Worker        manifest_path.write_text(
122*3c875a21SAndroid Build Coastguard Worker            textwrap.dedent(
123*3c875a21SAndroid Build Coastguard Worker                """\
124*3c875a21SAndroid Build Coastguard Worker                <?xml version="1.0" encoding="UTF-8"?>
125*3c875a21SAndroid Build Coastguard Worker                <manifest>
126*3c875a21SAndroid Build Coastguard Worker                    <default revision="main" remote="aosp" />
127*3c875a21SAndroid Build Coastguard Worker
128*3c875a21SAndroid Build Coastguard Worker                    <project path="external/project" />
129*3c875a21SAndroid Build Coastguard Worker                </manifest>
130*3c875a21SAndroid Build Coastguard Worker                """
131*3c875a21SAndroid Build Coastguard Worker            )
132*3c875a21SAndroid Build Coastguard Worker        )
133*3c875a21SAndroid Build Coastguard Worker        manifest = ManifestParser(manifest_path).parse()
134*3c875a21SAndroid Build Coastguard Worker        assert manifest.project_with_path("external/project").revision == "main"
135*3c875a21SAndroid Build Coastguard Worker
136*3c875a21SAndroid Build Coastguard Worker    def test_path_default(self, tmp_path: Path) -> None:
137*3c875a21SAndroid Build Coastguard Worker        """Tests that the default path is used when not defined by the project."""
138*3c875a21SAndroid Build Coastguard Worker        manifest_path = tmp_path / "manifest.xml"
139*3c875a21SAndroid Build Coastguard Worker        manifest_path.write_text(
140*3c875a21SAndroid Build Coastguard Worker            textwrap.dedent(
141*3c875a21SAndroid Build Coastguard Worker                """\
142*3c875a21SAndroid Build Coastguard Worker                <?xml version="1.0" encoding="UTF-8"?>
143*3c875a21SAndroid Build Coastguard Worker                <manifest>
144*3c875a21SAndroid Build Coastguard Worker                    <default revision="main" remote="aosp" />
145*3c875a21SAndroid Build Coastguard Worker
146*3c875a21SAndroid Build Coastguard Worker                    <project name="external/project" />
147*3c875a21SAndroid Build Coastguard Worker                </manifest>
148*3c875a21SAndroid Build Coastguard Worker                """
149*3c875a21SAndroid Build Coastguard Worker            )
150*3c875a21SAndroid Build Coastguard Worker        )
151*3c875a21SAndroid Build Coastguard Worker        manifest = ManifestParser(manifest_path).parse()
152*3c875a21SAndroid Build Coastguard Worker        assert manifest.project_with_path("external/project") is not None
153*3c875a21SAndroid Build Coastguard Worker
154*3c875a21SAndroid Build Coastguard Worker    def test_remote_explicit(self, tmp_path: Path) -> None:
155*3c875a21SAndroid Build Coastguard Worker        """Tests that the project remote is used when defined."""
156*3c875a21SAndroid Build Coastguard Worker        manifest_path = tmp_path / "manifest.xml"
157*3c875a21SAndroid Build Coastguard Worker        manifest_path.write_text(
158*3c875a21SAndroid Build Coastguard Worker            textwrap.dedent(
159*3c875a21SAndroid Build Coastguard Worker                """\
160*3c875a21SAndroid Build Coastguard Worker                <?xml version="1.0" encoding="UTF-8"?>
161*3c875a21SAndroid Build Coastguard Worker                <manifest>
162*3c875a21SAndroid Build Coastguard Worker                    <default revision="main" remote="aosp" />
163*3c875a21SAndroid Build Coastguard Worker
164*3c875a21SAndroid Build Coastguard Worker                    <project path="external/project" remote="origin" />
165*3c875a21SAndroid Build Coastguard Worker                </manifest>
166*3c875a21SAndroid Build Coastguard Worker                """
167*3c875a21SAndroid Build Coastguard Worker            )
168*3c875a21SAndroid Build Coastguard Worker        )
169*3c875a21SAndroid Build Coastguard Worker        manifest = ManifestParser(manifest_path).parse()
170*3c875a21SAndroid Build Coastguard Worker        assert manifest.project_with_path("external/project").remote == "origin"
171*3c875a21SAndroid Build Coastguard Worker
172*3c875a21SAndroid Build Coastguard Worker    def test_revision_explicit(self, tmp_path: Path) -> None:
173*3c875a21SAndroid Build Coastguard Worker        """Tests that the project revision is used when defined."""
174*3c875a21SAndroid Build Coastguard Worker        manifest_path = tmp_path / "manifest.xml"
175*3c875a21SAndroid Build Coastguard Worker        manifest_path.write_text(
176*3c875a21SAndroid Build Coastguard Worker            textwrap.dedent(
177*3c875a21SAndroid Build Coastguard Worker                """\
178*3c875a21SAndroid Build Coastguard Worker                <?xml version="1.0" encoding="UTF-8"?>
179*3c875a21SAndroid Build Coastguard Worker                <manifest>
180*3c875a21SAndroid Build Coastguard Worker                    <default revision="main" remote="aosp" />
181*3c875a21SAndroid Build Coastguard Worker
182*3c875a21SAndroid Build Coastguard Worker                    <project path="external/project" revision="master" />
183*3c875a21SAndroid Build Coastguard Worker                </manifest>
184*3c875a21SAndroid Build Coastguard Worker                """
185*3c875a21SAndroid Build Coastguard Worker            )
186*3c875a21SAndroid Build Coastguard Worker        )
187*3c875a21SAndroid Build Coastguard Worker        manifest = ManifestParser(manifest_path).parse()
188*3c875a21SAndroid Build Coastguard Worker        assert manifest.project_with_path("external/project").revision == "master"
189*3c875a21SAndroid Build Coastguard Worker
190*3c875a21SAndroid Build Coastguard Worker    def test_path_explicit(self, tmp_path: Path) -> None:
191*3c875a21SAndroid Build Coastguard Worker        """Tests that the project path is used when defined."""
192*3c875a21SAndroid Build Coastguard Worker        manifest_path = tmp_path / "manifest.xml"
193*3c875a21SAndroid Build Coastguard Worker        manifest_path.write_text(
194*3c875a21SAndroid Build Coastguard Worker            textwrap.dedent(
195*3c875a21SAndroid Build Coastguard Worker                """\
196*3c875a21SAndroid Build Coastguard Worker                <?xml version="1.0" encoding="UTF-8"?>
197*3c875a21SAndroid Build Coastguard Worker                <manifest>
198*3c875a21SAndroid Build Coastguard Worker                    <default revision="main" remote="aosp" />
199*3c875a21SAndroid Build Coastguard Worker
200*3c875a21SAndroid Build Coastguard Worker                    <project name="external/project" path="other/path" />
201*3c875a21SAndroid Build Coastguard Worker                </manifest>
202*3c875a21SAndroid Build Coastguard Worker                """
203*3c875a21SAndroid Build Coastguard Worker            )
204*3c875a21SAndroid Build Coastguard Worker        )
205*3c875a21SAndroid Build Coastguard Worker        manifest = ManifestParser(manifest_path).parse()
206*3c875a21SAndroid Build Coastguard Worker        assert manifest.project_with_path("other/path") is not None
207*3c875a21SAndroid Build Coastguard Worker
208*3c875a21SAndroid Build Coastguard Workerclass TestManifest:
209*3c875a21SAndroid Build Coastguard Worker    """Tests for Manifest."""
210*3c875a21SAndroid Build Coastguard Worker
211*3c875a21SAndroid Build Coastguard Worker    def test_for_tree(self, repo_tree: Path) -> None:
212*3c875a21SAndroid Build Coastguard Worker        """Tests the Manifest.for_tree constructor."""
213*3c875a21SAndroid Build Coastguard Worker        manifest_dir = Path(repo_tree / ".repo/manifests")
214*3c875a21SAndroid Build Coastguard Worker        manifest_dir.mkdir()
215*3c875a21SAndroid Build Coastguard Worker        (manifest_dir / "default.xml").write_text(
216*3c875a21SAndroid Build Coastguard Worker            textwrap.dedent(
217*3c875a21SAndroid Build Coastguard Worker                """\
218*3c875a21SAndroid Build Coastguard Worker                <?xml version="1.0" encoding="UTF-8"?>
219*3c875a21SAndroid Build Coastguard Worker                <manifest>
220*3c875a21SAndroid Build Coastguard Worker                    <default remote="aosp" revision="main" />
221*3c875a21SAndroid Build Coastguard Worker
222*3c875a21SAndroid Build Coastguard Worker                    <project path="external/a" />
223*3c875a21SAndroid Build Coastguard Worker                    <project path="external/b" />
224*3c875a21SAndroid Build Coastguard Worker                    <project path="external/c" />
225*3c875a21SAndroid Build Coastguard Worker                </manifest>
226*3c875a21SAndroid Build Coastguard Worker                """
227*3c875a21SAndroid Build Coastguard Worker            )
228*3c875a21SAndroid Build Coastguard Worker        )
229*3c875a21SAndroid Build Coastguard Worker        manifest = Manifest.for_tree(repo_tree)
230*3c875a21SAndroid Build Coastguard Worker        assert len(manifest.projects_by_path) == 3
231*3c875a21SAndroid Build Coastguard Worker
232*3c875a21SAndroid Build Coastguard Worker    def test_project_with_path(self, repo_tree: Path) -> None:
233*3c875a21SAndroid Build Coastguard Worker        """Tests that Manifest.project_with_path returns the correct project."""
234*3c875a21SAndroid Build Coastguard Worker        manifest_dir = Path(repo_tree / ".repo/manifests")
235*3c875a21SAndroid Build Coastguard Worker        manifest_dir.mkdir()
236*3c875a21SAndroid Build Coastguard Worker        (manifest_dir / "default.xml").write_text(
237*3c875a21SAndroid Build Coastguard Worker            textwrap.dedent(
238*3c875a21SAndroid Build Coastguard Worker                """\
239*3c875a21SAndroid Build Coastguard Worker                <?xml version="1.0" encoding="UTF-8"?>
240*3c875a21SAndroid Build Coastguard Worker                <manifest>
241*3c875a21SAndroid Build Coastguard Worker                    <default remote="aosp" revision="main" />
242*3c875a21SAndroid Build Coastguard Worker
243*3c875a21SAndroid Build Coastguard Worker                    <project path="external/a" />
244*3c875a21SAndroid Build Coastguard Worker                    <project path="external/b" />
245*3c875a21SAndroid Build Coastguard Worker                    <project path="external/c" />
246*3c875a21SAndroid Build Coastguard Worker                </manifest>
247*3c875a21SAndroid Build Coastguard Worker                """
248*3c875a21SAndroid Build Coastguard Worker            )
249*3c875a21SAndroid Build Coastguard Worker        )
250*3c875a21SAndroid Build Coastguard Worker        manifest = Manifest.for_tree(repo_tree)
251*3c875a21SAndroid Build Coastguard Worker        assert manifest.project_with_path("external/b").path == "external/b"
252*3c875a21SAndroid Build Coastguard Worker
253*3c875a21SAndroid Build Coastguard Worker    def test_project_with_path_missing(self, repo_tree: Path) -> None:
254*3c875a21SAndroid Build Coastguard Worker        """Tests that Manifest.project_with_path raises an error when not found."""
255*3c875a21SAndroid Build Coastguard Worker        manifest_dir = Path(repo_tree / ".repo/manifests")
256*3c875a21SAndroid Build Coastguard Worker        manifest_dir.mkdir()
257*3c875a21SAndroid Build Coastguard Worker        (manifest_dir / "default.xml").write_text(
258*3c875a21SAndroid Build Coastguard Worker            textwrap.dedent(
259*3c875a21SAndroid Build Coastguard Worker                """\
260*3c875a21SAndroid Build Coastguard Worker                <?xml version="1.0" encoding="UTF-8"?>
261*3c875a21SAndroid Build Coastguard Worker                <manifest>
262*3c875a21SAndroid Build Coastguard Worker                    <default remote="aosp" revision="main" />
263*3c875a21SAndroid Build Coastguard Worker
264*3c875a21SAndroid Build Coastguard Worker                    <project path="external/a" />
265*3c875a21SAndroid Build Coastguard Worker                    <project path="external/b" />
266*3c875a21SAndroid Build Coastguard Worker                    <project path="external/c" />
267*3c875a21SAndroid Build Coastguard Worker                </manifest>
268*3c875a21SAndroid Build Coastguard Worker                """
269*3c875a21SAndroid Build Coastguard Worker            )
270*3c875a21SAndroid Build Coastguard Worker        )
271*3c875a21SAndroid Build Coastguard Worker        manifest = Manifest.for_tree(repo_tree)
272*3c875a21SAndroid Build Coastguard Worker        with pytest.raises(KeyError):
273*3c875a21SAndroid Build Coastguard Worker            manifest.project_with_path("external/d")
274