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

Configure Feed

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

1// Copyright 2021 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 "encoding/gob" 20 "reflect" 21 "strings" 22 "testing" 23 "time" 24 25 "github.com/grafana/regexp" 26) 27 28/* 29BenchmarkMinimalRepoListEncodings/slice-8 570 2145665 ns/op 753790 bytes 3981 B/op 0 allocs/op 30BenchmarkMinimalRepoListEncodings/map-8 360 3337522 ns/op 740778 bytes 377777 B/op 13002 allocs/op 31*/ 32func BenchmarkMinimalRepoListEncodings(b *testing.B) { 33 size := uint32(13000) // 2021-06-24 rough estimate of number of repos on a replica. 34 35 type Slice struct { 36 ID uint32 37 HasSymbols bool 38 Branches []RepositoryBranch 39 IndexTimeUnix int64 40 } 41 42 branches := []RepositoryBranch{{Name: "HEAD", Version: strings.Repeat("a", 40)}} 43 mapData := make(map[uint32]*MinimalRepoListEntry, size) 44 sliceData := make([]Slice, 0, size) 45 indexTime := time.Now().Unix() 46 47 for id := uint32(1); id <= size; id++ { 48 mapData[id] = &MinimalRepoListEntry{ 49 HasSymbols: true, 50 Branches: branches, 51 IndexTimeUnix: indexTime, 52 } 53 sliceData = append(sliceData, Slice{ 54 ID: id, 55 HasSymbols: true, 56 Branches: branches, 57 IndexTimeUnix: indexTime, 58 }) 59 } 60 61 b.Run("slice", benchmarkEncoding(sliceData)) 62 63 b.Run("map", benchmarkEncoding(mapData)) 64} 65 66func benchmarkEncoding(data interface{}) func(*testing.B) { 67 return func(b *testing.B) { 68 b.Helper() 69 70 var buf bytes.Buffer 71 enc := gob.NewEncoder(&buf) 72 err := enc.Encode(data) 73 if err != nil { 74 b.Fatal(err) 75 } 76 77 b.ReportAllocs() 78 b.ResetTimer() 79 b.ReportMetric(float64(buf.Len()), "bytes") 80 for i := 0; i < b.N; i++ { 81 _ = enc.Encode(data) 82 buf.Reset() 83 } 84 } 85} 86 87func TestSizeBytesSearchResult(t *testing.T) { 88 sr := SearchResult{ 89 Stats: Stats{}, // 129 bytes 90 Progress: Progress{}, // 16 bytes 91 Files: []FileMatch{{ // 24 bytes + 460 bytes 92 Score: 0, // 8 bytes 93 Debug: "", // 16 bytes 94 FileName: "", // 16 bytes 95 Repository: "", // 16 bytes 96 Branches: nil, // 24 bytes 97 LineMatches: nil, // 24 bytes 98 ChunkMatches: []ChunkMatch{{ // 24 bytes + 208 bytes (see TestSizeByteChunkMatches) 99 Content: []byte("foo"), 100 ContentStart: Location{}, 101 FileName: false, 102 Ranges: []Range{{}}, 103 SymbolInfo: []*Symbol{{}}, 104 Score: 0, 105 DebugScore: "", 106 }}, 107 RepositoryID: 0, // 4 bytes 108 RepositoryPriority: 0, // 8 bytes 109 Content: nil, // 24 bytes 110 Checksum: nil, // 24 bytes 111 Language: "", // 16 bytes 112 SubRepositoryName: "", // 16 bytes 113 SubRepositoryPath: "", // 16 bytes 114 Version: "", // 16 bytes 115 }}, 116 RepoURLs: nil, // 48 bytes 117 LineFragments: nil, // 48 bytes 118 } 119 120 var wantBytes uint64 = 725 121 if sr.SizeBytes() != wantBytes { 122 t.Fatalf("want %d, got %d", wantBytes, sr.SizeBytes()) 123 } 124} 125 126func TestSizeBytesChunkMatches(t *testing.T) { 127 cm := ChunkMatch{ 128 Content: []byte("foo"), // 24 + 3 bytes 129 ContentStart: Location{}, // 12 bytes 130 FileName: false, // 1 byte 131 Ranges: []Range{{}}, // 24 bytes (slice header) + 24 bytes (content) 132 SymbolInfo: []*Symbol{{}}, // 24 bytes (slice header) + 4 * 16 bytes (string header) + 8 bytes (pointer) 133 Score: 0, // 8 byte 134 DebugScore: "", // 16 bytes (string header) 135 } 136 137 var wantBytes uint64 = 208 138 if cm.sizeBytes() != wantBytes { 139 t.Fatalf("want %d, got %d", wantBytes, cm.sizeBytes()) 140 } 141} 142 143func TestMatchSize(t *testing.T) { 144 cases := []struct { 145 v any 146 size int 147 }{{ 148 v: FileMatch{}, 149 size: 256, 150 }, { 151 v: ChunkMatch{}, 152 size: 120, 153 }} 154 for _, c := range cases { 155 got := reflect.TypeOf(c.v).Size() 156 if int(got) != c.size { 157 t.Errorf(`sizeof struct %T has changed from %d to %d. 158These are match structs that occur a lot in memory, so we optimize size. 159When changing, please ensure there isn't unnecessary padding via the 160tool fieldalignment then update this test.`, c.v, c.size, got) 161 } 162 } 163} 164 165func TestSearchOptions_String(t *testing.T) { 166 // To make sure we don't forget to update the string implementation we use 167 // reflection to generate a SearchOptions with every field being non 168 // default. We then check that the field name is present in the output. 169 opts := SearchOptions{} 170 var fieldNames []string 171 rv := reflect.ValueOf(&opts).Elem() 172 for i := 0; i < rv.NumField(); i++ { 173 f := rv.Field(i) 174 name := rv.Type().Field(i).Name 175 fieldNames = append(fieldNames, name) 176 switch f.Kind() { 177 case reflect.Bool: 178 f.SetBool(true) 179 case reflect.Int: 180 f.SetInt(1) 181 case reflect.Int64: 182 f.SetInt(1) 183 case reflect.Float64: 184 f.SetFloat(1) 185 case reflect.Map: 186 // Only map is SpanContext 187 f.Set(reflect.ValueOf(map[string]string{"key": "value"})) 188 default: 189 t.Fatalf("add support for %s field (%s)", f.Kind(), name) 190 } 191 } 192 193 s := opts.String() 194 for _, name := range fieldNames { 195 found, err := regexp.MatchString("\\b"+regexp.QuoteMeta(name)+"\\b", s) 196 if err != nil { 197 t.Fatal(err) 198 } 199 if !found { 200 t.Errorf("could not find field %q in string output of SearchOptions:\n%s", name, s) 201 } 202 } 203 204 webDefaults := SearchOptions{ 205 MaxWallTime: 10 * time.Second, 206 } 207 webDefaults.SetDefaults() 208 209 // Now we hand craft a few corner and common cases 210 cases := []struct { 211 Opts SearchOptions 212 Want string 213 }{{ 214 // Empty 215 Opts: SearchOptions{}, 216 Want: "zoekt.SearchOptions{ }", 217 }, { 218 // healthz options 219 Opts: SearchOptions{ShardMaxMatchCount: 1, TotalMaxMatchCount: 1, MaxDocDisplayCount: 1}, 220 Want: "zoekt.SearchOptions{ ShardMaxMatchCount=1 TotalMaxMatchCount=1 MaxDocDisplayCount=1 }", 221 }, { 222 // zoekt-webserver defaults 223 Opts: webDefaults, 224 Want: "zoekt.SearchOptions{ ShardMaxMatchCount=100000 TotalMaxMatchCount=1000000 MaxWallTime=10s }", 225 }} 226 227 for _, tc := range cases { 228 got := tc.Opts.String() 229 if got != tc.Want { 230 t.Errorf("unexpected String for %#v:\ngot: %s\nwant: %s", tc.Opts, got, tc.Want) 231 } 232 } 233} 234 235func TestRepositoryMergeMutable(t *testing.T) { 236 a := Repository{ 237 ID: 0, 238 Name: "name", 239 Branches: []RepositoryBranch{ 240 { 241 Name: "branchName", 242 Version: "branchVersion", 243 }, 244 }, 245 RawConfig: nil, 246 URL: "url", 247 CommitURLTemplate: "commitUrlTemplate", 248 FileURLTemplate: "fileUrlTemplate", 249 LineFragmentTemplate: "lineFragmentTemplate", 250 } 251 252 t.Run("different ID", func(t *testing.T) { 253 b := a 254 b.ID = 1 255 mutated, err := a.MergeMutable(&b) 256 if err == nil { 257 t.Fatalf("want err, got mutated=%t", mutated) 258 } 259 }) 260 t.Run("different Name", func(t *testing.T) { 261 b := a 262 b.Name = "otherName" 263 mutated, err := a.MergeMutable(&b) 264 if err == nil { 265 t.Fatalf("want err, got mutated=%t", mutated) 266 } 267 }) 268 t.Run("different Branches", func(t *testing.T) { 269 b := a 270 b.Branches = []RepositoryBranch{ 271 { 272 Name: "otherBranchName", 273 Version: "branchVersion", 274 }, 275 } 276 mutated, err := a.MergeMutable(&b) 277 if err == nil { 278 t.Fatalf("want err, got mutated=%t", mutated) 279 } 280 }) 281 t.Run("different RawConfig", func(t *testing.T) { 282 b := a 283 b.RawConfig = map[string]string{"foo": "bar"} 284 mutated, err := a.MergeMutable(&b) 285 if err != nil { 286 t.Fatalf("got err %v", err) 287 } 288 if !mutated { 289 t.Fatalf("want mutated=true, got false") 290 } 291 if !reflect.DeepEqual(a.RawConfig, b.RawConfig) { 292 t.Fatalf("got different RawConfig, %v vs %v", a.RawConfig, b.RawConfig) 293 } 294 }) 295 t.Run("different URL", func(t *testing.T) { 296 b := a 297 b.URL = "otherURL" 298 mutated, err := a.MergeMutable(&b) 299 if err != nil { 300 t.Fatalf("got err %v", err) 301 } 302 if !mutated { 303 t.Fatalf("want mutated=true, got false") 304 } 305 if a.URL != b.URL { 306 t.Fatalf("got different URL, %s vs %s", a.URL, b.URL) 307 } 308 }) 309 t.Run("different CommitURLTemplate", func(t *testing.T) { 310 b := a 311 b.CommitURLTemplate = "otherCommitUrlTemplate" 312 mutated, err := a.MergeMutable(&b) 313 if err != nil { 314 t.Fatalf("got err %v", err) 315 } 316 if !mutated { 317 t.Fatalf("want mutated=true, got false") 318 } 319 if a.CommitURLTemplate != b.CommitURLTemplate { 320 t.Fatalf("got different CommitURLTemplate, %s vs %s", a.CommitURLTemplate, b.CommitURLTemplate) 321 } 322 }) 323 t.Run("different FileURLTemplate", func(t *testing.T) { 324 b := a 325 b.FileURLTemplate = "otherFileUrlTemplate" 326 mutated, err := a.MergeMutable(&b) 327 if err != nil { 328 t.Fatalf("got err %v", err) 329 } 330 if !mutated { 331 t.Fatalf("want mutated=true, got false") 332 } 333 if a.FileURLTemplate != b.FileURLTemplate { 334 t.Fatalf("got different FileURLTemplate, %s vs %s", a.FileURLTemplate, b.FileURLTemplate) 335 } 336 }) 337 t.Run("different LineFragmentTemplate", func(t *testing.T) { 338 b := a 339 b.LineFragmentTemplate = "otherLineFragmentTemplate" 340 mutated, err := a.MergeMutable(&b) 341 if err != nil { 342 t.Fatalf("got err %v", err) 343 } 344 if !mutated { 345 t.Fatalf("want mutated=true, got false") 346 } 347 if a.LineFragmentTemplate != b.LineFragmentTemplate { 348 t.Fatalf("got different LineFragmentTemplate, %s vs %s", a.LineFragmentTemplate, b.LineFragmentTemplate) 349 } 350 }) 351 t.Run("all same", func(t *testing.T) { 352 b := a 353 mutated, err := a.MergeMutable(&b) 354 if err != nil { 355 t.Fatalf("got err %v", err) 356 } 357 if mutated { 358 t.Fatalf("want mutated=false, got true") 359 } 360 if !reflect.DeepEqual(a, b) { 361 t.Fatalf("got different Repository, %v vs %v", a, b) 362 } 363 }) 364} 365 366func TestMonthsSince1970(t *testing.T) { 367 tests := []struct { 368 name string 369 input time.Time 370 expected uint16 371 }{ 372 {"Before 1970", time.Date(1950, 12, 31, 0, 0, 0, 0, time.UTC), 0}, 373 {"Unix 0", time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC), 0}, 374 {"Feb 1970", time.Date(1970, 2, 1, 0, 0, 0, 0, time.UTC), 1}, 375 {"Year 1989", time.Date(1989, 12, 13, 0, 0, 0, 0, time.UTC), 239}, 376 {"Sep 2024", time.Date(2024, 9, 20, 0, 0, 0, 0, time.UTC), 656}, 377 {"Oct 2024", time.Date(2024, 10, 20, 0, 0, 0, 0, time.UTC), 657}, 378 {"Apr 7431", time.Date(7431, 4, 1, 0, 0, 0, 0, time.UTC), 65535}, 379 {"9999", time.Date(9999, 0, 0, 0, 0, 0, 0, time.UTC), 65535}, 380 } 381 382 for _, tt := range tests { 383 t.Run(tt.name, func(t *testing.T) { 384 result := monthsSince1970(tt.input) 385 if result != tt.expected { 386 t.Errorf("expected %d, got %d", tt.expected, result) 387 } 388 }) 389 } 390}