fork of https://github.com/sourcegraph/zoekt
0

Configure Feed

Select the types of activity you want to include in your feed.

1// Copyright 2016 Google Inc. All rights reserved. 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package zoekt // import "github.com/sourcegraph/zoekt" 16 17import ( 18 "bytes" 19 _ "embed" 20 "encoding/gob" 21 "fmt" 22 "math/rand" 23 "reflect" 24 "testing" 25 "testing/quick" 26 "time" 27 28 "github.com/google/go-cmp/cmp" 29 "github.com/google/go-cmp/cmp/cmpopts" 30 webproto "github.com/sourcegraph/zoekt/grpc/protos/zoekt/webserver/v1" 31 "google.golang.org/protobuf/proto" 32 33 fuzz "github.com/AdaLogics/go-fuzz-headers" 34) 35 36func TestProtoRoundtrip(t *testing.T) { 37 t.Run("FileMatch", func(t *testing.T) { 38 f := func(f1 FileMatch) bool { 39 p1 := f1.ToProto() 40 f2 := FileMatchFromProto(p1) 41 return reflect.DeepEqual(f1, f2) 42 } 43 if err := quick.Check(f, nil); err != nil { 44 t.Fatal(err) 45 } 46 }) 47 48 t.Run("ChunkMatch", func(t *testing.T) { 49 f := func(f1 ChunkMatch) bool { 50 p1 := f1.ToProto() 51 f2 := ChunkMatchFromProto(p1) 52 return reflect.DeepEqual(f1, f2) 53 } 54 if err := quick.Check(f, nil); err != nil { 55 t.Fatal(err) 56 } 57 }) 58 59 t.Run("Range", func(t *testing.T) { 60 f := func(f1 Range) bool { 61 p1 := f1.ToProto() 62 f2 := RangeFromProto(p1) 63 return reflect.DeepEqual(f1, f2) 64 } 65 if err := quick.Check(f, nil); err != nil { 66 t.Fatal(err) 67 } 68 }) 69 70 t.Run("Location", func(t *testing.T) { 71 f := func(f1 Range) bool { 72 p1 := f1.ToProto() 73 f2 := RangeFromProto(p1) 74 return reflect.DeepEqual(f1, f2) 75 } 76 if err := quick.Check(f, nil); err != nil { 77 t.Fatal(err) 78 } 79 }) 80 81 t.Run("LineMatch", func(t *testing.T) { 82 f := func(f1 LineMatch) bool { 83 p1 := f1.ToProto() 84 f2 := LineMatchFromProto(p1) 85 return reflect.DeepEqual(f1, f2) 86 } 87 if err := quick.Check(f, nil); err != nil { 88 t.Fatal(err) 89 } 90 }) 91 92 t.Run("Symbol", func(t *testing.T) { 93 f := func(f1 *Symbol) bool { 94 p1 := f1.ToProto() 95 f2 := SymbolFromProto(p1) 96 return reflect.DeepEqual(f1, f2) 97 } 98 if err := quick.Check(f, nil); err != nil { 99 t.Fatal(err) 100 } 101 }) 102 103 t.Run("FlushReson", func(t *testing.T) { 104 f := func(f1 FlushReason) bool { 105 p1 := f1.ToProto() 106 f2 := FlushReasonFromProto(p1) 107 return reflect.DeepEqual(f1.String(), f2.String()) 108 } 109 if err := quick.Check(f, nil); err != nil { 110 t.Fatal(err) 111 } 112 }) 113 114 t.Run("Stats", func(t *testing.T) { 115 f := func(f1 Stats) bool { 116 p1 := f1.ToProto() 117 f2 := StatsFromProto(p1) 118 return reflect.DeepEqual(f1, f2) 119 } 120 if err := quick.Check(f, nil); err != nil { 121 t.Fatal(err) 122 } 123 }) 124 125 t.Run("Progress", func(t *testing.T) { 126 f := func(f1 Progress) bool { 127 p1 := f1.ToProto() 128 f2 := ProgressFromProto(p1) 129 return reflect.DeepEqual(f1, f2) 130 } 131 if err := quick.Check(f, nil); err != nil { 132 t.Fatal(err) 133 } 134 }) 135 136 t.Run("SearchResult", func(t *testing.T) { 137 t.Run("unary", func(t *testing.T) { 138 f := func(f1 *SearchResult) bool { 139 var repoURLs map[string]string 140 var lineFragments map[string]string 141 142 if f1 != nil { 143 repoURLs = f1.RepoURLs 144 lineFragments = f1.LineFragments 145 } 146 147 p1 := f1.ToProto() 148 f2 := SearchResultFromProto(p1, repoURLs, lineFragments) 149 150 return reflect.DeepEqual(f1, f2) 151 } 152 if err := quick.Check(f, nil); err != nil { 153 t.Fatal(err) 154 } 155 }) 156 157 t.Run("stream", func(t *testing.T) { 158 f := func(f1 *SearchResult) bool { 159 var repoURLs map[string]string 160 var lineFragments map[string]string 161 162 if f1 != nil { 163 repoURLs = f1.RepoURLs 164 lineFragments = f1.LineFragments 165 } 166 167 p1 := f1.ToStreamProto() 168 f2 := SearchResultFromStreamProto(p1, repoURLs, lineFragments) 169 170 return reflect.DeepEqual(f1, f2) 171 } 172 if err := quick.Check(f, nil); err != nil { 173 t.Fatal(err) 174 } 175 }) 176 }) 177 178 t.Run("Repository", func(t *testing.T) { 179 f := func(f1 *Repository) bool { 180 p1 := f1.ToProto() 181 f2 := RepositoryFromProto(p1) 182 if diff := cmp.Diff(f1, &f2, cmpopts.IgnoreUnexported(Repository{})); diff != "" { 183 fmt.Printf("got diff: %s", diff) 184 return false 185 } 186 return true 187 } 188 if err := quick.Check(f, nil); err != nil { 189 t.Fatal(err) 190 } 191 }) 192 193 t.Run("IndexMetadata", func(t *testing.T) { 194 f := func(f1 *IndexMetadata) bool { 195 p1 := f1.ToProto() 196 f2 := IndexMetadataFromProto(p1) 197 if diff := cmp.Diff(f1, &f2); diff != "" { 198 fmt.Printf("got diff: %s", diff) 199 return false 200 } 201 return true 202 } 203 if err := quick.Check(f, nil); err != nil { 204 t.Fatal(err) 205 } 206 }) 207 208 t.Run("RepoStats", func(t *testing.T) { 209 f := func(f1 RepoStats) bool { 210 p1 := f1.ToProto() 211 f2 := RepoStatsFromProto(p1) 212 if diff := cmp.Diff(f1, f2); diff != "" { 213 fmt.Printf("got diff: %s", diff) 214 return false 215 } 216 return true 217 } 218 if err := quick.Check(f, nil); err != nil { 219 t.Fatal(err) 220 } 221 }) 222 223 t.Run("RepoListEntry", func(t *testing.T) { 224 r1 := &RepoListEntry{ 225 Repository: Repository{ 226 ID: 1, 227 Name: "test", 228 URL: "testurl", 229 Source: "testsource", 230 Branches: []RepositoryBranch{{ 231 Name: "branch", 232 Version: "version", 233 }}, 234 SubRepoMap: map[string]*Repository{ 235 "test": { 236 ID: 2, 237 Name: "subrepo", 238 Branches: []RepositoryBranch{}, 239 SubRepoMap: map[string]*Repository{}, 240 FileTombstones: map[string]struct{}{}, 241 }, 242 }, 243 CommitURLTemplate: "committemplate", 244 FileURLTemplate: "fileurltemplate", 245 LineFragmentTemplate: "linefragmenttemplate", 246 priority: 10, 247 RawConfig: map[string]string{ 248 "a": "b", 249 }, 250 Rank: 32, 251 IndexOptions: "indexoptions", 252 HasSymbols: true, 253 Tombstone: false, 254 LatestCommitDate: time.Now(), 255 FileTombstones: map[string]struct{}{ 256 "test1": {}, 257 }, 258 }, 259 IndexMetadata: IndexMetadata{ 260 IndexFormatVersion: 32, 261 IndexFeatureVersion: 42, 262 IndexMinReaderVersion: 52, 263 IndexTime: time.Now(), 264 PlainASCII: true, 265 LanguageMap: map[string]uint16{ 266 "go": 1, 267 }, 268 ZoektVersion: "32", 269 ID: "52", 270 }, 271 Stats: RepoStats{ 272 Repos: 3, 273 Shards: 4, 274 Documents: 5, 275 IndexBytes: 6, 276 ContentBytes: 7, 277 NewLinesCount: 8, 278 DefaultBranchNewLinesCount: 9, 279 OtherBranchesNewLinesCount: 10, 280 }, 281 } 282 283 p1 := r1.ToProto() 284 r2 := RepoListEntryFromProto(p1) 285 if diff := cmp.Diff(r1, r2, cmpopts.IgnoreUnexported(Repository{})); diff != "" { 286 t.Fatalf("got diff: %s", diff) 287 } 288 }) 289 290 t.Run("RepositoryBranch", func(t *testing.T) { 291 f := func(f1 RepositoryBranch) bool { 292 p1 := f1.ToProto() 293 f2 := RepositoryBranchFromProto(p1) 294 if diff := cmp.Diff(f1, f2); diff != "" { 295 fmt.Printf("got diff: %s", diff) 296 return false 297 } 298 return true 299 } 300 if err := quick.Check(f, nil); err != nil { 301 t.Fatal(err) 302 } 303 }) 304 305 t.Run("MinimalRepoListEntry", func(t *testing.T) { 306 f := func(f1 MinimalRepoListEntry) bool { 307 p1 := f1.ToProto() 308 f2 := MinimalRepoListEntryFromProto(p1) 309 if diff := cmp.Diff(f1, f2); diff != "" { 310 fmt.Printf("got diff: %s", diff) 311 return false 312 } 313 return true 314 } 315 if err := quick.Check(f, nil); err != nil { 316 t.Fatal(err) 317 } 318 }) 319 320 t.Run("ListOptions", func(t *testing.T) { 321 f := func(f1 *ListOptions) bool { 322 p1 := f1.ToProto() 323 f2 := ListOptionsFromProto(p1) 324 if diff := cmp.Diff(f1, f2); diff != "" { 325 fmt.Printf("got diff: %s", diff) 326 return false 327 } 328 return true 329 } 330 if err := quick.Check(f, nil); err != nil { 331 t.Fatal(err) 332 } 333 }) 334 335 t.Run("SearchOptions", func(t *testing.T) { 336 f := func(f1 *SearchOptions) bool { 337 if f1 != nil { 338 // Ignore deprecated and unimplemented fields 339 f1.ShardMaxImportantMatch = 0 340 f1.TotalMaxImportantMatch = 0 341 f1.SpanContext = nil 342 } 343 p1 := f1.ToProto() 344 f2 := SearchOptionsFromProto(p1) 345 if diff := cmp.Diff(f1, f2); diff != "" { 346 fmt.Printf("got diff: %s", diff) 347 return false 348 } 349 return true 350 } 351 if err := quick.Check(f, nil); err != nil { 352 t.Fatal(err) 353 } 354 }) 355} 356 357func (*IndexMetadata) Generate(r *rand.Rand, _ int) reflect.Value { 358 indexTime := time.Now().Add(time.Duration(r.Int63n(1000)) * time.Hour) 359 var i IndexMetadata 360 i.IndexFormatVersion = gen(i.IndexFormatVersion, r) 361 i.IndexFeatureVersion = gen(i.IndexFeatureVersion, r) 362 i.IndexMinReaderVersion = gen(i.IndexMinReaderVersion, r) 363 i.IndexTime = indexTime 364 i.PlainASCII = gen(i.PlainASCII, r) 365 i.LanguageMap = gen(i.LanguageMap, r) 366 i.ZoektVersion = gen(i.ZoektVersion, r) 367 i.ID = gen(i.ID, r) 368 return reflect.ValueOf(&i) 369} 370 371func (*Repository) Generate(rng *rand.Rand, _ int) reflect.Value { 372 latestCommitDate := time.Now().Add(time.Duration(rng.Int63n(1000)) * time.Hour) 373 var r Repository 374 v := &Repository{ 375 ID: gen(r.ID, rng), 376 Name: gen(r.Name, rng), 377 URL: gen(r.URL, rng), 378 Source: gen(r.Source, rng), 379 Branches: gen(r.Branches, rng), 380 SubRepoMap: map[string]*Repository{}, 381 CommitURLTemplate: gen(r.CommitURLTemplate, rng), 382 FileURLTemplate: gen(r.FileURLTemplate, rng), 383 LineFragmentTemplate: gen(r.LineFragmentTemplate, rng), 384 priority: gen(r.priority, rng), 385 RawConfig: gen(r.RawConfig, rng), 386 Rank: gen(r.Rank, rng), 387 IndexOptions: gen(r.IndexOptions, rng), 388 HasSymbols: gen(r.HasSymbols, rng), 389 Tombstone: gen(r.Tombstone, rng), 390 LatestCommitDate: latestCommitDate, 391 FileTombstones: gen(r.FileTombstones, rng), 392 } 393 return reflect.ValueOf(v) 394} 395 396func (RepoListField) Generate(rng *rand.Rand, _ int) reflect.Value { 397 if rng.Intn(2) == 0 { 398 return reflect.ValueOf(RepoListField(RepoListFieldRepos)) 399 } else { 400 return reflect.ValueOf(RepoListField(RepoListFieldReposMap)) 401 } 402} 403 404func gen[T any](sample T, r *rand.Rand) T { 405 var t T 406 v, _ := quick.Value(reflect.TypeOf(t), r) 407 return v.Interface().(T) 408} 409 410// This is a real search result that is intended to be a reasonable representative 411// for serialization benchmarks. 412// Generated by modifying the code to dump the proto to a file, then running a 413// fairly broadly-matching search. 414var ( 415 //go:embed testdata/search_result_1.pb 416 exampleSearchResultBytes []byte 417 418 // The proto struct representation of the search result 419 exampleSearchResultProto = func() *webproto.SearchResponse { 420 sr := new(webproto.SearchResponse) 421 err := proto.Unmarshal(exampleSearchResultBytes, sr) 422 if err != nil { 423 panic(err) 424 } 425 return sr 426 }() 427 428 // The non-proto struct representation of the search result 429 exampleSearchResultGo = SearchResultFromProto(exampleSearchResultProto, nil, nil) 430) 431 432func BenchmarkGobRoundtrip(b *testing.B) { 433 for _, count := range []int{1, 100, 1000, 10000} { 434 b.Run(fmt.Sprintf("count=%d", count), func(b *testing.B) { 435 for i := 0; i < b.N; i++ { 436 var buf bytes.Buffer 437 enc := gob.NewEncoder(&buf) 438 439 for i := 0; i < count; i++ { 440 err := enc.Encode(exampleSearchResultGo) 441 if err != nil { 442 panic(err) 443 } 444 445 } 446 447 dec := gob.NewDecoder(&buf) 448 for i := 0; i < count; i++ { 449 var res SearchResult 450 err := dec.Decode(&res) 451 if err != nil { 452 panic(err) 453 } 454 } 455 } 456 }) 457 } 458} 459 460func BenchmarkProtoRoundtrip(b *testing.B) { 461 for _, count := range []int{1, 100, 1000, 10000} { 462 b.Run(fmt.Sprintf("count=%d", count), func(b *testing.B) { 463 for i := 0; i < b.N; i++ { 464 buffers := make([][]byte, 0, count) 465 for i := 0; i < count; i++ { 466 buf, err := proto.Marshal(exampleSearchResultProto) 467 if err != nil { 468 b.Fatal(err) 469 } 470 buffers = append(buffers, buf) 471 } 472 473 for _, buf := range buffers { 474 res := new(webproto.SearchResponse) 475 err := proto.Unmarshal(buf, res) 476 if err != nil { 477 b.Fatal(err) 478 } 479 } 480 } 481 }) 482 } 483} 484 485func Fuzz_RepoList_ProtoRoundTrip(f *testing.F) { 486 f.Fuzz(func(t *testing.T, data []byte) { 487 fc := fuzz.NewConsumer(data) 488 fc.AllowUnexportedFields() 489 490 original := &RepoList{} 491 err := fc.GenerateStruct(original) 492 if err != nil { 493 return 494 } 495 496 p := original.ToProto() 497 converted := RepoListFromProto(p) 498 499 opts := []cmp.Option{ 500 cmpopts.IgnoreUnexported(Repository{}), 501 cmpopts.EquateEmpty(), 502 } 503 504 if diff := cmp.Diff(original, converted, opts...); diff != "" { 505 t.Fatalf("unexpected diff (-want +got)\n%s", diff) 506 } 507 }) 508}