| /* |
| Package golden provides tools for comparing large mutli-line strings. |
| |
| Golden files are files in the ./testdata/ subdirectory of the package under test. |
| Golden files can be automatically updated to match new values by running |
| `go test pkgname -update`. To ensure the update is correct |
| compare the diff of the old expected value to the new expected value. |
| */ |
| package golden |
| |
| import ( |
| "bytes" |
| "flag" |
| "fmt" |
| "os" |
| "path/filepath" |
| |
| "gotest.tools/v3/assert" |
| "gotest.tools/v3/assert/cmp" |
| "gotest.tools/v3/internal/format" |
| "gotest.tools/v3/internal/source" |
| ) |
| |
| func init() { |
| flag.BoolVar(&source.Update, "test.update-golden", false, "deprecated flag") |
| } |
| |
| type helperT interface { |
| Helper() |
| } |
| |
| // NormalizeCRLFToLF enables end-of-line normalization for actual values passed |
| // to Assert and String, as well as the values saved to golden files with |
| // -update. |
| // |
| // 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. |
| // |
| // This does not affect the contents of the golden files themselves. And depending on the |
| // git settings on your system (or in github action platform default like windows), the |
| // golden files may contain CRLF line endings. You can avoid this by setting the |
| // .gitattributes file in your repo to use LF line endings for all files, or just the golden |
| // files, by adding the following line to your .gitattributes file: |
| // |
| // * text=auto eol=lf |
| var NormalizeCRLFToLF = os.Getenv("GOTESTTOOLS_GOLDEN_NormalizeCRLFToLF") != "false" |
| |
| // FlagUpdate returns true when the -update flag has been set. |
| func FlagUpdate() bool { |
| return source.IsUpdate() |
| } |
| |
| // 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 |
| } |
| |
| // 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() |
| } |
| expected, err := os.ReadFile(Path(filename)) |
| assert.NilError(t, err) |
| return expected |
| } |
| |
| // Path returns the full path to a file in ./testdata |
| func Path(filename string) string { |
| if filepath.IsAbs(filename) { |
| return filename |
| } |
| return filepath.Join("testdata", filename) |
| } |
| |
| func removeCarriageReturn(in []byte) []byte { |
| if !NormalizeCRLFToLF { |
| return in |
| } |
| return bytes.Replace(in, []byte("\r\n"), []byte("\n"), -1) |
| } |
| |
| // Assert compares actual to the expected value in the golden file. |
| // |
| // Running `go test pkgname -update` will write the value of actual |
| // to the golden file. |
| // |
| // This is equivalent to assert.Assert(t, String(actual, filename)) |
| func Assert(t assert.TestingT, actual string, filename string, msgAndArgs ...interface{}) { |
| if ht, ok := t.(helperT); ok { |
| ht.Helper() |
| } |
| assert.Assert(t, String(actual, filename), msgAndArgs...) |
| } |
| |
| // String compares actual to the contents of filename and returns success |
| // if the strings are equal. |
| // |
| // Running `go test pkgname -update` will write the value of actual |
| // 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. |
| func String(actual string, filename string) cmp.Comparison { |
| return func() cmp.Result { |
| actualBytes := removeCarriageReturn([]byte(actual)) |
| result, expected := compare(actualBytes, filename) |
| if result != nil { |
| return result |
| } |
| diff := format.UnifiedDiff(format.DiffConfig{ |
| A: string(expected), |
| B: string(actualBytes), |
| From: "expected", |
| To: "actual", |
| }) |
| return cmp.ResultFailure("\n" + diff + failurePostamble(filename)) |
| } |
| } |
| |
| func failurePostamble(filename string) string { |
| return fmt.Sprintf(` |
| |
| You can run 'go test . -update' 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 -update` will write the value of actual |
| // to the golden file. |
| // |
| // This is equivalent to assert.Assert(t, Bytes(actual, filename)) |
| func AssertBytes( |
| t assert.TestingT, |
| actual []byte, |
| filename string, |
| msgAndArgs ...interface{}, |
| ) { |
| if ht, ok := t.(helperT); ok { |
| ht.Helper() |
| } |
| assert.Assert(t, Bytes(actual, filename), msgAndArgs...) |
| } |
| |
| // Bytes compares actual to the contents of filename and returns success |
| // if the bytes are equal. |
| // |
| // Running `go test pkgname -update` will write the value of actual |
| // to the golden file. |
| func Bytes(actual []byte, filename string) cmp.Comparison { |
| return func() cmp.Result { |
| result, expected := compare(actual, filename) |
| if result != nil { |
| return result |
| } |
| msg := fmt.Sprintf("%v (actual) != %v (expected)", actual, expected) |
| return cmp.ResultFailure(msg + failurePostamble(filename)) |
| } |
| } |
| |
| func compare(actual []byte, filename string) (cmp.Result, []byte) { |
| if err := update(filename, actual); err != nil { |
| return cmp.ResultFromError(err), nil |
| } |
| expected, err := os.ReadFile(Path(filename)) |
| if err != nil { |
| return cmp.ResultFromError(err), nil |
| } |
| if bytes.Equal(expected, actual) { |
| return cmp.ResultSuccess, nil |
| } |
| return nil, expected |
| } |
| |
| func update(filename string, actual []byte) error { |
| if !source.IsUpdate() { |
| return nil |
| } |
| if dir := filepath.Dir(Path(filename)); dir != "." { |
| if err := os.MkdirAll(dir, 0755); err != nil { |
| return err |
| } |
| } |
| return os.WriteFile(Path(filename), actual, 0644) |
| } |