blob: 0048e3e35b5da7771dc4db87c5b2606eca32b14d [file] [log] [blame]
package diffstore
import (
"fmt"
"image"
"net"
"net/http/httptest"
"path"
"path/filepath"
"sort"
"testing"
assert "github.com/stretchr/testify/require"
"go.skia.org/infra/go/fileutil"
"go.skia.org/infra/go/testutils"
"go.skia.org/infra/go/tiling"
"go.skia.org/infra/go/timer"
"go.skia.org/infra/go/util"
"go.skia.org/infra/golden/go/diff"
"go.skia.org/infra/golden/go/types"
"google.golang.org/grpc"
)
const (
TEST_N_DIGESTS = 20
// Prefix for the image url handler.
IMAGE_URL_PREFIX = "/img/"
)
func TestMemDiffStore(t *testing.T) {
testutils.LargeTest(t)
// Get a small tile and get them cached.
w, cleanup := testutils.TempDir(t)
defer cleanup()
baseDir := path.Join(w, TEST_DATA_BASE_DIR+"-diffstore")
client, tile := getSetupAndTile(t, baseDir)
mapper := NewGoldDiffStoreMapper(&diff.DiffMetrics{})
diffStore, err := NewMemDiffStore(client, baseDir, []string{TEST_GCS_BUCKET_NAME}, TEST_GCS_IMAGE_DIR, 10, mapper)
assert.NoError(t, err)
memDiffStore := diffStore.(*MemDiffStore)
testDiffStore(t, tile, baseDir, diffStore, memDiffStore)
}
type DummyDiffStoreMapper struct {
GoldDiffStoreMapper
}
func (d DummyDiffStoreMapper) DiffFn(leftImg *image.NRGBA, rightImg *image.NRGBA) (interface{}, *image.NRGBA) {
return 42, nil
}
func TestDiffFn(t *testing.T) {
testutils.LargeTest(t)
w, cleanup := testutils.TempDir(t)
defer cleanup()
baseDir := path.Join(w, TEST_DATA_BASE_DIR+"-difffn")
client, _ := getSetupAndTile(t, baseDir)
// Instantiate a new MemDiffStore with the DummyDiffFn.
mapper := DummyDiffStoreMapper{GoldDiffStoreMapper: NewGoldDiffStoreMapper(&diff.DiffMetrics{}).(GoldDiffStoreMapper)}
diffStore, err := NewMemDiffStore(client, baseDir, []string{TEST_GCS_BUCKET_NAME}, TEST_GCS_IMAGE_DIR, 10, mapper)
assert.NoError(t, err)
memDiffStore := diffStore.(*MemDiffStore)
img1 := image.NewNRGBA(image.Rect(1, 2, 3, 4))
img2 := image.NewNRGBA(image.Rect(9, 8, 7, 6))
// Check that proper values are returned by the diff function.
diffMetrics, diffImg := memDiffStore.mapper.DiffFn(img1, img2)
assert.Equal(t, 42, diffMetrics)
assert.Nil(t, diffImg)
}
func TestNetDiffStore(t *testing.T) {
testutils.LargeTest(t)
w, cleanup := testutils.TempDir(t)
defer cleanup()
baseDir := path.Join(w, TEST_DATA_BASE_DIR+"-netdiffstore")
client, tile := getSetupAndTile(t, baseDir)
mapper := NewGoldDiffStoreMapper(&diff.DiffMetrics{})
memDiffStore, err := NewMemDiffStore(client, baseDir, []string{TEST_GCS_BUCKET_NAME}, TEST_GCS_IMAGE_DIR, 10, mapper)
assert.NoError(t, err)
// Start the server that wraps around the MemDiffStore.
codec := MetricMapCodec{}
serverImpl := NewDiffServiceServer(memDiffStore, codec)
lis, err := net.Listen("tcp", "localhost:0")
assert.NoError(t, err)
// Start the grpc server.
server := grpc.NewServer()
RegisterDiffServiceServer(server, serverImpl)
go func() {
_ = server.Serve(lis)
}()
defer server.Stop()
// Start the http server.
imgHandler, err := memDiffStore.ImageHandler(IMAGE_URL_PREFIX)
assert.NoError(t, err)
httpServer := httptest.NewServer(imgHandler)
defer func() { httpServer.Close() }()
// Create the NetDiffStore.
addr := lis.Addr().String()
conn, err := grpc.Dial(addr, grpc.WithInsecure())
assert.NoError(t, err)
defer func() {
assert.NoError(t, conn.Close())
}()
netDiffStore, err := NewNetDiffStore(conn, httpServer.Listener.Addr().String(), codec)
assert.NoError(t, err)
// run tests against it.
testDiffStore(t, tile, baseDir, netDiffStore, memDiffStore.(*MemDiffStore))
}
func testDiffStore(t *testing.T, tile *tiling.Tile, baseDir string, diffStore diff.DiffStore, memDiffStore *MemDiffStore) {
// Pick the test with highest number of digests.
byName := map[string]util.StringSet{}
for _, trace := range tile.Traces {
gTrace := trace.(*types.GoldenTrace)
name := gTrace.Params_[types.PRIMARY_KEY_FIELD]
if _, ok := byName[name]; !ok {
byName[name] = util.StringSet{}
}
byName[name].AddLists(gTrace.Values)
}
testDigests := make([][]string, 0, len(byName))
for _, digests := range byName {
delete(digests, types.MISSING_DIGEST)
testDigests = append(testDigests, digests.Keys())
}
sort.Sort(digestsSlice(testDigests))
// Warm the digests and make sure they are in the cache.
digests := testDigests[0][:TEST_N_DIGESTS]
diffStore.WarmDigests(diff.PRIORITY_NOW, digests, true)
for _, d := range digests {
assert.True(t, memDiffStore.imgLoader.IsOnDisk(d), fmt.Sprintf("Could not find '%s'", d))
}
// Warm the diffs and make sure they are in the cache.
diffStore.WarmDiffs(diff.PRIORITY_NOW, digests, digests)
memDiffStore.sync()
diffIDs := make([]string, 0, len(digests)*len(digests))
for _, d1 := range digests {
for _, d2 := range digests {
if d1 != d2 {
id := memDiffStore.mapper.DiffID(d1, d2)
diffIDs = append(diffIDs, id)
assert.True(t, memDiffStore.diffMetricsCache.Contains(id))
}
}
}
// Get the results and make sure they are correct.
foundDiffs := make(map[string]map[string]interface{}, len(digests))
ti := timer.New("Get warmed diffs.")
for _, oneDigest := range digests {
found, err := diffStore.Get(diff.PRIORITY_NOW, oneDigest, digests)
assert.NoError(t, err)
foundDiffs[oneDigest] = found
// Load the diff from disk and compare.
for twoDigest, dr := range found {
id := memDiffStore.mapper.DiffID(oneDigest, twoDigest)
loadedDr, err := memDiffStore.metricsStore.loadDiffMetrics(id)
assert.NoError(t, err)
assert.Equal(t, dr, loadedDr, "Comparing: %s", id)
}
}
ti.Stop()
testDiffs(t, baseDir, memDiffStore, digests, digests, foundDiffs)
// Get the results directly and make sure they are correct.
digests = testDigests[1][:TEST_N_DIGESTS]
ti = timer.New("Get cold diffs")
foundDiffs = make(map[string]map[string]interface{}, len(digests))
for _, oneDigest := range digests {
found, err := diffStore.Get(diff.PRIORITY_NOW, oneDigest, digests)
assert.NoError(t, err)
foundDiffs[oneDigest] = found
}
ti.Stop()
testDiffs(t, baseDir, memDiffStore, digests, digests, foundDiffs)
// Diff against an arbitrary GCS location.
gcsImgID := GCSPathToImageID(TEST_GCS_SECONDARY_BUCKET, TEST_PATH_IMG_1)
foundDiffs = map[string]map[string]interface{}{}
for _, oneDigest := range digests {
found, err := diffStore.Get(diff.PRIORITY_NOW, oneDigest, []string{gcsImgID})
assert.NoError(t, err)
assert.Equal(t, 1, len(found))
foundDiffs[oneDigest] = found
}
testDiffs(t, baseDir, memDiffStore, digests, []string{gcsImgID}, foundDiffs)
}
func testDiffs(t *testing.T, baseDir string, diffStore *MemDiffStore, leftDigests, rightDigests []string, result map[string]map[string]interface{}) {
diffStore.sync()
for _, left := range leftDigests {
for _, right := range rightDigests {
if left != right {
_, ok := result[left][right]
assert.True(t, ok, fmt.Sprintf("left: %s, right:%s", left, right))
diffPath := diffStore.mapper.DiffPath(left, right)
assert.True(t, fileutil.FileExists(filepath.Join(diffStore.localDiffDir, diffPath)), fmt.Sprintf("Could not find %s", diffPath))
}
}
}
}
type DummyDiffMetrics struct {
NumDiffPixels int
PercentDiffPixels float32
}
type digestsSlice [][]string
func (d digestsSlice) Len() int { return len(d) }
func (d digestsSlice) Less(i, j int) bool { return len(d[i]) > len(d[j]) }
func (d digestsSlice) Swap(i, j int) { d[i], d[j] = d[j], d[i] }