diff options
Diffstat (limited to 'tests')
| -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 | ||
