2017-08-14 14:28:08 -04:00
|
|
|
/*Package golden provides tools for comparing large mutli-line strings.
|
|
|
|
|
|
|
|
Golden files are files in the ./testdata/ subdirectory of the package under test.
|
2020-02-22 12:12:14 -05:00
|
|
|
Golden files can be automatically updated to match new values by running
|
|
|
|
`go test pkgname -test.update-golden`. To ensure the update is correct
|
|
|
|
compare the diff of the old expected value to the new expected value.
|
2017-08-14 14:28:08 -04:00
|
|
|
*/
|
2020-02-22 12:12:14 -05:00
|
|
|
package golden // import "gotest.tools/v3/golden"
|
2017-08-14 14:28:08 -04:00
|
|
|
|
|
|
|
import (
|
2018-02-28 10:11:02 -05:00
|
|
|
"bytes"
|
2017-08-14 14:28:08 -04:00
|
|
|
"flag"
|
|
|
|
"fmt"
|
|
|
|
"io/ioutil"
|
2020-02-22 12:12:14 -05:00
|
|
|
"os"
|
2017-08-14 14:28:08 -04:00
|
|
|
"path/filepath"
|
|
|
|
|
2020-02-22 12:12:14 -05:00
|
|
|
"gotest.tools/v3/assert"
|
|
|
|
"gotest.tools/v3/assert/cmp"
|
|
|
|
"gotest.tools/v3/internal/format"
|
2017-08-14 14:28:08 -04:00
|
|
|
)
|
|
|
|
|
|
|
|
var flagUpdate = flag.Bool("test.update-golden", false, "update golden file")
|
|
|
|
|
2018-02-28 10:11:02 -05:00
|
|
|
type helperT interface {
|
|
|
|
Helper()
|
|
|
|
}
|
|
|
|
|
2022-03-01 09:50:32 -05:00
|
|
|
// NormalizeCRLFToLF enables end-of-line normalization for actual values passed
|
|
|
|
// to Assert and String, as well as the values saved to golden files with
|
|
|
|
// -test.update-golden.
|
|
|
|
//
|
|
|
|
// Defaults to true. If you use the core.autocrlf=true git setting on windows
|
|
|
|
// you will need to set this to false.
|
|
|
|
//
|
|
|
|
// The value may be set to false by setting GOTESTTOOLS_GOLDEN_NormalizeCRLFToLF=false
|
|
|
|
// in the environment before running tests.
|
|
|
|
//
|
|
|
|
// The default value may change in a future major release.
|
|
|
|
var NormalizeCRLFToLF = os.Getenv("GOTESTTOOLS_GOLDEN_NormalizeCRLFToLF") != "false"
|
|
|
|
|
|
|
|
// FlagUpdate returns true when the -test.update-golden flag has been set.
|
|
|
|
func FlagUpdate() bool {
|
|
|
|
return *flagUpdate
|
|
|
|
}
|
|
|
|
|
2020-02-22 12:12:14 -05:00
|
|
|
// Open opens the file in ./testdata
|
|
|
|
func Open(t assert.TestingT, filename string) *os.File {
|
|
|
|
if ht, ok := t.(helperT); ok {
|
|
|
|
ht.Helper()
|
|
|
|
}
|
|
|
|
f, err := os.Open(Path(filename))
|
|
|
|
assert.NilError(t, err)
|
|
|
|
return f
|
|
|
|
}
|
|
|
|
|
2018-02-28 10:11:02 -05:00
|
|
|
// Get returns the contents of the file in ./testdata
|
|
|
|
func Get(t assert.TestingT, filename string) []byte {
|
|
|
|
if ht, ok := t.(helperT); ok {
|
|
|
|
ht.Helper()
|
|
|
|
}
|
2017-08-14 14:28:08 -04:00
|
|
|
expected, err := ioutil.ReadFile(Path(filename))
|
2018-02-28 10:11:02 -05:00
|
|
|
assert.NilError(t, err)
|
2017-08-14 14:28:08 -04:00
|
|
|
return expected
|
|
|
|
}
|
|
|
|
|
2018-02-28 10:11:02 -05:00
|
|
|
// Path returns the full path to a file in ./testdata
|
2017-08-14 14:28:08 -04:00
|
|
|
func Path(filename string) string {
|
2018-02-28 10:11:02 -05:00
|
|
|
if filepath.IsAbs(filename) {
|
|
|
|
return filename
|
|
|
|
}
|
2017-08-14 14:28:08 -04:00
|
|
|
return filepath.Join("testdata", filename)
|
|
|
|
}
|
|
|
|
|
2017-12-21 15:25:54 -05:00
|
|
|
func removeCarriageReturn(in []byte) []byte {
|
2022-03-01 09:50:32 -05:00
|
|
|
if !NormalizeCRLFToLF {
|
|
|
|
return in
|
|
|
|
}
|
2017-12-21 15:25:54 -05:00
|
|
|
return bytes.Replace(in, []byte("\r\n"), []byte("\n"), -1)
|
|
|
|
}
|
|
|
|
|
2020-02-22 12:12:14 -05:00
|
|
|
// Assert compares actual to the expected value in the golden file.
|
|
|
|
//
|
|
|
|
// Running `go test pkgname -test.update-golden` will write the value of actual
|
2017-09-01 14:54:03 -04:00
|
|
|
// to the golden file.
|
2020-02-22 12:12:14 -05:00
|
|
|
//
|
|
|
|
// This is equivalent to assert.Assert(t, String(actual, filename))
|
2018-06-08 12:23:38 -04:00
|
|
|
func Assert(t assert.TestingT, actual string, filename string, msgAndArgs ...interface{}) {
|
2018-02-28 10:11:02 -05:00
|
|
|
if ht, ok := t.(helperT); ok {
|
|
|
|
ht.Helper()
|
2017-08-14 14:28:08 -04:00
|
|
|
}
|
2018-06-08 12:23:38 -04:00
|
|
|
assert.Assert(t, String(actual, filename), msgAndArgs...)
|
2018-02-28 10:11:02 -05:00
|
|
|
}
|
2017-08-14 14:28:08 -04:00
|
|
|
|
2018-02-28 10:11:02 -05:00
|
|
|
// String compares actual to the contents of filename and returns success
|
|
|
|
// if the strings are equal.
|
2020-02-22 12:12:14 -05:00
|
|
|
//
|
|
|
|
// Running `go test pkgname -test.update-golden` will write the value of actual
|
2017-12-21 15:25:54 -05:00
|
|
|
// to the golden file.
|
|
|
|
//
|
|
|
|
// Any \r\n substrings in actual are converted to a single \n character
|
|
|
|
// before comparing it to the expected string. When updating the golden file the
|
|
|
|
// normalized version will be written to the file. This allows Windows to use
|
|
|
|
// the same golden files as other operating systems.
|
2018-02-28 10:11:02 -05:00
|
|
|
func String(actual string, filename string) cmp.Comparison {
|
|
|
|
return func() cmp.Result {
|
2017-12-21 15:25:54 -05:00
|
|
|
actualBytes := removeCarriageReturn([]byte(actual))
|
2022-03-01 09:50:32 -05:00
|
|
|
result, expected := compare(actualBytes, filename)
|
2018-02-28 10:11:02 -05:00
|
|
|
if result != nil {
|
|
|
|
return result
|
|
|
|
}
|
2018-06-08 12:23:38 -04:00
|
|
|
diff := format.UnifiedDiff(format.DiffConfig{
|
|
|
|
A: string(expected),
|
|
|
|
B: string(actualBytes),
|
|
|
|
From: "expected",
|
|
|
|
To: "actual",
|
2018-02-28 10:11:02 -05:00
|
|
|
})
|
2020-02-22 12:12:14 -05:00
|
|
|
return cmp.ResultFailure("\n" + diff + failurePostamble(filename))
|
2018-02-28 10:11:02 -05:00
|
|
|
}
|
2017-08-14 14:28:08 -04:00
|
|
|
}
|
|
|
|
|
2020-02-22 12:12:14 -05:00
|
|
|
func failurePostamble(filename string) string {
|
|
|
|
return fmt.Sprintf(`
|
|
|
|
|
|
|
|
You can run 'go test . -test.update-golden' to automatically update %s to the new expected value.'
|
|
|
|
`, Path(filename))
|
|
|
|
}
|
|
|
|
|
|
|
|
// AssertBytes compares actual to the expected value in the golden.
|
|
|
|
//
|
|
|
|
// Running `go test pkgname -test.update-golden` will write the value of actual
|
|
|
|
// to the golden file.
|
|
|
|
//
|
|
|
|
// This is equivalent to assert.Assert(t, Bytes(actual, filename))
|
2018-02-28 10:11:02 -05:00
|
|
|
func AssertBytes(
|
|
|
|
t assert.TestingT,
|
|
|
|
actual []byte,
|
|
|
|
filename string,
|
|
|
|
msgAndArgs ...interface{},
|
2018-06-08 12:23:38 -04:00
|
|
|
) {
|
2018-02-28 10:11:02 -05:00
|
|
|
if ht, ok := t.(helperT); ok {
|
|
|
|
ht.Helper()
|
|
|
|
}
|
2018-06-08 12:23:38 -04:00
|
|
|
assert.Assert(t, Bytes(actual, filename), msgAndArgs...)
|
2018-02-28 10:11:02 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// Bytes compares actual to the contents of filename and returns success
|
|
|
|
// if the bytes are equal.
|
2020-02-22 12:12:14 -05:00
|
|
|
//
|
|
|
|
// Running `go test pkgname -test.update-golden` will write the value of actual
|
2017-12-21 15:25:54 -05:00
|
|
|
// to the golden file.
|
2018-02-28 10:11:02 -05:00
|
|
|
func Bytes(actual []byte, filename string) cmp.Comparison {
|
|
|
|
return func() cmp.Result {
|
2022-03-01 09:50:32 -05:00
|
|
|
result, expected := compare(actual, filename)
|
2018-02-28 10:11:02 -05:00
|
|
|
if result != nil {
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
msg := fmt.Sprintf("%v (actual) != %v (expected)", actual, expected)
|
2020-02-22 12:12:14 -05:00
|
|
|
return cmp.ResultFailure(msg + failurePostamble(filename))
|
2018-02-28 10:11:02 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-01 09:50:32 -05:00
|
|
|
func compare(actual []byte, filename string) (cmp.Result, []byte) {
|
|
|
|
if err := update(filename, actual); err != nil {
|
2018-02-28 10:11:02 -05:00
|
|
|
return cmp.ResultFromError(err), nil
|
|
|
|
}
|
|
|
|
expected, err := ioutil.ReadFile(Path(filename))
|
|
|
|
if err != nil {
|
|
|
|
return cmp.ResultFromError(err), nil
|
|
|
|
}
|
|
|
|
if bytes.Equal(expected, actual) {
|
|
|
|
return cmp.ResultSuccess, nil
|
|
|
|
}
|
|
|
|
return nil, expected
|
2017-08-14 14:28:08 -04:00
|
|
|
}
|
2022-03-01 09:50:32 -05:00
|
|
|
|
|
|
|
func update(filename string, actual []byte) error {
|
2022-03-01 10:51:15 -05:00
|
|
|
if !*flagUpdate {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
if dir := filepath.Dir(Path(filename)); dir != "." {
|
2022-03-01 09:50:32 -05:00
|
|
|
if err := os.MkdirAll(dir, 0755); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
2022-03-01 10:51:15 -05:00
|
|
|
return ioutil.WriteFile(Path(filename), actual, 0644)
|
2022-03-01 09:50:32 -05:00
|
|
|
}
|