diff options
author | Aravind Vasudevan <aravindvasudev@google.com> | 2023-10-06 00:40:25 +0000 |
---|---|---|
committer | LUCI <gerrit-scoped@luci-project-accounts.iam.gserviceaccount.com> | 2023-10-06 18:21:45 +0000 |
commit | 2844a5f3cc81ffe2b749e574bdeb61809deab5b9 (patch) | |
tree | f57cc7f5e9420b47ed4c8506e4d2c3097ac49af5 /tests/test_git_command.py | |
parent | 47944bbe2ea69009c0da78573f6536ad2c77f026 (diff) | |
download | git-repo-2844a5f3cc81ffe2b749e574bdeb61809deab5b9.tar.gz |
git_command: Augment underlying git errors with suggestions
This change appends suggestions to the underlying git error to make the
error slightly more actionable.
DD: go/improve-repo-error-reporting & go/tee-repo-stderr
Bug: b/292704435
Change-Id: I2bf8bea5fca42c6a9acd2fadc70f58f22456e027
Reviewed-on: https://gerrit-review.googlesource.com/c/git-repo/+/387774
Commit-Queue: Aravind Vasudevan <aravindvasudev@google.com>
Reviewed-by: Jason Chang <jasonnc@google.com>
Tested-by: Aravind Vasudevan <aravindvasudev@google.com>
Reviewed-by: Aravind Vasudevan <aravindvasudev@google.com>
Diffstat (limited to 'tests/test_git_command.py')
-rw-r--r-- | tests/test_git_command.py | 133 |
1 files changed, 133 insertions, 0 deletions
diff --git a/tests/test_git_command.py b/tests/test_git_command.py index c803d280..881cccb8 100644 --- a/tests/test_git_command.py +++ b/tests/test_git_command.py | |||
@@ -14,6 +14,7 @@ | |||
14 | 14 | ||
15 | """Unittests for the git_command.py module.""" | 15 | """Unittests for the git_command.py module.""" |
16 | 16 | ||
17 | import io | ||
17 | import os | 18 | import os |
18 | import re | 19 | import re |
19 | import subprocess | 20 | import subprocess |
@@ -74,6 +75,10 @@ class GitCommandWaitTest(unittest.TestCase): | |||
74 | class MockPopen(object): | 75 | class MockPopen(object): |
75 | rc = 0 | 76 | rc = 0 |
76 | 77 | ||
78 | def __init__(self): | ||
79 | self.stdout = io.BufferedReader(io.BytesIO()) | ||
80 | self.stderr = io.BufferedReader(io.BytesIO()) | ||
81 | |||
77 | def communicate( | 82 | def communicate( |
78 | self, input: str = None, timeout: float = None | 83 | self, input: str = None, timeout: float = None |
79 | ) -> [str, str]: | 84 | ) -> [str, str]: |
@@ -117,6 +122,115 @@ class GitCommandWaitTest(unittest.TestCase): | |||
117 | self.assertEqual(1, r.Wait()) | 122 | self.assertEqual(1, r.Wait()) |
118 | 123 | ||
119 | 124 | ||
125 | class GitCommandStreamLogsTest(unittest.TestCase): | ||
126 | """Tests the GitCommand class stderr log streaming cases.""" | ||
127 | |||
128 | def setUp(self): | ||
129 | self.mock_process = mock.MagicMock() | ||
130 | self.mock_process.communicate.return_value = (None, None) | ||
131 | self.mock_process.wait.return_value = 0 | ||
132 | |||
133 | self.mock_popen = mock.MagicMock() | ||
134 | self.mock_popen.return_value = self.mock_process | ||
135 | mock.patch("subprocess.Popen", self.mock_popen).start() | ||
136 | |||
137 | def tearDown(self): | ||
138 | mock.patch.stopall() | ||
139 | |||
140 | def test_does_not_stream_logs_when_input_is_set(self): | ||
141 | git_command.GitCommand(None, ["status"], input="foo") | ||
142 | |||
143 | self.mock_popen.assert_called_once_with( | ||
144 | ["git", "status"], | ||
145 | cwd=None, | ||
146 | env=mock.ANY, | ||
147 | encoding="utf-8", | ||
148 | errors="backslashreplace", | ||
149 | stdin=subprocess.PIPE, | ||
150 | stdout=None, | ||
151 | stderr=None, | ||
152 | ) | ||
153 | self.mock_process.communicate.assert_called_once_with(input="foo") | ||
154 | self.mock_process.stderr.read1.assert_not_called() | ||
155 | |||
156 | def test_does_not_stream_logs_when_stdout_is_set(self): | ||
157 | git_command.GitCommand(None, ["status"], capture_stdout=True) | ||
158 | |||
159 | self.mock_popen.assert_called_once_with( | ||
160 | ["git", "status"], | ||
161 | cwd=None, | ||
162 | env=mock.ANY, | ||
163 | encoding="utf-8", | ||
164 | errors="backslashreplace", | ||
165 | stdin=None, | ||
166 | stdout=subprocess.PIPE, | ||
167 | stderr=None, | ||
168 | ) | ||
169 | self.mock_process.communicate.assert_called_once_with(input=None) | ||
170 | self.mock_process.stderr.read1.assert_not_called() | ||
171 | |||
172 | def test_does_not_stream_logs_when_stderr_is_set(self): | ||
173 | git_command.GitCommand(None, ["status"], capture_stderr=True) | ||
174 | |||
175 | self.mock_popen.assert_called_once_with( | ||
176 | ["git", "status"], | ||
177 | cwd=None, | ||
178 | env=mock.ANY, | ||
179 | encoding="utf-8", | ||
180 | errors="backslashreplace", | ||
181 | stdin=None, | ||
182 | stdout=None, | ||
183 | stderr=subprocess.PIPE, | ||
184 | ) | ||
185 | self.mock_process.communicate.assert_called_once_with(input=None) | ||
186 | self.mock_process.stderr.read1.assert_not_called() | ||
187 | |||
188 | def test_does_not_stream_logs_when_merge_output_is_set(self): | ||
189 | git_command.GitCommand(None, ["status"], merge_output=True) | ||
190 | |||
191 | self.mock_popen.assert_called_once_with( | ||
192 | ["git", "status"], | ||
193 | cwd=None, | ||
194 | env=mock.ANY, | ||
195 | encoding="utf-8", | ||
196 | errors="backslashreplace", | ||
197 | stdin=None, | ||
198 | stdout=None, | ||
199 | stderr=subprocess.STDOUT, | ||
200 | ) | ||
201 | self.mock_process.communicate.assert_called_once_with(input=None) | ||
202 | self.mock_process.stderr.read1.assert_not_called() | ||
203 | |||
204 | @mock.patch("sys.stderr") | ||
205 | def test_streams_stderr_when_no_stream_is_set(self, mock_stderr): | ||
206 | logs = "\n".join( | ||
207 | [ | ||
208 | "Enumerating objects: 5, done.", | ||
209 | "Counting objects: 100% (5/5), done.", | ||
210 | "Writing objects: 100% (3/3), 330 bytes | 330 KiB/s, done.", | ||
211 | "remote: Processing changes: refs: 1, new: 1, done ", | ||
212 | "remote: SUCCESS", | ||
213 | ] | ||
214 | ) | ||
215 | self.mock_process.stderr = io.BufferedReader( | ||
216 | io.BytesIO(bytes(logs, "utf-8")) | ||
217 | ) | ||
218 | |||
219 | cmd = git_command.GitCommand(None, ["push"]) | ||
220 | |||
221 | self.mock_popen.assert_called_once_with( | ||
222 | ["git", "push"], | ||
223 | cwd=None, | ||
224 | env=mock.ANY, | ||
225 | stdin=None, | ||
226 | stdout=None, | ||
227 | stderr=subprocess.PIPE, | ||
228 | ) | ||
229 | self.mock_process.communicate.assert_not_called() | ||
230 | mock_stderr.write.assert_called_once_with(logs) | ||
231 | self.assertEqual(cmd.stderr, logs) | ||
232 | |||
233 | |||
120 | class GitCallUnitTest(unittest.TestCase): | 234 | class GitCallUnitTest(unittest.TestCase): |
121 | """Tests the _GitCall class (via git_command.git).""" | 235 | """Tests the _GitCall class (via git_command.git).""" |
122 | 236 | ||
@@ -214,3 +328,22 @@ class GitRequireTests(unittest.TestCase): | |||
214 | with self.assertRaises(git_command.GitRequireError) as e: | 328 | with self.assertRaises(git_command.GitRequireError) as e: |
215 | git_command.git_require((2,), fail=True, msg="so sad") | 329 | git_command.git_require((2,), fail=True, msg="so sad") |
216 | self.assertNotEqual(0, e.code) | 330 | self.assertNotEqual(0, e.code) |
331 | |||
332 | |||
333 | class GitCommandErrorTest(unittest.TestCase): | ||
334 | """Test for the GitCommandError class.""" | ||
335 | |||
336 | def test_augument_stderr(self): | ||
337 | self.assertEqual( | ||
338 | git_command.GitCommandError( | ||
339 | git_stderr="couldn't find remote ref refs/heads/foo" | ||
340 | ).suggestion, | ||
341 | "Check if the provided ref exists in the remote.", | ||
342 | ) | ||
343 | |||
344 | self.assertEqual( | ||
345 | git_command.GitCommandError( | ||
346 | git_stderr="'foobar' does not appear to be a git repository" | ||
347 | ).suggestion, | ||
348 | "Are you running this repo command outside of a repo workspace?", | ||
349 | ) | ||