[gold] Use comment.Store in generating search results.
Bug: skia:6630
Change-Id: Ie13c240d94c586843c59815dea36d2b76263df40
Reviewed-on: https://skia-review.googlesource.com/c/buildbot/+/271605
Reviewed-by: Leandro Lovisolo <lovisolo@google.com>
diff --git a/golden/cmd/skiacorrectness/main.go b/golden/cmd/skiacorrectness/main.go
index e622a34..1b5dcae 100644
--- a/golden/cmd/skiacorrectness/main.go
+++ b/golden/cmd/skiacorrectness/main.go
@@ -424,7 +424,8 @@
}
sklog.Infof("Indexer created.")
- searchAPI := search.New(diffStore, expStore, ixr, cls, tjs, publiclyViewableParams)
+ // TODO(kjlubick) include non-nil comment.Store when it is implemented.
+ searchAPI := search.New(diffStore, expStore, ixr, cls, tjs, nil, publiclyViewableParams)
sklog.Infof("Search API created")
diff --git a/golden/go/search/frontend/types.go b/golden/go/search/frontend/types.go
index ce5d004..ea6590d 100644
--- a/golden/go/search/frontend/types.go
+++ b/golden/go/search/frontend/types.go
@@ -3,8 +3,11 @@
package frontend
import (
+ "time"
+
"go.skia.org/infra/go/paramtools"
"go.skia.org/infra/go/tiling"
+ "go.skia.org/infra/golden/go/comment/trace"
"go.skia.org/infra/golden/go/diff"
"go.skia.org/infra/golden/go/search/common"
"go.skia.org/infra/golden/go/types"
@@ -14,10 +17,11 @@
// Search(...) function of SearchAPI and intended to be
// returned as JSON in an HTTP response.
type SearchResponse struct {
- Digests []*SRDigest `json:"digests"`
- Offset int `json:"offset"`
- Size int `json:"size"`
- Commits []*tiling.Commit `json:"commits"`
+ Digests []*SRDigest `json:"digests"`
+ Offset int `json:"offset"`
+ Size int `json:"size"`
+ Commits []*tiling.Commit `json:"commits"`
+ TraceComments []TraceComment `json:"trace_comments"`
}
// SRDigest is a single search result digest returned
@@ -45,8 +49,9 @@
// DigestDetails contains details about a digest.
type DigestDetails struct {
- Digest *SRDigest `json:"digest"`
- Commits []*tiling.Commit `json:"commits"`
+ Digest *SRDigest `json:"digest"`
+ Commits []*tiling.Commit `json:"commits"`
+ TraceComments []TraceComment `json:"trace_comments"`
}
// Point is a single point. Used to draw the trace diagrams on the frontend.
@@ -59,13 +64,50 @@
// Trace describes a single trace, used in TraceGroup.
// TODO(kjlubick) Having many traces yields a large amount of JSON, which can take ~hundreds of
// milliseconds to gzip. We can probably remove the X and Y from Point (as they are somewhat
-// redundant with the the index of the point and the index of the trace respectively).
-// Additionally, we might be able to "compress" Params into an OrderedParamsSet and have
-// an array of ints here instead of the whole map.
+// redundant with the the index of the point and the index of the trace respectively). A quick
+// grep over a JSON response from production shows that the Points take up over 2/3 of the size
+// of the response (each point takes up ~20 bytes and when the tile size is 200, that means each
+// trace adds 4k of code size). Additionally, we might be able to "compress" Params into an
+// OrderedParamsSet and have an array of ints here instead of the whole map.
type Trace struct {
- Data []Point `json:"data"` // One Point for each test result.
- ID tiling.TraceID `json:"label"` // The id of the trace. Keep the json as label to be compatible with dots-sk.
+ Data []Point `json:"data"` // One Point for each test result.
+ // The id of the trace. Keep the json as label to be compatible with dots-sk.
+ ID tiling.TraceID `json:"label"`
Params map[string]string `json:"params"`
+ // CommentIndices are indices into the TraceComments slice on the final result. For example,
+ // a 1 means the second TraceComment in the top level TraceComments applies to this trace.
+ CommentIndices []int `json:"comment_indicies"`
+}
+
+// TraceComment is the frontend representation of a trace.Comment
+type TraceComment struct {
+ ID string `json:"id"`
+ // CreatedBy is the email address of the user who created this trace comment.
+ CreatedBy string `json:"created_by"`
+ // UpdatedBy is the email address of the user who most recently updated this trace comment.
+ UpdatedBy string `json:"updated_by"`
+ // CreatedTS is when the comment was created.
+ CreatedTS time.Time `json:"created_ts"`
+ // UpdatedTS is when the comment was updated.
+ UpdatedTS time.Time `json:"updated_ts"`
+ // Text is an arbitrary string. There can be special rules that only the frontend cares about
+ // (e.g. some markdown or coordinates).
+ Text string `json:"text"`
+ // QueryToMatch represents which traces this trace comment should apply to.
+ QueryToMatch paramtools.ParamSet `json:"query"`
+}
+
+// ToTraceComment converts a trace.Comment into a TraceComment
+func ToTraceComment(c trace.Comment) TraceComment {
+ return TraceComment{
+ ID: c.ID,
+ CreatedBy: c.CreatedBy,
+ UpdatedBy: c.UpdatedBy,
+ CreatedTS: c.CreatedTS,
+ UpdatedTS: c.UpdatedTS,
+ Text: c.Comment,
+ QueryToMatch: c.QueryToMatch,
+ }
}
// DigestStatus is a digest and its status, used in TraceGroup.
diff --git a/golden/go/search/search.go b/golden/go/search/search.go
index 1e31a70..7fc3622 100644
--- a/golden/go/search/search.go
+++ b/golden/go/search/search.go
@@ -17,6 +17,7 @@
"go.skia.org/infra/go/util"
"go.skia.org/infra/golden/go/clstore"
"go.skia.org/infra/golden/go/code_review"
+ "go.skia.org/infra/golden/go/comment"
"go.skia.org/infra/golden/go/diff"
"go.skia.org/infra/golden/go/expstorage"
"go.skia.org/infra/golden/go/indexer"
@@ -56,6 +57,7 @@
indexSource indexer.IndexSource
changeListStore clstore.Store
tryJobStore tjstore.Store
+ commentStore comment.Store
// storeCache allows for better performance by caching values from changeListStore and
// tryJobStore for a little while, before evicting them.
@@ -68,13 +70,14 @@
}
// New returns a new SearchImpl instance.
-func New(ds diff.DiffStore, es expstorage.ExpectationsStore, is indexer.IndexSource, cls clstore.Store, tjs tjstore.Store, publiclyViewableParams paramtools.ParamSet) *SearchImpl {
+func New(ds diff.DiffStore, es expstorage.ExpectationsStore, is indexer.IndexSource, cls clstore.Store, tjs tjstore.Store, cs comment.Store, publiclyViewableParams paramtools.ParamSet) *SearchImpl {
return &SearchImpl{
diffStore: ds,
expectationsStore: es,
indexSource: is,
changeListStore: cls,
tryJobStore: tjs,
+ commentStore: cs,
publiclyViewableParams: publiclyViewableParams,
storeCache: ttlcache.New(searchCacheFreshness, searchCacheCleanup),
@@ -147,7 +150,7 @@
// bulk triage, but only the digests that are going to be shown are padded
// with additional information.
displayRet, offset := s.sortAndLimitDigests(ctx, q, ret, int(q.Offset), int(q.Limit))
- s.addParamsAndTraces(ctx, displayRet, inter, exp, idx)
+ traceComments := s.addParamsTracesAndComments(ctx, displayRet, inter, exp, idx)
// Return all digests with the selected offset within the result set.
searchRet := &frontend.SearchResponse{
@@ -155,7 +158,8 @@
Offset: offset,
Size: len(displayRet),
// TODO(kjlubick) maybe omit Commits for ChangeList Queries.
- Commits: idx.Tile().GetTile(types.ExcludeIgnoredTraces).Commits,
+ Commits: idx.Tile().GetTile(types.ExcludeIgnoredTraces).Commits,
+ TraceComments: traceComments,
}
return searchRet, nil
}
@@ -211,14 +215,16 @@
return nil, skerr.Wrapf(err, "Fetching reference diffs for test %s, digest %s", test, digest)
}
+ var traceComments []frontend.TraceComment
if hasTraces {
// Get the params and traces.
- s.addParamsAndTraces(ctx, ret, inter, exp, idx)
+ traceComments = s.addParamsTracesAndComments(ctx, ret, inter, exp, idx)
}
return &frontend.DigestDetails{
- Digest: ret[0],
- Commits: tile.Commits,
+ Digest: ret[0],
+ Commits: tile.Commits,
+ TraceComments: traceComments,
}, nil
}
@@ -590,25 +596,43 @@
return digestInfo[offset:end], offset
}
-// addParamsAndTraces adds information to the given result that is necessary
+// addParamsTracesAndComments adds information to the given result that is necessary
// to draw them, i.e. the information what digest/image appears at what commit and
// what were the union of parameters that generate the digest. This should be
// only done for digests that are intended to be displayed.
-func (s *SearchImpl) addParamsAndTraces(ctx context.Context, digestInfo []*frontend.SRDigest, inter srInterMap, exp expectations.Classifier, idx indexer.IndexSearcher) {
+func (s *SearchImpl) addParamsTracesAndComments(ctx context.Context, digestInfo []*frontend.SRDigest, inter srInterMap, exp expectations.Classifier, idx indexer.IndexSearcher) []frontend.TraceComment {
tile := idx.Tile().GetTile(types.ExcludeIgnoredTraces)
last := tile.LastCommitIndex()
+ var traceComments []frontend.TraceComment
+ // TODO(kjlubick) remove this check once the commentStore is implemented and included from main.
+ if s.commentStore != nil {
+ xtc, err := s.commentStore.ListComments(ctx)
+ if err != nil {
+ sklog.Warningf("Omitting comments due to error: %s", err)
+ traceComments = nil
+ } else {
+ for _, tc := range xtc {
+ traceComments = append(traceComments, frontend.ToTraceComment(tc))
+ }
+ sort.Slice(traceComments, func(i, j int) bool {
+ return traceComments[i].UpdatedTS.Before(traceComments[j].UpdatedTS)
+ })
+ }
+ }
+
for _, di := range digestInfo {
// Add the parameters and the drawable traces to the result.
di.ParamSet = inter[di.Test][di.Digest].params
di.ParamSet.Normalize()
- di.Traces = s.getDrawableTraces(di.Test, di.Digest, last, exp, inter[di.Test][di.Digest].traces)
+ di.Traces = s.getDrawableTraces(di.Test, di.Digest, last, exp, inter[di.Test][di.Digest].traces, traceComments)
di.Traces.TileSize = len(tile.Commits)
}
+ return traceComments
}
// getDrawableTraces returns an instance of TraceGroup which allows us
// to draw the traces for the given test/digest.
-func (s *SearchImpl) getDrawableTraces(test types.TestName, digest types.Digest, last int, exp expectations.Classifier, traces map[tiling.TraceID]*types.GoldenTrace) *frontend.TraceGroup {
+func (s *SearchImpl) getDrawableTraces(test types.TestName, digest types.Digest, last int, exp expectations.Classifier, traces map[tiling.TraceID]*types.GoldenTrace, comments []frontend.TraceComment) *frontend.TraceGroup {
// Get the information necessary to draw the traces.
traceIDs := make([]tiling.TraceID, 0, len(traces))
for traceID := range traces {
@@ -668,6 +692,12 @@
}
// Trim the leading traces if necessary.
tr.Data = tr.Data[insertNext+1:]
+
+ for i, c := range comments {
+ if c.QueryToMatch.MatchesParams(tr.Params) {
+ tr.CommentIndices = append(tr.CommentIndices, i)
+ }
+ }
}
return &frontend.TraceGroup{
diff --git a/golden/go/search/search_test.go b/golden/go/search/search_test.go
index b3920de..dc9bab0 100644
--- a/golden/go/search/search_test.go
+++ b/golden/go/search/search_test.go
@@ -4,15 +4,21 @@
"context"
"errors"
"testing"
+ "time"
"github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
"go.skia.org/infra/go/paramtools"
"go.skia.org/infra/go/testutils"
"go.skia.org/infra/go/testutils/unittest"
+ "go.skia.org/infra/go/tiling"
mock_clstore "go.skia.org/infra/golden/go/clstore/mocks"
"go.skia.org/infra/golden/go/code_review"
+ "go.skia.org/infra/golden/go/comment"
+ mock_comment "go.skia.org/infra/golden/go/comment/mocks"
+ "go.skia.org/infra/golden/go/comment/trace"
"go.skia.org/infra/golden/go/diff"
mock_diffstore "go.skia.org/infra/golden/go/diffstore/mocks"
"go.skia.org/infra/golden/go/digest_counter"
@@ -52,7 +58,7 @@
addDiffData(mds, data.BetaUntriaged1Digest, data.BetaGood1Digest, makeBigDiffMetric())
// BetaUntriaged1Digest has no negative images to compare against.
- s := New(mds, makeThreeDevicesExpectationStore(), makeThreeDevicesIndexer(), nil, nil, everythingPublic)
+ s := New(mds, makeThreeDevicesExpectationStore(), makeThreeDevicesIndexer(), nil, nil, emptyCommentStore(), everythingPublic)
q := &query.Search{
ChangeListID: "",
@@ -206,8 +212,9 @@
addDiffData(mds, data.BetaUntriaged1Digest, data.BetaGood1Digest, makeBigDiffMetric())
// BetaUntriaged1Digest has no negative images to compare against.
- s := New(mds, makeThreeDevicesExpectationStore(), makeThreeDevicesIndexer(), nil, nil, everythingPublic)
+ s := New(mds, makeThreeDevicesExpectationStore(), makeThreeDevicesIndexer(), nil, nil, emptyCommentStore(), everythingPublic)
+ // spotCheck is the subset of data we assert against.
type spotCheck struct {
test types.TestName
digest types.Digest
@@ -488,6 +495,114 @@
}, []spotCheck{})
}
+// TestSearch_ThreeDevicesCorpusWithComments_CommentsInResults ensures that search results contain
+// comments when it matches the traces.
+func TestSearch_ThreeDevicesCorpusWithComments_CommentsInResults(t *testing.T) {
+ unittest.SmallTest(t)
+
+ bullheadComment := trace.Comment{
+ ID: "1",
+ CreatedBy: "zulu@example.com",
+ UpdatedBy: "zulu@example.com",
+ CreatedTS: time.Date(2020, time.February, 19, 18, 17, 16, 0, time.UTC),
+ UpdatedTS: time.Date(2020, time.February, 19, 18, 17, 16, 0, time.UTC),
+ Comment: "All bullhead devices draw upside down",
+ QueryToMatch: paramtools.ParamSet{
+ "device": []string{data.BullheadDevice},
+ },
+ }
+
+ alphaTestComment := trace.Comment{
+ ID: "2",
+ CreatedBy: "yankee@example.com",
+ UpdatedBy: "xray@example.com",
+ CreatedTS: time.Date(2020, time.February, 2, 18, 17, 16, 0, time.UTC),
+ UpdatedTS: time.Date(2020, time.February, 20, 18, 17, 16, 0, time.UTC),
+ Comment: "Watch pixel 0,4 to make sure it's not purple",
+ QueryToMatch: paramtools.ParamSet{
+ types.PRIMARY_KEY_FIELD: []string{string(data.AlphaTest)},
+ },
+ }
+
+ betaTestBullheadComment := trace.Comment{
+ ID: "4",
+ CreatedBy: "victor@example.com",
+ UpdatedBy: "victor@example.com",
+ CreatedTS: time.Date(2020, time.February, 22, 18, 17, 16, 0, time.UTC),
+ UpdatedTS: time.Date(2020, time.February, 22, 18, 17, 16, 0, time.UTC),
+ Comment: "Being upside down, this test should be ABGR instead of RGBA",
+ QueryToMatch: paramtools.ParamSet{
+ "device": []string{data.BullheadDevice},
+ types.PRIMARY_KEY_FIELD: []string{string(data.BetaTest)},
+ },
+ }
+
+ commentAppliesToNothing := trace.Comment{
+ ID: "3",
+ CreatedBy: "uniform@example.com",
+ UpdatedBy: "uniform@example.com",
+ CreatedTS: time.Date(2020, time.February, 26, 26, 26, 26, 0, time.UTC),
+ UpdatedTS: time.Date(2020, time.February, 26, 26, 26, 26, 0, time.UTC),
+ Comment: "On Wednesdays, this device draws pink",
+ QueryToMatch: paramtools.ParamSet{
+ "device": []string{"This device does not exist"},
+ },
+ }
+
+ mcs := &mock_comment.Store{}
+ // Return these in an arbitrary, unsorted order
+ mcs.On("ListComments", testutils.AnyContext).Return([]trace.Comment{commentAppliesToNothing, alphaTestComment, betaTestBullheadComment, bullheadComment}, nil)
+
+ s := New(makeStubDiffStore(), makeThreeDevicesExpectationStore(), makeThreeDevicesIndexer(), nil, nil, mcs, everythingPublic)
+
+ q := &query.Search{
+ // Set all to true so all 6 traces show up in the final results.
+ Unt: true,
+ Pos: true,
+ Neg: true,
+ Head: true,
+
+ Metric: diff.CombinedMetric,
+ FRGBAMin: 0,
+ FRGBAMax: 255,
+ FDiffMax: -1,
+ Sort: query.SortAscending,
+ }
+
+ resp, err := s.Search(context.Background(), q)
+ require.NoError(t, err)
+ require.NotNil(t, resp)
+ // There are 4 unique digests at HEAD on the three_devices corpus. Do a quick smoke test to make
+ // sure we have one search result for each of them.
+ require.Len(t, resp.Digests, 4)
+ f := frontend.ToTraceComment
+ // This should be sorted by UpdatedTS.
+ assert.Equal(t, []frontend.TraceComment{
+ f(bullheadComment), f(alphaTestComment), f(betaTestBullheadComment), f(commentAppliesToNothing),
+ }, resp.TraceComments)
+
+ // These numbers are indices into the resp.TraceComments. The nil entries are expected to have
+ // no comments that match them.
+ expectedComments := map[tiling.TraceID][]int{
+ data.AnglerAlphaTraceID: {1},
+ data.AnglerBetaTraceID: nil,
+ data.BullheadAlphaTraceID: {0, 1},
+ data.BullheadBetaTraceID: {0, 2},
+ data.CrosshatchAlphaTraceID: {1},
+ data.CrosshatchBetaTraceID: nil,
+ }
+ // We only check that the traces have their associated comments. We rely on the other tests
+ // to make sure the other fields are correct.
+ traceCount := 0
+ for _, r := range resp.Digests {
+ for _, tr := range r.Traces.Traces {
+ traceCount++
+ assert.Equal(t, expectedComments[tr.ID], tr.CommentIndices, "trace id %q under digest", tr.ID, r.Digest)
+ }
+ }
+ assert.Equal(t, 6, traceCount, "Not all traces were in the final result")
+}
+
// TestSearchThreeDevicesChangeListSunnyDay covers the case
// where two tryjobs have been run on a given CL and PS, one on the
// angler bot and one on the bullhead bot. The master branch
@@ -595,7 +710,7 @@
mds := makeDiffStoreWithNoFailures()
addDiffData(mds, BetaBrandNewDigest, data.BetaGood1Digest, makeSmallDiffMetric())
- s := New(mds, mes, makeThreeDevicesIndexer(), mcls, mtjs, everythingPublic)
+ s := New(mds, mes, makeThreeDevicesIndexer(), mcls, mtjs, nil, everythingPublic)
q := &query.Search{
ChangeListID: clID,
@@ -682,7 +797,7 @@
addDiffData(mds, digestWeWantDetailsAbout, data.AlphaGood1Digest, nil)
addDiffData(mds, digestWeWantDetailsAbout, data.AlphaBad1Digest, makeBigDiffMetric())
- s := New(mds, makeThreeDevicesExpectationStore(), makeThreeDevicesIndexer(), nil, nil, everythingPublic)
+ s := New(mds, makeThreeDevicesExpectationStore(), makeThreeDevicesIndexer(), nil, nil, emptyCommentStore(), everythingPublic)
details, err := s.GetDigestDetails(context.Background(), testWeWantDetailsAbout, digestWeWantDetailsAbout, "", "")
require.NoError(t, err)
@@ -779,7 +894,7 @@
data.AlphaBad1Digest: makeBigDiffMetric(),
}, nil)
- s := New(mds, mes, makeThreeDevicesIndexer(), nil, nil, everythingPublic)
+ s := New(mds, mes, makeThreeDevicesIndexer(), nil, nil, emptyCommentStore(), everythingPublic)
details, err := s.GetDigestDetails(context.Background(), testWeWantDetailsAbout, digestWeWantDetailsAbout, testCLID, testCRS)
require.NoError(t, err)
@@ -797,7 +912,7 @@
mds := makeDiffStoreWithNoFailures()
addDiffData(mds, digestWeWantDetailsAbout, data.BetaGood1Digest, makeSmallDiffMetric())
- s := New(mds, makeThreeDevicesExpectationStore(), makeThreeDevicesIndexer(), nil, nil, everythingPublic)
+ s := New(mds, makeThreeDevicesExpectationStore(), makeThreeDevicesIndexer(), nil, nil, nil, everythingPublic)
d, err := s.GetDigestDetails(context.Background(), testWeWantDetailsAbout, digestWeWantDetailsAbout, "", "")
require.NoError(t, err)
@@ -835,7 +950,7 @@
mds := makeDiffStoreWithNoFailures()
mds.On("Get", testutils.AnyContext, digestWeWantDetailsAbout, types.DigestSlice{data.BetaGood1Digest}).Return(nil, errors.New("invalid digest"))
- s := New(mds, makeThreeDevicesExpectationStore(), makeThreeDevicesIndexer(), nil, nil, everythingPublic)
+ s := New(mds, makeThreeDevicesExpectationStore(), makeThreeDevicesIndexer(), nil, nil, nil, everythingPublic)
r, err := s.GetDigestDetails(context.Background(), testWeWantDetailsAbout, digestWeWantDetailsAbout, "", "")
require.NoError(t, err)
@@ -850,7 +965,7 @@
const digestWeWantDetailsAbout = data.AlphaGood1Digest
const testWeWantDetailsAbout = types.TestName("invalid test")
- s := New(nil, nil, makeThreeDevicesIndexer(), nil, nil, everythingPublic)
+ s := New(nil, nil, makeThreeDevicesIndexer(), nil, nil, nil, everythingPublic)
_, err := s.GetDigestDetails(context.Background(), testWeWantDetailsAbout, digestWeWantDetailsAbout, "", "")
require.Error(t, err)
@@ -863,7 +978,7 @@
const digestWeWantDetailsAbout = types.Digest("invalid digest")
const testWeWantDetailsAbout = types.TestName("invalid test")
- s := New(nil, nil, makeThreeDevicesIndexer(), nil, nil, everythingPublic)
+ s := New(nil, nil, makeThreeDevicesIndexer(), nil, nil, nil, everythingPublic)
_, err := s.GetDigestDetails(context.Background(), testWeWantDetailsAbout, digestWeWantDetailsAbout, "", "")
require.Error(t, err)
@@ -880,7 +995,7 @@
mds := makeDiffStoreWithNoFailures()
addDiffData(mds, leftDigest, rightDigest, makeSmallDiffMetric())
- s := New(mds, makeThreeDevicesExpectationStore(), makeThreeDevicesIndexer(), nil, nil, everythingPublic)
+ s := New(mds, makeThreeDevicesExpectationStore(), makeThreeDevicesIndexer(), nil, nil, nil, everythingPublic)
cd, err := s.DiffDigests(context.Background(), testWeWantDetailsAbout, leftDigest, rightDigest, "", "")
require.NoError(t, err)
@@ -925,7 +1040,7 @@
mds := makeDiffStoreWithNoFailures()
addDiffData(mds, leftDigest, rightDigest, makeSmallDiffMetric())
- s := New(mds, mes, makeThreeDevicesIndexer(), nil, nil, everythingPublic)
+ s := New(mds, mes, makeThreeDevicesIndexer(), nil, nil, nil, everythingPublic)
cd, err := s.DiffDigests(context.Background(), testWeWantDetailsAbout, leftDigest, rightDigest, clID, crs)
require.NoError(t, err)
@@ -1049,7 +1164,7 @@
},
}, nil).Once()
- s := New(nil, mes, mi, nil, mtjs, everythingPublic)
+ s := New(nil, mes, mi, nil, mtjs, nil, everythingPublic)
dl, err := s.UntriagedUnignoredTryJobExclusiveDigests(context.Background(), expectedID)
require.NoError(t, err)
@@ -1140,3 +1255,23 @@
},
}
}
+
+func emptyCommentStore() comment.Store {
+ mcs := &mock_comment.Store{}
+ mcs.On("ListComments", testutils.AnyContext).Return(nil, nil)
+ return mcs
+}
+
+// makeStubDiffStore returns a diffstore that returns the small diff metric for every call to Get.
+func makeStubDiffStore() *mock_diffstore.DiffStore {
+ mds := &mock_diffstore.DiffStore{}
+ mds.On("UnavailableDigests", testutils.AnyContext).Return(map[types.Digest]*diff.DigestFailure{}, nil)
+ mds.On("Get", testutils.AnyContext, mock.Anything, mock.Anything).Return(func(_ context.Context, _ types.Digest, rights types.DigestSlice) map[types.Digest]*diff.DiffMetrics {
+ rv := make(map[types.Digest]*diff.DiffMetrics, len(rights))
+ for _, right := range rights {
+ rv[right] = makeSmallDiffMetric()
+ }
+ return rv
+ }, nil)
+ return mds
+}