# Copyright (C) 2026 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """E2E tests for manifest groups beyond `repo init -g`.""" import platform from typing import Dict, List, Optional, Set from harness import GitHost from harness import Gitlink from harness import manifest_xml from harness import RepoClient from harness import ServerRepo import pytest # `a` & `b` are in the default group; `c` & `d` are not. GROUPED_MANIFEST = manifest_xml( '', '', '', '', ) def create_grouped_projects(host: GitHost) -> Dict[str, ServerRepo]: """Create the projects of `GROUPED_MANIFEST` & the manifest itself.""" repos = {} for name in "abcd": repos[name] = host.create_repo(f"platform/{name}", {"f": name}) host.create_manifest(GROUPED_MANIFEST) return repos def listed(client: RepoClient, *args: str) -> Set[str]: """The project paths printed by `repo list `.""" return set(client.list_projects(*args)) def test_reinit_groups(host: GitHost, client: RepoClient) -> None: """Re-running `init -g` adds & removes projects on the next sync.""" create_grouped_projects(host) this_platform = f"platform-{platform.system().lower()}" client.init(host.manifest_url) client.sync() assert client.checkouts() == {"a", "b"} assert client.config(".repo/manifests", "manifest.groups") is None client.init(host.manifest_url, "-g", "default,g2") client.sync() assert client.checkouts() == {"a", "b", "c"} assert ( client.config(".repo/manifests", "manifest.groups") == f"default,g2,{this_platform}" ) client.init(host.manifest_url, "-g", "g2") client.sync() assert client.checkouts() == {"c"} assert client.list_projects() == {"c": "platform/c"} assert ( client.config(".repo/manifests", "manifest.groups") == f"g2,{this_platform}" ) # The object dirs of removed projects are kept for a later re-add. assert (client.repodir / "project-objects/platform/a.git").is_dir() # Back to the default groups. client.init(host.manifest_url, "-g", "default") client.sync() assert client.checkouts() == {"a", "b"} assert (client.top / "a/f").read_text() == "a" @pytest.mark.parametrize( "init_args,expected", [ ([], {"base"}), (["-g", "default,extra"], {"base", "extra", "extra2"}), (["-g", "default,extra,-g2"], {"base", "extra"}), (["-g", "g2"], {"extra2"}), ], ) def test_include_groups( host: GitHost, client: RepoClient, init_args: List[str], expected: Set[str] ) -> None: """`` adds its groups to every included project.""" for name in ("base", "extra", "extra2"): host.create_repo(f"platform/{name}", {"f": name}) host.create_manifest( { "default.xml": manifest_xml( '', '', ), "extra.xml": ( "\n" ' \n' ' \n' "\n" ), } ) client.init(host.manifest_url, *init_args) client.sync() assert client.checkouts() == expected assert set(client.list_projects()) == expected # The include's groups are merged with the project's own. assert listed(client, "-a", "-g", "extra") == {"extra", "extra2"} assert listed(client, "-a", "-g", "g2") == {"extra2"} @pytest.mark.parametrize( "init_args,platforms", [ # None means the platform the test runs on. ([], None), (["--platform=all"], {"linux", "darwin", "windows"}), (["--platform=none"], set()), (["-p", "darwin"], {"darwin"}), (["-g", "default,platform-linux", "--platform=none"], {"linux"}), ], ) def test_platform_groups( host: GitHost, client: RepoClient, init_args: List[str], platforms: Optional[Set[str]], ) -> None: """`platform-*` groups follow the host OS unless `--platform` is given.""" names = ("linux", "darwin", "windows") host.create_repo("platform/base", {"f": "base"}) for name in names: host.create_repo(f"platform/{name}", {"f": name}) host.create_manifest( manifest_xml( '', *( f'' for name in names ), ) ) if platforms is None: platforms = {platform.system().lower()} client.init(host.manifest_url, *init_args) client.sync() assert client.checkouts() == {"base"} | platforms assert set(client.list_projects()) == {"base"} | platforms def test_groups_with_superproject(host: GitHost, client: RepoClient) -> None: """Group selection applies on top of superproject pinning.""" pins = {} for name in "ab": repo = host.create_repo(f"platform/{name}", {"f": "v1"}) pins[name] = repo.rev_parse("main") repo.commit({"f": "v2"}) host.create_repo( "superproject", {name: Gitlink(rev) for name, rev in pins.items()} ) host.create_manifest( manifest_xml( '', '', '', ) ) client.init(host.manifest_url, "--use-superproject") assert client.sync().stderr == "" assert client.checkouts() == {"a"} assert client.head("a") == pins["a"] assert host.requests_for("platform/b") == [] client.init(host.manifest_url, "-g", "all") assert client.sync().stderr == "" assert client.checkouts() == {"a", "b"} assert client.head("b") == pins["b"] assert (client.top / "b/f").read_text() == "v1"