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

Configure Feed

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

at tngl 13 kB View raw
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 fuzz "github.com/AdaLogics/go-fuzz-headers" 29 "github.com/google/go-cmp/cmp" 30 "github.com/google/go-cmp/cmp/cmpopts" 31 "google.golang.org/protobuf/proto" 32 33 webserverv1 "github.com/sourcegraph/zoekt/grpc/protos/zoekt/webserver/v1" 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 f1.SpanContext = nil 339 } 340 p1 := f1.ToProto() 341 f2 := SearchOptionsFromProto(p1) 342 if diff := cmp.Diff(f1, f2); diff != "" { 343 fmt.Printf("got diff: %s", diff) 344 return false 345 } 346 return true 347 } 348 if err := quick.Check(f, nil); err != nil { 349 t.Fatal(err) 350 } 351 }) 352} 353 354func (*IndexMetadata) Generate(r *rand.Rand, _ int) reflect.Value { 355 indexTime := time.Now().Add(time.Duration(r.Int63n(1000)) * time.Hour) 356 var i IndexMetadata 357 i.IndexFormatVersion = gen(i.IndexFormatVersion, r) 358 i.IndexFeatureVersion = gen(i.IndexFeatureVersion, r) 359 i.IndexMinReaderVersion = gen(i.IndexMinReaderVersion, r) 360 i.IndexTime = indexTime 361 i.PlainASCII = gen(i.PlainASCII, r) 362 i.LanguageMap = gen(i.LanguageMap, r) 363 i.ZoektVersion = gen(i.ZoektVersion, r) 364 i.ID = gen(i.ID, r) 365 return reflect.ValueOf(&i) 366} 367 368func (*Repository) Generate(rng *rand.Rand, _ int) reflect.Value { 369 latestCommitDate := time.Now().Add(time.Duration(rng.Int63n(1000)) * time.Hour) 370 var r Repository 371 v := &Repository{ 372 ID: gen(r.ID, rng), 373 Name: gen(r.Name, rng), 374 URL: gen(r.URL, rng), 375 Source: gen(r.Source, rng), 376 Branches: gen(r.Branches, rng), 377 SubRepoMap: map[string]*Repository{}, 378 CommitURLTemplate: gen(r.CommitURLTemplate, rng), 379 FileURLTemplate: gen(r.FileURLTemplate, rng), 380 LineFragmentTemplate: gen(r.LineFragmentTemplate, rng), 381 priority: gen(r.priority, rng), 382 RawConfig: gen(r.RawConfig, rng), 383 Rank: gen(r.Rank, rng), 384 IndexOptions: gen(r.IndexOptions, rng), 385 HasSymbols: gen(r.HasSymbols, rng), 386 Tombstone: gen(r.Tombstone, rng), 387 LatestCommitDate: latestCommitDate, 388 FileTombstones: gen(r.FileTombstones, rng), 389 } 390 return reflect.ValueOf(v) 391} 392 393func (RepoListField) Generate(rng *rand.Rand, _ int) reflect.Value { 394 if rng.Intn(2) == 0 { 395 return reflect.ValueOf(RepoListField(RepoListFieldRepos)) 396 } else { 397 return reflect.ValueOf(RepoListField(RepoListFieldReposMap)) 398 } 399} 400 401func gen[T any](sample T, r *rand.Rand) T { 402 var t T 403 v, _ := quick.Value(reflect.TypeOf(t), r) 404 return v.Interface().(T) 405} 406 407// This is a real search result that is intended to be a reasonable representative 408// for serialization benchmarks. 409// Generated by modifying the code to dump the proto to a file, then running a 410// fairly broadly-matching search. 411var ( 412 //go:embed testdata/search_result_1.pb 413 exampleSearchResultBytes []byte 414 415 // The proto struct representation of the search result 416 exampleSearchResultProto = func() *webserverv1.SearchResponse { 417 sr := new(webserverv1.SearchResponse) 418 err := proto.Unmarshal(exampleSearchResultBytes, sr) 419 if err != nil { 420 panic(err) 421 } 422 return sr 423 }() 424 425 // The non-proto struct representation of the search result 426 exampleSearchResultGo = SearchResultFromProto(exampleSearchResultProto, nil, nil) 427) 428 429func BenchmarkGobRoundtrip(b *testing.B) { 430 for _, count := range []int{1, 100, 1000, 10000} { 431 b.Run(fmt.Sprintf("count=%d", count), func(b *testing.B) { 432 for i := 0; i < b.N; i++ { 433 var buf bytes.Buffer 434 enc := gob.NewEncoder(&buf) 435 436 for range count { 437 err := enc.Encode(exampleSearchResultGo) 438 if err != nil { 439 panic(err) 440 } 441 442 } 443 444 dec := gob.NewDecoder(&buf) 445 for range count { 446 var res SearchResult 447 err := dec.Decode(&res) 448 if err != nil { 449 panic(err) 450 } 451 } 452 } 453 }) 454 } 455} 456 457func BenchmarkProtoRoundtrip(b *testing.B) { 458 for _, count := range []int{1, 100, 1000, 10000} { 459 b.Run(fmt.Sprintf("count=%d", count), func(b *testing.B) { 460 for i := 0; i < b.N; i++ { 461 buffers := make([][]byte, 0, count) 462 for range count { 463 buf, err := proto.Marshal(exampleSearchResultProto) 464 if err != nil { 465 b.Fatal(err) 466 } 467 buffers = append(buffers, buf) 468 } 469 470 for _, buf := range buffers { 471 res := new(webserverv1.SearchResponse) 472 err := proto.Unmarshal(buf, res) 473 if err != nil { 474 b.Fatal(err) 475 } 476 } 477 } 478 }) 479 } 480} 481 482func Fuzz_RepoList_ProtoRoundTrip(f *testing.F) { 483 f.Fuzz(func(t *testing.T, data []byte) { 484 fc := fuzz.NewConsumer(data) 485 fc.AllowUnexportedFields() 486 487 original := &RepoList{} 488 err := fc.GenerateStruct(original) 489 if err != nil { 490 return 491 } 492 493 p := original.ToProto() 494 converted := RepoListFromProto(p) 495 496 opts := []cmp.Option{ 497 cmpopts.IgnoreUnexported(Repository{}), 498 cmpopts.EquateEmpty(), 499 } 500 501 if diff := cmp.Diff(original, converted, opts...); diff != "" { 502 t.Fatalf("unexpected diff (-want +got)\n%s", diff) 503 } 504 }) 505}