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