...

Source file src/gonum.org/v1/plot/cmpimg/checkplot.go

Documentation: gonum.org/v1/plot/cmpimg

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

View as plain text