blob: 84bb2425b5759d2ec3420ef0861b7e5b229534d9 [file] [log] [blame]
package search
import (
"bytes"
"context"
"encoding/json"
"math/rand"
"os"
"sort"
"testing"
"time"
assert "github.com/stretchr/testify/require"
"go.skia.org/infra/go/eventbus"
"go.skia.org/infra/go/gcs"
"go.skia.org/infra/go/tiling"
"go.skia.org/infra/go/util"
"go.skia.org/infra/golden/go/expstorage"
"go.skia.org/infra/golden/go/indexer"
"go.skia.org/infra/golden/go/mocks"
"go.skia.org/infra/golden/go/serialize"
"go.skia.org/infra/golden/go/storage"
"go.skia.org/infra/golden/go/types"
)
func checkQuery(t assert.TestingT, api *SearchAPI, idx *indexer.SearchIndex, qStr string, exp types.Expectations, buf *bytes.Buffer) int {
q := &Query{}
// We ignore incorrect queries. They are tested somewhere else.
err := clearParseQuery(q, qStr)
if err != nil {
return 0
}
tile := idx.GetTile(q.IncludeIgnores)
// TODO(stephana): Remove the lines below to also exercise the search for
// issues. This requires to refresh the set of input queries.
// Ignore queries for gerrit issues right now.
if q.Issue > 0 {
return 0
}
// Ignore queries with blames since they are ephemeral.
if q.BlameGroupID != "" {
return 0
}
// Addjust the old default value for MaxRGBA
if q.FRGBAMax < 0 {
q.FRGBAMax = 255
}
resp, err := api.Search(context.Background(), q)
assert.NoError(t, err)
// Serialize the response to json.
buf.Reset()
assert.NoError(t, json.NewEncoder(buf).Encode(resp))
expDigests := getTargetDigests(t, q, tile, exp)
foundDigests := util.StringSet{}
for _, digestRec := range resp.Digests {
foundDigests[digestRec.Digest] = true
}
set1 := expDigests.Keys()
set2 := foundDigests.Keys()
sort.Strings(set1)
sort.Strings(set2)
assert.Equal(t, set1, set2)
return 1
}
func getTargetDigests(t assert.TestingT, q *Query, tile *tiling.Tile, exp types.Expectations) util.StringSet {
// Account for a given commit range.
startIdx := 0
endIdx := tile.LastCommitIndex()
if q.FCommitBegin != "" {
startIdx, _ = tiling.FindCommit(tile.Commits, q.FCommitBegin)
assert.True(t, startIdx >= 0)
}
if q.FCommitEnd != "" {
endIdx, _ = tiling.FindCommit(tile.Commits, q.FCommitEnd)
assert.True(t, endIdx >= 0)
}
assert.True(t, startIdx <= endIdx)
digestSet := util.StringSet{}
for _, trace := range tile.Traces {
gTrace := trace.(*types.GoldenTrace)
digestSet.AddLists(gTrace.Values)
}
allDigests := map[string]int{}
for idx, digest := range digestSet.Keys() {
allDigests[digest] = idx
}
result := util.StringSet{}
lastIdx := endIdx - startIdx
for _, trace := range tile.Traces {
if tiling.Matches(trace, q.Query) {
gTrace := trace.(*types.GoldenTrace)
vals := gTrace.Values[startIdx : endIdx+1]
p := gTrace.Params_
test := p[types.PRIMARY_KEY_FIELD]
relevantDigests := []string(nil)
if q.Head {
idx := lastIdx
for (idx >= 0) && (vals[idx] == types.MISSING_DIGEST) {
idx--
}
if idx >= 0 {
relevantDigests = []string{vals[idx]}
}
} else {
relevantDigests = vals
}
for _, digest := range relevantDigests {
if !q.excludeClassification(exp.Classification(test, digest)) {
result[digest] = true
}
}
}
}
delete(result, types.MISSING_DIGEST)
return result
}
func getStoragesIndexTile(t *testing.T, bucket, storagePath, outputPath string, randomize bool) (*storage.Storage, *indexer.SearchIndex, *tiling.Tile, *indexer.Indexer) {
err := gcs.DownloadTestDataFile(t, bucket, storagePath, outputPath)
assert.NoError(t, err, "Unable to download testdata.")
return getStoragesAndIndexerFromTile(t, outputPath, randomize)
}
func getStoragesAndIndexerFromTile(t assert.TestingT, path string, randomize bool) (*storage.Storage, *indexer.SearchIndex, *tiling.Tile, *indexer.Indexer) {
sample := loadSample(t, path, randomize)
tileBuilder := mocks.NewMockTileBuilderFromTile(t, sample.Tile)
eventBus := eventbus.New()
expStore := expstorage.NewMemExpectationsStore(eventBus)
err := expStore.AddChange(sample.Expectations.TestExp(), "testuser")
assert.NoError(t, err)
storages := &storage.Storage{
ExpectationsStore: expStore,
MasterTileBuilder: tileBuilder,
DigestStore: &mocks.MockDigestStore{
FirstSeen: time.Now().Unix(),
OkValue: true,
},
DiffStore: mocks.NewMockDiffStore(),
EventBus: eventBus,
}
assert.NoError(t, storages.InitBaseliner())
ixr, err := indexer.New(storages, 10*time.Minute)
assert.NoError(t, err)
idx := ixr.GetIndex()
tile := idx.GetTile(false)
return storages, idx, tile, ixr
}
func loadSample(t assert.TestingT, fileName string, randomize bool) *serialize.Sample {
file, err := os.Open(fileName)
assert.NoError(t, err)
sample, err := serialize.DeserializeSample(file)
assert.NoError(t, err)
if randomize {
sample.Tile = randomizeTile(sample.Tile, sample.Expectations)
}
return sample
}
func randomizeTile(tile *tiling.Tile, exp types.Expectations) *tiling.Tile {
allDigestSet := util.StringSet{}
testExp := exp.TestExp()
for _, digests := range testExp {
for d := range digests {
allDigestSet[d] = true
}
}
allDigests := allDigestSet.Keys()
tileLen := tile.LastCommitIndex() + 1
ret := tile.Copy()
for _, trace := range tile.Traces {
gTrace := trace.(*types.GoldenTrace)
for i := 0; i < tileLen; i++ {
gTrace.Values[i] = allDigests[int(rand.Uint32())%len(allDigests)]
}
}
return ret
}