github.com/google/syzkaller@v0.0.0-20251211124644-a066d2bc4b02/pkg/vcs/git_test.go (about) 1 // Copyright 2017 syzkaller project authors. All rights reserved. 2 // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. 3 4 package vcs 5 6 import ( 7 "os" 8 "reflect" 9 "sort" 10 "testing" 11 "time" 12 13 "github.com/google/go-cmp/cmp" 14 "github.com/stretchr/testify/assert" 15 ) 16 17 func TestGitParseCommit(t *testing.T) { 18 tests := map[string]*Commit{ 19 `2075b16e32c26e4031b9fd3cbe26c54676a8fcb5 20 rbtree: include rcu.h 21 foobar@foobar.de 22 Foo Bar 23 Fri May 11 16:02:14 2018 -0700 24 78eb0c6356cda285c6ee6e29bea0c0188368103e 25 Fri May 11 17:28:45 2018 -0700 26 Since commit c1adf20052d8 ("Introduce rb_replace_node_rcu()") 27 rbtree_augmented.h uses RCU related data structures but does not include 28 the header file. It works as long as it gets somehow included before 29 that and fails otherwise. 30 31 Link: http://lkml.kernel.org/r/20180504103159.19938-1-bigeasy@linutronix.de 32 Signed-off-by: Foo Bad Baz <another@email.de> 33 Reviewed-by: <yetanother@email.org> 34 Cc: Unrelated Guy <somewhere@email.com> 35 Acked-by: Subsystem reviewer <Subsystem@reviewer.com> 36 Reported-and-tested-by: and@me.com 37 Reported-and-Tested-by: Name-name <name@name.com> 38 Tested-by: Must be correct <mustbe@correct.com> 39 Signed-off-by: Linux Master <linux@linux-foundation.org> 40 `: { 41 Hash: "2075b16e32c26e4031b9fd3cbe26c54676a8fcb5", 42 Title: "rbtree: include rcu.h", 43 Author: "foobar@foobar.de", 44 AuthorName: "Foo Bar", 45 Recipients: NewRecipients([]string{ 46 "and@me.com", 47 "another@email.de", 48 "foobar@foobar.de", 49 "linux@linux-foundation.org", 50 "mustbe@correct.com", 51 "name@name.com", 52 "subsystem@reviewer.com", 53 "yetanother@email.org", 54 }, To), 55 Date: time.Date(2018, 5, 11, 16, 02, 14, 0, time.FixedZone("", -7*60*60)), 56 CommitDate: time.Date(2018, 5, 11, 17, 28, 45, 0, time.FixedZone("", -7*60*60)), 57 }, 58 } 59 for input, com := range tests { 60 res, err := gitParseCommit([]byte(input), nil, nil, nil) 61 if err != nil && com != nil { 62 t.Fatalf("want %+v, got error: %v", com, err) 63 } 64 if err == nil && com == nil { 65 t.Fatalf("want error, got commit %+v", res) 66 } 67 if com == nil { 68 continue 69 } 70 if com.Hash != res.Hash { 71 t.Fatalf("want hash %q, got %q", com.Hash, res.Hash) 72 } 73 if com.Title != res.Title { 74 t.Fatalf("want title %q, got %q", com.Title, res.Title) 75 } 76 if com.Author != res.Author { 77 t.Fatalf("want author %q, got %q", com.Author, res.Author) 78 } 79 if diff := cmp.Diff(com.Recipients, res.Recipients); diff != "" { 80 t.Fatalf("bad CC: %v", diff) 81 } 82 if !com.Date.Equal(res.Date) { 83 t.Fatalf("want date %v, got %v", com.Date, res.Date) 84 } 85 if !com.CommitDate.Equal(res.CommitDate) { 86 t.Fatalf("want date %v, got %v", com.CommitDate, res.CommitDate) 87 } 88 } 89 } 90 91 func TestGitParseReleaseTags(t *testing.T) { 92 input := ` 93 v3.1 94 v2.6.12 95 v2.6.39 96 v3.0 97 v3.10 98 v2.6.13 99 v3.11 100 v3.19 101 v3.9 102 v3.2 103 v4.9-rc1 104 v4.9 105 v4.9-rc3 106 v4.9-rc2 107 v2.6.32 108 v4.0 109 vv4.1 110 v2.6-rc5 111 v4.1foo 112 voo 113 v1.foo 114 v2.6-rc2 115 v10.2.foo 116 v1.2. 117 v1. 118 ` 119 want := []string{ 120 "v4.9", 121 "v4.0", 122 "v3.19", 123 "v3.11", 124 "v3.10", 125 "v3.9", 126 "v3.2", 127 "v3.1", 128 "v3.0", 129 "v2.6.39", 130 "v2.6.32", 131 "v2.6.13", 132 "v2.6.12", 133 } 134 got := gitParseReleaseTags([]byte(input), false) 135 if !reflect.DeepEqual(got, want) { 136 t.Fatalf("got bad tags\ngot: %+v\nwant: %+v", got, want) 137 } 138 wantRC := []string{ 139 "v4.9", 140 "v4.9-rc3", 141 "v4.9-rc2", 142 "v4.9-rc1", 143 "v4.0", 144 "v3.19", 145 "v3.11", 146 "v3.10", 147 "v3.9", 148 "v3.2", 149 "v3.1", 150 "v3.0", 151 "v2.6.39", 152 "v2.6.32", 153 "v2.6.13", 154 "v2.6.12", 155 "v2.6-rc5", 156 "v2.6-rc2", 157 } 158 gotRC := gitParseReleaseTags([]byte(input), true) 159 if !reflect.DeepEqual(gotRC, wantRC) { 160 t.Fatalf("got bad tags\ngot: %+v\nwant: %+v", gotRC, wantRC) 161 } 162 } 163 164 func TestGetCommitsByTitles(t *testing.T) { 165 baseDir := t.TempDir() 166 repo := MakeTestRepo(t, baseDir) 167 168 validateSuccess := func(commit *Commit, results []*Commit, missing []string, err error) { 169 if err != nil { 170 t.Fatalf("expected success, got %v", err) 171 } 172 if len(missing) > 0 { 173 t.Fatalf("expected 0 missing, got %v", missing) 174 } 175 if len(results) != 1 { 176 t.Fatalf("expected 1 results, got %v", len(results)) 177 } 178 if results[0].Hash != commit.Hash { 179 t.Fatalf("found unexpected commit %v", results[0].Hash) 180 } 181 } 182 183 // Put three commits in branch-a, two with the title we search for. 184 // We expect GetCommitsByTitles to only return the most recent match. 185 repo.Git("checkout", "-b", "branch-a") 186 repo.Git("commit", "--no-edit", "--allow-empty", "-m", "target") 187 repo.Git("commit", "--no-edit", "--allow-empty", "-m", "abc") 188 repo.Git("commit", "--no-edit", "--allow-empty", "-m", "target") 189 commitA, _ := repo.repo.Commit(HEAD) 190 results, missing, err := repo.repo.GetCommitsByTitles([]string{"target"}) 191 validateSuccess(commitA, results, missing, err) 192 193 // Put another commit with the title we search for in another branch. 194 // We expect GetCommitsByTitles to only find commits in the current branch. 195 repo.Git("checkout", "-b", "branch-b") 196 repo.Git("commit", "--no-edit", "--allow-empty", "-m", "target") 197 repo.Git("checkout", "branch-a") 198 results, missing, err = repo.repo.GetCommitsByTitles([]string{"target"}) 199 validateSuccess(commitA, results, missing, err) 200 201 // We expect GetCommitsByTitles to only find commits in the current branch. 202 repo.Git("checkout", "branch-b") 203 results, missing, err = repo.repo.GetCommitsByTitles([]string{"xyz"}) 204 if err != nil { 205 t.Fatalf("expected success, got %v", err) 206 } 207 if len(results) > 0 { 208 t.Fatalf("expected 0 results, got %v", len(results)) 209 } 210 if len(missing) != 1 { 211 t.Fatalf("expected 1 missing, got %v", missing) 212 } 213 if missing[0] != "xyz" { 214 t.Fatalf("found unexpected value in missing %v", missing[0]) 215 } 216 } 217 218 func TestContains(t *testing.T) { 219 baseDir := t.TempDir() 220 repo := MakeTestRepo(t, baseDir) 221 222 // We expect Contains to return true, if commit is in current checkout. 223 repo.Git("checkout", "-b", "branch-a") 224 repo.Git("commit", "--no-edit", "--allow-empty", "-m", "target") 225 commitA, _ := repo.repo.Commit(HEAD) 226 if contained, _ := repo.repo.Contains(commitA.Hash); !contained { 227 t.Fatalf("contains claims commit that should be present is not") 228 } 229 if contained, _ := repo.repo.Contains("fake-hash"); contained { 230 t.Fatalf("contains claims commit that is not present is present") 231 } 232 233 // Commits must only be searched for from the checkedout HEAD. 234 repo.Git("checkout", "-b", "branch-b") 235 repo.Git("commit", "--no-edit", "--allow-empty", "-m", "target") 236 commitB, _ := repo.repo.Commit(HEAD) 237 repo.Git("checkout", "branch-a") 238 if contained, _ := repo.repo.Contains(commitB.Hash); contained { 239 t.Fatalf("contains found commit that is not in current branch") 240 } 241 } 242 243 func TestLatestCommits(t *testing.T) { 244 baseDir := t.TempDir() 245 repo := MakeTestRepo(t, baseDir) 246 247 repo.Git("checkout", "-b", "branch-a") 248 repo.Git("commit", "--no-edit", "--allow-empty", "-m", "target") 249 repo.Git("commit", "--no-edit", "--allow-empty", "-m", "target") 250 got, err := repo.repo.LatestCommits("", time.Time{}) 251 assert.NoError(t, err) 252 assert.Len(t, got, 2, "expected 2 commits") 253 for i, commit := range got { 254 if contained, _ := repo.repo.Contains(commit.Hash); !contained { 255 t.Fatalf("commit %d is not contained", i) 256 } 257 } 258 259 // Now ignore the first commit. 260 got2, err := repo.repo.LatestCommits(got[1].Hash, time.Time{}) 261 assert.NoError(t, err) 262 assert.Len(t, got2, 1, "expected 1 commit") 263 assert.Equal(t, got2[0].Hash, got[0].Hash, "expected to see the HEAD commit") 264 265 // TODO: test the afterDate argument. 266 // It will require setting the GIT_COMMITTER_DATE env variable. 267 } 268 269 func TestObject(t *testing.T) { 270 baseDir := t.TempDir() 271 repo := MakeTestRepo(t, baseDir) 272 firstRev := []byte("First revision") 273 secondRev := []byte("Second revision") 274 275 if err := os.WriteFile(baseDir+"/object.txt", firstRev, 0644); err != nil { 276 t.Fatal(err) 277 } 278 repo.Git("add", "object.txt") 279 repo.Git("commit", "--no-edit", "--allow-empty", "-m", "target") 280 281 if err := os.WriteFile(baseDir+"/object.txt", secondRev, 0644); err != nil { 282 t.Fatal(err) 283 } 284 repo.Git("add", "object.txt") 285 repo.Git("commit", "--no-edit", "--allow-empty", "-m", "target") 286 287 commits, err := repo.repo.LatestCommits("", time.Time{}) 288 if err != nil { 289 t.Fatal(err) 290 } 291 if len(commits) != 2 { 292 t.Fatalf("expected 2 commits, got %d", len(commits)) 293 } 294 // Verify file's contents at the first revision. 295 data, err := repo.repo.Object("object.txt", commits[1].Hash) 296 if err != nil { 297 t.Fatal(err) 298 } 299 if diff := cmp.Diff(data, firstRev); diff != "" { 300 t.Fatal(diff) 301 } 302 // And at the second one. 303 data, err = repo.repo.Object("object.txt", commits[0].Hash) 304 if err != nil { 305 t.Fatal(err) 306 } 307 if diff := cmp.Diff(data, secondRev); diff != "" { 308 t.Fatal(diff) 309 } 310 com, err := repo.repo.Commit(commits[0].Hash) 311 if err != nil { 312 t.Fatal(err.Error()) 313 } 314 patch := []byte(`diff --git a/object.txt b/object.txt 315 index 103167d..fbf7a68 100644 316 --- a/object.txt 317 +++ b/object.txt 318 @@ -1 +1 @@ 319 -First revision 320 \ No newline at end of file 321 +Second revision 322 \ No newline at end of file 323 `) 324 if diff := cmp.Diff(com.Patch, patch); diff != "" { 325 t.Fatal(diff) 326 } 327 } 328 329 func TestMergeBase(t *testing.T) { 330 baseDir := t.TempDir() 331 repo := MakeTestRepo(t, baseDir) 332 333 // Create base branch. 334 repo.Git("checkout", "-b", "base") 335 repo.Git("commit", "--no-edit", "--allow-empty", "-m", "target") 336 baseCommit, _ := repo.repo.Commit(HEAD) 337 338 // Fork off another branch. 339 repo.Git("checkout", "-b", "fork") 340 repo.Git("commit", "--no-edit", "--allow-empty", "-m", "target") 341 forkCommit, _ := repo.repo.Commit(HEAD) 342 343 // Ensure that merge base points to the base commit. 344 mergeCommits, err := repo.repo.MergeBases(baseCommit.Hash, forkCommit.Hash) 345 if err != nil { 346 t.Fatal(err) 347 } else if len(mergeCommits) != 1 || mergeCommits[0].Hash != baseCommit.Hash { 348 t.Fatalf("expected base commit, got %v", mergeCommits) 349 } 350 351 // Let branches diverge. 352 repo.Git("checkout", "base") 353 repo.Git("commit", "--no-edit", "--allow-empty", "-m", "newBase") 354 newBaseCommit, _ := repo.repo.Commit(HEAD) 355 356 repo.Git("checkout", "fork") 357 repo.Git("commit", "--no-edit", "--allow-empty", "-m", "newFork") 358 newForkCommit, _ := repo.repo.Commit(HEAD) 359 360 // The merge base should remain the same. 361 mergeCommits, err = repo.repo.MergeBases(newBaseCommit.Hash, newForkCommit.Hash) 362 if err != nil { 363 t.Fatal(err) 364 } else if len(mergeCommits) != 1 || mergeCommits[0].Hash != baseCommit.Hash { 365 t.Fatalf("expected base commit (%s), got %d other commits", 366 baseCommit.Hash, len(mergeCommits)) 367 } 368 369 // Now do the merge. 370 repo.Git("merge", "base") 371 372 // And advance the fork branch. 373 repo.Git("commit", "--no-edit", "--allow-empty", "-m", "target") 374 newNewForkCommit, _ := repo.repo.Commit(HEAD) 375 376 // The merge base should point to the last commit in `base`. 377 mergeCommits, err = repo.repo.MergeBases(newBaseCommit.Hash, newNewForkCommit.Hash) 378 if err != nil { 379 t.Fatal(err) 380 } else if len(mergeCommits) != 1 || mergeCommits[0].Hash != newBaseCommit.Hash { 381 t.Fatalf("expected base commit, got %v", mergeCommits) 382 } 383 } 384 385 func TestGitCustomRefs(t *testing.T) { 386 remoteRepoDir := t.TempDir() 387 remote := MakeTestRepo(t, remoteRepoDir) 388 remote.Git("commit", "--no-edit", "--allow-empty", "-m", "base commit") 389 remote.Git("checkout", "-b", "base_branch") 390 remote.Git("tag", "base_tag") 391 392 // Create a commit non reachable from any branch or tag. 393 remote.Git("checkout", "base_branch") 394 remote.Git("checkout", "-b", "temp_branch") 395 remote.Git("commit", "--no-edit", "--allow-empty", "-m", "detached commit") 396 // Add a ref to prevent the commit from getting garbage collected. 397 remote.Git("update-ref", "refs/custom/test", "temp_branch") 398 refCommit, _ := remote.repo.Commit(HEAD) 399 400 // Remove the branch, let the commit stay only in refs. 401 remote.Git("checkout", "base_branch") 402 remote.Git("branch", "-D", "temp_branch") 403 404 // Create a local repo. 405 localRepoDir := t.TempDir() 406 local := newGitRepo(localRepoDir, nil, nil) 407 408 // Fetch the commit from the custom ref. 409 _, err := local.CheckoutCommit(remoteRepoDir, refCommit.Hash) 410 assert.NoError(t, err) 411 } 412 413 func TestGitRemoteTags(t *testing.T) { 414 remoteRepoDir := t.TempDir() 415 remote := MakeTestRepo(t, remoteRepoDir) 416 remote.Git("commit", "--no-edit", "--allow-empty", "-m", "base commit") 417 remote.Git("checkout", "-b", "base_branch") 418 remote.Git("tag", "v1.0") 419 420 // Diverge sub_branch and add a tag. 421 remote.Git("commit", "--no-edit", "--allow-empty", "-m", "sub-branch") 422 remote.Git("checkout", "-b", "sub_branch") 423 remote.Git("tag", "v2.0") 424 425 // Create a local repo. 426 localRepoDir := t.TempDir() 427 local := newGitRepo(localRepoDir, nil, nil) 428 429 // Ensure all tags were fetched. 430 commit, err := local.CheckoutCommit(remoteRepoDir, "sub_branch") 431 assert.NoError(t, err) 432 tags, err := local.previousReleaseTags(commit.Hash, true, false, false) 433 assert.NoError(t, err) 434 sort.Strings(tags) 435 assert.Equal(t, []string{"v1.0", "v2.0"}, tags) 436 } 437 438 func TestGitFetchShortHash(t *testing.T) { 439 remoteRepoDir := t.TempDir() 440 remote := MakeTestRepo(t, remoteRepoDir) 441 remote.Git("commit", "--no-edit", "--allow-empty", "-m", "base commit") 442 remote.Git("checkout", "-b", "base_branch") 443 remote.Git("tag", "base_tag") 444 remote.Git("checkout", "-b", "temp_branch") 445 remote.Git("commit", "--no-edit", "--allow-empty", "-m", "detached commit") 446 refCommit, _ := remote.repo.Commit(HEAD) 447 448 // Create a local repo. 449 localRepoDir := t.TempDir() 450 local := newGitRepo(localRepoDir, nil, nil) 451 452 // Fetch the commit from the custom ref. 453 _, err := local.CheckoutCommit(remoteRepoDir, refCommit.Hash[:12]) 454 assert.NoError(t, err) 455 } 456 457 func TestParseGitDiff(t *testing.T) { 458 files := ParseGitDiff([]byte(`diff --git a/a.txt b/a.txt 459 index 4c5fd91..8fe1e32 100644 460 --- a/a.txt 461 +++ b/a.txt 462 @@ -1 +1 @@ 463 -First file 464 +First file! 465 diff --git a/b.txt b/b.txt 466 new file mode 100644 467 index 0000000..f8a9677 468 --- /dev/null 469 +++ b/b.txt 470 @@ -0,0 +1 @@ 471 +Second file. 472 diff --git a/c/c.txt b/c/c.txt 473 new file mode 100644 474 index 0000000..e69de29 475 `)) 476 assert.ElementsMatch(t, files, []string{"a.txt", "b.txt", "c/c.txt"}) 477 }