...

Source file src/gotest.tools/v3/golden/golden.go

Documentation: gotest.tools/v3/golden

     1  /*
     2  Package golden provides tools for comparing large mutli-line strings.
     3  
     4  Golden files are files in the ./testdata/ subdirectory of the package under test.
     5  Golden files can be automatically updated to match new values by running
     6  `go test pkgname -update`. To ensure the update is correct
     7  compare the diff of the old expected value to the new expected value.
     8  */
     9  package golden
    10  
    11  import (
    12  	"bytes"
    13  	"flag"
    14  	"fmt"
    15  	"os"
    16  	"path/filepath"
    17  
    18  	"gotest.tools/v3/assert"
    19  	"gotest.tools/v3/assert/cmp"
    20  	"gotest.tools/v3/internal/format"
    21  	"gotest.tools/v3/internal/source"
    22  )
    23  
    24  func init() {
    25  	flag.BoolVar(&source.Update, "test.update-golden", false, "deprecated flag")
    26  }
    27  
    28  type helperT interface {
    29  	Helper()
    30  }
    31  
    32  // NormalizeCRLFToLF enables end-of-line normalization for actual values passed
    33  // to Assert and String, as well as the values saved to golden files with
    34  // -update.
    35  //
    36  // Defaults to true. If you use the core.autocrlf=true git setting on windows
    37  // you will need to set this to false.
    38  //
    39  // The value may be set to false by setting GOTESTTOOLS_GOLDEN_NormalizeCRLFToLF=false
    40  // in the environment before running tests.
    41  //
    42  // The default value may change in a future major release.
    43  //
    44  // This does not affect the contents of the golden files themselves. And depending on the
    45  // git settings on your system (or in github action platform default like windows), the
    46  // golden files may contain CRLF line endings.  You can avoid this by setting the
    47  // .gitattributes file in your repo to use LF line endings for all files, or just the golden
    48  // files, by adding the following line to your .gitattributes file:
    49  //
    50  // * text=auto eol=lf
    51  var NormalizeCRLFToLF = os.Getenv("GOTESTTOOLS_GOLDEN_NormalizeCRLFToLF") != "false"
    52  
    53  // FlagUpdate returns true when the -update flag has been set.
    54  func FlagUpdate() bool {
    55  	return source.IsUpdate()
    56  }
    57  
    58  // Open opens the file in ./testdata
    59  func Open(t assert.TestingT, filename string) *os.File {
    60  	if ht, ok := t.(helperT); ok {
    61  		ht.Helper()
    62  	}
    63  	f, err := os.Open(Path(filename))
    64  	assert.NilError(t, err)
    65  	return f
    66  }
    67  
    68  // Get returns the contents of the file in ./testdata
    69  func Get(t assert.TestingT, filename string) []byte {
    70  	if ht, ok := t.(helperT); ok {
    71  		ht.Helper()
    72  	}
    73  	expected, err := os.ReadFile(Path(filename))
    74  	assert.NilError(t, err)
    75  	return expected
    76  }
    77  
    78  // Path returns the full path to a file in ./testdata
    79  func Path(filename string) string {
    80  	if filepath.IsAbs(filename) {
    81  		return filename
    82  	}
    83  	return filepath.Join("testdata", filename)
    84  }
    85  
    86  func removeCarriageReturn(in []byte) []byte {
    87  	if !NormalizeCRLFToLF {
    88  		return in
    89  	}
    90  	return bytes.Replace(in, []byte("\r\n"), []byte("\n"), -1)
    91  }
    92  
    93  // Assert compares actual to the expected value in the golden file.
    94  //
    95  // Running `go test pkgname -update` will write the value of actual
    96  // to the golden file.
    97  //
    98  // This is equivalent to assert.Assert(t, String(actual, filename))
    99  func Assert(t assert.TestingT, actual string, filename string, msgAndArgs ...interface{}) {
   100  	if ht, ok := t.(helperT); ok {
   101  		ht.Helper()
   102  	}
   103  	assert.Assert(t, String(actual, filename), msgAndArgs...)
   104  }
   105  
   106  // String compares actual to the contents of filename and returns success
   107  // if the strings are equal.
   108  //
   109  // Running `go test pkgname -update` will write the value of actual
   110  // to the golden file.
   111  //
   112  // Any \r\n substrings in actual are converted to a single \n character
   113  // before comparing it to the expected string. When updating the golden file the
   114  // normalized version will be written to the file. This allows Windows to use
   115  // the same golden files as other operating systems.
   116  func String(actual string, filename string) cmp.Comparison {
   117  	return func() cmp.Result {
   118  		actualBytes := removeCarriageReturn([]byte(actual))
   119  		result, expected := compare(actualBytes, filename)
   120  		if result != nil {
   121  			return result
   122  		}
   123  		diff := format.UnifiedDiff(format.DiffConfig{
   124  			A:    string(expected),
   125  			B:    string(actualBytes),
   126  			From: "expected",
   127  			To:   "actual",
   128  		})
   129  		return cmp.ResultFailure("\n" + diff + failurePostamble(filename))
   130  	}
   131  }
   132  
   133  func failurePostamble(filename string) string {
   134  	return fmt.Sprintf(`
   135  
   136  You can run 'go test . -update' to automatically update %s to the new expected value.'
   137  `, Path(filename))
   138  }
   139  
   140  // AssertBytes compares actual to the expected value in the golden.
   141  //
   142  // Running `go test pkgname -update` will write the value of actual
   143  // to the golden file.
   144  //
   145  // This is equivalent to assert.Assert(t, Bytes(actual, filename))
   146  func AssertBytes(
   147  	t assert.TestingT,
   148  	actual []byte,
   149  	filename string,
   150  	msgAndArgs ...interface{},
   151  ) {
   152  	if ht, ok := t.(helperT); ok {
   153  		ht.Helper()
   154  	}
   155  	assert.Assert(t, Bytes(actual, filename), msgAndArgs...)
   156  }
   157  
   158  // Bytes compares actual to the contents of filename and returns success
   159  // if the bytes are equal.
   160  //
   161  // Running `go test pkgname -update` will write the value of actual
   162  // to the golden file.
   163  func Bytes(actual []byte, filename string) cmp.Comparison {
   164  	return func() cmp.Result {
   165  		result, expected := compare(actual, filename)
   166  		if result != nil {
   167  			return result
   168  		}
   169  		msg := fmt.Sprintf("%v (actual) != %v (expected)", actual, expected)
   170  		return cmp.ResultFailure(msg + failurePostamble(filename))
   171  	}
   172  }
   173  
   174  func compare(actual []byte, filename string) (cmp.Result, []byte) {
   175  	if err := update(filename, actual); err != nil {
   176  		return cmp.ResultFromError(err), nil
   177  	}
   178  	expected, err := os.ReadFile(Path(filename))
   179  	if err != nil {
   180  		return cmp.ResultFromError(err), nil
   181  	}
   182  	if bytes.Equal(expected, actual) {
   183  		return cmp.ResultSuccess, nil
   184  	}
   185  	return nil, expected
   186  }
   187  
   188  func update(filename string, actual []byte) error {
   189  	if !source.IsUpdate() {
   190  		return nil
   191  	}
   192  	if dir := filepath.Dir(Path(filename)); dir != "." {
   193  		if err := os.MkdirAll(dir, 0755); err != nil {
   194  			return err
   195  		}
   196  	}
   197  	return os.WriteFile(Path(filename), actual, 0644)
   198  }
   199  

View as plain text