[goldctl] CloudClient.matchImageAgainstBaseline(): Return image matching algorithm name.
When running in pass/fail mode, this will allow the "goldctl imgtest add" command to determine whether to automatically triage the image as positive or not.
Bug: skia:9527, skia:10245
Change-Id: I4429fa396c37806756a02ff5f9f6a56d527b09d3
Reviewed-on: https://skia-review.googlesource.com/c/buildbot/+/289697
Commit-Queue: Leandro Lovisolo <lovisolo@google.com>
Reviewed-by: Kevin Lubick <kjlubick@google.com>
diff --git a/gold-client/go/goldclient/goldclient.go b/gold-client/go/goldclient/goldclient.go
index de7ab64..31382e2 100644
--- a/gold-client/go/goldclient/goldclient.go
+++ b/gold-client/go/goldclient/goldclient.go
@@ -363,12 +363,17 @@
})
egroup.Go(func() error {
- ret, err = c.matchImageAgainstBaseline(name, traceId, imgBytes, imgHash, optionalKeys)
+ match, algorithmName, err := c.matchImageAgainstBaseline(name, traceId, imgBytes, imgHash, optionalKeys)
if err != nil {
return skerr.Wrapf(err, "matching image against baseline")
}
+ ret = match
- if !ret {
+ if match && algorithmName != imgmatching.ExactMatching {
+ // TODO(lovisolo): Triage image as positive.
+ }
+
+ if !match {
link := fmt.Sprintf("%s/detail?test=%s&digest=%s", c.resultState.GoldURL, name, imgHash)
if c.resultState.SharedConfig.ChangeListID != "" {
link += "&issue=" + c.resultState.SharedConfig.ChangeListID
@@ -422,7 +427,8 @@
}
_, traceID := c.makeResultKeyAndTraceId(name, keys)
- return c.matchImageAgainstBaseline(name, traceID, imgBytes, imgHash, optionalKeys)
+ match, _, err := c.matchImageAgainstBaseline(name, traceID, imgBytes, imgHash, optionalKeys)
+ return match, skerr.Wrap(err)
}
// matchImageAgainstBaseline matches the given image against the baseline. A non-exact image
@@ -432,16 +438,22 @@
//
// It assumes that the baseline has already been downloaded from Gold.
//
-// Returns true if the image matches the baseline, or false otherwise. A non-nil error is returned
-// if there are any problems parsing or instantiating the specified image matching algorithm.
-func (c *CloudClient) matchImageAgainstBaseline(testName types.TestName, traceId tiling.TraceID, imageBytes []byte, imageHash types.Digest, optionalKeys map[string]string) (bool, error) {
+// Returns true if the image matches the baseline, or false otherwise.
+//
+// Returns the algorithm name used to determine the match. If the image's digest was labeled as
+// either positive or negative in the baseline, imgmatching.ExactMatching will be returned
+// regardless of whether a non-exact image matching algorithm was specified via the optionalKeys.
+//
+// A non-nil error is returned if there are any problems parsing or instantiating the specified
+// image matching algorithm, for example if there are any missing parameters.
+func (c *CloudClient) matchImageAgainstBaseline(testName types.TestName, traceId tiling.TraceID, imageBytes []byte, imageHash types.Digest, optionalKeys map[string]string) (bool, imgmatching.AlgorithmName, error) {
// First we check whether the digest is a known positive or negative, regardless of the specified
// image matching algorithm.
if c.resultState.Expectations[testName][imageHash] == expectations.Positive {
- return true, nil
+ return true, imgmatching.ExactMatching, nil
}
if c.resultState.Expectations[testName][imageHash] == expectations.Negative {
- return false, nil
+ return false, imgmatching.ExactMatching, nil
}
// Extract the specified image matching algorithm from the optionalKeys (defaulting to exact
@@ -449,40 +461,40 @@
// algorithm requires one (i.e. all but exact matching).
algorithmName, matcher, err := imgmatching.MakeMatcher(optionalKeys)
if err != nil {
- return false, skerr.Wrapf(err, "parsing image matching algorithm from optional keys")
+ return false, "", skerr.Wrapf(err, "parsing image matching algorithm from optional keys")
}
// Nothing else to do if performing exact matching: we've already checked whether the image is a
// known positive.
if algorithmName == imgmatching.ExactMatching {
- return false, nil
+ return false, algorithmName, nil
}
// Decode test output PNG image.
image, err := png.Decode(bytes.NewReader(imageBytes))
if err != nil {
- return false, skerr.Wrapf(err, "decoding PNG image")
+ return false, "", skerr.Wrapf(err, "decoding PNG image")
}
// Fetch the most recent positive digest.
sklog.Infof("Fetching most recent positive digest for trace with ID %q.", traceId)
mostRecentPositiveDigest, err := c.MostRecentPositiveDigest(traceId)
if err != nil {
- return false, skerr.Wrapf(err, "retrieving most recent positive image")
+ return false, "", skerr.Wrapf(err, "retrieving most recent positive image")
}
if mostRecentPositiveDigest == tiling.MissingDigest {
- return false, skerr.Fmt("no recent positive digests for trace with ID %q", traceId)
+ return false, "", skerr.Fmt("no recent positive digests for trace with ID %q", traceId)
}
// Download from GCS the image corresponding to the most recent positive digest.
mostRecentPositiveImage, _, err := c.getDigestFromCacheOrGCS(context.TODO(), mostRecentPositiveDigest)
if err != nil {
- return false, skerr.Wrapf(err, "downloading most recent positive image from GCS")
+ return false, "", skerr.Wrapf(err, "downloading most recent positive image from GCS")
}
// Return algorithm's output.
sklog.Infof("Non-exact image comparison using algorithm %q against most recent positive digest %q.", algorithmName, mostRecentPositiveDigest)
- return matcher.Match(mostRecentPositiveImage, image), nil
+ return matcher.Match(mostRecentPositiveImage, image), algorithmName, nil
}
// Finalize implements the GoldClient interface.
diff --git a/gold-client/go/goldclient/goldclient_test.go b/gold-client/go/goldclient/goldclient_test.go
index 0ce44ad..e4af424 100644
--- a/gold-client/go/goldclient/goldclient_test.go
+++ b/gold-client/go/goldclient/goldclient_test.go
@@ -1416,9 +1416,10 @@
}
// Parameters traceId and imageBytes are not used in exact matching.
- got, err := goldClient.matchImageAgainstBaseline(testName, "" /* =traceId */, []byte{} /* =imageBytes */, digest, nil /* =optionalKeys */)
+ got, algorithmName, err := goldClient.matchImageAgainstBaseline(testName, "" /* =traceId */, []byte{} /* =imageBytes */, digest, nil /* =optionalKeys */)
assert.NoError(t, err)
+ assert.Equal(t, imgmatching.ExactMatching, algorithmName)
assert.Equal(t, want, got)
})
}
@@ -1454,9 +1455,10 @@
}
// Parameters traceId and imageBytes are not used in exact matching.
- got, err := goldClient.matchImageAgainstBaseline(testName, "" /* =traceId */, []byte{} /* =imageBytes */, digest, optionalKeys)
+ got, algorithmName, err := goldClient.matchImageAgainstBaseline(testName, "" /* =traceId */, []byte{} /* =imageBytes */, digest, optionalKeys)
assert.NoError(t, err)
+ assert.Equal(t, imgmatching.ExactMatching, algorithmName)
assert.Equal(t, want, got)
})
}
@@ -1490,8 +1492,9 @@
},
}
- got, err := goldClient.matchImageAgainstBaseline(testName, "" /* =traceId */, nil /* =imageBytes */, digest, optionalKeys)
+ got, algorithmName, err := goldClient.matchImageAgainstBaseline(testName, "" /* =traceId */, nil /* =imageBytes */, digest, optionalKeys)
assert.NoError(t, err)
+ assert.Equal(t, imgmatching.ExactMatching, algorithmName)
assert.Equal(t, want, got)
})
}
@@ -1566,8 +1569,9 @@
string(imgmatching.PixelDeltaThreshold): pixelDeltaThreshold,
}
- actual, err := goldClient.matchImageAgainstBaseline(testName, traceId, tc.imageBytes, digest, optionalKeys)
+ actual, algorithmName, err := goldClient.matchImageAgainstBaseline(testName, traceId, tc.imageBytes, digest, optionalKeys)
assert.NoError(t, err)
+ assert.Equal(t, imgmatching.FuzzyMatching, algorithmName)
assert.Equal(t, tc.expected, actual)
})
}
@@ -1612,7 +1616,7 @@
goldClient, cleanup, _, _ := makeGoldClientForMatchImageAgainstBaselineTests(t)
defer cleanup()
- _, err := goldClient.matchImageAgainstBaseline("my_test", "" /* =traceId */, nil /* =imageBytes */, "11111111111111111111111111111111", tc.optionalKeys)
+ _, _, err := goldClient.matchImageAgainstBaseline("my_test", "" /* =traceId */, nil /* =imageBytes */, "11111111111111111111111111111111", tc.optionalKeys)
assert.Error(t, err)
assert.Contains(t, err.Error(), tc.error)
})
@@ -1644,7 +1648,7 @@
string(imgmatching.PixelDeltaThreshold): "0",
}
- _, err := goldClient.matchImageAgainstBaseline(testName, traceId, imageBytes, digest, optionalKeys)
+ _, _, err := goldClient.matchImageAgainstBaseline(testName, traceId, imageBytes, digest, optionalKeys)
assert.Error(t, err)
assert.Contains(t, err.Error(), `no recent positive digests for trace with ID ",name=my_test,"`)
}
@@ -1673,8 +1677,9 @@
},
}
- got, err := goldClient.matchImageAgainstBaseline(testName, "" /* =traceId */, nil /* =imageBytes */, digest, optionalKeys)
+ got, algorithmName, err := goldClient.matchImageAgainstBaseline(testName, "" /* =traceId */, nil /* =imageBytes */, digest, optionalKeys)
assert.NoError(t, err)
+ assert.Equal(t, imgmatching.ExactMatching, algorithmName)
assert.Equal(t, want, got)
})
}
@@ -1731,8 +1736,9 @@
string(imgmatching.EdgeThreshold): edgeThreshold,
}
- actual, err := goldClient.matchImageAgainstBaseline(testName, traceId, testImageBytes, digest, optionalKeys)
+ actual, algorithmName, err := goldClient.matchImageAgainstBaseline(testName, traceId, testImageBytes, digest, optionalKeys)
assert.NoError(t, err)
+ assert.Equal(t, imgmatching.SobelFuzzyMatching, algorithmName)
assert.Equal(t, expected, actual)
})
}
@@ -1791,7 +1797,7 @@
goldClient, cleanup, _, _ := makeGoldClientForMatchImageAgainstBaselineTests(t)
defer cleanup()
- _, err := goldClient.matchImageAgainstBaseline("my_test", "" /* =traceId */, nil /* =imageBytes */, "11111111111111111111111111111111", tc.optionalKeys)
+ _, _, err := goldClient.matchImageAgainstBaseline("my_test", "" /* =traceId */, nil /* =imageBytes */, "11111111111111111111111111111111", tc.optionalKeys)
assert.Error(t, err)
assert.Contains(t, err.Error(), tc.error)
})
@@ -1808,7 +1814,7 @@
imgmatching.AlgorithmNameOptKey: "unknown algorithm",
}
- _, err := goldClient.matchImageAgainstBaseline("" /* =testName */, "" /* =traceId */, nil /* =imageBytes */, "" /* =digest */, optionalKeys)
+ _, _, err := goldClient.matchImageAgainstBaseline("" /* =testName */, "" /* =traceId */, nil /* =imageBytes */, "" /* =digest */, optionalKeys)
assert.Error(t, err)
assert.Contains(t, err.Error(), "unrecognized image matching algorithm")
}