[gold] Add more tests for more complex queries.
Bug: skia:10582
Change-Id: Id49e4440823745bf3a782106d046d9d5f35ee61f
Reviewed-on: https://skia-review.googlesource.com/c/buildbot/+/401776
Reviewed-by: Leandro Lovisolo <lovisolo@google.com>
diff --git a/golden/go/search/query/query.go b/golden/go/search/query/query.go
index a72d336..4862f05 100644
--- a/golden/go/search/query/query.go
+++ b/golden/go/search/query/query.go
@@ -49,16 +49,16 @@
q.TraceValues = validate.QueryFormValue(r, "query")
q.RightTraceValues = validate.QueryFormValue(r, "rquery")
- q.Limit = int32(validate.Int64FormValue(r, "limit", 50))
- q.Offset = int32(validate.Int64FormValue(r, "offset", 0))
- q.Offset = util.MaxInt32(q.Offset, 0)
+ q.Limit = int(validate.Int64FormValue(r, "limit", 50))
+ q.Offset = int(validate.Int64FormValue(r, "offset", 0))
+ q.Offset = util.MaxInt(q.Offset, 0)
validate.StrFormValue(r, "metric", &q.Metric, []string{CombinedMetric, PercentMetric, PixelMetric}, CombinedMetric)
validate.StrFormValue(r, "sort", &q.Sort, []string{SortDescending, SortAscending}, SortDescending)
// Parse and validate the filter values.
- q.RGBAMinFilter = int32(validate.Int64FormValue(r, "frgbamin", 0))
- q.RGBAMaxFilter = int32(validate.Int64FormValue(r, "frgbamax", 255))
+ q.RGBAMinFilter = int(validate.Int64FormValue(r, "frgbamin", 0))
+ q.RGBAMaxFilter = int(validate.Int64FormValue(r, "frgbamax", 255))
// Parse out the issue and patchsets.
q.Patchsets = validate.Int64SliceFormValue(r, "patchsets", nil)
diff --git a/golden/go/search/query/types.go b/golden/go/search/query/types.go
index 4fe8ba2..4df2cdc 100644
--- a/golden/go/search/query/types.go
+++ b/golden/go/search/query/types.go
@@ -43,13 +43,13 @@
IncludeDigestsProducedOnMaster bool `json:"master"`
// Filtering.
- RGBAMinFilter int32 `json:"frgbamin"` // Min RGBA delta
- RGBAMaxFilter int32 `json:"frgbamax"` // Max RGBA delta
- MustIncludeReferenceFilter bool `json:"fref"` // Only digests with reference.
+ RGBAMinFilter int `json:"frgbamin"` // Min RGBA delta
+ RGBAMaxFilter int `json:"frgbamax"` // Max RGBA delta
+ MustIncludeReferenceFilter bool `json:"fref"` // Only digests with reference.
// Pagination.
- Offset int32 `json:"offset"`
- Limit int32 `json:"limit"`
+ Offset int `json:"offset"`
+ Limit int `json:"limit"`
}
// IgnoreState returns the types.IgnoreState that this
diff --git a/golden/go/search/search.go b/golden/go/search/search.go
index 69d6e11..2a96c5f 100644
--- a/golden/go/search/search.go
+++ b/golden/go/search/search.go
@@ -187,7 +187,7 @@
// Sort the digests and fill the ones that are going to be displayed with
// additional data.
- displayRet, offset := s.sortAndLimitDigests(ctx, q, results, int(q.Offset), int(q.Limit))
+ displayRet, offset := s.sortAndLimitDigests(ctx, q, results, q.Offset, q.Limit)
s.addTriageHistory(ctx, s.makeTriageHistoryGetter(q.CodeReviewSystemID, q.ChangelistID), displayRet)
traceComments := s.getTraceComments(ctx)
prepareTraceGroups(displayRet, exp, traceComments, isChangelistSearch)
@@ -791,7 +791,7 @@
continue
}
- rgbaMaxDiff := int32(util.MaxInt(ref.MaxRGBADiffs[:]...))
+ rgbaMaxDiff := util.MaxInt(ref.MaxRGBADiffs[:]...)
if (rgbaMaxDiff < q.RGBAMinFilter) || (rgbaMaxDiff > q.RGBAMaxFilter) {
continue
}
diff --git a/golden/go/search2/search2.go b/golden/go/search2/search2.go
index a6c59f8..c6468b7 100644
--- a/golden/go/search2/search2.go
+++ b/golden/go/search2/search2.go
@@ -3,6 +3,7 @@
package search2
import (
+ "bytes"
"context"
"encoding/hex"
"sort"
@@ -395,6 +396,7 @@
ctx, span := trace.StartSpan(ctx, "search2_Search")
defer span.End()
+ ctx = context.WithValue(ctx, queryKey, *q)
ctx, err := s.addCommitsData(ctx)
if err != nil {
return nil, skerr.Wrap(err)
@@ -405,13 +407,13 @@
}
// Find all digests and traces that match the given search criteria.
- traceDigests, err := s.getMatchingDigestsAndTraces(ctx, q)
+ traceDigests, err := s.getMatchingDigestsAndTraces(ctx)
if err != nil {
return nil, skerr.Wrap(err)
}
// Lookup the closest diffs to the given digests. This returns a subset according to the
// limit and offset in the query.
- closestDiffs, allClosestLabels, err := s.getClosestDiffs(ctx, traceDigests, q)
+ closestDiffs, allClosestLabels, err := s.getClosestDiffs(ctx, traceDigests)
if err != nil {
return nil, skerr.Wrap(err)
}
@@ -441,7 +443,7 @@
return &frontend.SearchResponse{
Results: results,
- Offset: int(q.Offset),
+ Offset: q.Offset,
Size: len(allClosestLabels),
BulkTriageData: bulkTriageData,
Commits: commits,
@@ -453,26 +455,31 @@
type searchContextKey string
const (
- actualWindowLength = searchContextKey("actualWindowLength")
- commitToIdx = searchContextKey("commitToIdx")
- firstCommitID = searchContextKey("firstCommitID")
- firstTileID = searchContextKey("firstTileID")
+ actualWindowLengthKey = searchContextKey("actualWindowLengthKey")
+ commitToIdxKey = searchContextKey("commitToIdxKey")
+ firstCommitIDKey = searchContextKey("firstCommitIDKey")
+ firstTileIDKey = searchContextKey("firstTileIDKey")
+ queryKey = searchContextKey("query")
)
func getFirstCommitID(ctx context.Context) schema.CommitID {
- return ctx.Value(firstCommitID).(schema.CommitID)
+ return ctx.Value(firstCommitIDKey).(schema.CommitID)
}
func getFirstTileID(ctx context.Context) schema.TileID {
- return ctx.Value(firstTileID).(schema.TileID)
+ return ctx.Value(firstTileIDKey).(schema.TileID)
}
func getCommitToIdxMap(ctx context.Context) map[schema.CommitID]int {
- return ctx.Value(commitToIdx).(map[schema.CommitID]int)
+ return ctx.Value(commitToIdxKey).(map[schema.CommitID]int)
}
func getActualWindowLength(ctx context.Context) int {
- return ctx.Value(actualWindowLength).(int)
+ return ctx.Value(actualWindowLengthKey).(int)
+}
+
+func getQuery(ctx context.Context) query.Search {
+ return ctx.Value(queryKey).(query.Search)
}
// addCommitsData finds the current sliding window of data (The last N commits) and adds the
@@ -501,16 +508,16 @@
return nil, skerr.Fmt("No commits with data")
}
// ids is ordered most recent commit to last commit at this point
- ctx = context.WithValue(ctx, actualWindowLength, len(ids))
- ctx = context.WithValue(ctx, firstCommitID, ids[len(ids)-1])
- ctx = context.WithValue(ctx, firstTileID, firstObservedTile)
+ ctx = context.WithValue(ctx, actualWindowLengthKey, len(ids))
+ ctx = context.WithValue(ctx, firstCommitIDKey, ids[len(ids)-1])
+ ctx = context.WithValue(ctx, firstTileIDKey, firstObservedTile)
idToIndex := map[schema.CommitID]int{}
idx := 0
for i := len(ids) - 1; i >= 0; i-- {
idToIndex[ids[i]] = idx
idx++
}
- ctx = context.WithValue(ctx, commitToIdx, idToIndex)
+ ctx = context.WithValue(ctx, commitToIdxKey, idToIndex)
return ctx, nil
}
@@ -522,7 +529,7 @@
// getMatchingDigestsAndTraces returns the tuples of digest+traceID that match the given query.
// The order of the result is abitrary.
-func (s *Impl) getMatchingDigestsAndTraces(ctx context.Context, q *query.Search) ([]stageOneResult, error) {
+func (s *Impl) getMatchingDigestsAndTraces(ctx context.Context) ([]stageOneResult, error) {
ctx, span := trace.StartSpan(ctx, "getMatchingDigestsAndTraces")
defer span.End()
const statement = `WITH
@@ -542,6 +549,7 @@
NonIgnoredTraces ON UntriagedDigests.grouping_id = NonIgnoredTraces.grouping_id AND
UntriagedDigests.digest = NonIgnoredTraces.digest`
+ q := getQuery(ctx)
var triageStatuses []schema.ExpectationLabel
if q.IncludeUntriagedDigests {
triageStatuses = append(triageStatuses, schema.LabelUntriaged)
@@ -589,7 +597,7 @@
// input. We are able to batch the queries by grouping and do so for better performance.
// While this returns a subset of data as defined by the query, it also returns sufficient
// information to bulk-triage all of the inputs.
-func (s *Impl) getClosestDiffs(ctx context.Context, inputs []stageOneResult, q *query.Search) ([]stageTwoResult, map[groupingDigestKey]expectations.Label, error) {
+func (s *Impl) getClosestDiffs(ctx context.Context, inputs []stageOneResult) ([]stageTwoResult, map[groupingDigestKey]expectations.Label, error) {
ctx, span := trace.StartSpan(ctx, "getClosestDiffs")
defer span.End()
byGrouping := map[schema.MD5Hash][]stageOneResult{}
@@ -605,8 +613,6 @@
byDigest[dID] = bd
}
- bulkTriageData := map[groupingDigestKey]expectations.Label{}
-
for groupingID, inputs := range byGrouping {
const statement = `WITH
ObservedDigestsInTile AS (
@@ -620,7 +626,7 @@
ComparisonBetweenUntriagedAndObserved AS (
SELECT DiffMetrics.* FROM DiffMetrics
JOIN ObservedDigestsInTile ON DiffMetrics.right_digest = ObservedDigestsInTile.digest
- WHERE left_digest = ANY($1) AND max_channel_diff >= $4 AND max_channel_diff <= $5
+ WHERE left_digest = ANY($1)
)
-- This will return the right_digest with the smallest combined_metric for each left_digest + label
SELECT DISTINCT ON (left_digest, label)
@@ -636,7 +642,7 @@
for _, input := range inputs {
digests = append(digests, input.digest)
}
- rows, err := s.db.Query(ctx, statement, digests, groupingID[:], getFirstTileID(ctx), q.RGBAMinFilter, q.RGBAMaxFilter)
+ rows, err := s.db.Query(ctx, statement, digests, groupingID[:], getFirstTileID(ctx))
if err != nil {
return nil, nil, skerr.Wrap(err)
}
@@ -670,36 +676,65 @@
if stageTwo.closestNegative != nil && stageTwo.closestPositive != nil {
if stageTwo.closestPositive.CombinedMetric < stageTwo.closestNegative.CombinedMetric {
stageTwo.closestDigest = stageTwo.closestPositive
- bulkTriageData[groupingDigestKey{groupingID: groupingID, digest: leftDigest}] = expectations.Positive
} else {
stageTwo.closestDigest = stageTwo.closestNegative
- bulkTriageData[groupingDigestKey{groupingID: groupingID, digest: leftDigest}] = expectations.Negative
}
} else {
// there is only one type of diff, so it defaults to the closest.
stageTwo.closestDigest = srdd
- bulkTriageData[groupingDigestKey{groupingID: groupingID, digest: leftDigest}] = label.ToExpectation()
}
byDigest[leftDigest] = stageTwo
}
rows.Close()
}
- rv := make([]stageTwoResult, 0, len(byDigest))
+ q := getQuery(ctx)
+ bulkTriageData := map[groupingDigestKey]expectations.Label{}
+ results := make([]stageTwoResult, 0, len(byDigest))
for _, s2 := range byDigest {
- rv = append(rv, s2)
+ // Filter out any results without a closest triaged digest (if that option is selected).
+ if q.MustIncludeReferenceFilter && s2.closestDigest == nil {
+ continue
+ }
+ if s2.closestDigest != nil {
+ // Apply RGBA Filter here - if the closest digest isn't within range, we remove it.
+ maxDiff := util.MaxInt(s2.closestDigest.MaxRGBADiffs[:]...)
+ if maxDiff < q.RGBAMinFilter || maxDiff > q.RGBAMaxFilter {
+ continue
+ }
+ closestLabel := s2.closestDigest.Status
+ key := groupingDigestKey{groupingID: sql.AsMD5Hash(s2.groupingID), digest: sql.AsMD5Hash(s2.leftDigest)}
+ bulkTriageData[key] = closestLabel
+ }
+ results = append(results, s2)
+
}
- sort.Slice(rv, func(i, j int) bool {
- if rv[i].closestDigest == nil {
+ if q.Offset >= len(results) {
+ return nil, bulkTriageData, nil
+ }
+ sortAsc := q.Sort == query.SortAscending
+ sort.Slice(results, func(i, j int) bool {
+ if results[i].closestDigest == nil {
return true // sort results with no reference image to the top
}
- if rv[j].closestDigest == nil {
+ if results[j].closestDigest == nil {
return false
}
- return rv[i].closestDigest.CombinedMetric >= rv[j].closestDigest.CombinedMetric
+ if results[i].closestDigest.CombinedMetric == results[j].closestDigest.CombinedMetric {
+ // Tiebreak using digest in ascending order.
+ return bytes.Compare(results[i].leftDigest, results[j].leftDigest) < 0
+ }
+ if sortAsc {
+ return results[i].closestDigest.CombinedMetric < results[j].closestDigest.CombinedMetric
+ }
+ return results[i].closestDigest.CombinedMetric > results[j].closestDigest.CombinedMetric
})
- // TODO(kjlubick) use query.limit and offset
- return rv, bulkTriageData, nil
+
+ if q.Limit <= 0 {
+ return results, bulkTriageData, nil
+ }
+ end := util.MinInt(len(results), q.Offset+q.Limit)
+ return results[q.Offset:end], bulkTriageData, nil
}
// getParamsetsForDigests fetches all the traces that produced the digests in the data and
@@ -708,12 +743,34 @@
func (s *Impl) getParamsetsForDigests(ctx context.Context, inputs []stageTwoResult) (map[types.Digest]paramtools.ParamSet, error) {
ctx, span := trace.StartSpan(ctx, "getParamsetsForDigests")
defer span.End()
- var digests []schema.DigestBytes
+ var leftDigests []schema.DigestBytes
+ var rightDigests []schema.DigestBytes
for _, input := range inputs {
- digests = append(digests, input.leftDigest)
- digests = append(digests, input.rightDigests...)
+ leftDigests = append(leftDigests, input.leftDigest)
+ rightDigests = append(rightDigests, input.rightDigests...)
}
- span.AddAttributes(trace.Int64Attribute("num_digests", int64(len(digests))))
+ span.AddAttributes(trace.Int64Attribute("num_digests", int64(len(leftDigests)+len(rightDigests))))
+ digestToTraces, err := s.getLeftParamsets(ctx, leftDigests)
+ if err != nil {
+ return nil, skerr.Wrap(err)
+ }
+ if err := s.addRightParamsets(ctx, digestToTraces, rightDigests); err != nil {
+ return nil, skerr.Wrap(err)
+ }
+ rv, err := s.expandTracesIntoParamsets(ctx, digestToTraces)
+ if err != nil {
+ return nil, skerr.Wrap(err)
+ }
+ return rv, nil
+}
+
+// getLeftParamsets finds the traces that draw the given digests. It will respect the ignore rules
+// setting.
+func (s *Impl) getLeftParamsets(ctx context.Context, digests []schema.DigestBytes) (map[types.Digest][]schema.TraceID, error) {
+ ctx, span := trace.StartSpan(ctx, "getLeftParamsets")
+ defer span.End()
+ span.AddAttributes(trace.Int64Attribute("num_left_digests", int64(len(digests))))
+
const statement = `WITH
DigestsAndTraces AS (
SELECT DISTINCT encode(digest, 'hex') as digest, trace_id
@@ -722,9 +779,14 @@
SELECT DigestsAndTraces.digest, DigestsAndTraces.trace_id
FROM DigestsAndTraces
JOIN Traces ON DigestsAndTraces.trace_id = Traces.trace_id
-WHERE matches_any_ignore_rule = FALSE
+WHERE matches_any_ignore_rule = ANY($3)
`
- rows, err := s.db.Query(ctx, statement, digests, getFirstTileID(ctx))
+ q := getQuery(ctx)
+ ignoreStatues := []bool{false}
+ if q.IncludeIgnoredTraces {
+ ignoreStatues = append(ignoreStatues, true)
+ }
+ rows, err := s.db.Query(ctx, statement, digests, getFirstTileID(ctx), ignoreStatues)
if err != nil {
return nil, skerr.Wrap(err)
}
@@ -738,12 +800,33 @@
}
digestToTraces[digest] = append(digestToTraces[digest], traceID)
}
+ return digestToTraces, nil
+}
- rv, err := s.expandTracesIntoParamsets(ctx, digestToTraces)
+// addRightParamsets finds the traces that draw the given digests. We do not need to consider the
+// ignore rules because those only apply to the search results (that is, the left side).
+func (s *Impl) addRightParamsets(ctx context.Context, digestToTraces map[types.Digest][]schema.TraceID, digests []schema.DigestBytes) error {
+ ctx, span := trace.StartSpan(ctx, "getLeftParamsets")
+ defer span.End()
+ span.AddAttributes(trace.Int64Attribute("num_right_digests", int64(len(digests))))
+
+ const statement = `SELECT DISTINCT encode(digest, 'hex') as digest, trace_id
+FROM TiledTraceDigests WHERE digest = ANY($1) AND tile_id >= $2
+`
+ rows, err := s.db.Query(ctx, statement, digests, getFirstTileID(ctx))
if err != nil {
- return nil, skerr.Wrap(err)
+ return skerr.Wrap(err)
}
- return rv, nil
+ defer rows.Close()
+ for rows.Next() {
+ var digest types.Digest
+ var traceID schema.TraceID
+ if err := rows.Scan(&digest, &traceID); err != nil {
+ return skerr.Wrap(err)
+ }
+ digestToTraces[digest] = append(digestToTraces[digest], traceID)
+ }
+ return nil
}
// expandTracesIntoParamsets effectively returns a map detailing "who drew a given digest?". This
@@ -810,7 +893,7 @@
const statement = `SELECT keys FROM Traces WHERE trace_id = $1`
row := s.db.QueryRow(ctx, statement, traceID)
var keys paramtools.Params
- if err := row.Scan(&traceID, &keys); err != nil {
+ if err := row.Scan(&keys); err != nil {
return nil, skerr.Wrap(err)
}
s.traceCache.Add(string(traceID), keys)
diff --git a/golden/go/search2/search2_test.go b/golden/go/search2/search2_test.go
index 6575755..1246611 100644
--- a/golden/go/search2/search2_test.go
+++ b/golden/go/search2/search2_test.go
@@ -518,10 +518,10 @@
IncludePositiveDigests: false,
IncludeNegativeDigests: false,
IncludeUntriagedDigests: true,
- Sort: "desc",
+ Sort: query.SortDescending,
IncludeIgnoredTraces: false,
TraceValues: paramtools.ParamSet{
- types.CorpusField: []string{"round"},
+ types.CorpusField: []string{dks.RoundCorpus},
},
RGBAMinFilter: 0,
RGBAMaxFilter: 255,
@@ -534,7 +534,7 @@
Status: expectations.Untriaged,
ParamSet: paramtools.ParamSet{
dks.ColorModeKey: []string{dks.GreyColorMode, dks.RGBColorMode},
- types.CorpusField: []string{"round"},
+ types.CorpusField: []string{dks.RoundCorpus},
dks.DeviceKey: []string{dks.IPadDevice, dks.IPhoneDevice},
dks.OSKey: []string{dks.IOS}, // Note: Android + Taimen are ignored
types.PrimaryKeyField: []string{dks.CircleTest},
@@ -545,7 +545,7 @@
DigestIndices: []int{-1, 2, -1, -1, 2, -1, -1, 0, -1, -1},
Params: paramtools.Params{
dks.ColorModeKey: dks.GreyColorMode,
- types.CorpusField: "round",
+ types.CorpusField: dks.RoundCorpus,
dks.DeviceKey: dks.IPhoneDevice,
dks.OSKey: dks.IOS,
types.PrimaryKeyField: dks.CircleTest,
@@ -555,7 +555,7 @@
DigestIndices: []int{1, -1, 1, -1, 1, -1, 1, -1, 0, -1},
Params: paramtools.Params{
dks.ColorModeKey: dks.RGBColorMode,
- types.CorpusField: "round",
+ types.CorpusField: dks.RoundCorpus,
dks.DeviceKey: dks.IPhoneDevice,
dks.OSKey: dks.IOS,
types.PrimaryKeyField: dks.CircleTest,
@@ -565,7 +565,7 @@
DigestIndices: []int{1, 1, 1, 1, 1, 1, 1, 0, 0, 0},
Params: paramtools.Params{
dks.ColorModeKey: dks.RGBColorMode,
- types.CorpusField: "round",
+ types.CorpusField: dks.RoundCorpus,
dks.DeviceKey: dks.IPadDevice,
dks.OSKey: dks.IOS,
types.PrimaryKeyField: dks.CircleTest,
@@ -575,7 +575,7 @@
DigestIndices: []int{2, 2, 2, 2, 2, 2, 2, 0, 0, 0},
Params: paramtools.Params{
dks.ColorModeKey: dks.GreyColorMode,
- types.CorpusField: "round",
+ types.CorpusField: dks.RoundCorpus,
dks.DeviceKey: dks.IPadDevice,
dks.OSKey: dks.IOS,
types.PrimaryKeyField: dks.CircleTest,
@@ -597,7 +597,7 @@
Status: expectations.Positive,
ParamSet: paramtools.ParamSet{
dks.ColorModeKey: []string{dks.RGBColorMode},
- types.CorpusField: []string{"round"},
+ types.CorpusField: []string{dks.RoundCorpus},
dks.DeviceKey: []string{dks.QuadroDevice, dks.IPadDevice, dks.IPhoneDevice, dks.WalleyeDevice},
dks.OSKey: []string{dks.AndroidOS, dks.Windows10dot2OS, dks.IOS},
types.PrimaryKeyField: []string{dks.CircleTest},
@@ -612,7 +612,7 @@
Status: expectations.Untriaged,
ParamSet: paramtools.ParamSet{
dks.ColorModeKey: []string{dks.RGBColorMode},
- types.CorpusField: []string{"round"},
+ types.CorpusField: []string{dks.RoundCorpus},
dks.DeviceKey: []string{dks.QuadroDevice},
dks.OSKey: []string{dks.Windows10dot3OS},
types.PrimaryKeyField: []string{dks.CircleTest},
@@ -623,7 +623,7 @@
DigestIndices: []int{-1, -1, -1, 0, 0, 0, 0, 0, 0, 0},
Params: paramtools.Params{
dks.ColorModeKey: dks.RGBColorMode,
- types.CorpusField: "round",
+ types.CorpusField: dks.RoundCorpus,
dks.DeviceKey: dks.QuadroDevice,
dks.OSKey: dks.Windows10dot3OS,
types.PrimaryKeyField: dks.CircleTest,
@@ -643,7 +643,7 @@
Status: expectations.Positive,
ParamSet: paramtools.ParamSet{
dks.ColorModeKey: []string{dks.RGBColorMode},
- types.CorpusField: []string{"round"},
+ types.CorpusField: []string{dks.RoundCorpus},
dks.DeviceKey: []string{dks.QuadroDevice, dks.IPadDevice, dks.IPhoneDevice, dks.WalleyeDevice},
dks.OSKey: []string{dks.AndroidOS, dks.Windows10dot2OS, dks.IOS},
types.PrimaryKeyField: []string{dks.CircleTest},
@@ -658,7 +658,7 @@
Status: expectations.Untriaged,
ParamSet: paramtools.ParamSet{
dks.ColorModeKey: []string{dks.GreyColorMode},
- types.CorpusField: []string{"round"},
+ types.CorpusField: []string{dks.RoundCorpus},
dks.DeviceKey: []string{dks.QuadroDevice},
dks.OSKey: []string{dks.Windows10dot3OS},
types.PrimaryKeyField: []string{dks.CircleTest},
@@ -669,7 +669,7 @@
DigestIndices: []int{-1, -1, -1, 0, 0, 0, 0, 0, 0, -1},
Params: paramtools.Params{
dks.ColorModeKey: dks.GreyColorMode,
- types.CorpusField: "round",
+ types.CorpusField: dks.RoundCorpus,
dks.DeviceKey: dks.QuadroDevice,
dks.OSKey: dks.Windows10dot3OS,
types.PrimaryKeyField: dks.CircleTest,
@@ -689,7 +689,7 @@
Status: expectations.Positive,
ParamSet: paramtools.ParamSet{
dks.ColorModeKey: []string{dks.GreyColorMode},
- types.CorpusField: []string{"round"},
+ types.CorpusField: []string{dks.RoundCorpus},
dks.DeviceKey: []string{dks.QuadroDevice, dks.IPadDevice, dks.IPhoneDevice, dks.WalleyeDevice},
dks.OSKey: []string{dks.AndroidOS, dks.Windows10dot2OS, dks.IOS},
types.PrimaryKeyField: []string{dks.CircleTest},
@@ -712,17 +712,428 @@
}, res)
}
+func TestSearch_IncludeIgnoredAtHead_Success(t *testing.T) {
+ unittest.LargeTest(t)
+
+ ctx := context.Background()
+ db := sqltest.NewCockroachDBForTestsWithProductionSchema(ctx, t)
+ require.NoError(t, sqltest.BulkInsertDataTables(ctx, db, dks.Build()))
+
+ s := New(db, 100)
+ res, err := s.Search(ctx, &query.Search{
+ OnlyIncludeDigestsProducedAtHead: true,
+ IncludePositiveDigests: false,
+ IncludeNegativeDigests: false,
+ IncludeUntriagedDigests: true,
+ Sort: query.SortDescending,
+ IncludeIgnoredTraces: true,
+ TraceValues: paramtools.ParamSet{
+ types.CorpusField: []string{dks.RoundCorpus},
+ },
+ RGBAMinFilter: 0,
+ RGBAMaxFilter: 255,
+ })
+ require.NoError(t, err)
+ require.Len(t, res.Results, 3)
+ assert.Equal(t, frontend.TraceGroup{
+ Traces: []frontend.Trace{{
+ ID: "0b61c8d85467fc95b1306128ceb2ef6d",
+ DigestIndices: []int{-1, 2, -1, -1, 2, -1, -1, 0, -1, -1},
+ Params: paramtools.Params{
+ dks.ColorModeKey: dks.GreyColorMode,
+ types.CorpusField: dks.RoundCorpus,
+ dks.DeviceKey: dks.IPhoneDevice,
+ dks.OSKey: dks.IOS,
+ types.PrimaryKeyField: dks.CircleTest,
+ },
+ }, {
+ ID: "22b530e029c22e396c5a24c0900c9ed5",
+ DigestIndices: []int{1, -1, 1, -1, 1, -1, 1, -1, 0, -1},
+ Params: paramtools.Params{
+ dks.ColorModeKey: dks.RGBColorMode,
+ types.CorpusField: dks.RoundCorpus,
+ dks.DeviceKey: dks.IPhoneDevice,
+ dks.OSKey: dks.IOS,
+ types.PrimaryKeyField: dks.CircleTest,
+ },
+ }, {
+ ID: "273119ca291863331e906fe71bde0e7d",
+ DigestIndices: []int{1, 1, 1, 1, 1, 1, 1, 0, 0, 0},
+ Params: paramtools.Params{
+ dks.ColorModeKey: dks.RGBColorMode,
+ types.CorpusField: dks.RoundCorpus,
+ dks.DeviceKey: dks.IPadDevice,
+ dks.OSKey: dks.IOS,
+ types.PrimaryKeyField: dks.CircleTest,
+ },
+ }, {
+ ID: "3b44c31afc832ef9d1a2d25a5b873152",
+ DigestIndices: []int{2, 2, 2, 2, 2, 2, 2, 0, 0, 0},
+ Params: paramtools.Params{
+ dks.ColorModeKey: dks.GreyColorMode,
+ types.CorpusField: dks.RoundCorpus,
+ dks.DeviceKey: dks.IPadDevice,
+ dks.OSKey: dks.IOS,
+ types.PrimaryKeyField: dks.CircleTest,
+ },
+ }, {
+ // This trace matches an ignore rule, but should be visible due to the search
+ // terms
+ ID: "902ac9eee937cd11b4ccc81d535ff33f",
+ DigestIndices: []int{-1, -1, -1, -1, -1, -1, 0, 0, 0, 0},
+ Params: paramtools.Params{
+ dks.ColorModeKey: dks.RGBColorMode,
+ types.CorpusField: dks.RoundCorpus,
+ dks.DeviceKey: dks.TaimenDevice,
+ dks.OSKey: dks.AndroidOS,
+ types.PrimaryKeyField: dks.CircleTest,
+ },
+ }},
+ Digests: []frontend.DigestStatus{
+ {Digest: dks.DigestC05Unt, Status: expectations.Untriaged},
+ {Digest: dks.DigestC01Pos, Status: expectations.Positive},
+ {Digest: dks.DigestC02Pos, Status: expectations.Positive},
+ },
+ TotalDigests: 3,
+ }, res.Results[0].TraceGroup)
+ assert.Equal(t, paramtools.ParamSet{
+ dks.ColorModeKey: []string{dks.GreyColorMode, dks.RGBColorMode},
+ types.CorpusField: []string{dks.RoundCorpus},
+ dks.DeviceKey: []string{dks.IPadDevice, dks.IPhoneDevice, dks.TaimenDevice},
+ dks.OSKey: []string{dks.AndroidOS, dks.IOS},
+ types.PrimaryKeyField: []string{dks.CircleTest},
+ }, res.Results[0].ParamSet)
+ // Other two results should be the same as they didn't have any ignored data. We spot-check
+ // that here.
+ assert.Equal(t, paramtools.ParamSet{
+ dks.ColorModeKey: []string{dks.RGBColorMode},
+ types.CorpusField: []string{dks.RoundCorpus},
+ dks.DeviceKey: []string{dks.QuadroDevice},
+ dks.OSKey: []string{dks.Windows10dot3OS},
+ types.PrimaryKeyField: []string{dks.CircleTest},
+ }, res.Results[1].ParamSet)
+ assert.Equal(t, paramtools.ParamSet{
+ dks.ColorModeKey: []string{dks.GreyColorMode},
+ types.CorpusField: []string{dks.RoundCorpus},
+ dks.DeviceKey: []string{dks.QuadroDevice},
+ dks.OSKey: []string{dks.Windows10dot3OS},
+ types.PrimaryKeyField: []string{dks.CircleTest},
+ }, res.Results[2].ParamSet)
+}
+
+func TestSearch_RespectMinMaxRGBAFilter_Success(t *testing.T) {
+ unittest.LargeTest(t)
+
+ ctx := context.Background()
+ db := sqltest.NewCockroachDBForTestsWithProductionSchema(ctx, t)
+ require.NoError(t, sqltest.BulkInsertDataTables(ctx, db, dks.Build()))
+
+ s := New(db, 100)
+ res, err := s.Search(ctx, &query.Search{
+ OnlyIncludeDigestsProducedAtHead: true,
+ IncludePositiveDigests: false,
+ IncludeNegativeDigests: false,
+ IncludeUntriagedDigests: true,
+ Sort: query.SortDescending,
+ IncludeIgnoredTraces: false,
+ TraceValues: paramtools.ParamSet{
+ types.CorpusField: []string{dks.RoundCorpus},
+ },
+ RGBAMinFilter: 4,
+ RGBAMaxFilter: 20,
+ })
+ require.NoError(t, err)
+ // The other two results are removed because they are above or below the filter.
+ assert.Equal(t, &frontend.SearchResponse{
+ Results: []*frontend.SearchResult{{
+ Digest: dks.DigestC03Unt,
+ Test: dks.CircleTest,
+ Status: expectations.Untriaged,
+ ParamSet: paramtools.ParamSet{
+ dks.ColorModeKey: []string{dks.RGBColorMode},
+ types.CorpusField: []string{dks.RoundCorpus},
+ dks.DeviceKey: []string{dks.QuadroDevice},
+ dks.OSKey: []string{dks.Windows10dot3OS},
+ types.PrimaryKeyField: []string{dks.CircleTest},
+ },
+ TraceGroup: frontend.TraceGroup{
+ Traces: []frontend.Trace{{
+ ID: "9156c4774e7d90db488b6aadf416ff8e",
+ DigestIndices: []int{-1, -1, -1, 0, 0, 0, 0, 0, 0, 0},
+ Params: paramtools.Params{
+ dks.ColorModeKey: dks.RGBColorMode,
+ types.CorpusField: dks.RoundCorpus,
+ dks.DeviceKey: dks.QuadroDevice,
+ dks.OSKey: dks.Windows10dot3OS,
+ types.PrimaryKeyField: dks.CircleTest,
+ },
+ }},
+ Digests: []frontend.DigestStatus{{
+ Digest: dks.DigestC03Unt, Status: expectations.Untriaged,
+ }},
+ TotalDigests: 1,
+ },
+ RefDiffs: map[common.RefClosest]*frontend.SRDiffDigest{
+ common.PositiveRef: {
+ CombinedMetric: 0.89245414, QueryMetric: 0.89245414, PixelDiffPercent: 50, NumDiffPixels: 32,
+ MaxRGBADiffs: [4]int{1, 7, 4, 0},
+ DimDiffer: false,
+ Digest: dks.DigestC01Pos,
+ Status: expectations.Positive,
+ ParamSet: paramtools.ParamSet{
+ dks.ColorModeKey: []string{dks.RGBColorMode},
+ types.CorpusField: []string{dks.RoundCorpus},
+ dks.DeviceKey: []string{dks.QuadroDevice, dks.IPadDevice, dks.IPhoneDevice, dks.WalleyeDevice},
+ dks.OSKey: []string{dks.AndroidOS, dks.Windows10dot2OS, dks.IOS},
+ types.PrimaryKeyField: []string{dks.CircleTest},
+ },
+ },
+ common.NegativeRef: nil,
+ },
+ ClosestRef: common.PositiveRef,
+ }},
+ Offset: 0,
+ Size: 1,
+ Commits: kitchenSinkCommits,
+ BulkTriageData: web_frontend.TriageRequestData{
+ dks.CircleTest: {
+ dks.DigestC03Unt: expectations.Positive,
+ },
+ },
+ }, res)
+}
+
+func TestSearch_RespectLimitOffsetOrder_Success(t *testing.T) {
+ unittest.LargeTest(t)
+
+ ctx := context.Background()
+ db := sqltest.NewCockroachDBForTestsWithProductionSchema(ctx, t)
+ require.NoError(t, sqltest.BulkInsertDataTables(ctx, db, dks.Build()))
+
+ s := New(db, 100)
+ res, err := s.Search(ctx, &query.Search{
+ OnlyIncludeDigestsProducedAtHead: true,
+ IncludePositiveDigests: true,
+ IncludeNegativeDigests: false,
+ IncludeUntriagedDigests: false,
+ Sort: query.SortAscending,
+ IncludeIgnoredTraces: false,
+ TraceValues: paramtools.ParamSet{
+ types.CorpusField: []string{dks.CornersCorpus},
+ },
+ RGBAMinFilter: 0,
+ RGBAMaxFilter: 255,
+ Offset: 3, // Carefully selected to return one result from square and triangle each.
+ Limit: 2,
+ })
+ require.NoError(t, err)
+ assert.Equal(t, &frontend.SearchResponse{
+ Results: []*frontend.SearchResult{{
+ Digest: dks.DigestA08Pos,
+ Test: dks.SquareTest,
+ Status: expectations.Positive,
+ ParamSet: paramtools.ParamSet{
+ dks.ColorModeKey: []string{dks.RGBColorMode},
+ types.CorpusField: []string{dks.CornersCorpus},
+ dks.DeviceKey: []string{dks.WalleyeDevice},
+ dks.OSKey: []string{dks.AndroidOS},
+ types.PrimaryKeyField: []string{dks.SquareTest},
+ },
+ TraceGroup: frontend.TraceGroup{
+ Traces: []frontend.Trace{{
+ ID: "4686a4134535ad178b67325f5f2f613a",
+ DigestIndices: []int{-1, -1, -1, -1, -1, 4, 3, 2, 1, 0},
+ Params: paramtools.Params{
+ dks.ColorModeKey: dks.RGBColorMode,
+ types.CorpusField: dks.CornersCorpus,
+ dks.DeviceKey: dks.WalleyeDevice,
+ dks.OSKey: dks.AndroidOS,
+ types.PrimaryKeyField: dks.SquareTest,
+ },
+ }},
+ Digests: []frontend.DigestStatus{
+ {Digest: dks.DigestA08Pos, Status: expectations.Positive},
+ {Digest: dks.DigestA07Pos, Status: expectations.Positive},
+ {Digest: dks.DigestA06Unt, Status: expectations.Untriaged},
+ {Digest: dks.DigestA01Pos, Status: expectations.Positive},
+ {Digest: dks.DigestA05Unt, Status: expectations.Untriaged},
+ },
+ TotalDigests: 5,
+ },
+ RefDiffs: map[common.RefClosest]*frontend.SRDiffDigest{
+ common.PositiveRef: {
+ CombinedMetric: 0.15655607, QueryMetric: 0.15655607, PixelDiffPercent: 3.125, NumDiffPixels: 2,
+ MaxRGBADiffs: [4]int{4, 0, 0, 0},
+ DimDiffer: false,
+ Digest: dks.DigestA01Pos,
+ Status: expectations.Positive,
+ ParamSet: paramtools.ParamSet{
+ dks.ColorModeKey: []string{dks.RGBColorMode},
+ types.CorpusField: []string{dks.CornersCorpus},
+ dks.DeviceKey: []string{dks.QuadroDevice, dks.IPadDevice, dks.IPhoneDevice, dks.TaimenDevice, dks.WalleyeDevice},
+ dks.OSKey: []string{dks.AndroidOS, dks.Windows10dot2OS, dks.Windows10dot3OS, dks.IOS},
+ types.PrimaryKeyField: []string{dks.SquareTest},
+ },
+ },
+ common.NegativeRef: {
+ CombinedMetric: 10, QueryMetric: 10, PixelDiffPercent: 100, NumDiffPixels: 64,
+ MaxRGBADiffs: [4]int{255, 255, 255, 255},
+ DimDiffer: false,
+ Digest: dks.DigestA09Neg,
+ Status: expectations.Negative,
+ // Even though this is ignored, we are free to show it on the right side
+ // (just not a part of the actual results).
+ ParamSet: paramtools.ParamSet{
+ dks.ColorModeKey: []string{dks.RGBColorMode},
+ types.CorpusField: []string{dks.CornersCorpus},
+ dks.DeviceKey: []string{dks.TaimenDevice},
+ dks.OSKey: []string{dks.AndroidOS},
+ types.PrimaryKeyField: []string{dks.SquareTest},
+ },
+ },
+ },
+ ClosestRef: common.PositiveRef,
+ }, {
+ Digest: dks.DigestB01Pos,
+ Test: dks.TriangleTest,
+ Status: expectations.Positive,
+ ParamSet: paramtools.ParamSet{
+ dks.ColorModeKey: []string{dks.RGBColorMode},
+ types.CorpusField: []string{dks.CornersCorpus},
+ // Of note - this test is *not* ignored for the taimen device
+ dks.DeviceKey: []string{dks.QuadroDevice, dks.IPadDevice, dks.IPhoneDevice, dks.TaimenDevice, dks.WalleyeDevice},
+ dks.OSKey: []string{dks.AndroidOS, dks.Windows10dot2OS, dks.Windows10dot3OS, dks.IOS},
+ types.PrimaryKeyField: []string{dks.TriangleTest},
+ },
+ TraceGroup: frontend.TraceGroup{
+ Traces: []frontend.Trace{{
+ ID: "1a16cbc8805378f0a6ef654a035d86c4",
+ DigestIndices: []int{-1, -1, -1, -1, -1, -1, 0, 0, 0, 0},
+ Params: paramtools.Params{
+ dks.ColorModeKey: dks.RGBColorMode,
+ types.CorpusField: dks.CornersCorpus,
+ dks.DeviceKey: dks.TaimenDevice,
+ dks.OSKey: dks.AndroidOS,
+ types.PrimaryKeyField: dks.TriangleTest,
+ },
+ }, {
+ ID: "555f149dfe944816076a57c633578dbc",
+ DigestIndices: []int{-1, -1, -1, 0, 0, 0, 0, 0, 0, 0},
+ Params: paramtools.Params{
+ dks.ColorModeKey: dks.RGBColorMode,
+ types.CorpusField: dks.CornersCorpus,
+ dks.DeviceKey: dks.QuadroDevice,
+ dks.OSKey: dks.Windows10dot3OS,
+ types.PrimaryKeyField: dks.TriangleTest,
+ },
+ }, {
+ ID: "7346d80b7d5d1087fd61ae40098f4277",
+ DigestIndices: []int{2, 0, 0, -1, -1, -1, -1, -1, -1, -1},
+ Params: paramtools.Params{
+ dks.ColorModeKey: dks.RGBColorMode,
+ types.CorpusField: dks.CornersCorpus,
+ dks.DeviceKey: dks.QuadroDevice,
+ dks.OSKey: dks.Windows10dot2OS,
+ types.PrimaryKeyField: dks.TriangleTest,
+ },
+ }, {
+ ID: "760c2db998331eafd3023f4b6d135b06",
+ DigestIndices: []int{1, -1, 2, -1, 2, -1, 1, -1, 0, -1},
+ Params: paramtools.Params{
+ dks.ColorModeKey: dks.RGBColorMode,
+ types.CorpusField: dks.CornersCorpus,
+ dks.DeviceKey: dks.IPhoneDevice,
+ dks.OSKey: dks.IOS,
+ types.PrimaryKeyField: dks.TriangleTest,
+ },
+ }, {
+ ID: "8fe41dfab19e0a291f37964416432128",
+ DigestIndices: []int{1, 1, 2, 1, 1, 2, 1, 0, 0, 0},
+ Params: paramtools.Params{
+ dks.ColorModeKey: dks.RGBColorMode,
+ types.CorpusField: dks.CornersCorpus,
+ dks.DeviceKey: dks.IPadDevice,
+ dks.OSKey: dks.IOS,
+ types.PrimaryKeyField: dks.TriangleTest,
+ },
+ }, {
+ ID: "ab734e10b7aed9d06a91f46d14746270",
+ DigestIndices: []int{-1, -1, -1, -1, -1, 0, 0, 0, 0, 0},
+ Params: paramtools.Params{
+ dks.ColorModeKey: dks.RGBColorMode,
+ types.CorpusField: dks.CornersCorpus,
+ dks.DeviceKey: dks.WalleyeDevice,
+ dks.OSKey: dks.AndroidOS,
+ types.PrimaryKeyField: dks.TriangleTest,
+ },
+ }},
+ Digests: []frontend.DigestStatus{
+ {Digest: dks.DigestB01Pos, Status: expectations.Positive},
+ {Digest: dks.DigestB03Neg, Status: expectations.Negative},
+ {Digest: dks.DigestBlank, Status: expectations.Untriaged},
+ },
+ TotalDigests: 3,
+ },
+ RefDiffs: map[common.RefClosest]*frontend.SRDiffDigest{
+ common.PositiveRef: {
+ CombinedMetric: 1.9362538, QueryMetric: 1.9362538, PixelDiffPercent: 43.75, NumDiffPixels: 28,
+ MaxRGBADiffs: [4]int{11, 5, 42, 0},
+ DimDiffer: false,
+ Digest: dks.DigestB02Pos,
+ Status: expectations.Positive,
+ ParamSet: paramtools.ParamSet{
+ dks.ColorModeKey: []string{dks.GreyColorMode},
+ types.CorpusField: []string{dks.CornersCorpus},
+ dks.DeviceKey: []string{dks.QuadroDevice, dks.IPadDevice, dks.IPhoneDevice, dks.WalleyeDevice},
+ dks.OSKey: []string{dks.AndroidOS, dks.Windows10dot2OS, dks.Windows10dot3OS, dks.IOS},
+ types.PrimaryKeyField: []string{dks.TriangleTest},
+ },
+ },
+ common.NegativeRef: {
+ CombinedMetric: 2.9445405, QueryMetric: 2.9445405, PixelDiffPercent: 10.9375, NumDiffPixels: 7,
+ MaxRGBADiffs: [4]int{250, 244, 197, 51},
+ DimDiffer: false,
+ Digest: dks.DigestB03Neg,
+ Status: expectations.Negative,
+ ParamSet: paramtools.ParamSet{
+ dks.ColorModeKey: []string{dks.RGBColorMode},
+ types.CorpusField: []string{dks.CornersCorpus},
+ dks.DeviceKey: []string{dks.IPadDevice, dks.IPhoneDevice},
+ dks.OSKey: []string{dks.IOS},
+ types.PrimaryKeyField: []string{dks.TriangleTest},
+ },
+ },
+ },
+ ClosestRef: common.PositiveRef,
+ }},
+ Offset: 3,
+ Size: 6,
+ Commits: kitchenSinkCommits,
+ BulkTriageData: web_frontend.TriageRequestData{
+ dks.SquareTest: {
+ dks.DigestA01Pos: expectations.Positive,
+ dks.DigestA02Pos: expectations.Positive,
+ dks.DigestA03Pos: expectations.Positive,
+ dks.DigestA08Pos: expectations.Positive,
+ }, dks.TriangleTest: {
+ dks.DigestB02Pos: expectations.Positive,
+ dks.DigestB01Pos: expectations.Positive,
+ },
+ },
+ }, res)
+}
+
func TestMakeTraceGroup_TwoMostlyStableTraces_Success(t *testing.T) {
unittest.SmallTest(t)
- ctx := context.WithValue(context.Background(), commitToIdx, map[schema.CommitID]int{
+ ctx := context.WithValue(context.Background(), commitToIdxKey, map[schema.CommitID]int{
"10": 0,
"11": 1,
"12": 2,
"17": 3,
"20": 4,
})
- ctx = context.WithValue(ctx, actualWindowLength, 5)
+ ctx = context.WithValue(ctx, actualWindowLengthKey, 5)
inputData := []traceDigestCommit{
{traceID: schema.TraceID{0xaa}, commitID: "10", digest: dks.DigestA01Pos},
{traceID: schema.TraceID{0xaa}, commitID: "11", digest: dks.DigestA01Pos},
@@ -757,7 +1168,7 @@
func TestMakeTraceGroup_OneFlakyTrace_PrioritizeShowingMostUsedDigests(t *testing.T) {
unittest.SmallTest(t)
- ctx := context.WithValue(context.Background(), commitToIdx, map[schema.CommitID]int{
+ ctx := context.WithValue(context.Background(), commitToIdxKey, map[schema.CommitID]int{
"10": 0,
"11": 1,
"12": 2,
@@ -777,7 +1188,7 @@
"31": 16,
"32": 17,
})
- ctx = context.WithValue(ctx, actualWindowLength, 18)
+ ctx = context.WithValue(ctx, actualWindowLengthKey, 18)
inputData := []traceDigestCommit{
{traceID: schema.TraceID{0xaa}, commitID: "10", digest: "dC"},
{traceID: schema.TraceID{0xaa}, commitID: "11", digest: "dC"},