github.com/google/syzkaller@v0.0.0-20251211124644-a066d2bc4b02/dashboard/app/coverage_test.go (about) 1 // Copyright 2025 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 main 5 6 import ( 7 "context" 8 "errors" 9 "net/http" 10 "strings" 11 "testing" 12 13 "github.com/google/syzkaller/pkg/coveragedb" 14 "github.com/google/syzkaller/pkg/coveragedb/mocks" 15 "github.com/google/syzkaller/pkg/coveragedb/spannerclient" 16 "github.com/google/syzkaller/pkg/covermerger" 17 mergermocks "github.com/google/syzkaller/pkg/covermerger/mocks" 18 "github.com/stretchr/testify/assert" 19 "github.com/stretchr/testify/mock" 20 "google.golang.org/api/iterator" 21 ) 22 23 func setCoverageDBClient(ctx context.Context, client spannerclient.SpannerClient) context.Context { 24 return context.WithValue(ctx, &keyCoverageDBClient, client) 25 } 26 27 func TestFileCoverage_BadRequest(t *testing.T) { 28 badURL := "/test2/coverage/file?dateto=2025-01-31'&period=month" + 29 "&commit=c0e75905caf368e19aab585d20151500e750de89&filepath=virt/kvm/kvm_main.c" 30 c := NewCtx(t) 31 defer c.Close() 32 c.setCoverageMocks("test2", nil, nil) 33 _, err := c.GET(badURL) 34 var httpErr *HTTPError 35 assert.True(t, errors.As(err, &httpErr)) 36 assert.Equal(t, http.StatusBadRequest, httpErr.Code) 37 } 38 39 func TestFileCoverage(t *testing.T) { 40 tests := []struct { 41 name string 42 covDB func(t *testing.T) spannerclient.SpannerClient 43 fileProv func(t *testing.T) covermerger.FileVersProvider 44 url string 45 wantInRes []string 46 }{ 47 { 48 name: "empty db", 49 covDB: func(t *testing.T) spannerclient.SpannerClient { return emptyCoverageDBFixture(t, 1) }, 50 fileProv: func(t *testing.T) covermerger.FileVersProvider { return staticFileProvider(t) }, 51 url: "/test2/coverage/file?dateto=2025-01-31&period=month" + 52 "&commit=c0e75905caf368e19aab585d20151500e750de89&filepath=virt/kvm/kvm_main.c", 53 wantInRes: []string{"1 line1"}, 54 }, 55 { 56 name: "regular db", 57 covDB: func(t *testing.T) spannerclient.SpannerClient { return coverageDBFixture(t) }, 58 fileProv: func(t *testing.T) covermerger.FileVersProvider { return staticFileProvider(t) }, 59 url: "/test2/coverage/file?dateto=2025-01-31&period=month" + 60 "&commit=c0e75905caf368e19aab585d20151500e750de89&filepath=virt/kvm/kvm_main.c", 61 wantInRes: []string{ 62 "4 1 line1", 63 "5 2 line2", 64 "6 3 line3"}, 65 }, 66 { 67 name: "multimanager db", 68 covDB: func(t *testing.T) spannerclient.SpannerClient { return multiManagerCovDBFixture(t) }, 69 fileProv: func(t *testing.T) covermerger.FileVersProvider { return staticFileProvider(t) }, 70 url: "/test2/coverage/file?dateto=2025-01-31&period=month" + 71 "&commit=c0e75905caf368e19aab585d20151500e750de89&filepath=virt/kvm/kvm_main.c" + 72 "&manager=special-cc-manager&unique-only=1", 73 wantInRes: []string{ 74 " 0 1 line1", // Covered, is not unique. 75 " 5 2 line2", // Covered and is unique. 76 " 3 line3", // Covered only by "*" managers. 77 }, 78 }, 79 } 80 for _, test := range tests { 81 t.Run(test.name, func(t *testing.T) { 82 c := NewCtx(t) 83 defer c.Close() 84 c.setCoverageMocks("test2", test.covDB(t), test.fileProv(t)) 85 fileCovPage, err := c.GET(test.url) 86 assert.NoError(t, err) 87 got := string(fileCovPage) 88 for _, want := range test.wantInRes { 89 if !strings.Contains(got, want) { 90 t.Errorf(`"%s" wasn't found in "%s"'`, want, got) 91 } 92 } 93 }) 94 } 95 } 96 97 func staticFileProvider(t *testing.T) covermerger.FileVersProvider { 98 m := mergermocks.NewFileVersProvider(t) 99 m.On("GetFileVersions", mock.Anything, mock.Anything). 100 Return(func(targetFilePath string, repoCommits ...covermerger.RepoCommit, 101 ) covermerger.FileVersions { 102 res := covermerger.FileVersions{} 103 for _, rc := range repoCommits { 104 res[rc] = `line1 105 line2 106 line3` 107 } 108 return res 109 }, nil) 110 return m 111 } 112 113 func emptyCoverageDBFixture(t *testing.T, times int) spannerclient.SpannerClient { 114 mRowIterator := mocks.NewRowIterator(t) 115 mRowIterator.On("Stop").Return().Times(times) 116 mRowIterator.On("Next"). 117 Return(nil, iterator.Done).Times(times) 118 119 mTran := mocks.NewReadOnlyTransaction(t) 120 mTran.On("Query", mock.Anything, mock.Anything). 121 Return(mRowIterator).Times(times) 122 123 m := mocks.NewSpannerClient(t) 124 m.On("Single"). 125 Return(mTran).Times(times) 126 return m 127 } 128 129 func coverageDBFixture(t *testing.T) spannerclient.SpannerClient { 130 mRowIt := newRowIteratorMock(t, []*coveragedb.LinesCoverage{{ 131 LinesInstrumented: []int64{1, 2, 3}, 132 HitCounts: []int64{4, 5, 6}, 133 }}) 134 135 mTran := mocks.NewReadOnlyTransaction(t) 136 mTran.On("Query", mock.Anything, mock.Anything). 137 Return(mRowIt).Once() 138 139 m := mocks.NewSpannerClient(t) 140 m.On("Single"). 141 Return(mTran).Once() 142 return m 143 } 144 145 func multiManagerCovDBFixture(t *testing.T) spannerclient.SpannerClient { 146 mReadFullCoverageTran := mocks.NewReadOnlyTransaction(t) 147 mReadFullCoverageTran.On("Query", mock.Anything, mock.Anything). 148 Return(newRowIteratorMock(t, []*coveragedb.LinesCoverage{{ 149 LinesInstrumented: []int64{1, 2, 3}, 150 HitCounts: []int64{4, 5, 6}, 151 }})).Once() 152 153 mReadPartialCoverageTran := mocks.NewReadOnlyTransaction(t) 154 mReadPartialCoverageTran.On("Query", mock.Anything, mock.Anything). 155 Return(newRowIteratorMock(t, []*coveragedb.LinesCoverage{{ 156 LinesInstrumented: []int64{1, 2}, 157 HitCounts: []int64{3, 5}, 158 }})).Once() 159 160 m := mocks.NewSpannerClient(t) 161 // The order matters. Full coverage is fetched second. 162 m.On("Single"). 163 Return(mReadPartialCoverageTran).Once() 164 m.On("Single"). 165 Return(mReadFullCoverageTran).Once() 166 167 return m 168 } 169 170 func newRowIteratorMock[K any](t *testing.T, cov []*K, 171 ) *mocks.RowIterator { 172 m := mocks.NewRowIterator(t) 173 m.On("Stop").Once().Return() 174 for _, item := range cov { 175 mRow := mocks.NewRow(t) 176 mRow.On("ToStruct", mock.Anything). 177 Run(func(args mock.Arguments) { 178 arg := args.Get(0).(*K) 179 *arg = *item 180 }). 181 Return(nil).Once() 182 183 m.On("Next"). 184 Return(mRow, nil).Once() 185 } 186 187 m.On("Next"). 188 Return(nil, iterator.Done).Once() 189 return m 190 }