summaryrefslogtreecommitdiff
path: root/tests/test_subcmds_forall.py
blob: 67ec43cfd1885c2055e7038b956caa23e912f9e7 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
# Copyright (C) 2024 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.

"""Unittests for the forall subcmd."""

import contextlib
import io
from pathlib import Path

import utils_for_test

import manifest_xml
import subcmds


def _create_manifest_with_8_projects(
    topdir: Path,
) -> manifest_xml.XmlManifest:
    """Create a setup of 8 projects to execute forall."""
    repodir = topdir / ".repo"
    manifest_dir = repodir / "manifests"
    manifest_file = repodir / manifest_xml.MANIFEST_FILE_NAME

    repodir.mkdir()
    manifest_dir.mkdir()

    # Set up a manifest git dir for parsing to work.
    gitdir = repodir / "manifests.git"
    gitdir.mkdir()
    (gitdir / "config").write_text(
        """[remote "origin"]
            url = https://localhost:0/manifest
            verbose = false
        """
    )

    # Add the manifest data.
    manifest_file.write_text(
        """
            <manifest>
                <remote name="origin" fetch="http://localhost" />
                <default remote="origin" revision="refs/heads/main" />
                <project name="project1" path="tests/path1" />
                <project name="project2" path="tests/path2" />
                <project name="project3" path="tests/path3" />
                <project name="project4" path="tests/path4" />
                <project name="project5" path="tests/path5" />
                <project name="project6" path="tests/path6" />
                <project name="project7" path="tests/path7" />
                <project name="project8" path="tests/path8" />
            </manifest>
        """,
        encoding="utf-8",
    )

    # Set up 8 empty projects to match the manifest.
    for x in range(1, 9):
        (repodir / "projects" / "tests" / f"path{x}.git").mkdir(parents=True)
        (repodir / "project-objects" / f"project{x}.git").mkdir(parents=True)
        git_path = topdir / "tests" / f"path{x}"
        utils_for_test.init_git_tree(git_path)

    return manifest_xml.XmlManifest(str(repodir), str(manifest_file))


def test_forall_all_projects_called_once(tmp_path: Path) -> None:
    """Test that all projects get a command run once each."""
    manifest = _create_manifest_with_8_projects(tmp_path)

    cmd = subcmds.forall.Forall()
    cmd.manifest = manifest

    # Use echo project names as the test of forall.
    opts, args = cmd.OptionParser.parse_args(["-c", "echo $REPO_PROJECT"])
    opts.verbose = False

    # Set revisionId directly so GetRevisionId() short-circuits without
    # touching git.  Using mock.patch.object on the class does not work
    # with Python 3.14+, which defaults to "forkserver" on Linux —
    # class-level patches do not survive into forkserver worker processes.
    for proj in manifest.projects:
        proj.revisionId = "refs/heads/main"

    with contextlib.redirect_stdout(io.StringIO()) as stdout:
        # Run the forall command.
        cmd.Execute(opts, args)

    output = stdout.getvalue()
    # Verify that we got every project name in the output.
    for x in range(1, 9):
        assert f"project{x}" in output

    # Split the captured output into lines to count them.
    line_count = sum(1 for x in output.splitlines() if x)
    # Verify that we didn't get more lines than expected.
    assert line_count == 8