github.com/google/syzkaller@v0.0.0-20240517125934-c0f1611a36d6/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.HeadCommit() 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.HeadCommit() 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.HeadCommit() 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 TestCommitHashes(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("checkout", "-b", "branch-b") 250 repo.Git("commit", "--no-edit", "--allow-empty", "-m", "target") 251 got, err := repo.repo.ListCommitHashes("HEAD") 252 if err != nil { 253 t.Fatal(err) 254 } 255 if len(got) != 2 { 256 t.Fatalf("expected 2 commits") 257 } 258 for i, commit := range got { 259 if contained, _ := repo.repo.Contains(commit); !contained { 260 t.Fatalf("commit %d is not contained", i) 261 } 262 } 263 264 // Now change HEAD. 265 repo.Git("checkout", "branch-a") 266 got, err = repo.repo.ListCommitHashes("HEAD") 267 if err != nil { 268 t.Fatal(err) 269 } 270 if len(got) != 1 { 271 t.Fatalf("expected 1 commit, got %d", len(got)) 272 } 273 if contained, _ := repo.repo.Contains(got[0]); !contained { 274 t.Fatalf("commit in branch-b is not contained") 275 } 276 } 277 278 func TestObject(t *testing.T) { 279 baseDir := t.TempDir() 280 repo := MakeTestRepo(t, baseDir) 281 firstRev := []byte("First revision") 282 secondRev := []byte("Second revision") 283 284 if err := os.WriteFile(baseDir+"/object.txt", firstRev, 0644); err != nil { 285 t.Fatal(err) 286 } 287 repo.Git("add", "object.txt") 288 repo.Git("commit", "--no-edit", "--allow-empty", "-m", "target") 289 290 if err := os.WriteFile(baseDir+"/object.txt", secondRev, 0644); err != nil { 291 t.Fatal(err) 292 } 293 repo.Git("add", "object.txt") 294 repo.Git("commit", "--no-edit", "--allow-empty", "-m", "target") 295 296 commits, err := repo.repo.ListCommitHashes("HEAD") 297 if err != nil { 298 t.Fatal(err) 299 } 300 if len(commits) != 2 { 301 t.Fatalf("expected 2 commits, got %d", len(commits)) 302 } 303 // Verify file's contents at the first revision. 304 data, err := repo.repo.Object("object.txt", commits[1]) 305 if err != nil { 306 t.Fatal(err) 307 } 308 if diff := cmp.Diff(data, firstRev); diff != "" { 309 t.Fatal(diff) 310 } 311 // And at the second one. 312 data, err = repo.repo.Object("object.txt", commits[0]) 313 if err != nil { 314 t.Fatal(err) 315 } 316 if diff := cmp.Diff(data, secondRev); diff != "" { 317 t.Fatal(diff) 318 } 319 } 320 321 func TestMergeBase(t *testing.T) { 322 baseDir := t.TempDir() 323 repo := MakeTestRepo(t, baseDir) 324 325 // Create base branch. 326 repo.Git("checkout", "-b", "base") 327 repo.Git("commit", "--no-edit", "--allow-empty", "-m", "target") 328 baseCommit, _ := repo.repo.HeadCommit() 329 330 // Fork off another branch. 331 repo.Git("checkout", "-b", "fork") 332 repo.Git("commit", "--no-edit", "--allow-empty", "-m", "target") 333 forkCommit, _ := repo.repo.HeadCommit() 334 335 // Ensure that merge base points to the base commit. 336 mergeCommits, err := repo.repo.MergeBases(baseCommit.Hash, forkCommit.Hash) 337 if err != nil { 338 t.Fatal(err) 339 } else if len(mergeCommits) != 1 || mergeCommits[0].Hash != baseCommit.Hash { 340 t.Fatalf("expected base commit, got %v", mergeCommits) 341 } 342 343 // Let branches diverge. 344 repo.Git("checkout", "base") 345 repo.Git("commit", "--no-edit", "--allow-empty", "-m", "newBase") 346 newBaseCommit, _ := repo.repo.HeadCommit() 347 348 repo.Git("checkout", "fork") 349 repo.Git("commit", "--no-edit", "--allow-empty", "-m", "newFork") 350 newForkCommit, _ := repo.repo.HeadCommit() 351 352 // The merge base should remain the same. 353 mergeCommits, err = repo.repo.MergeBases(newBaseCommit.Hash, newForkCommit.Hash) 354 if err != nil { 355 t.Fatal(err) 356 } else if len(mergeCommits) != 1 || mergeCommits[0].Hash != baseCommit.Hash { 357 t.Fatalf("expected base commit (%s), got %d other commits", 358 baseCommit.Hash, len(mergeCommits)) 359 } 360 361 // Now do the merge. 362 repo.Git("merge", "base") 363 364 // And advance the fork branch. 365 repo.Git("commit", "--no-edit", "--allow-empty", "-m", "target") 366 newNewForkCommit, _ := repo.repo.HeadCommit() 367 368 // The merge base should point to the last commit in `base`. 369 mergeCommits, err = repo.repo.MergeBases(newBaseCommit.Hash, newNewForkCommit.Hash) 370 if err != nil { 371 t.Fatal(err) 372 } else if len(mergeCommits) != 1 || mergeCommits[0].Hash != newBaseCommit.Hash { 373 t.Fatalf("expected base commit, got %v", mergeCommits) 374 } 375 } 376 377 func TestGitCustomRefs(t *testing.T) { 378 remoteRepoDir := t.TempDir() 379 remote := MakeTestRepo(t, remoteRepoDir) 380 remote.Git("commit", "--no-edit", "--allow-empty", "-m", "base commit") 381 remote.Git("checkout", "-b", "base_branch") 382 remote.Git("tag", "base_tag") 383 384 // Create a commit non reachable from any branch or tag. 385 remote.Git("checkout", "base_branch") 386 remote.Git("checkout", "-b", "temp_branch") 387 remote.Git("commit", "--no-edit", "--allow-empty", "-m", "detached commit") 388 // Add a ref to prevent the commit from getting garbage collected. 389 remote.Git("update-ref", "refs/custom/test", "temp_branch") 390 refCommit, _ := remote.repo.HeadCommit() 391 392 // Remove the branch, let the commit stay only in refs. 393 remote.Git("checkout", "base_branch") 394 remote.Git("branch", "-D", "temp_branch") 395 396 // Create a local repo. 397 localRepoDir := t.TempDir() 398 local := newGit(localRepoDir, nil, nil) 399 400 // Fetch the commit from the custom ref. 401 _, err := local.CheckoutCommit(remoteRepoDir, refCommit.Hash) 402 assert.NoError(t, err) 403 } 404 405 func TestGitRemoteTags(t *testing.T) { 406 remoteRepoDir := t.TempDir() 407 remote := MakeTestRepo(t, remoteRepoDir) 408 remote.Git("commit", "--no-edit", "--allow-empty", "-m", "base commit") 409 remote.Git("checkout", "-b", "base_branch") 410 remote.Git("tag", "v1.0") 411 412 // Diverge sub_branch and add a tag. 413 remote.Git("commit", "--no-edit", "--allow-empty", "-m", "sub-branch") 414 remote.Git("checkout", "-b", "sub_branch") 415 remote.Git("tag", "v2.0") 416 417 // Create a local repo. 418 localRepoDir := t.TempDir() 419 local := newGit(localRepoDir, nil, nil) 420 421 // Ensure all tags were fetched. 422 commit, err := local.CheckoutCommit(remoteRepoDir, "sub_branch") 423 assert.NoError(t, err) 424 tags, err := local.previousReleaseTags(commit.Hash, true, false, false) 425 assert.NoError(t, err) 426 sort.Strings(tags) 427 assert.Equal(t, []string{"v1.0", "v2.0"}, tags) 428 } 429 430 func TestGitFetchShortHash(t *testing.T) { 431 remoteRepoDir := t.TempDir() 432 remote := MakeTestRepo(t, remoteRepoDir) 433 remote.Git("commit", "--no-edit", "--allow-empty", "-m", "base commit") 434 remote.Git("checkout", "-b", "base_branch") 435 remote.Git("tag", "base_tag") 436 remote.Git("checkout", "-b", "temp_branch") 437 remote.Git("commit", "--no-edit", "--allow-empty", "-m", "detached commit") 438 refCommit, _ := remote.repo.HeadCommit() 439 440 // Create a local repo. 441 localRepoDir := t.TempDir() 442 local := newGit(localRepoDir, nil, nil) 443 444 // Fetch the commit from the custom ref. 445 _, err := local.CheckoutCommit(remoteRepoDir, refCommit.Hash[:12]) 446 assert.NoError(t, err) 447 }