github.com/google/syzkaller@v0.0.0-20240517125934-c0f1611a36d6/pkg/vcs/git_repo_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  	"fmt"
     8  	"os"
     9  	"path/filepath"
    10  	"sort"
    11  	"testing"
    12  
    13  	"github.com/google/go-cmp/cmp"
    14  	"github.com/google/syzkaller/pkg/debugtracer"
    15  )
    16  
    17  func init() {
    18  	// Disable sandboxing entirely because we create test repos without sandboxing.
    19  	os.Setenv("SYZ_DISABLE_SANDBOXING", "yes")
    20  }
    21  
    22  func TestGitRepo(t *testing.T) {
    23  	t.Parallel()
    24  	baseDir := t.TempDir()
    25  	repo1 := CreateTestRepo(t, baseDir, "repo1")
    26  	repo2 := CreateTestRepo(t, baseDir, "repo2")
    27  	repo := newGit(filepath.Join(baseDir, "repo"), nil, nil)
    28  	{
    29  		com, err := repo.Poll(repo1.Dir, "master")
    30  		if err != nil {
    31  			t.Fatal(err)
    32  		}
    33  		if diff := cmp.Diff(com, repo1.Commits["master"]["1"]); diff != "" {
    34  			t.Fatal(diff)
    35  		}
    36  	}
    37  	{
    38  		com, err := repo.CheckoutBranch(repo1.Dir, "branch1")
    39  		if err != nil {
    40  			t.Fatal(err)
    41  		}
    42  		if diff := cmp.Diff(com, repo1.Commits["branch1"]["1"]); diff != "" {
    43  			t.Fatal(diff)
    44  		}
    45  	}
    46  	{
    47  		want := repo1.Commits["branch1"]["0"]
    48  		com, err := repo.CheckoutCommit(repo1.Dir, want.Hash)
    49  		if err != nil {
    50  			t.Fatal(err)
    51  		}
    52  		if diff := cmp.Diff(com, want); diff != "" {
    53  			t.Fatal(diff)
    54  		}
    55  	}
    56  	{
    57  		want := repo2.Commits["branch1"]["0"]
    58  		com, err := repo.CheckoutCommit(repo2.Dir, want.Hash)
    59  		if err != nil {
    60  			t.Fatal(err)
    61  		}
    62  		if diff := cmp.Diff(com, want); diff != "" {
    63  			t.Fatal(diff)
    64  		}
    65  	}
    66  	{
    67  		want := repo2.Commits["branch1"]["1"]
    68  		com, err := repo.CheckoutCommit(repo2.Dir, want.Hash)
    69  		if err != nil {
    70  			t.Fatal(err)
    71  		}
    72  		if diff := cmp.Diff(com, want); diff != "" {
    73  			t.Fatal(diff)
    74  		}
    75  	}
    76  	{
    77  		com, err := repo.CheckoutBranch(repo2.Dir, "branch2")
    78  		if err != nil {
    79  			t.Fatal(err)
    80  		}
    81  		if diff := cmp.Diff(com, repo2.Commits["branch2"]["1"]); diff != "" {
    82  			t.Fatal(diff)
    83  		}
    84  	}
    85  	{
    86  		want := repo2.Commits["branch2"]["0"]
    87  		com, err := repo.SwitchCommit(want.Hash)
    88  		if err != nil {
    89  			t.Fatal(err)
    90  		}
    91  		if diff := cmp.Diff(com, want); diff != "" {
    92  			t.Fatal(diff)
    93  		}
    94  	}
    95  	{
    96  		type Test struct {
    97  			head     *Commit
    98  			commit   *Commit
    99  			contains bool
   100  		}
   101  		tests := []Test{
   102  			{repo2.Commits["branch2"]["1"], repo2.Commits["branch2"]["1"], true},
   103  			{repo2.Commits["branch2"]["1"], repo2.Commits["branch2"]["0"], true},
   104  			{repo2.Commits["branch2"]["1"], repo2.Commits["master"]["0"], true},
   105  			{repo2.Commits["branch2"]["1"], repo2.Commits["master"]["1"], false},
   106  			{repo2.Commits["branch2"]["1"], repo2.Commits["branch1"]["0"], false},
   107  			{repo2.Commits["branch2"]["1"], repo2.Commits["branch1"]["1"], false},
   108  			{repo2.Commits["branch2"]["0"], repo2.Commits["branch2"]["0"], true},
   109  			{repo2.Commits["branch2"]["0"], repo2.Commits["branch2"]["1"], false},
   110  			{repo2.Commits["branch2"]["0"], repo2.Commits["master"]["0"], true},
   111  			{repo2.Commits["branch2"]["0"], repo2.Commits["master"]["1"], false},
   112  		}
   113  		for i, test := range tests {
   114  			if _, err := repo.SwitchCommit(test.head.Hash); err != nil {
   115  				t.Fatal(err)
   116  			}
   117  			if contains, err := repo.Contains(test.commit.Hash); err != nil {
   118  				t.Fatal(err)
   119  			} else if contains != test.contains {
   120  				t.Errorf("test %v: got %v, want %v", i, contains, test.contains)
   121  			}
   122  		}
   123  	}
   124  }
   125  
   126  func TestMetadata(t *testing.T) {
   127  	t.Parallel()
   128  	repoDir := t.TempDir()
   129  	repo := MakeTestRepo(t, repoDir)
   130  	prevHash := ""
   131  	for i, test := range metadataTests {
   132  		repo.CommitChange(test.description)
   133  		com, err := repo.repo.HeadCommit()
   134  		if err != nil {
   135  			t.Fatal(err)
   136  		}
   137  		checkCommit(t, i, test, com, false)
   138  		if len(com.Parents) != 1 || com.Parents[0] != prevHash {
   139  			t.Fatalf("bad parents: %+q, expect %q", com.Parents, prevHash)
   140  		}
   141  		prevHash = com.Hash
   142  	}
   143  	commits, err := repo.repo.ExtractFixTagsFromCommits("HEAD", extractFixTagsEmail)
   144  	if err != nil {
   145  		t.Fatal(err)
   146  	}
   147  	if len(metadataTests) != len(commits) {
   148  		t.Fatalf("want %v commits, got %v", len(metadataTests), len(commits))
   149  	}
   150  	for i, test := range metadataTests {
   151  		checkCommit(t, i, test, commits[len(commits)-i-1], true)
   152  		for _, title := range []string{test.title, test.title2} {
   153  			if title == "" {
   154  				continue
   155  			}
   156  			com, err := repo.repo.GetCommitByTitle(title)
   157  			if err != nil {
   158  				t.Error(err)
   159  			} else if com == nil {
   160  				t.Errorf("no commits found by title %q", title)
   161  			} else if com.Title != title {
   162  				t.Errorf("wrong commit %q found by title %q", com.Title, title)
   163  			}
   164  		}
   165  	}
   166  }
   167  
   168  func checkCommit(t *testing.T, idx int, test testCommit, com *Commit, checkTags bool) {
   169  	if !checkTags {
   170  		return
   171  	}
   172  	if test.title != com.Title {
   173  		t.Errorf("#%v: want title %q, got %q", idx, test.title, com.Title)
   174  	}
   175  	if test.author != com.Author {
   176  		t.Errorf("#%v: want author %q, got %q", idx, test.author, com.Author)
   177  	}
   178  	if userName != com.AuthorName {
   179  		t.Errorf("#%v: want author name %q, got %q", idx, userName, com.Author)
   180  	}
   181  	if diff := cmp.Diff(test.cc, com.Recipients.GetEmails(To)); diff != "" {
   182  		t.Logf("%#v", com.Recipients)
   183  		t.Error(diff)
   184  	}
   185  	if diff := cmp.Diff(test.tags, com.Tags); checkTags && diff != "" {
   186  		t.Error(diff)
   187  	}
   188  }
   189  
   190  type testCommit struct {
   191  	description string
   192  	title       string
   193  	title2      string
   194  	author      string
   195  	cc          []string
   196  	tags        []string
   197  }
   198  
   199  // nolint: lll
   200  var metadataTests = []testCommit{
   201  	{
   202  		description: `dashboard/app: bump max repros per bug to 10
   203  
   204  Reported-by: syzbot+8e4090902540da8c6e8f@my.mail.com
   205  `,
   206  		title:  "dashboard/app: bump max repros per bug to 10",
   207  		author: userEmail,
   208  		cc:     []string{userEmail},
   209  		tags:   []string{"8e4090902540da8c6e8f"},
   210  	},
   211  	{
   212  		description: `executor: remove dead code
   213  
   214  Reported-by: syzbot+8e4090902540da8c6e8f@my.mail.com
   215  Reported-by: syzbot <syzbot+a640a0fc325c29c3efcb@my.mail.com>
   216  `,
   217  		title:  "executor: remove dead code",
   218  		author: userEmail,
   219  		cc:     []string{userEmail},
   220  		tags:   []string{"8e4090902540da8c6e8f", "a640a0fc325c29c3efcb"},
   221  	},
   222  	{
   223  		description: `pkg/csource: fix string escaping bug
   224  
   225  Reported-and-tested-by: syzbot+8e4090902540da8c6e8fa640a0fc325c29c3efcb@my.mail.com
   226  Tested-by: syzbot+4234987263748623784623758235@my.mail.com
   227  `,
   228  		title:  "pkg/csource: fix string escaping bug",
   229  		author: userEmail,
   230  		cc:     []string{"syzbot+4234987263748623784623758235@my.mail.com", "syzbot+8e4090902540da8c6e8fa640a0fc325c29c3efcb@my.mail.com", userEmail},
   231  		tags:   []string{"8e4090902540da8c6e8fa640a0fc325c29c3efcb", "4234987263748623784623758235"},
   232  	},
   233  	{
   234  		description: `When freeing a lockf struct that already is part of a linked list, make sure to update the next pointer for the preceding lock. Prevents a double free panic.
   235  
   236  ok millert@
   237  Reported-by: syzbot+6dd701dc797b23b8c761@my.mail.com
   238  `,
   239  		title:  "When freeing a lockf struct that already is part of a linked list, make sure to update the next pointer for the preceding lock. Prevents a double free panic.",
   240  		author: userEmail,
   241  		cc:     []string{userEmail},
   242  		tags:   []string{"6dd701dc797b23b8c761"},
   243  	},
   244  	{
   245  		description: `ipmr: properly check rhltable_init() return value
   246  
   247  commit 8fb472c09b9d ("ipmr: improve hash scalability")
   248  added a call to rhltable_init() without checking its return value.
   249   
   250  This problem was then later copied to IPv6 and factorized in commit
   251  0bbbf0e7d0e7 ("ipmr, ip6mr: Unite creation of new mr_table")
   252   
   253  Fixes: 8fb472c09b9d ("ipmr: improve hash scalability")
   254  Fixes: 0bbbf0e7d0e7 ("ipmr, ip6mr: Unite creation of new mr_table")
   255  Reported-by: syzbot+6dd701dc797b23b8c761@my.mail.com
   256  `,
   257  		title:  "ipmr: properly check rhltable_init() return value",
   258  		title2: "net-backports: ipmr: properly check rhltable_init() return value",
   259  		author: userEmail,
   260  		cc:     []string{userEmail},
   261  		tags:   []string{"6dd701dc797b23b8c761"},
   262  	},
   263  	{
   264  		description: `f2fs: sanity check for total valid node blocks
   265  
   266  Reported-by: syzbot+bf9253040425feb155ad@my.mail.com
   267  Reported-by: syzbot+bf9253040425feb155ad@my.mail.com
   268  `,
   269  		title:  "f2fs: sanity check for total valid node blocks",
   270  		author: userEmail,
   271  		cc:     []string{userEmail},
   272  		tags:   []string{"bf9253040425feb155ad"},
   273  	},
   274  	{
   275  		description: `USB: fix usbmon BUG trigger
   276  
   277  Automated tests triggered this by opening usbmon and accessing the
   278  mmap while simultaneously resizing the buffers. This bug was with
   279  us since 2006, because typically applications only size the buffers
   280  once and thus avoid racing. Reported by Kirill A. Shutemov.
   281  
   282  Reported-by: <syzbot+f9831b881b3e849829fc@my.mail.com>
   283  Signed-off-by: Pete Zaitcev <zaitcev@redhat.com>
   284  Cc: stable <stable@vger.kernel.org>
   285  Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
   286  `,
   287  		title:  "USB: fix usbmon BUG trigger",
   288  		author: userEmail,
   289  		cc:     []string{"gregkh@linuxfoundation.org", userEmail, "zaitcev@redhat.com"},
   290  		tags:   []string{"f9831b881b3e849829fc"},
   291  	},
   292  	{
   293  		description: `Do more sanity checks when accepting socket addresses in routing messages from user land. Reported-by: syzbot+638dbf7851da8e255af5@my.mail.com`,
   294  		title:       "Do more sanity checks when accepting socket addresses in routing messages from user land. Reported-by: syzbot+638dbf7851da8e255af5@my.mail.com",
   295  		author:      userEmail,
   296  		cc:          []string{userEmail},
   297  		tags:        []string{"638dbf7851da8e255af5"},
   298  	},
   299  	{
   300  		description: `Reported-by: syzbot+3e3c7cfa8093f8de047e@my.mail.com
   301  
   302  Comment out an assertion that's now bogus and add a comment.
   303  `,
   304  		title:  "Reported-by: syzbot+3e3c7cfa8093f8de047e@my.mail.com",
   305  		author: userEmail,
   306  		cc:     []string{userEmail},
   307  		tags:   []string{"3e3c7cfa8093f8de047e"},
   308  	},
   309  }
   310  
   311  func TestBisect(t *testing.T) {
   312  	t.Parallel()
   313  	repoDir := t.TempDir()
   314  	repo := MakeTestRepo(t, repoDir)
   315  	var commits []string
   316  	for i := 0; i < 5; i++ {
   317  		repo.CommitChange(fmt.Sprintf("commit %v", i))
   318  		com, err := repo.repo.HeadCommit()
   319  		if err != nil {
   320  			t.Fatal(err)
   321  		}
   322  		commits = append(commits, com.Hash)
   323  		t.Logf("%v %v", com.Hash, com.Title)
   324  	}
   325  	type predFunc func() (BisectResult, error)
   326  	type Test struct {
   327  		pred   predFunc
   328  		result []string
   329  	}
   330  	makePred := func(res1, res2, res3 BisectResult) predFunc {
   331  		return func() (BisectResult, error) {
   332  			current, err := repo.repo.HeadCommit()
   333  			if err != nil {
   334  				t.Fatal(err)
   335  			}
   336  			switch current.Hash {
   337  			case commits[1]:
   338  				return res1, nil
   339  			case commits[2]:
   340  				return res2, nil
   341  			case commits[3]:
   342  				return res3, nil
   343  			default:
   344  				return 0, fmt.Errorf("unknown commit %v", current.Hash)
   345  			}
   346  		}
   347  	}
   348  	tests := []Test{
   349  		{
   350  			// All are bad.
   351  			func() (BisectResult, error) {
   352  				return BisectBad, nil
   353  			},
   354  			[]string{commits[1]},
   355  		},
   356  		{
   357  			// All are good.
   358  			func() (BisectResult, error) {
   359  				return BisectGood, nil
   360  			},
   361  			[]string{commits[4]},
   362  		},
   363  		{
   364  			// All are skipped.
   365  			func() (BisectResult, error) {
   366  				return BisectSkip, nil
   367  			},
   368  			[]string{commits[1], commits[2], commits[3], commits[4]},
   369  		},
   370  		{
   371  			// Some are skipped.
   372  			makePred(BisectSkip, BisectSkip, BisectGood),
   373  			[]string{commits[4]},
   374  		},
   375  		{
   376  			// Some are skipped.
   377  			makePred(BisectGood, BisectSkip, BisectBad),
   378  			[]string{commits[2], commits[3]},
   379  		},
   380  		{
   381  			// Some are skipped.
   382  			makePred(BisectSkip, BisectSkip, BisectGood),
   383  			[]string{commits[4]},
   384  		},
   385  	}
   386  	for i, test := range tests {
   387  		t.Logf("TEST %v", i)
   388  		result, err := repo.repo.Bisect(commits[4], commits[0], &debugtracer.TestTracer{T: t}, test.pred)
   389  		if err != nil {
   390  			t.Fatal(err)
   391  		}
   392  		var got []string
   393  		for _, com := range result {
   394  			got = append(got, com.Hash)
   395  		}
   396  		sort.Strings(got) // git result order is non-deterministic (wat)
   397  		sort.Strings(test.result)
   398  		if diff := cmp.Diff(test.result, got); diff != "" {
   399  			t.Logf("result: %+v", got)
   400  			t.Fatal(diff)
   401  		}
   402  	}
   403  }