blob: 0886a16185e4051121ad862e9b1438819cdebcca [file] [log] [blame]
package digesttools_test
import (
"math"
"sort"
"testing"
"github.com/stretchr/testify/mock"
assert "github.com/stretchr/testify/require"
"go.skia.org/infra/go/testutils/unittest"
"go.skia.org/infra/golden/go/diff"
"go.skia.org/infra/golden/go/digest_counter"
"go.skia.org/infra/golden/go/digesttools"
"go.skia.org/infra/golden/go/mocks"
"go.skia.org/infra/golden/go/types"
)
// TestClosestDigest tests the basic interaction between the DiffFinder
// and DiffStore for finding the closest positive and negative diffs.
func TestClosestDigest(t *testing.T) {
unittest.SmallTest(t)
mds := &mocks.DiffStore{}
mdc := &mocks.DigestCounter{}
defer mds.AssertExpectations(t)
defer mdc.AssertExpectations(t)
exp := types.Expectations{
mockTest: map[types.Digest]types.Label{
mockDigestA: types.POSITIVE,
mockDigestB: types.NEGATIVE,
mockDigestC: types.UNTRIAGED,
mockDigestD: types.UNTRIAGED,
mockDigestF: types.POSITIVE,
mockDigestG: types.POSITIVE,
},
}
digestCounts := map[types.TestName]digest_counter.DigestCount{
mockTest: {
mockDigestA: 2,
mockDigestB: 2,
mockDigestC: 2,
mockDigestD: 2,
mockDigestE: 2,
},
}
mdc.On("ByTest").Return(digestCounts)
mds.On("UnavailableDigests").Return(map[types.Digest]*diff.DigestFailure{})
cdf := digesttools.NewClosestDiffFinder(exp, mdc, mds)
cdf.Precompute()
// Only mockDigestA is both triaged positive and in the digestCounts (meaning, we saw that digest
// in this tile).
expectedToCompareAgainst := types.DigestSlice{mockDigestA}
mds.On("Get", diff.PRIORITY_NOW, mockDigestF, expectedToCompareAgainst).Return(diffEIsClosest(), nil).Once()
// First test against a test that has positive digests.
c := cdf.ClosestDigest(mockTest, mockDigestF, types.POSITIVE)
assert.InDelta(t, 0.0372, float64(c.Diff), 0.01)
assert.Equal(t, mockDigestE, c.Digest)
assert.Equal(t, []int{5, 3, 4, 0}, c.MaxRGBA)
// mockDigestB is the only negative digest that shows up in the tile.
expectedToCompareAgainst = types.DigestSlice{mockDigestB}
mds.On("Get", diff.PRIORITY_NOW, mockDigestF, expectedToCompareAgainst).Return(diffBIsClosest(), nil).Once()
// Now test against negative digests.
c = cdf.ClosestDigest(mockTest, mockDigestF, types.NEGATIVE)
assert.InDelta(t, 0.0558, float64(c.Diff), 0.01)
assert.Equal(t, mockDigestB, c.Digest)
assert.Equal(t, []int{2, 7, 1, 3}, c.MaxRGBA)
}
// TestClosestDigestWithUnavailable tests some more tricky logic dealing
// with unavailable digests and tests with no digests.
func TestClosestDigestWithUnavailable(t *testing.T) {
unittest.SmallTest(t)
mds := &mocks.DiffStore{}
mdc := &mocks.DigestCounter{}
defer mds.AssertExpectations(t)
defer mdc.AssertExpectations(t)
exp := types.Expectations{
mockTest: map[types.Digest]types.Label{
mockDigestA: types.POSITIVE,
mockDigestB: types.NEGATIVE,
mockDigestC: types.POSITIVE,
mockDigestD: types.POSITIVE,
mockDigestF: types.POSITIVE,
mockDigestG: types.POSITIVE,
},
}
digestCounts := map[types.TestName]digest_counter.DigestCount{
mockTest: {
mockDigestA: 2,
mockDigestB: 2,
mockDigestC: 2,
mockDigestD: 2,
mockDigestE: 2,
},
}
mdc.On("ByTest").Return(digestCounts)
mds.On("UnavailableDigests").Return(map[types.Digest]*diff.DigestFailure{
mockDigestA: {},
mockDigestB: {},
})
cdf := digesttools.NewClosestDiffFinder(exp, mdc, mds)
cdf.Precompute()
// mockDigestA should not be in this list because it is in the unavailable list.
expectedToCompareAgainst := types.DigestSlice{mockDigestC, mockDigestD}
sort.Sort(expectedToCompareAgainst)
mds.On("Get", diff.PRIORITY_NOW, mockDigestF, mock.AnythingOfType("types.DigestSlice")).Run(func(args mock.Arguments) {
actual := args.Get(2).(types.DigestSlice)
sort.Sort(actual)
assert.Equal(t, expectedToCompareAgainst, actual)
}).Return(diffEIsClosest(), nil).Once()
c := cdf.ClosestDigest(mockTest, mockDigestF, types.POSITIVE)
assert.InDelta(t, 0.0372, float64(c.Diff), 0.01)
assert.Equal(t, mockDigestE, c.Digest)
assert.Equal(t, []int{5, 3, 4, 0}, c.MaxRGBA)
// There is only one negative digest, and it is in the unavailable list, so it should
// return that it couldn't find one.
c = cdf.ClosestDigest(mockTest, mockDigestF, types.NEGATIVE)
assert.InDelta(t, math.MaxFloat32, float64(c.Diff), 0.01)
assert.Equal(t, digesttools.NoDigestFound, c.Digest)
assert.Equal(t, []int{}, c.MaxRGBA)
// Now test against a test with no digests at all in the latest tile.
c = cdf.ClosestDigest(testThatDoesNotExist, mockDigestF, types.POSITIVE)
assert.Equal(t, float32(math.MaxFloat32), c.Diff)
assert.Equal(t, digesttools.NoDigestFound, c.Digest)
assert.Equal(t, []int{}, c.MaxRGBA)
}
const (
mockTest = types.TestName("test_foo")
testThatDoesNotExist = types.TestName("test_bar")
mockDigestA = types.Digest("aaa")
mockDigestB = types.Digest("bbb")
mockDigestC = types.Digest("ccc")
mockDigestD = types.Digest("ddd")
mockDigestE = types.Digest("eee")
mockDigestF = types.Digest("fff")
mockDigestG = types.Digest("ggg")
)
// diffEIsClosest creates data such that mockDigestE is the closest match.
func diffEIsClosest() map[types.Digest]interface{} {
return map[types.Digest]interface{}{
mockDigestE: &diff.DiffMetrics{
PixelDiffPercent: 0.1,
MaxRGBADiffs: []int{5, 3, 4, 0},
},
mockDigestA: &diff.DiffMetrics{
PixelDiffPercent: 10,
MaxRGBADiffs: []int{15, 13, 14, 10},
},
mockDigestB: &diff.DiffMetrics{
PixelDiffPercent: 20,
MaxRGBADiffs: []int{25, 23, 24, 20},
},
}
}
// diffBIsClosest creates data such that mockDigestB is the closest match.
func diffBIsClosest() map[types.Digest]interface{} {
return map[types.Digest]interface{}{
mockDigestE: &diff.DiffMetrics{
PixelDiffPercent: 30,
MaxRGBADiffs: []int{35, 33, 34, 30},
},
mockDigestA: &diff.DiffMetrics{
PixelDiffPercent: 10,
MaxRGBADiffs: []int{15, 13, 14, 10},
},
mockDigestB: &diff.DiffMetrics{
PixelDiffPercent: .2,
MaxRGBADiffs: []int{2, 7, 1, 3},
},
}
}