feat(golden): initial commit

This commit is contained in:
2021-09-16 02:30:47 +01:00
commit 1d50225ce6
12 changed files with 1138 additions and 0 deletions

61
.github/workflows/ci.yml vendored Normal file
View File

@@ -0,0 +1,61 @@
---
name: CI
on: [push]
jobs:
lint:
name: Lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: golangci-lint
uses: golangci/golangci-lint-action@v2
with:
version: v1.42
env:
VERBOSE: "true"
tidy:
name: Tidy
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-go@v2
with:
go-version: 1.15
- uses: actions/cache@v2
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- name: Check if mods are tidy
run: make check-tidy
test:
name: Test
strategy:
fail-fast: false
matrix:
os:
- ubuntu-latest
- macos-latest
- windows-latest
go_version:
- "1.15"
- "1.16"
- "1.17"
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v2
- uses: actions/setup-go@v2
with:
go-version: ${{ matrix.go_version }}
- uses: actions/cache@v2
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- name: Run tests
run: go test -v -count=1 -race ./...

3
.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
bin/*
testdata/*
coverage.out

95
.golangci.yml Normal file
View File

@@ -0,0 +1,95 @@
linters-settings:
funlen:
lines: 100
statements: 150
gocyclo:
min-complexity: 20
golint:
min-confidence: 0
govet:
check-shadowing: true
enable-all: true
disable:
- fieldalignment
lll:
line-length: 80
tab-width: 4
maligned:
suggest-new: true
misspell:
locale: US
linters:
disable-all: true
enable:
- asciicheck
- bodyclose
- deadcode
- depguard
- durationcheck
- errcheck
- errorlint
- exhaustive
- exportloopref
- funlen
- gochecknoinits
- goconst
- gocritic
- gocyclo
- godot
- gofumpt
- goimports
- goprintffuncname
- gosec
- gosimple
- govet
- importas
- ineffassign
- lll
- misspell
- nakedret
- nilerr
- noctx
- nolintlint
- prealloc
- predeclared
- revive
- rowserrcheck
- sqlclosecheck
- staticcheck
- structcheck
- tparallel
- typecheck
- unconvert
- unparam
- unused
- varcheck
- wastedassign
- whitespace
issues:
exclude:
- Using the variable on range scope `tt` in function literal
- Using the variable on range scope `tc` in function literal
exclude-rules:
- path: "_test\\.go"
linters:
- funlen
- dupl
- source: "^//go:generate "
linters:
- lll
- source: "`json:"
linters:
- lll
- source: "`xml:"
linters:
- lll
- source: "`yaml:"
linters:
- lll
run:
timeout: 2m
allow-parallel-runners: true
modules-download-mode: readonly

20
LICENSE Normal file
View File

@@ -0,0 +1,20 @@
The MIT License (MIT)
Copyright (c) 2021 Jim Myhrberg
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

193
Makefile Normal file
View File

@@ -0,0 +1,193 @@
GOMODNAME := $(shell grep 'module' go.mod | sed -e 's/^module //')
SOURCES := $(shell find . -name "*.go" -or -name "go.mod" -or -name "go.sum" \
-or -name "Makefile")
# Verbose output
ifdef VERBOSE
V = -v
endif
#
# Environment
#
BINDIR := bin
TOOLDIR := $(BINDIR)/tools
# Global environment variables for all targets
SHELL ?= /bin/bash
SHELL := env \
GO111MODULE=on \
GOBIN=$(CURDIR)/$(TOOLDIR) \
CGO_ENABLED=1 \
PATH='$(CURDIR)/$(BINDIR):$(CURDIR)/$(TOOLDIR):$(PATH)' \
$(SHELL)
#
# Defaults
#
# Default target
.DEFAULT_GOAL := test
#
# Tools
#
TOOLS += $(TOOLDIR)/gobin
$(TOOLDIR)/gobin:
GO111MODULE=off go get -u github.com/myitcv/gobin
# external tool
define tool # 1: binary-name, 2: go-import-path
TOOLS += $(TOOLDIR)/$(1)
$(TOOLDIR)/$(1): $(TOOLDIR)/gobin Makefile
gobin $(V) "$(2)"
endef
$(eval $(call tool,godoc,golang.org/x/tools/cmd/godoc))
$(eval $(call tool,gofumpt,mvdan.cc/gofumpt))
$(eval $(call tool,goimports,golang.org/x/tools/cmd/goimports))
$(eval $(call tool,golangci-lint,github.com/golangci/golangci-lint/cmd/golangci-lint@v1.42))
$(eval $(call tool,gomod,github.com/Helcaraxan/gomod))
.PHONY: tools
tools: $(TOOLS)
#
# Development
#
BENCH ?= .
TESTARGS ?=
.PHONY: clean
clean:
rm -f $(TOOLS)
rm -f ./coverage.out ./go.mod.tidy-check ./go.sum.tidy-check
.PHONY: test
test:
go test $(V) -count=1 -race $(TESTARGS) ./...
.PHONY: test-deps
test-deps:
go test all
.PHONY: lint
lint: $(TOOLDIR)/golangci-lint
golangci-lint $(V) run
.PHONY: format
format: $(TOOLDIR)/goimports $(TOOLDIR)/gofumpt
goimports -w . && gofumpt -w .
.SILENT: bench
.PHONY: bench
bench:
go test $(V) -count=1 -bench=$(BENCH) $(TESTARGS) ./...
#
# Code Generation
#
.PHONY: generate
generate:
go generate ./...
.PHONY: check-generate
check-generate:
$(eval CHKDIR := $(shell mktemp -d))
cp -av . "$(CHKDIR)"
make -C "$(CHKDIR)/" generate
( diff -rN . "$(CHKDIR)" && rm -rf "$(CHKDIR)" ) || \
( rm -rf "$(CHKDIR)" && exit 1 )
#
# Coverage
#
.PHONY: cov
cov: coverage.out
.PHONY: cov-html
cov-html: coverage.out
go tool cover -html=./coverage.out
.PHONY: cov-func
cov-func: coverage.out
go tool cover -func=./coverage.out
coverage.out: $(SOURCES)
go test $(V) -covermode=count -coverprofile=./coverage.out ./...
#
# Dependencies
#
.PHONY: deps
deps:
go mod download
.PHONY: deps-update
deps-update:
go get -u -t ./...
.PHONY: deps-analyze
deps-analyze: $(TOOLDIR)/gomod
gomod analyze
.PHONY: tidy
tidy:
go mod tidy $(V)
.PHONY: verify
verify:
go mod verify
.SILENT: check-tidy
.PHONY: check-tidy
check-tidy:
cp go.mod go.mod.tidy-check
cp go.sum go.sum.tidy-check
go mod tidy
( \
diff go.mod go.mod.tidy-check && \
diff go.sum go.sum.tidy-check && \
rm -f go.mod go.sum && \
mv go.mod.tidy-check go.mod && \
mv go.sum.tidy-check go.sum \
) || ( \
rm -f go.mod go.sum && \
mv go.mod.tidy-check go.mod && \
mv go.sum.tidy-check go.sum; \
exit 1 \
)
#
# Documentation
#
# Serve docs
.PHONY: docs
docs: godoc
$(info serviing docs on http://127.0.0.1:6060/pkg/$(GOMODNAME)/)
@godoc -http=127.0.0.1:6060
#
# Release
#
.PHONY: new-version
new-version: check-npx
npx standard-version
.PHONY: next-version
next-version: check-npx
npx standard-version --dry-run
.PHONY: check-npx
check-npx:
$(if $(shell which npx),,\
$(error No npx found in PATH, please install NodeJS))

5
README.md Normal file
View File

@@ -0,0 +1,5 @@
# go-golden
Yet another Go package for working with `*.golden` test files.
Currently a work in progress.

49
global.go Normal file
View File

@@ -0,0 +1,49 @@
package golden
import "testing"
var global = New()
// Updating returns true when golden is set to update golden files. Used to
// determine if golden.Set() should be called or not.
func Updating() bool {
return global.Updating()
}
// Get returns the content of the default golden file for the given *testing.T
// instance as determined by t.Name(). If no golden file can be found/read, it
// will fail the test with t.Fatal().
func Get(t *testing.T) []byte {
return global.Get(t)
}
// Set writes given data of the default golden file for the given *testing.T
// instance as determined by t.Name(). If writing fails it will fail the test
// with t.Fatal() detailing the error.
func Set(t *testing.T, data []byte) {
global.Set(t, data)
}
// File returns the filename for the default golden file for the given
// *testing.T instance as determined by t.Name().
func File(t *testing.T) string {
return global.File(t)
}
// GetNamed return the content of the specifically named golden file belonging
// to the given *testing.T instance as determined by t.Name(). If no golden file
// can be found/read, it will fail the test with t.Fatal().
func GetNamed(t *testing.T, name string) []byte {
return global.GetNamed(t, name)
}
// SetNamed writes given data of the specifically named golden file belonging to
// the given *testing.T instance as determined by t.Name(). If writing fails it
// will fail the test with t.Fatal() detailing the error.
func SetNamed(t *testing.T, name string, data []byte) {
global.SetNamed(t, name, data)
}
func NamedFile(t *testing.T, name string) string {
return global.NamedFile(t, name)
}

561
global_test.go Normal file
View File

@@ -0,0 +1,561 @@
package golden
import (
"io/ioutil"
"os"
"path/filepath"
"testing"
"github.com/jimeh/envctl"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestUpdating(t *testing.T) {
tests := []struct {
name string
env map[string]string
want bool
}{
{
name: "GOLDEN_UPDATE not set",
want: false,
},
{
name: "GOLDEN_UPDATE set to 0",
env: map[string]string{"GOLDEN_UPDATE": "0"},
want: false,
},
{
name: "GOLDEN_UPDATE set to 1",
env: map[string]string{"GOLDEN_UPDATE": "1"},
want: true,
},
{
name: "GOLDEN_UPDATE set to 2",
env: map[string]string{"GOLDEN_UPDATE": "2"},
want: false,
},
{
name: "GOLDEN_UPDATE set to y",
env: map[string]string{"GOLDEN_UPDATE": "y"},
want: true,
},
{
name: "GOLDEN_UPDATE set to n",
env: map[string]string{"GOLDEN_UPDATE": "n"},
want: false,
},
{
name: "GOLDEN_UPDATE set to t",
env: map[string]string{"GOLDEN_UPDATE": "t"},
want: true,
},
{
name: "GOLDEN_UPDATE set to f",
env: map[string]string{"GOLDEN_UPDATE": "f"},
want: false,
},
{
name: "GOLDEN_UPDATE set to yes",
env: map[string]string{"GOLDEN_UPDATE": "yes"},
want: true,
},
{
name: "GOLDEN_UPDATE set to no",
env: map[string]string{"GOLDEN_UPDATE": "no"},
want: false,
},
{
name: "GOLDEN_UPDATE set to on",
env: map[string]string{"GOLDEN_UPDATE": "on"},
want: true,
},
{
name: "GOLDEN_UPDATE set to off",
env: map[string]string{"GOLDEN_UPDATE": "off"},
want: false,
},
{
name: "GOLDEN_UPDATE set to true",
env: map[string]string{"GOLDEN_UPDATE": "true"},
want: true,
},
{
name: "GOLDEN_UPDATE set to false",
env: map[string]string{"GOLDEN_UPDATE": "false"},
want: false,
},
{
name: "GOLDEN_UPDATE set to foobarnopebbq",
env: map[string]string{"GOLDEN_UPDATE": "foobarnopebbq"},
want: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
envctl.WithClean(tt.env, func() {
got := Updating()
assert.Equal(t, tt.want, got)
})
})
}
}
func TestFile(t *testing.T) {
got := File(t)
assert.Equal(t, filepath.Join("testdata", "TestFile.golden"), got)
tests := []struct {
name string
want string
}{
{
name: "",
want: filepath.Join("testdata", "TestFile", "#00.golden"),
},
{
name: "foobar",
want: filepath.Join("testdata", "TestFile", "foobar.golden"),
},
{
name: "foo/bar",
want: filepath.Join("testdata", "TestFile", "foo/bar.golden"),
},
{
name: `"foobar"`,
want: filepath.Join("testdata", "TestFile", "\"foobar\".golden"),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := File(t)
assert.Equal(t, tt.want, got)
})
}
}
func TestGet(t *testing.T) {
t.Cleanup(func() {
err := os.RemoveAll(filepath.Join("testdata", t.Name()))
require.NoError(t, err)
err = os.Remove(filepath.Join("testdata", t.Name()+".golden"))
require.NoError(t, err)
})
err := os.MkdirAll("testdata", 0o755)
require.NoError(t, err)
content := []byte("foobar\nhello world :)")
err = ioutil.WriteFile( //nolint:gosec
filepath.Join("testdata", "TestGet.golden"), content, 0o644,
)
require.NoError(t, err)
got := Get(t)
assert.Equal(t, content, got)
tests := []struct {
name string
file string
want []byte
}{
{
name: "",
file: filepath.Join("testdata", "TestGet", "#00.golden"),
want: []byte("number double-zero here"),
},
{
name: "foobar",
file: filepath.Join("testdata", "TestGet", "foobar.golden"),
want: []byte("foobar here"),
},
{
name: "foo/bar",
file: filepath.Join("testdata", "TestGet", "foo", "bar.golden"),
want: []byte("foo/bar style sub-sub-folders works too"),
},
{
name: "john's lost flip-flop",
file: filepath.Join(
"testdata", "TestGet", "john's_lost_flip-flop.golden",
),
want: []byte("Did John lose his flip-flop again?"),
},
{
name: "thing: it's a thing!",
file: filepath.Join(
"testdata", "TestGet", "thing:_it's_a_thing!.golden",
),
want: []byte("A thing? Really? Are we getting lazy? :P"),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
f := File(t)
dir := filepath.Dir(f)
err := os.MkdirAll(dir, 0o755)
require.NoError(t, err)
err = ioutil.WriteFile(f, tt.want, 0o644) //nolint:gosec
require.NoError(t, err)
got := Get(t)
assert.Equal(t, tt.file, f)
assert.Equal(t, tt.want, got)
})
}
}
func TestSet(t *testing.T) {
t.Cleanup(func() {
err := os.RemoveAll(filepath.Join("testdata", t.Name()))
require.NoError(t, err)
err = os.Remove(filepath.Join("testdata", t.Name()+".golden"))
require.NoError(t, err)
})
content := []byte("This is the default golden file for TestSet ^_^")
Set(t, content)
b, err := ioutil.ReadFile(filepath.Join("testdata", "TestSet.golden"))
require.NoError(t, err)
assert.Equal(t, content, b)
tests := []struct {
name string
file string
content []byte
}{
{
name: "",
file: filepath.Join("testdata", "TestSet", "#00.golden"),
content: []byte("number double-zero strikes again"),
},
{
name: "foobar",
file: filepath.Join("testdata", "TestSet", "foobar.golden"),
content: []byte("foobar here"),
},
{
name: "foo/bar",
file: filepath.Join("testdata", "TestSet", "foo", "bar.golden"),
content: []byte("foo/bar style sub-sub-folders works too"),
},
{
name: "john's lost flip-flop",
file: filepath.Join(
"testdata", "TestSet", "john's_lost_flip-flop.golden",
),
content: []byte("Did John lose his flip-flop again?"),
},
{
name: "thing: it's a thing!",
file: filepath.Join(
"testdata", "TestSet", "thing:_it's_a_thing!.golden",
),
content: []byte("A thing? Really? Are we getting lazy? :P"),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
f := File(t)
Set(t, tt.content)
got, err := ioutil.ReadFile(f)
require.NoError(t, err)
assert.Equal(t, tt.file, f)
assert.Equal(t, tt.content, got)
})
}
}
func TestGetNamed(t *testing.T) {
t.Cleanup(func() {
err := os.RemoveAll(filepath.Join("testdata", t.Name()))
require.NoError(t, err)
err = os.Remove(filepath.Join("testdata", t.Name()+".golden"))
require.NoError(t, err)
})
err := os.MkdirAll(filepath.Join("testdata", "TestGetNamed"), 0o755)
require.NoError(t, err)
content := []byte("this is the default golden file for TestGetNamed")
err = ioutil.WriteFile( //nolint:gosec
filepath.Join("testdata", "TestGetNamed.golden"), content, 0o644,
)
require.NoError(t, err)
got := GetNamed(t, "")
assert.Equal(t, content, got)
content = []byte("this is the named golden file for TestGetNamed")
err = ioutil.WriteFile( //nolint:gosec
filepath.Join("testdata", "TestGetNamed", "sub-name.golden"),
content, 0o644,
)
require.NoError(t, err)
got = GetNamed(t, "sub-name")
assert.Equal(t, content, got)
tests := []struct {
name string
named string
file string
want []byte
}{
{
name: "",
file: filepath.Join("testdata", "TestGetNamed", "#00.golden"),
want: []byte("number double-zero here"),
},
{
name: "",
named: "sub-zero-one",
file: filepath.Join(
"testdata", "TestGetNamed", "#01/sub-zero-one.golden",
),
want: []byte("number zero-one here"),
},
{
name: "foobar",
named: "email",
file: filepath.Join(
"testdata", "TestGetNamed", "foobar/email.golden",
),
want: []byte("foobar email here"),
},
{
name: "foobar",
named: "json",
file: filepath.Join(
"testdata", "TestGetNamed", "foobar#01/json.golden",
),
want: []byte("foobar json here"),
},
{
name: "foo/bar",
named: "hello/world",
file: filepath.Join(
"testdata", "TestGetNamed",
"foo", "bar",
"hello", "world.golden",
),
want: []byte("foo/bar style sub-sub-folders works too"),
},
{
name: "john's lost flip-flop",
named: "left",
file: filepath.Join(
"testdata", "TestGetNamed", "john's_lost_flip-flop",
"left.golden",
),
want: []byte("Did John lose his left flip-flop again?"),
},
{
name: "john's lost flip-flop",
named: "right",
file: filepath.Join(
"testdata", "TestGetNamed", "john's_lost_flip-flop#01",
"right.golden",
),
want: []byte("Did John lose his right flip-flop again?"),
},
{
name: "thing: it's",
named: "a thing!",
file: filepath.Join(
"testdata", "TestGetNamed", "thing:_it's", "a thing!.golden",
),
want: []byte("A thing? Really? Are we getting lazy? :P"),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
f := NamedFile(t, tt.named)
dir := filepath.Dir(f)
err := os.MkdirAll(dir, 0o755)
require.NoError(t, err)
err = ioutil.WriteFile(f, tt.want, 0o644) //nolint:gosec
require.NoError(t, err)
got := GetNamed(t, tt.named)
assert.Equal(t, filepath.FromSlash(tt.file), f)
assert.Equal(t, tt.want, got)
})
}
}
func TestSetNamed(t *testing.T) {
t.Cleanup(func() {
err := os.RemoveAll(filepath.Join("testdata", t.Name()))
require.NoError(t, err)
err = os.Remove(filepath.Join("testdata", t.Name()+".golden"))
require.NoError(t, err)
})
content := []byte("This is the default golden file for TestSetNamed ^_^")
SetNamed(t, "", content)
b, err := ioutil.ReadFile(filepath.Join("testdata", "TestSetNamed.golden"))
require.NoError(t, err)
assert.Equal(t, content, b)
content = []byte("This is the named golden file for TestSetNamed ^_^")
SetNamed(t, "sub-name", content)
b, err = ioutil.ReadFile(
filepath.Join("testdata", "TestSetNamed", "sub-name.golden"),
)
require.NoError(t, err)
assert.Equal(t, content, b)
tests := []struct {
name string
named string
file string
content []byte
}{
{
name: "",
file: filepath.Join("testdata", "TestSetNamed", "#00.golden"),
content: []byte("number double-zero strikes again"),
},
{
name: "",
named: "sub-zero-one",
file: filepath.Join(
"testdata", "TestSetNamed", "#01", "sub-zero-one.golden",
),
content: []byte("number zero-one sub-zero-one strikes again"),
},
{
name: "foobar",
named: "email",
file: filepath.Join(
"testdata", "TestSetNamed", "foobar", "email.golden",
),
content: []byte("foobar here"),
},
{
name: "foobar",
named: "json",
file: filepath.Join(
"testdata", "TestSetNamed", "foobar#01", "json.golden",
),
content: []byte("foobar here"),
},
{
name: "foo/bar",
file: filepath.Join(
"testdata", "TestSetNamed", "foo", "bar.golden",
),
content: []byte("foo/bar style sub-sub-folders works too"),
},
{
name: "john's lost flip-flop",
named: "left",
file: filepath.Join(
"testdata", "TestSetNamed", "john's_lost_flip-flop",
"left.golden",
),
content: []byte("Did John lose his left flip-flop again?"),
},
{
name: "john's lost flip-flop",
named: "right",
file: filepath.Join(
"testdata", "TestSetNamed", "john's_lost_flip-flop#01",
"right.golden",
),
content: []byte("Did John lose his right flip-flop again?"),
},
{
name: "thing: it's",
named: "a thing!",
file: filepath.Join(
"testdata", "TestSetNamed", "thing:_it's", "a thing!.golden",
),
content: []byte("A thing? Really? Are we getting lazy? :P"),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
f := NamedFile(t, tt.named)
SetNamed(t, tt.named, tt.content)
got, err := ioutil.ReadFile(f)
require.NoError(t, err)
assert.Equal(t, tt.file, f)
assert.Equal(t, tt.content, got)
})
}
}
func TestNamedFile(t *testing.T) {
got := NamedFile(t, "")
assert.Equal(t, "testdata/TestNamedFile.golden", got)
got = NamedFile(t, "sub-name")
assert.Equal(t, "testdata/TestNamedFile/sub-name.golden", got)
tests := []struct {
name string
named string
want string
}{
{
name: "",
named: "",
want: "testdata/TestNamedFile/#00.golden",
},
{
name: "",
named: "sub-thing",
want: "testdata/TestNamedFile/#01/sub-thing.golden",
},
{
name: "foobar",
want: "testdata/TestNamedFile/foobar.golden",
},
{
name: "fozbaz",
named: "email",
want: "testdata/TestNamedFile/fozbaz/email.golden",
},
{
name: "fozbaz",
named: "json",
want: "testdata/TestNamedFile/fozbaz#01/json.golden",
},
{
name: "foo/bar",
named: "hello/world",
want: "testdata/TestNamedFile/foo/bar/hello/world.golden",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := NamedFile(t, tt.named)
assert.Equal(t, tt.want, got)
})
}
}

8
go.mod Normal file
View File

@@ -0,0 +1,8 @@
module github.com/jimeh/go-golden
go 1.15
require (
github.com/jimeh/envctl v0.1.0
github.com/stretchr/testify v1.7.0
)

14
go.sum Normal file
View File

@@ -0,0 +1,14 @@
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/jimeh/envctl v0.1.0 h1:KTv3D+pi5M4/PgFVE/W8ssWqiZP3pDJ8Cga50L+1avo=
github.com/jimeh/envctl v0.1.0/go.mod h1:aM27ffBbO1yUBKUzgJGCUorS4z+wyh+qhQe1ruxXZZo=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

109
golden.go Normal file
View File

@@ -0,0 +1,109 @@
// Package golden is yet another package for working with *.golden test files.
package golden
import (
"io/ioutil"
"os"
"path/filepath"
"testing"
)
const (
DefaultDirMode = 0o755
DefaultFileMode = 0o644
DefaultSuffix = ".golden"
DefaultDirname = "testdata"
)
type Golden struct {
DirMode os.FileMode
FileMode os.FileMode
Suffix string
Dirname string
UpdatingFunc UpdatingFunc
}
func New() *Golden {
return &Golden{
DirMode: DefaultDirMode,
FileMode: DefaultFileMode,
Suffix: DefaultSuffix,
Dirname: DefaultDirname,
UpdatingFunc: EnvVarUpdatingFunc,
}
}
// Updating returns true when the function assigned to UpdatingFunc returns
// true.
func (s *Golden) Updating() bool {
return s.UpdatingFunc()
}
// Get returns the content of the default golden file for the given *testing.T
// instance as determined by t.Name(). If no golden file can be found/read, it
// will fail the test with t.Fatal().
func (s *Golden) Get(t *testing.T) []byte {
return s.GetNamed(t, "")
}
// Set writes given data of the default golden file for the given *testing.T
// instance as determined by t.Name(). If writing fails it will fail the test
// with t.Fatal() detailing the error.
func (s *Golden) Set(t *testing.T, data []byte) {
s.SetNamed(t, "", data)
}
func (s *Golden) File(t *testing.T) string {
return s.NamedFile(t, "")
}
func (s *Golden) GetNamed(t *testing.T, name string) []byte {
if t == nil {
return nil
}
f := s.NamedFile(t, name)
b, err := ioutil.ReadFile(f)
if err != nil {
t.Fatalf("golden: failed reading %s: %s", f, err.Error())
}
return b
}
func (s *Golden) SetNamed(t *testing.T, name string, data []byte) {
if t == nil {
return
}
f := s.NamedFile(t, name)
dir := filepath.Dir(f)
t.Logf("golden: writing .golden file: %s", f)
err := os.MkdirAll(dir, s.DirMode)
if err != nil {
t.Fatalf("golden: failed to create directory: %s", err.Error())
return
}
err = ioutil.WriteFile(f, data, s.FileMode)
if err != nil {
t.Fatalf("golden: filed to write file: %s", err.Error())
}
}
func (s *Golden) NamedFile(t *testing.T, name string) string {
if t == nil || t.Name() == "" {
t.Fatalf("golden: could not determine filename for: %+v", t)
return ""
}
base := []string{s.Dirname, filepath.FromSlash(t.Name())}
if name != "" {
base = append(base, name)
}
return filepath.Clean(filepath.Join(base...) + s.Suffix)
}

20
updating.go Normal file
View File

@@ -0,0 +1,20 @@
package golden
import "os"
var truthyStrings = []string{"1", "y", "t", "yes", "on", "true"}
type UpdatingFunc func() bool
// EnvVarUpdateFunc checks if the GOLDEN_UPDATE environment variable is set to
// one of "1", "y", "t", "yes", "on", or "true".
func EnvVarUpdatingFunc() bool {
env := os.Getenv("GOLDEN_UPDATE")
for _, v := range truthyStrings {
if env == v {
return true
}
}
return false
}