blob: d886fa0a452567f329fb42d848148bb2d231dab5 [file] [log] [blame]
package gcs_baseliner
import (
"testing"
"github.com/stretchr/testify/mock"
assert "github.com/stretchr/testify/require"
"go.skia.org/infra/go/deepequal"
"go.skia.org/infra/go/testutils/unittest"
"go.skia.org/infra/golden/go/baseline"
"go.skia.org/infra/golden/go/mocks"
"go.skia.org/infra/golden/go/storage"
three_devices "go.skia.org/infra/golden/go/testutils/data_three_devices"
"go.skia.org/infra/golden/go/types"
)
// Test that the baseliner passes on the request to the storage.GCSClient
// for a baseline it hasn't seen before
func TestFetchBaselineSunnyDay(t *testing.T) {
unittest.SmallTest(t)
testCommitHash := "abcd12345"
mgs := makeMockGCSStorage()
defer mgs.AssertExpectations(t)
mgs.On("ReadBaseline", testCommitHash, baseline.MasterBranch).Return(three_devices.MakeTestBaseline(), nil).Once()
baseliner, err := New(mgs, nil, nil, nil)
assert.NoError(t, err)
b, err := baseliner.FetchBaseline(testCommitHash, baseline.MasterBranch, false)
assert.NoError(t, err)
deepequal.AssertDeepEqual(t, three_devices.MakeTestBaseline(), b)
}
// Test that the baseliner behaves differently when requesting a baseline
// for a given tryjob.
func TestFetchBaselineIssueSunnyDay(t *testing.T) {
unittest.SmallTest(t)
testCommitHash := "abcd12345"
testIssueID := int64(1234)
// These are valid, but arbitrary md5 hashes
IotaNewDigest := types.Digest("1115fba4ce5b4cb9ffd595beb63e7389")
KappaNewDigest := types.Digest("222d894f5b680a9f7bd74c8004b7d88d")
LambdaNewDigest := types.Digest("3333fe3127b984e4ff39f4885ddb0d98")
additionalTriages := &baseline.Baseline{
Expectations: types.Expectations{
"brand-new-test": map[types.Digest]types.Label{
IotaNewDigest: types.POSITIVE,
KappaNewDigest: types.NEGATIVE,
},
three_devices.BetaTest: map[types.Digest]types.Label{
LambdaNewDigest: types.POSITIVE,
// Change these two pre-existing digests
three_devices.BetaGood1Digest: types.NEGATIVE,
three_devices.BetaUntriaged1Digest: types.POSITIVE,
},
},
Issue: testIssueID,
StartCommit: nil, // This can all be blank, and in some real-world data, is blank
EndCommit: nil,
Filled: 0,
Total: 0,
}
mgs := makeMockGCSStorage()
defer mgs.AssertExpectations(t)
// mock the master baseline
mgs.On("ReadBaseline", testCommitHash, int64(0)).Return(three_devices.MakeTestBaseline(), nil).Once()
// mock the expectations that a user would have applied to their CL (that
// are not live on master yet).
mgs.On("ReadBaseline", "", testIssueID).Return(additionalTriages, nil).Once()
baseliner, err := New(mgs, nil, nil, nil)
assert.NoError(t, err)
b, err := baseliner.FetchBaseline(testCommitHash, testIssueID, false)
assert.NoError(t, err)
assert.Equal(t, testIssueID, b.Issue)
// The expectation should be the master baseline merged in with the additionalTriages
// with additionalTriages overwriting existing expectations, if applicable.
assert.Equal(t, types.Expectations{
"brand-new-test": map[types.Digest]types.Label{
IotaNewDigest: types.POSITIVE,
KappaNewDigest: types.NEGATIVE,
},
// AlphaTest should be unchanged from the master baseline.
three_devices.AlphaTest: map[types.Digest]types.Label{
three_devices.AlphaBad1Digest: types.NEGATIVE,
three_devices.AlphaGood1Digest: types.POSITIVE,
three_devices.AlphaUntriaged1Digest: types.UNTRIAGED,
},
three_devices.BetaTest: map[types.Digest]types.Label{
LambdaNewDigest: types.POSITIVE,
// Note that the state caused by this set of expectations overwrites what
// was on the master branch
three_devices.BetaGood1Digest: types.NEGATIVE,
three_devices.BetaUntriaged1Digest: types.POSITIVE,
},
}, b.Expectations)
// Ensure that reading the issue branch does not impact the master branch
b, err = baseliner.FetchBaseline(testCommitHash, baseline.MasterBranch, false)
assert.NoError(t, err)
assert.Equal(t, three_devices.MakeTestBaseline(), b)
}
func TestFetchBaselineCachingSunnyDay(t *testing.T) {
unittest.SmallTest(t)
testCommitHash := "abcd12345"
mgs := makeMockGCSStorage()
defer mgs.AssertExpectations(t)
// ReadBaseline should only be called once despite multiple requests below
mgs.On("ReadBaseline", testCommitHash, baseline.MasterBranch).Return(three_devices.MakeTestBaseline(), nil).Once()
baseliner, err := New(mgs, nil, nil, nil)
assert.NoError(t, err)
for i := 0; i < 10; i++ {
b, err := baseliner.FetchBaseline(testCommitHash, baseline.MasterBranch, false)
assert.NoError(t, err)
assert.NotNil(t, b)
deepequal.AssertDeepEqual(t, three_devices.MakeTestBaseline(), b)
}
}
// TODO(kjlubick): TestFetchBaseline which returns nil (difficult because it shells out to
// PushMasterBaseline)
// TestPushMasterBaselineSunnyDay verifies that the baseliner pulls in the latest tile
// (which has the traces and known images) and combines it with the triage status from
// ExpectationsStorage to create a baseline per commit.
func TestPushMasterBaselineSunnyDay(t *testing.T) {
unittest.SmallTest(t)
mgs := makeMockGCSStorage()
mcs := &mocks.TileInfo{}
mes := &mocks.ExpectationsStore{}
defer mgs.AssertExpectations(t)
defer mcs.AssertExpectations(t)
defer mes.AssertExpectations(t)
mcs.On("AllCommits").Return(three_devices.MakeTestCommits())
mcs.On("DataCommits").Return(three_devices.MakeTestCommits())
mcs.On("GetTile", types.ExcludeIgnoredTraces).Return(three_devices.MakeTestTile())
mes.On("Get").Return(three_devices.MakeTestExpectations(), nil)
mgs.On("WriteBaseline", mock.AnythingOfType("*baseline.Baseline")).Run(func(args mock.Arguments) {
b := args.Get(0).(*baseline.Baseline)
assert.NotNil(t, b)
assert.NotNil(t, b.StartCommit)
// These commits are per-commit baselines, thus the start and end are the same
deepequal.AssertDeepEqual(t, *b.StartCommit, *b.EndCommit)
assert.Equal(t, 6, b.Total)
// These per-commit baselines keep track of only the positive images we have seen,
// so make sure we see betaGood1Digest for all 3 and alphaGood1Digest show up in the
// third commit
switch b.StartCommit.Hash {
default:
assert.Fail(t, "Bad hash", b.StartCommit.Hash)
case three_devices.FirstCommitHash:
assert.Equal(t, 1, b.Filled)
assertLabel(t, b, three_devices.BetaTest, three_devices.BetaGood1Digest, types.POSITIVE)
case three_devices.SecondCommitHash:
assert.Equal(t, 1, b.Filled)
assertLabel(t, b, three_devices.BetaTest, three_devices.BetaGood1Digest, types.POSITIVE)
case three_devices.ThirdCommitHash:
assert.Equal(t, 2, b.Filled)
assertLabel(t, b, three_devices.AlphaTest, three_devices.AlphaGood1Digest, types.POSITIVE)
assertLabel(t, b, three_devices.BetaTest, three_devices.BetaGood1Digest, types.POSITIVE)
}
}).Return("gs://test-bucket/baselines/foo-baseline.json", nil).Times(3) // once per commit
baseliner, err := New(mgs, mes, nil, nil)
assert.NoError(t, err)
b, err := baseliner.PushMasterBaselines(mcs, "")
assert.NoError(t, err)
assert.Nil(t, b) // baseline should be nil because commit is ""
}
func makeMockGCSStorage() *mocks.GCSClient {
mgs := mocks.GCSClient{}
mgs.On("Options").Return(storage.GCSClientOptions{
HashesGSPath: "gs://test-bucket/hashes",
BaselineGSPath: "gs://test-bucket/baselines/",
}).Maybe()
return &mgs
}
func assertLabel(t *testing.T, b *baseline.Baseline, testName types.TestName, hash types.Digest, label types.Label) {
test, ok := b.Expectations[testName]
if !ok {
assert.Failf(t, "assertLabel", "Could not find test %s in baseline %#v", testName, b)
}
lab, ok := test[hash]
if !ok {
assert.Failf(t, "assertLabel", "Could not find hash %s in test %#v (name %s) (baseline %#v)", hash, test, testName, b)
}
assert.Equal(t, label, lab)
}