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
|
// Package golden provides a helper function to assert the output of tests.
package golden
import (
"flag"
"os"
"path/filepath"
"runtime"
"strconv"
"strings"
"testing"
"github.com/aymanbagabas/go-udiff"
)
var update = flag.Bool("update", false, "update .golden files")
// RequireEqual is a helper function to assert the given output is
// the expected from the golden files, printing its diff in case it is not.
//
// Golden files contain the raw expected output of your tests, which can
// contain control codes and escape sequences. When comparing the output of
// your tests, [RequireEqual] will escape the control codes and sequences
// before comparing the output with the golden files.
//
// You can update the golden files by running your tests with the -update flag.
func RequireEqual[T []byte | string](tb testing.TB, out T) {
tb.Helper()
golden := filepath.Join("testdata", tb.Name()+".golden")
if *update {
if err := os.MkdirAll(filepath.Dir(golden), 0o750); err != nil { //nolint: mnd
tb.Fatal(err)
}
if err := os.WriteFile(golden, []byte(out), 0o600); err != nil { //nolint: mnd
tb.Fatal(err)
}
}
goldenBts, err := os.ReadFile(golden)
if err != nil {
tb.Fatal(err)
}
goldenStr := normalizeWindowsLineBreaks(string(goldenBts))
goldenStr = escapeSeqs(goldenStr)
outStr := escapeSeqs(string(out))
diff := udiff.Unified("golden", "run", goldenStr, outStr)
if diff != "" {
tb.Fatalf("output does not match, expected:\n\n%s\n\ngot:\n\n%s\n\ndiff:\n\n%s", goldenStr, outStr, diff)
}
}
// RequireEqualEscape is a helper function to assert the given output is
// the expected from the golden files, printing its diff in case it is not.
//
// Deprecated: Use [RequireEqual] instead.
func RequireEqualEscape(tb testing.TB, out []byte, escapes bool) { //nolint:revive
RequireEqual(tb, out)
}
// escapeSeqs escapes control codes and escape sequences from the given string.
// The only preserved exception is the newline character.
func escapeSeqs(in string) string {
s := strings.Split(in, "\n")
for i, l := range s {
q := strconv.Quote(l)
q = strings.TrimPrefix(q, `"`)
q = strings.TrimSuffix(q, `"`)
s[i] = q
}
return strings.Join(s, "\n")
}
// normalizeWindowsLineBreaks replaces all \r\n with \n.
// This is needed because Git for Windows checks out with \r\n by default.
func normalizeWindowsLineBreaks(str string) string {
if runtime.GOOS == "windows" {
return strings.ReplaceAll(str, "\r\n", "\n")
}
return str
}
|