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 var 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: 112, 153 }, { 154 v: candidateMatch{}, 155 size: 72, 156 }, { 157 v: candidateChunk{}, 158 size: 40, 159 }} 160 for _, c := range cases { 161 got := reflect.TypeOf(c.v).Size() 162 if int(got) != c.size { 163 t.Errorf(`sizeof struct %T has changed from %d to %d. 164These are match structs that occur a lot in memory, so we optimize size. 165When changing, please ensure there isn't unnecessary padding via the 166tool fieldalignment then update this test.`, c.v, c.size, got) 167 } 168 } 169} 170 171func TestSearchOptions_String(t *testing.T) { 172 // To make sure we don't forget to update the string implementation we use 173 // reflection to generate a SearchOptions with every field being non 174 // default. We then check that the field name is present in the output. 175 opts := SearchOptions{} 176 var fieldNames []string 177 rv := reflect.ValueOf(&opts).Elem() 178 for i := 0; i < rv.NumField(); i++ { 179 f := rv.Field(i) 180 name := rv.Type().Field(i).Name 181 fieldNames = append(fieldNames, name) 182 switch f.Kind() { 183 case reflect.Bool: 184 f.SetBool(true) 185 case reflect.Int: 186 f.SetInt(1) 187 case reflect.Int64: 188 f.SetInt(1) 189 case reflect.Float64: 190 f.SetFloat(1) 191 case reflect.Map: 192 // Only map is SpanContext 193 f.Set(reflect.ValueOf(map[string]string{"key": "value"})) 194 default: 195 t.Fatalf("add support for %s field (%s)", f.Kind(), name) 196 } 197 } 198 199 s := opts.String() 200 for _, name := range fieldNames { 201 found, err := regexp.MatchString("\\b"+regexp.QuoteMeta(name)+"\\b", s) 202 if err != nil { 203 t.Fatal(err) 204 } 205 if !found { 206 t.Errorf("could not find field %q in string output of SearchOptions:\n%s", name, s) 207 } 208 } 209 210 webDefaults := SearchOptions{ 211 MaxWallTime: 10 * time.Second, 212 } 213 webDefaults.SetDefaults() 214 215 // Now we hand craft a few corner and common cases 216 cases := []struct { 217 Opts SearchOptions 218 Want string 219 }{{ 220 // Empty 221 Opts: SearchOptions{}, 222 Want: "zoekt.SearchOptions{ }", 223 }, { 224 // healthz options 225 Opts: SearchOptions{ShardMaxMatchCount: 1, TotalMaxMatchCount: 1, MaxDocDisplayCount: 1}, 226 Want: "zoekt.SearchOptions{ ShardMaxMatchCount=1 TotalMaxMatchCount=1 MaxDocDisplayCount=1 }", 227 }, { 228 // zoekt-webserver defaults 229 Opts: webDefaults, 230 Want: "zoekt.SearchOptions{ ShardMaxMatchCount=100000 TotalMaxMatchCount=1000000 MaxWallTime=10s }", 231 }} 232 233 for _, tc := range cases { 234 got := tc.Opts.String() 235 if got != tc.Want { 236 t.Errorf("unexpected String for %#v:\ngot: %s\nwant: %s", tc.Opts, got, tc.Want) 237 } 238 } 239}