forked from gonum/plot
/
checkplot.go
125 lines (111 loc) · 3.49 KB
/
checkplot.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
// Copyright ©2015 The Gonum Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package cmpimg
import (
"bytes"
"encoding/base64"
"flag"
"image"
"image/png"
"os"
"path/filepath"
"strings"
"testing"
)
var GenerateTestData = flag.Bool("regen", false, "Uses the current state to regenerate the test data.")
func goldenPath(path string) string {
ext := filepath.Ext(path)
noext := strings.TrimSuffix(path, ext)
return noext + "_golden" + ext
}
// CheckPlot checks a generated plot against a previously created reference.
// If GenerateTestData = true, it regenerates the reference.
// For image.Image formats, a base64 encoded png representation is output to
// the testing log when a difference is identified.
func CheckPlot(ExampleFunc func(), t *testing.T, filenames ...string) {
CheckPlotApprox(ExampleFunc, t, 0, filenames...)
}
// CheckPlotApprox checks a generated plot against a previously created reference.
// The normalized delta parameter describes how tight the matching should be
// performed, where delta=0 expresses a perfect match, and delta=1 a very loose match.
// If GenerateTestData = true, it regenerates the reference.
// For image.Image formats, a base64 encoded png representation is output to
// the testing log when a difference is identified.
func CheckPlotApprox(ExampleFunc func(), t *testing.T, delta float64, filenames ...string) {
t.Helper()
paths := make([]string, len(filenames))
for i, fn := range filenames {
paths[i] = filepath.Join("testdata", fn)
}
if *GenerateTestData {
// Recreate Golden images and exit.
ExampleFunc()
for _, path := range paths {
golden := goldenPath(path)
_ = os.Remove(golden)
if err := os.Rename(path, golden); err != nil {
t.Fatal(err)
}
}
return
}
// Run the example.
ExampleFunc()
// Read the images we've just generated and check them against the
// Golden Images.
for _, path := range paths {
got, err := os.ReadFile(path)
if err != nil {
t.Errorf("Failed to read %s: %v", path, err)
continue
}
golden := goldenPath(path)
want, err := os.ReadFile(golden)
if err != nil {
t.Errorf("Failed to read golden file %s: %v", golden, err)
continue
}
typ := filepath.Ext(path)[1:] // remove the dot in e.g. ".pdf"
ok, err := EqualApprox(typ, got, want, delta)
if err != nil {
t.Errorf("failed to compare image for %s: %v", path, err)
continue
}
if !ok {
t.Errorf("image mismatch for %s\n", path)
switch typ {
case "jpeg", "jpg", "png", "tiff", "tif":
v1, _, err := image.Decode(bytes.NewReader(got))
if err != nil {
t.Errorf("failed to decode %s: %v", path, err)
continue
}
v2, _, err := image.Decode(bytes.NewReader(want))
if err != nil {
t.Errorf("failed to decode %s: %v", golden, err)
continue
}
dst := image.NewRGBA64(v1.Bounds().Union(v2.Bounds()))
rect := Diff(dst, v1, v2)
t.Logf("image bounds union:%+v diff bounds intersection:%+v", dst.Bounds(), rect)
var buf bytes.Buffer
err = png.Encode(&buf, dst)
if err != nil {
t.Errorf("failed to encode difference png: %v", err)
continue
}
t.Log("image diff:")
t.Log("IMAGE:" + base64.StdEncoding.EncodeToString(buf.Bytes()))
buf.Reset()
err = png.Encode(&buf, v1)
if err != nil {
t.Errorf("failed to encode result png: %+v", err)
continue
}
t.Log("image check:")
t.Log("IMAGE:" + base64.StdEncoding.EncodeToString(buf.Bytes()))
}
}
}
}