| package tilesource |
| |
| import ( |
| "context" |
| "testing" |
| "time" |
| |
| "github.com/stretchr/testify/assert" |
| "github.com/stretchr/testify/require" |
| |
| metrics_utils "go.skia.org/infra/go/metrics2/testutils" |
| "go.skia.org/infra/go/testutils" |
| "go.skia.org/infra/go/testutils/unittest" |
| "go.skia.org/infra/go/vcsinfo" |
| mock_vcs "go.skia.org/infra/go/vcsinfo/mocks" |
| mock_updater "go.skia.org/infra/golden/go/code_review/mocks" |
| "go.skia.org/infra/golden/go/ignore" |
| mock_ignorestore "go.skia.org/infra/golden/go/ignore/mocks" |
| "go.skia.org/infra/golden/go/mocks" |
| "go.skia.org/infra/golden/go/publicparams" |
| data "go.skia.org/infra/golden/go/testutils/data_three_devices" |
| "go.skia.org/infra/golden/go/tiling" |
| "go.skia.org/infra/golden/go/types" |
| ) |
| |
| // TestUpdateTileSunnyDay tests building the tile for the first time (e.g. after reboot). |
| func TestUpdateTileSunnyDay(t *testing.T) { |
| unittest.SmallTest(t) |
| |
| mis := &mock_ignorestore.Store{} |
| mts := &mocks.TraceStore{} |
| mu := &mock_updater.ChangeListLandedUpdater{} |
| mvcs := &mock_vcs.VCS{} |
| defer mis.AssertExpectations(t) |
| defer mts.AssertExpectations(t) |
| defer mu.AssertExpectations(t) |
| defer mvcs.AssertExpectations(t) |
| |
| mvcs.On("Update", testutils.AnyContext, true, false).Return(nil) |
| mvcs.On("DetailsMulti", testutils.AnyContext, []string{ |
| zerothCommitHash, data.FirstCommitHash, data.SecondCommitHash, data.ThirdCommitHash, fourthCommitHash, |
| }, false).Return(makeSparseLongCommits(), nil) |
| |
| mts.On("GetDenseTile", testutils.AnyContext, nCommits).Return(data.MakeTestTile(), makeSparseTilingCommits(), nil) |
| |
| // No ignores in this test |
| mis.On("List", testutils.AnyContext).Return(nil, nil) |
| |
| mu.On("UpdateChangeListsAsLanded", testutils.AnyContext, makeSparseLongCommits()).Return(nil) |
| |
| ts := New(CachedTileSourceConfig{ |
| NCommits: nCommits, |
| IgnoreStore: mis, |
| TraceStore: mts, |
| CLUpdater: mu, |
| VCS: mvcs, |
| }) |
| require.Nil(t, ts.lastCpxTile) |
| |
| err := ts.updateTile(context.Background()) |
| require.NoError(t, err) |
| |
| cpxTile := ts.GetTile() |
| require.NotNil(t, cpxTile) |
| |
| assert.Equal(t, makeSparseTilingCommits(), cpxTile.AllCommits()) |
| assert.Equal(t, data.MakeTestCommits(), cpxTile.DataCommits()) |
| assert.Equal(t, nCommits, cpxTile.FilledCommits()) |
| assert.Equal(t, data.MakeTestTile(), cpxTile.GetTile(types.ExcludeIgnoredTraces)) |
| assert.Equal(t, data.MakeTestTile(), cpxTile.GetTile(types.IncludeIgnoredTraces)) |
| assert.Equal(t, "5", metrics_utils.GetRecordedMetric(t, filledTracesAtHeadMetric, nil)) |
| assert.Equal(t, "1", metrics_utils.GetRecordedMetric(t, emptyCommitsAtHeadMetric, nil)) |
| } |
| |
| // TestUpdateTileEmptyTile tests when there is no data for any commit |
| func TestUpdateTileEmptyTile(t *testing.T) { |
| unittest.SmallTest(t) |
| |
| mis := &mock_ignorestore.Store{} |
| mts := &mocks.TraceStore{} |
| mu := &mock_updater.ChangeListLandedUpdater{} |
| mvcs := &mock_vcs.VCS{} |
| defer mis.AssertExpectations(t) |
| defer mts.AssertExpectations(t) |
| defer mu.AssertExpectations(t) |
| defer mvcs.AssertExpectations(t) |
| |
| mvcs.On("Update", testutils.AnyContext, true, false).Return(nil) |
| mvcs.On("DetailsMulti", testutils.AnyContext, []string{ |
| zerothCommitHash, data.FirstCommitHash, data.SecondCommitHash, data.ThirdCommitHash, fourthCommitHash, |
| }, false).Return(makeSparseLongCommits(), nil) |
| |
| empty := &tiling.Tile{ |
| Commits: nil, |
| Traces: map[tiling.TraceID]*tiling.Trace{}, |
| } |
| |
| mts.On("GetDenseTile", testutils.AnyContext, nCommits).Return(empty, makeSparseTilingCommits(), nil) |
| |
| // No ignores in this test |
| mis.On("List", testutils.AnyContext).Return(nil, nil) |
| |
| mu.On("UpdateChangeListsAsLanded", testutils.AnyContext, makeSparseLongCommits()).Return(nil) |
| |
| ts := New(CachedTileSourceConfig{ |
| NCommits: nCommits, |
| IgnoreStore: mis, |
| TraceStore: mts, |
| CLUpdater: mu, |
| VCS: mvcs, |
| }) |
| require.Nil(t, ts.lastCpxTile) |
| |
| err := ts.updateTile(context.Background()) |
| require.NoError(t, err) |
| |
| cpxTile := ts.GetTile() |
| require.NotNil(t, cpxTile) |
| |
| assert.Equal(t, "0", metrics_utils.GetRecordedMetric(t, filledTracesAtHeadMetric, nil)) |
| assert.Equal(t, "5", metrics_utils.GetRecordedMetric(t, emptyCommitsAtHeadMetric, nil)) |
| } |
| |
| // TestUpdateTileNilCLUpdater tests building the tile with a nil updater (e.g. skia-public |
| // instance). |
| func TestUpdateTileNilCLUpdater(t *testing.T) { |
| unittest.SmallTest(t) |
| |
| mis := &mock_ignorestore.Store{} |
| mts := &mocks.TraceStore{} |
| mvcs := &mock_vcs.VCS{} |
| defer mis.AssertExpectations(t) |
| defer mts.AssertExpectations(t) |
| defer mvcs.AssertExpectations(t) |
| |
| mvcs.On("Update", testutils.AnyContext, true, false).Return(nil) |
| |
| mts.On("GetDenseTile", testutils.AnyContext, nCommits).Return(data.MakeTestTile(), makeSparseTilingCommits(), nil) |
| |
| // No ignores in this test |
| mis.On("List", testutils.AnyContext).Return(nil, nil) |
| |
| ts := New(CachedTileSourceConfig{ |
| NCommits: nCommits, |
| IgnoreStore: mis, |
| TraceStore: mts, |
| CLUpdater: nil, |
| VCS: mvcs, |
| }) |
| require.Nil(t, ts.lastCpxTile) |
| |
| err := ts.updateTile(context.Background()) |
| require.NoError(t, err) |
| |
| cpxTile := ts.GetTile() |
| require.NotNil(t, cpxTile) |
| |
| assert.Equal(t, makeSparseTilingCommits(), cpxTile.AllCommits()) |
| assert.Equal(t, data.MakeTestCommits(), cpxTile.DataCommits()) |
| assert.Equal(t, nCommits, cpxTile.FilledCommits()) |
| assert.Equal(t, data.MakeTestTile(), cpxTile.GetTile(types.ExcludeIgnoredTraces)) |
| assert.Equal(t, data.MakeTestTile(), cpxTile.GetTile(types.IncludeIgnoredTraces)) |
| assert.Equal(t, "5", metrics_utils.GetRecordedMetric(t, filledTracesAtHeadMetric, nil)) |
| assert.Equal(t, "1", metrics_utils.GetRecordedMetric(t, emptyCommitsAtHeadMetric, nil)) |
| } |
| |
| // TestUpdateTileHasPreviousPartial tests the case where some commits have already been |
| // processed previously via the Updater. |
| func TestUpdateTileHasPreviousPartial(t *testing.T) { |
| unittest.SmallTest(t) |
| |
| mis := &mock_ignorestore.Store{} |
| mts := &mocks.TraceStore{} |
| mu := &mock_updater.ChangeListLandedUpdater{} |
| mvcs := &mock_vcs.VCS{} |
| // TODO(kjlubick) It's probably best to make a real ComplexTile here and below instead |
| // of a mock. go/mocks#prefer-testing-state |
| mct := &mocks.ComplexTile{} |
| defer mis.AssertExpectations(t) |
| defer mts.AssertExpectations(t) |
| defer mu.AssertExpectations(t) |
| defer mvcs.AssertExpectations(t) |
| defer mct.AssertExpectations(t) |
| |
| // We should only process the last part of the commits |
| longCommits := makeSparseLongCommits()[2:] |
| |
| mvcs.On("Update", testutils.AnyContext, true, false).Return(nil) |
| mvcs.On("DetailsMulti", testutils.AnyContext, []string{ |
| data.SecondCommitHash, data.ThirdCommitHash, fourthCommitHash, |
| }, false).Return(longCommits, nil) |
| |
| mts.On("GetDenseTile", testutils.AnyContext, nCommits).Return(data.MakeTestTile(), makeSparseTilingCommits(), nil) |
| |
| // No ignores in this test |
| mis.On("List", testutils.AnyContext).Return(nil, nil) |
| |
| mu.On("UpdateChangeListsAsLanded", testutils.AnyContext, longCommits).Return(nil) |
| |
| mct.On("AllCommits").Return(makeSparseTilingCommits()[:2]) |
| |
| ts := New(CachedTileSourceConfig{ |
| NCommits: nCommits, |
| IgnoreStore: mis, |
| TraceStore: mts, |
| CLUpdater: mu, |
| VCS: mvcs, |
| }) |
| // Pretend there was a tile previously. |
| ts.lastCpxTile = mct |
| |
| err := ts.updateTile(context.Background()) |
| require.NoError(t, err) |
| |
| cpxTile := ts.GetTile() |
| require.NotNil(t, cpxTile) |
| |
| assert.Equal(t, makeSparseTilingCommits(), cpxTile.AllCommits()) |
| assert.Equal(t, data.MakeTestCommits(), cpxTile.DataCommits()) |
| assert.Equal(t, nCommits, cpxTile.FilledCommits()) |
| assert.Equal(t, data.MakeTestTile(), cpxTile.GetTile(types.ExcludeIgnoredTraces)) |
| assert.Equal(t, data.MakeTestTile(), cpxTile.GetTile(types.IncludeIgnoredTraces)) |
| assert.Equal(t, "5", metrics_utils.GetRecordedMetric(t, filledTracesAtHeadMetric, nil)) |
| assert.Equal(t, "1", metrics_utils.GetRecordedMetric(t, emptyCommitsAtHeadMetric, nil)) |
| } |
| |
| // TestUpdateTileHasPreviousAll tests the case where all commits have already been |
| // processed previously via the Updater. |
| func TestUpdateTileHasPreviousAll(t *testing.T) { |
| unittest.SmallTest(t) |
| |
| mis := &mock_ignorestore.Store{} |
| mts := &mocks.TraceStore{} |
| mvcs := &mock_vcs.VCS{} |
| mct := &mocks.ComplexTile{} |
| defer mis.AssertExpectations(t) |
| defer mts.AssertExpectations(t) |
| defer mvcs.AssertExpectations(t) |
| defer mct.AssertExpectations(t) |
| |
| mvcs.On("Update", testutils.AnyContext, true, false).Return(nil) |
| |
| mts.On("GetDenseTile", testutils.AnyContext, nCommits).Return(data.MakeTestTile(), makeSparseTilingCommits(), nil) |
| |
| // No ignores in this test |
| mis.On("List", testutils.AnyContext).Return(nil, nil) |
| |
| mct.On("AllCommits").Return(makeSparseTilingCommits()) |
| |
| ts := New(CachedTileSourceConfig{ |
| NCommits: nCommits, |
| IgnoreStore: mis, |
| TraceStore: mts, |
| // CLUpdater isn't called if we've already processed all commits before. |
| VCS: mvcs, |
| }) |
| // Pretend there was a tile previously. |
| ts.lastCpxTile = mct |
| |
| err := ts.updateTile(context.Background()) |
| require.NoError(t, err) |
| |
| cpxTile := ts.GetTile() |
| require.NotNil(t, cpxTile) |
| |
| assert.Equal(t, makeSparseTilingCommits(), cpxTile.AllCommits()) |
| assert.Equal(t, data.MakeTestCommits(), cpxTile.DataCommits()) |
| assert.Equal(t, nCommits, cpxTile.FilledCommits()) |
| assert.Equal(t, data.MakeTestTile(), cpxTile.GetTile(types.ExcludeIgnoredTraces)) |
| assert.Equal(t, data.MakeTestTile(), cpxTile.GetTile(types.IncludeIgnoredTraces)) |
| assert.Equal(t, "5", metrics_utils.GetRecordedMetric(t, filledTracesAtHeadMetric, nil)) |
| assert.Equal(t, "1", metrics_utils.GetRecordedMetric(t, emptyCommitsAtHeadMetric, nil)) |
| } |
| |
| // TestUpdateTileWithPublicParams tests the case where we are only allowed to show select devices. |
| func TestUpdateTileWithPublicParams(t *testing.T) { |
| unittest.SmallTest(t) |
| |
| mis := &mock_ignorestore.Store{} |
| mts := &mocks.TraceStore{} |
| mvcs := &mock_vcs.VCS{} |
| mct := &mocks.ComplexTile{} |
| defer mis.AssertExpectations(t) |
| defer mts.AssertExpectations(t) |
| defer mvcs.AssertExpectations(t) |
| defer mct.AssertExpectations(t) |
| |
| mvcs.On("Update", testutils.AnyContext, true, false).Return(nil) |
| |
| mts.On("GetDenseTile", testutils.AnyContext, nCommits).Return(data.MakeTestTile(), makeSparseTilingCommits(), nil) |
| |
| // No ignores in this test |
| mis.On("List", testutils.AnyContext).Return(nil, nil) |
| |
| mct.On("AllCommits").Return(makeSparseTilingCommits()) |
| |
| publicMatcher, err := publicparams.MatcherFromJSON([]byte(` |
| { |
| "gm": { |
| "device": ["angler", "bullhead"], |
| } |
| }`)) |
| require.NoError(t, err) |
| |
| ts := New(CachedTileSourceConfig{ |
| NCommits: nCommits, |
| IgnoreStore: mis, |
| TraceStore: mts, |
| VCS: mvcs, |
| PubliclyViewableParams: publicMatcher, |
| }) |
| // Pretend there was a tile previously. |
| ts.lastCpxTile = mct |
| |
| err = ts.updateTile(context.Background()) |
| require.NoError(t, err) |
| |
| cpxTile := ts.GetTile() |
| require.NotNil(t, cpxTile) |
| |
| assert.Equal(t, makeSparseTilingCommits(), cpxTile.AllCommits()) |
| assert.Equal(t, data.MakeTestCommits(), cpxTile.DataCommits()) |
| assert.Equal(t, nCommits, cpxTile.FilledCommits()) |
| |
| trimmedTile := data.MakeTestTile() |
| delete(trimmedTile.Traces, data.CrosshatchAlphaTraceID) |
| delete(trimmedTile.Traces, data.CrosshatchBetaTraceID) |
| trimmedTile.ParamSet["device"] = []string{data.AnglerDevice, data.BullheadDevice} |
| assert.Equal(t, trimmedTile, cpxTile.GetTile(types.ExcludeIgnoredTraces)) |
| // The removed traces should *not* come back even if we say to show ignored traces. |
| // PubliclyViewableParams is stronger than ignores. |
| assert.Equal(t, trimmedTile, cpxTile.GetTile(types.IncludeIgnoredTraces)) |
| // Normally, there were 5 traces with data (6 total, 1 empty). We should expect two traces |
| // have been removed due to the public list. As it happens, it's one of the crossHatch traces |
| // that is empty, so when we delete both of them, we are left with 4 traces with data. |
| assert.Equal(t, "4", metrics_utils.GetRecordedMetric(t, filledTracesAtHeadMetric, nil)) |
| assert.Equal(t, "1", metrics_utils.GetRecordedMetric(t, emptyCommitsAtHeadMetric, nil)) |
| } |
| |
| // TestUpdateTileWithRules tests the case where some traces are ignored. |
| func TestUpdateTileWithRules(t *testing.T) { |
| unittest.SmallTest(t) |
| |
| mis := &mock_ignorestore.Store{} |
| mts := &mocks.TraceStore{} |
| mvcs := &mock_vcs.VCS{} |
| mct := &mocks.ComplexTile{} |
| defer mis.AssertExpectations(t) |
| defer mts.AssertExpectations(t) |
| defer mvcs.AssertExpectations(t) |
| defer mct.AssertExpectations(t) |
| |
| mvcs.On("Update", testutils.AnyContext, true, false).Return(nil) |
| |
| mts.On("GetDenseTile", testutils.AnyContext, nCommits).Return(data.MakeTestTile(), makeSparseTilingCommits(), nil) |
| |
| // No ignores in this test |
| mis.On("List", testutils.AnyContext).Return([]ignore.Rule{ |
| { |
| Query: "device=crosshatch&name=test_beta", // hides one trace |
| }, |
| { |
| Query: "device=angler", // hides two traces |
| }, |
| }, nil) |
| |
| mct.On("AllCommits").Return(makeSparseTilingCommits()) |
| |
| ts := New(CachedTileSourceConfig{ |
| NCommits: nCommits, |
| IgnoreStore: mis, |
| TraceStore: mts, |
| VCS: mvcs, |
| }) |
| // Pretend there was a tile previously. |
| ts.lastCpxTile = mct |
| |
| err := ts.updateTile(context.Background()) |
| require.NoError(t, err) |
| |
| cpxTile := ts.GetTile() |
| require.NotNil(t, cpxTile) |
| |
| assert.Equal(t, makeSparseTilingCommits(), cpxTile.AllCommits()) |
| assert.Equal(t, data.MakeTestCommits(), cpxTile.DataCommits()) |
| assert.Equal(t, nCommits, cpxTile.FilledCommits()) |
| |
| trimmedTile := data.MakeTestTile() |
| delete(trimmedTile.Traces, data.AnglerAlphaTraceID) |
| delete(trimmedTile.Traces, data.AnglerBetaTraceID) |
| delete(trimmedTile.Traces, data.CrosshatchBetaTraceID) |
| assert.Equal(t, trimmedTile, cpxTile.GetTile(types.ExcludeIgnoredTraces)) |
| assert.Equal(t, data.MakeTestTile(), cpxTile.GetTile(types.IncludeIgnoredTraces)) |
| assert.Equal(t, "5", metrics_utils.GetRecordedMetric(t, filledTracesAtHeadMetric, nil)) |
| assert.Equal(t, "1", metrics_utils.GetRecordedMetric(t, emptyCommitsAtHeadMetric, nil)) |
| } |
| |
| const ( |
| // zerothCommitHash and fourthCommitHash are commits with no data, bolted on to the data in |
| // three_devices_data to emulate "sparse" commits. |
| zerothCommitHash = "000d148c4fb5b79ee6d40ac0308cf34f0c5b1ef6" |
| fourthCommitHash = "44459d416aa25919f421acdf6cd1fac336a4b7d6" |
| |
| // nCommits is how many commits actually have data in the tile we generate. |
| nCommits = 3 |
| ) |
| |
| // makeSparseLongCommits returns 5 vcsinfo.LongCommits; 3 are from three_devices_data with two |
| // extra commits, representing a "sparse" tile, where not every commit has data. |
| func makeSparseLongCommits() []*vcsinfo.LongCommit { |
| var rv []*vcsinfo.LongCommit |
| for _, tc := range makeSparseTilingCommits() { |
| rv = append(rv, &vcsinfo.LongCommit{ |
| // tilesource doesn't use any of these objects, so just fill out the hash |
| ShortCommit: &vcsinfo.ShortCommit{ |
| Hash: tc.Hash, |
| }, |
| }) |
| } |
| return rv |
| } |
| |
| // makeSparseLongCommits returns 5 tiling.Commit, 3 are from three_devices_data with two |
| // extra commits, representing a "sparse" tile, where not every commit has data. |
| func makeSparseTilingCommits() []tiling.Commit { |
| denseCommits := data.MakeTestCommits() |
| return []tiling.Commit{ |
| { |
| Hash: zerothCommitHash, |
| CommitTime: time.Date(2019, time.April, 22, 12, 0, 3, 0, time.UTC), |
| Author: "zero@example.com", |
| }, |
| denseCommits[0], denseCommits[1], denseCommits[2], |
| { |
| Hash: fourthCommitHash, |
| CommitTime: time.Date(2019, time.April, 28, 12, 0, 3, 0, time.UTC), |
| Author: "four@example.com", |
| }, |
| } |
| } |