github.com/google/syzkaller@v0.0.0-20251211124644-a066d2bc4b02/pkg/coveragedb/coveragedb_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 coveragedb 5 6 import ( 7 "context" 8 "testing" 9 10 "cloud.google.com/go/civil" 11 "github.com/google/syzkaller/pkg/coveragedb/mocks" 12 "github.com/google/syzkaller/pkg/coveragedb/spannerclient" 13 "github.com/stretchr/testify/assert" 14 "github.com/stretchr/testify/mock" 15 "google.golang.org/api/iterator" 16 ) 17 18 func TestFilesCoverageWithDetails(t *testing.T) { 19 period, _ := MakeTimePeriod( 20 civil.Date{Year: 2025, Month: 1, Day: 1}, 21 "day") 22 tests := []struct { 23 name string 24 scope *SelectScope 25 client func() spannerclient.SpannerClient 26 onlyUnique bool 27 want []*FileCoverageWithDetails 28 wantErr bool 29 }{ 30 { 31 name: "empty scope", 32 scope: &SelectScope{}, 33 want: nil, 34 wantErr: false, 35 }, 36 { 37 name: "single day, no filters, empty DB => no coverage", 38 scope: &SelectScope{ 39 Ns: "upstream", 40 Periods: []TimePeriod{period}, 41 }, 42 client: func() spannerclient.SpannerClient { return emptyCoverageDBFixture(t, 1) }, 43 want: nil, 44 wantErr: false, 45 }, 46 { 47 name: "single day, unique coverage, empty DB => no coverage", 48 scope: &SelectScope{ 49 Ns: "upstream", 50 Periods: []TimePeriod{period}, 51 }, 52 client: func() spannerclient.SpannerClient { return emptyCoverageDBFixture(t, 2) }, 53 onlyUnique: true, 54 want: nil, 55 wantErr: false, 56 }, 57 { 58 name: "single day, unique coverage, empty partial result => 0/3 covered", 59 scope: &SelectScope{ 60 Ns: "upstream", 61 Periods: []TimePeriod{period}, 62 }, 63 client: func() spannerclient.SpannerClient { 64 return fullCoverageDBFixture( 65 t, 66 []*FileCoverageWithLineInfo{ 67 { 68 FileCoverageWithDetails: FileCoverageWithDetails{ 69 Filepath: "file1", 70 Instrumented: 3, 71 Covered: 3, 72 }, 73 LinesInstrumented: []int64{1, 2, 3}, 74 HitCounts: []int64{1, 1, 1}, 75 }, 76 }, 77 nil) 78 }, 79 onlyUnique: true, 80 want: []*FileCoverageWithDetails{ 81 { 82 Filepath: "file1", 83 Instrumented: 3, 84 Covered: 0, 85 TimePeriod: period, 86 }, 87 }, 88 wantErr: false, 89 }, 90 { 91 name: "single day, unique coverage, full result match => 3/3 covered", 92 scope: &SelectScope{ 93 Ns: "upstream", 94 Periods: []TimePeriod{period}, 95 }, 96 client: func() spannerclient.SpannerClient { 97 return fullCoverageDBFixture( 98 t, 99 []*FileCoverageWithLineInfo{ 100 { 101 FileCoverageWithDetails: FileCoverageWithDetails{ 102 Filepath: "file1", 103 Instrumented: 3, 104 Covered: 3, 105 }, 106 LinesInstrumented: []int64{1, 2, 3}, 107 HitCounts: []int64{1, 1, 1}, 108 }, 109 }, 110 []*FileCoverageWithLineInfo{ 111 { 112 FileCoverageWithDetails: FileCoverageWithDetails{ 113 Filepath: "file1", 114 Instrumented: 3, 115 Covered: 3, 116 }, 117 LinesInstrumented: []int64{1, 2, 3}, 118 HitCounts: []int64{1, 1, 1}, 119 }, 120 }) 121 }, 122 onlyUnique: true, 123 want: []*FileCoverageWithDetails{ 124 { 125 Filepath: "file1", 126 Instrumented: 3, 127 Covered: 3, 128 TimePeriod: period, 129 }, 130 }, 131 wantErr: false, 132 }, 133 { 134 name: "single day, unique coverage, partial result match => 3/5 covered", 135 scope: &SelectScope{ 136 Ns: "upstream", 137 Periods: []TimePeriod{period}, 138 }, 139 client: func() spannerclient.SpannerClient { 140 return fullCoverageDBFixture( 141 t, 142 []*FileCoverageWithLineInfo{ 143 { 144 FileCoverageWithDetails: FileCoverageWithDetails{ 145 Filepath: "file1", 146 Instrumented: 5, 147 Covered: 5, 148 }, 149 LinesInstrumented: []int64{1, 2, 3, 4, 5}, 150 HitCounts: []int64{3, 4, 5, 6, 7}, 151 }, 152 }, 153 []*FileCoverageWithLineInfo{ 154 { 155 FileCoverageWithDetails: FileCoverageWithDetails{ 156 Filepath: "file1", 157 Instrumented: 4, 158 Covered: 3, 159 }, 160 LinesInstrumented: []int64{1, 2, 3, 5}, 161 HitCounts: []int64{3, 0, 5, 7}, 162 }, 163 }) 164 }, 165 onlyUnique: true, 166 want: []*FileCoverageWithDetails{ 167 { 168 Filepath: "file1", 169 Instrumented: 5, 170 Covered: 3, 171 TimePeriod: period, 172 }, 173 }, 174 wantErr: false, 175 }, 176 } 177 for _, test := range tests { 178 t.Run(test.name, func(t *testing.T) { 179 var testClient spannerclient.SpannerClient 180 if test.client != nil { 181 testClient = test.client() 182 } 183 got, gotErr := FilesCoverageWithDetails( 184 context.Background(), 185 testClient, test.scope, test.onlyUnique) 186 if test.wantErr { 187 assert.Error(t, gotErr) 188 } else { 189 assert.NoError(t, gotErr) 190 } 191 assert.Equal(t, test.want, got) 192 }) 193 } 194 } 195 196 func emptyCoverageDBFixture(t *testing.T, times int) spannerclient.SpannerClient { 197 mRowIterator := mocks.NewRowIterator(t) 198 mRowIterator.On("Stop").Return().Times(times) 199 mRowIterator.On("Next"). 200 Return(nil, iterator.Done).Times(times) 201 202 mTran := mocks.NewReadOnlyTransaction(t) 203 mTran.On("Query", mock.Anything, mock.Anything). 204 Return(mRowIterator).Times(times) 205 206 m := mocks.NewSpannerClient(t) 207 m.On("Single"). 208 Return(mTran).Times(times) 209 return m 210 } 211 212 func fullCoverageDBFixture( 213 t *testing.T, full, partial []*FileCoverageWithLineInfo, 214 ) spannerclient.SpannerClient { 215 mPartialTran := mocks.NewReadOnlyTransaction(t) 216 mPartialTran.On("Query", mock.Anything, mock.Anything). 217 Return(newRowIteratorMock(t, partial)).Once() 218 219 mFullTran := mocks.NewReadOnlyTransaction(t) 220 mFullTran.On("Query", mock.Anything, mock.Anything). 221 Return(newRowIteratorMock(t, full)).Once() 222 223 m := mocks.NewSpannerClient(t) 224 m.On("Single"). 225 Return(mPartialTran).Once() 226 m.On("Single"). 227 Return(mFullTran).Once() 228 return m 229 } 230 231 func newRowIteratorMock(t *testing.T, events []*FileCoverageWithLineInfo, 232 ) *mocks.RowIterator { 233 m := mocks.NewRowIterator(t) 234 m.On("Stop").Once().Return() 235 for _, item := range events { 236 mRow := mocks.NewRow(t) 237 mRow.On("ToStruct", mock.Anything). 238 Run(func(args mock.Arguments) { 239 arg := args.Get(0).(*FileCoverageWithLineInfo) 240 *arg = *item 241 }). 242 Return(nil).Once() 243 244 m.On("Next"). 245 Return(mRow, nil).Once() 246 } 247 248 m.On("Next"). 249 Return(nil, iterator.Done).Once() 250 return m 251 }