@@ -20,7 +20,8 @@ class ValidatePrPatchIdTest(unittest.TestCase):
2020 def setUp (self ):
2121 self ._tmp = tempfile .TemporaryDirectory ()
2222 self .addCleanup (self ._tmp .cleanup )
23- self .repo = pathlib .Path (self ._tmp .name )
23+ self .repo = pathlib .Path (self ._tmp .name ) / "repo"
24+ self .repo .mkdir ()
2425 self .real_git = shutil .which ("git" )
2526 self .assertIsNotNone (self .real_git )
2627
@@ -40,6 +41,11 @@ def setUp(self):
4041 ])
4142 self .base = self ._commit ("fixture: base" , LOCAL_SOB )
4243
44+ def _relocate_repo (self , name ):
45+ relocated = self .repo .parent / name
46+ self .repo .rename (relocated )
47+ self .repo = relocated
48+
4349 def _git (self , * args , input_text = None ):
4450 result = subprocess .run (
4551 ["git" , * args ],
@@ -65,12 +71,33 @@ def _write_fixture(self, lines):
6571 def _read_fixture (self ):
6672 return (self .repo / "fixture.txt" ).read_text ().splitlines ()
6773
74+ def _use_identical_occurrence_fixture (self ):
75+ block = [
76+ "same-before-4" ,
77+ "same-before-3" ,
78+ "same-before-2" ,
79+ "same-before-1" ,
80+ "anchor" ,
81+ "same-after-1" ,
82+ "same-after-2" ,
83+ "same-after-3" ,
84+ "same-after-4" ,
85+ ]
86+ separator = ["separator-{}" .format (i ) for i in range (1 , 8 )]
87+ self ._write_fixture (["prefix" , * block , * separator , * block , "suffix" ])
88+ self .base = self ._commit ("fixture: duplicate context" , LOCAL_SOB )
89+
6890 def _commit (self , subject , body ):
6991 self ._git ("add" , "fixture.txt" )
7092 self ._git ("commit" , "-F" , "-" ,
7193 input_text = "{}\n \n {}\n " .format (subject , body ))
7294 return self ._git ("rev-parse" , "HEAD" )
7395
96+ def _patch_id (self , commit ):
97+ patch = self ._git ("show" , commit )
98+ output = self ._git ("patch-id" , "--stable" , input_text = patch )
99+ return output .split ()[0 ]
100+
74101 def _build_case (self , change , context_parent = True ):
75102 self ._git ("checkout" , "-b" , "upstream-topic" , self .base )
76103 self ._set_identity ("Upstream Author" , "upstream@example.com" )
@@ -141,6 +168,15 @@ def _fake_git_env(self, mode):
141168 "a" * 40, "b" * 40, "c" * 40, "d" * 40))
142169 sys.exit(0)
143170
171+ if (mode == "rev-parse-invalid-utf8" and
172+ args[:3] == ["rev-parse", "--git-path", "objects"]):
173+ sys.stdout.buffer.write(b"\\ xff\\ n")
174+ sys.exit(0)
175+
176+ if mode == "merge-tree-failure" and args and args[0] == "merge-tree":
177+ sys.stderr.write("synthetic merge-tree failure\\ n")
178+ sys.exit(2)
179+
144180if mode == "merge-tree-extra-line" and args and args[0] == "merge-tree":
145181 result = subprocess.run([real_git, *args], capture_output=True)
146182 sys.stdout.buffer.write(result.stdout)
@@ -193,6 +229,15 @@ def test_accepts_context_only_replay(self):
193229 self .assertEqual (result .returncode , 0 , self ._output (result ))
194230 self .assertEqual (self ._patch_id_status (result , local ), "context" )
195231
232+ def test_accepts_context_only_replay_with_colon_in_object_path (self ):
233+ self ._relocate_repo ("repo:colon" )
234+ parent , local = self ._build_case ("context" )
235+
236+ result = self ._validate (parent , local )
237+
238+ self .assertEqual (result .returncode , 0 , self ._output (result ))
239+ self .assertEqual (self ._patch_id_status (result , local ), "context" )
240+
196241 def test_accepts_exact_cherry_pick (self ):
197242 parent , local = self ._build_case ("exact" , context_parent = False )
198243
@@ -225,6 +270,22 @@ def test_rejects_merge_tree_output_with_extra_line(self):
225270 self .assertEqual (result .returncode , 1 , self ._output (result ))
226271 self .assertIn ("patch-ID mismatch with upstream" , result .stdout )
227272
273+ def test_reports_replay_environment_failure (self ):
274+ parent , local = self ._build_case ("exact" , context_parent = False )
275+
276+ result = self ._validate (parent , local , "rev-parse-invalid-utf8" )
277+
278+ self .assertEqual (result .returncode , 1 , self ._output (result ))
279+ self .assertIn ("unable to verify patch replay" , result .stderr )
280+
281+ def test_reports_merge_tree_failure (self ):
282+ parent , local = self ._build_case ("exact" , context_parent = False )
283+
284+ result = self ._validate (parent , local , "merge-tree-failure" )
285+
286+ self .assertEqual (result .returncode , 1 , self ._output (result ))
287+ self .assertIn ("synthetic merge-tree failure" , result .stderr )
288+
228289 def test_rejects_multiple_patch_id_output_lines (self ):
229290 parent , local = self ._build_case ("exact" , context_parent = False )
230291
@@ -249,6 +310,18 @@ def test_rejects_same_change_at_different_occurrence(self):
249310 self .assertEqual (result .returncode , 1 , self ._output (result ))
250311 self .assertIn ("patch-ID mismatch with upstream" , result .stdout )
251312
313+ def test_rejects_equal_patch_id_at_different_occurrence (self ):
314+ self ._use_identical_occurrence_fixture ()
315+ parent , local = self ._build_case (
316+ "wrong-occurrence" , context_parent = False )
317+ upstream = self ._git ("rev-parse" , "refs/remotes/upstream/linux" )
318+
319+ self .assertEqual (self ._patch_id (local ), self ._patch_id (upstream ))
320+ result = self ._validate (parent , local )
321+
322+ self .assertEqual (result .returncode , 1 , self ._output (result ))
323+ self .assertIn ("patch-ID mismatch with upstream" , result .stdout )
324+
252325
253326if __name__ == "__main__" :
254327 unittest .main ()
0 commit comments