blob: 9fd17d85c7deb0a4198187ef0a23d45c8a16f0c8 [file]
# 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(
'<project name="platform/a" path="a" />',
'<project name="platform/b" path="b" groups="g1" />',
'<project name="platform/c" path="c" groups="g2,notdefault" />',
'<project name="platform/d" path="d" groups="notdefault" />',
)
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 <args>`."""
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:
"""`<include groups=...>` 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(
'<project name="platform/base" path="base" />',
'<include name="extra.xml" groups="notdefault,extra" />',
),
"extra.xml": (
"<manifest>\n"
' <project name="platform/extra" path="extra" />\n'
' <project name="platform/extra2" path="extra2" '
'groups="g2" />\n'
"</manifest>\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(
'<project name="platform/base" path="base" />',
*(
f'<project name="platform/{name}" path="{name}" '
f'groups="notdefault,platform-{name}" />'
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(
'<superproject name="superproject" />',
'<project name="platform/a" path="a" />',
'<project name="platform/b" path="b" groups="notdefault,g1" />',
)
)
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"