shiny/iconvg: add a decode + encode round-trip test

Given we promise deterministic encoding, and decoding is also
(trivially) deterministic, it's viable to add another test to
ensure that decoding and encoding an IconVG file gives us the
same bytes back.

Since the Encoder.HighResolutionCoordinates option is not a
part of the image metadata but needed for this test to work,
obtain it from the filename.

Also remove some comparisons of adj that can never be false
because no value of type uint8 is less than 0.

Change-Id: I356ec01c8dd582aaeff14b39bb0272ca93eb1d2a
Reviewed-on: https://go-review.googlesource.com/c/exp/+/279295
Run-TryBot: Dmitri Shuralyov <dmitshur@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Nigel Tao <nigeltao@golang.org>
Trust: Nigel Tao <nigeltao@golang.org>
Trust: Dmitri Shuralyov <dmitshur@golang.org>
diff --git a/shiny/iconvg/decode_test.go b/shiny/iconvg/decode_test.go
index c1778e6..c06d816 100644
--- a/shiny/iconvg/decode_test.go
+++ b/shiny/iconvg/decode_test.go
@@ -14,6 +14,7 @@
 	"io/ioutil"
 	"os"
 	"path/filepath"
+	"runtime"
 	"strings"
 	"testing"
 )
@@ -173,6 +174,54 @@
 	}
 }
 
+// The IconVG decoder and encoder are expected to be completely deterministic,
+// so check that we get the original bytes after a decode + encode round-trip.
+func TestDecodeEncodeRoundTrip(t *testing.T) {
+	for _, tc := range testdataTestCases {
+		ivgData, err := ioutil.ReadFile(filepath.FromSlash(tc.filename) + ".ivg")
+		if err != nil {
+			t.Errorf("%s: ReadFile: %v", tc.filename, err)
+			continue
+		}
+		var e resolutionPreservingEncoder
+		e.HighResolutionCoordinates = strings.HasSuffix(tc.filename, ".hires")
+		if err := Decode(&e, ivgData, nil); err != nil {
+			t.Errorf("%s: Decode: %v", tc.filename, err)
+			continue
+		}
+		got, err := e.Bytes()
+		if err != nil {
+			t.Errorf("%s: Encoder.Bytes: %v", tc.filename, err)
+			continue
+		}
+		if want := ivgData; !bytes.Equal(got, want) {
+			t.Errorf("%s:\ngot  %d bytes (on GOOS=%s GOARCH=%s, using compiler %q):\n% x\nwant %d bytes:\n% x",
+				tc.filename, len(got), runtime.GOOS, runtime.GOARCH, runtime.Compiler, got, len(want), want)
+			gotDisasm, err1 := disassemble(got)
+			wantDisasm, err2 := disassemble(want)
+			if err1 == nil && err2 == nil {
+				diffLines(t, string(gotDisasm), string(wantDisasm))
+			}
+		}
+	}
+}
+
+// resolutionPreservingEncoder is an Encoder
+// whose Reset method keeps prior resolution.
+type resolutionPreservingEncoder struct {
+	Encoder
+}
+
+// Reset resets the Encoder for the given Metadata.
+//
+// Unlike Encoder.Reset, it leaves the value
+// of e.HighResolutionCoordinates unmodified.
+func (e *resolutionPreservingEncoder) Reset(m Metadata) {
+	orig := e.HighResolutionCoordinates
+	e.Encoder.Reset(m)
+	e.HighResolutionCoordinates = orig
+}
+
 func TestDecodeAndRasterize(t *testing.T) {
 	for _, tc := range testdataTestCases {
 		ivgData, err := ioutil.ReadFile(filepath.FromSlash(tc.filename) + ".ivg")
diff --git a/shiny/iconvg/encode.go b/shiny/iconvg/encode.go
index 6c27f83..47e6a58 100644
--- a/shiny/iconvg/encode.go
+++ b/shiny/iconvg/encode.go
@@ -233,7 +233,7 @@
 	if e.err != nil {
 		return
 	}
-	if adj < 0 || 6 < adj {
+	if adj > 6 {
 		e.err = errInvalidSelectorAdjustment
 		return
 	}
@@ -272,7 +272,7 @@
 	if e.err != nil {
 		return
 	}
-	if adj < 0 || 6 < adj {
+	if adj > 6 {
 		e.err = errInvalidSelectorAdjustment
 		return
 	}
@@ -440,7 +440,7 @@
 	if e.err != nil {
 		return
 	}
-	if adj < 0 || 6 < adj {
+	if adj > 6 {
 		e.err = errInvalidSelectorAdjustment
 		return
 	}
diff --git a/shiny/iconvg/encode_test.go b/shiny/iconvg/encode_test.go
index 8c2592c..0f41f52 100644
--- a/shiny/iconvg/encode_test.go
+++ b/shiny/iconvg/encode_test.go
@@ -46,8 +46,13 @@
 		// to non-determinism in floating-point math, the encoder needs to be fixed.
 		//
 		// See golang.org/issue/43219#issuecomment-748531069.
-		t.Fatalf("\ngot  %d bytes (on GOOS=%s GOARCH=%s, using compiler %q):\n% x\nwant %d bytes:\n% x",
+		t.Errorf("\ngot  %d bytes (on GOOS=%s GOARCH=%s, using compiler %q):\n% x\nwant %d bytes:\n% x",
 			len(got), runtime.GOOS, runtime.GOARCH, runtime.Compiler, got, len(want), want)
+		gotDisasm, err1 := disassemble(got)
+		wantDisasm, err2 := disassemble(want)
+		if err1 == nil && err2 == nil {
+			diffLines(t, string(gotDisasm), string(wantDisasm))
+		}
 	}
 }