diff options
-rw-r--r-- | tests/test_subcmds_forall.py | 156 |
1 files changed, 156 insertions, 0 deletions
diff --git a/tests/test_subcmds_forall.py b/tests/test_subcmds_forall.py new file mode 100644 index 00000000..84744f89 --- /dev/null +++ b/tests/test_subcmds_forall.py | |||
@@ -0,0 +1,156 @@ | |||
1 | # Copyright (C) 2024 The Android Open Source Project | ||
2 | # | ||
3 | # Licensed under the Apache License, Version 2.0 (the "License"); | ||
4 | # you may not use this file except in compliance with the License. | ||
5 | # You may obtain a copy of the License at | ||
6 | # | ||
7 | # http://www.apache.org/licenses/LICENSE-2.0 | ||
8 | # | ||
9 | # Unless required by applicable law or agreed to in writing, software | ||
10 | # distributed under the License is distributed on an "AS IS" BASIS, | ||
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
12 | # See the License for the specific language governing permissions and | ||
13 | # limitations under the License. | ||
14 | |||
15 | """Unittests for the forall subcmd.""" | ||
16 | |||
17 | from io import StringIO | ||
18 | import os | ||
19 | from shutil import rmtree | ||
20 | import subprocess | ||
21 | import tempfile | ||
22 | import unittest | ||
23 | from unittest import mock | ||
24 | |||
25 | import git_command | ||
26 | import manifest_xml | ||
27 | import project | ||
28 | import subcmds | ||
29 | |||
30 | |||
31 | class AllCommands(unittest.TestCase): | ||
32 | """Check registered all_commands.""" | ||
33 | |||
34 | def setUp(self): | ||
35 | """Common setup.""" | ||
36 | self.tempdirobj = tempfile.TemporaryDirectory(prefix="forall_tests") | ||
37 | self.tempdir = self.tempdirobj.name | ||
38 | self.repodir = os.path.join(self.tempdir, ".repo") | ||
39 | self.manifest_dir = os.path.join(self.repodir, "manifests") | ||
40 | self.manifest_file = os.path.join( | ||
41 | self.repodir, manifest_xml.MANIFEST_FILE_NAME | ||
42 | ) | ||
43 | self.local_manifest_dir = os.path.join( | ||
44 | self.repodir, manifest_xml.LOCAL_MANIFESTS_DIR_NAME | ||
45 | ) | ||
46 | os.mkdir(self.repodir) | ||
47 | os.mkdir(self.manifest_dir) | ||
48 | |||
49 | def tearDown(self): | ||
50 | """Common teardown.""" | ||
51 | rmtree(self.tempdir, ignore_errors=True) | ||
52 | |||
53 | def initTempGitTree(self, git_dir): | ||
54 | """Create a new empty git checkout for testing.""" | ||
55 | |||
56 | # Tests need to assume, that main is default branch at init, | ||
57 | # which is not supported in config until 2.28. | ||
58 | cmd = ["git", "init", "-q"] | ||
59 | if git_command.git_require((2, 28, 0)): | ||
60 | cmd += ["--initial-branch=main"] | ||
61 | else: | ||
62 | # Use template dir for init | ||
63 | templatedir = os.path.join(self.tempdirobj.name, ".test-template") | ||
64 | os.makedirs(templatedir) | ||
65 | with open(os.path.join(templatedir, "HEAD"), "w") as fp: | ||
66 | fp.write("ref: refs/heads/main\n") | ||
67 | cmd += ["--template", templatedir] | ||
68 | cmd += [git_dir] | ||
69 | subprocess.check_call(cmd) | ||
70 | |||
71 | def getXmlManifestWith8Projects(self): | ||
72 | """Create and return a setup of 8 projects with enough dummy | ||
73 | files and setup to execute forall.""" | ||
74 | |||
75 | # Set up a manifest git dir for parsing to work | ||
76 | gitdir = os.path.join(self.repodir, "manifests.git") | ||
77 | os.mkdir(gitdir) | ||
78 | with open(os.path.join(gitdir, "config"), "w") as fp: | ||
79 | fp.write( | ||
80 | """[remote "origin"] | ||
81 | url = https://localhost:0/manifest | ||
82 | verbose = false | ||
83 | """ | ||
84 | ) | ||
85 | |||
86 | # Add the manifest data | ||
87 | manifest_data = """ | ||
88 | <manifest> | ||
89 | <remote name="origin" fetch="http://localhost" /> | ||
90 | <default remote="origin" revision="refs/heads/main" /> | ||
91 | <project name="project1" path="tests/path1" /> | ||
92 | <project name="project2" path="tests/path2" /> | ||
93 | <project name="project3" path="tests/path3" /> | ||
94 | <project name="project4" path="tests/path4" /> | ||
95 | <project name="project5" path="tests/path5" /> | ||
96 | <project name="project6" path="tests/path6" /> | ||
97 | <project name="project7" path="tests/path7" /> | ||
98 | <project name="project8" path="tests/path8" /> | ||
99 | </manifest> | ||
100 | """ | ||
101 | with open(self.manifest_file, "w", encoding="utf-8") as fp: | ||
102 | fp.write(manifest_data) | ||
103 | |||
104 | # Set up 8 empty projects to match the manifest | ||
105 | for x in range(1, 9): | ||
106 | os.makedirs( | ||
107 | os.path.join( | ||
108 | self.repodir, "projects/tests/path" + str(x) + ".git" | ||
109 | ) | ||
110 | ) | ||
111 | os.makedirs( | ||
112 | os.path.join( | ||
113 | self.repodir, "project-objects/project" + str(x) + ".git" | ||
114 | ) | ||
115 | ) | ||
116 | git_path = os.path.join(self.tempdir, "tests/path" + str(x)) | ||
117 | self.initTempGitTree(git_path) | ||
118 | |||
119 | return manifest_xml.XmlManifest(self.repodir, self.manifest_file) | ||
120 | |||
121 | # Use mock to capture stdout from the forall run | ||
122 | @unittest.mock.patch("sys.stdout", new_callable=StringIO) | ||
123 | def test_forall_all_projects_called_once(self, mock_stdout): | ||
124 | """Test that all projects get a command run once each.""" | ||
125 | |||
126 | manifest_with_8_projects = self.getXmlManifestWith8Projects() | ||
127 | |||
128 | cmd = subcmds.forall.Forall() | ||
129 | cmd.manifest = manifest_with_8_projects | ||
130 | |||
131 | # Use echo project names as the test of forall | ||
132 | opts, args = cmd.OptionParser.parse_args(["-c", "echo $REPO_PROJECT"]) | ||
133 | opts.verbose = False | ||
134 | |||
135 | # Mock to not have the Execute fail on remote check | ||
136 | with mock.patch.object( | ||
137 | project.Project, "GetRevisionId", return_value="refs/heads/main" | ||
138 | ): | ||
139 | # Run the forall command | ||
140 | cmd.Execute(opts, args) | ||
141 | |||
142 | # Verify that we got every project name in the prints | ||
143 | for x in range(1, 9): | ||
144 | self.assertIn("project" + str(x), mock_stdout.getvalue()) | ||
145 | |||
146 | # Split the captured output into lines to count them | ||
147 | line_count = 0 | ||
148 | for line in mock_stdout.getvalue().split("\n"): | ||
149 | # A commented out print to stderr as a reminder | ||
150 | # that stdout is mocked, include sys and uncomment if needed | ||
151 | # print(line, file=sys.stderr) | ||
152 | if len(line) > 0: | ||
153 | line_count += 1 | ||
154 | |||
155 | # Verify that we didn't get more lines than expected | ||
156 | assert line_count == 8 | ||