mirror of
https://github.com/jimeh/go-golden.git
synced 2026-02-19 11:16:47 +00:00
Compare commits
2 Commits
main
...
assertion-
| Author | SHA1 | Date | |
|---|---|---|---|
|
4e07a1a657
|
|||
|
3f967b571f
|
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
@@ -11,7 +11,7 @@ jobs:
|
|||||||
- name: golangci-lint
|
- name: golangci-lint
|
||||||
uses: golangci/golangci-lint-action@v2
|
uses: golangci/golangci-lint-action@v2
|
||||||
with:
|
with:
|
||||||
version: v1.42
|
version: v1.43
|
||||||
env:
|
env:
|
||||||
VERBOSE: "true"
|
VERBOSE: "true"
|
||||||
|
|
||||||
|
|||||||
5
.gitignore
vendored
5
.gitignore
vendored
@@ -1,5 +1,6 @@
|
|||||||
bin/*
|
bin/*
|
||||||
coverage.out
|
coverage.out
|
||||||
|
|
||||||
testdata/*
|
testdata/TestFile*
|
||||||
!testdata/TestExample*
|
testdata/TestGet*
|
||||||
|
testdata/TestSet*
|
||||||
|
|||||||
2
Makefile
2
Makefile
@@ -49,7 +49,7 @@ endef
|
|||||||
$(eval $(call tool,godoc,golang.org/x/tools/cmd/godoc))
|
$(eval $(call tool,godoc,golang.org/x/tools/cmd/godoc))
|
||||||
$(eval $(call tool,gofumpt,mvdan.cc/gofumpt))
|
$(eval $(call tool,gofumpt,mvdan.cc/gofumpt))
|
||||||
$(eval $(call tool,goimports,golang.org/x/tools/cmd/goimports))
|
$(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,golangci-lint,github.com/golangci/golangci-lint/cmd/golangci-lint@v1.43))
|
||||||
$(eval $(call tool,gomod,github.com/Helcaraxan/gomod))
|
$(eval $(call tool,gomod,github.com/Helcaraxan/gomod))
|
||||||
|
|
||||||
.PHONY: tools
|
.PHONY: tools
|
||||||
|
|||||||
@@ -70,4 +70,4 @@ for documentation and examples.
|
|||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
[MIT](https://github.com/jimeh/go-golden/blob/master/LICENSE)
|
[MIT](https://github.com/jimeh/go-golden/blob/main/LICENSE)
|
||||||
|
|||||||
69
assert.go
Normal file
69
assert.go
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
package golden
|
||||||
|
|
||||||
|
var defaultAsserter = NewAsserter()
|
||||||
|
|
||||||
|
// AssertJSONMarshaling asserts that the given "v" value JSON marshals to an
|
||||||
|
// expected value fetched from a golden file on disk, and then verifies that the
|
||||||
|
// marshaled result produces a value that is equal to "v" when unmarshaled.
|
||||||
|
//
|
||||||
|
// Used for objects that do NOT change when they are marshaled and unmarshaled.
|
||||||
|
func AssertJSONMarshaling(t TestingT, v interface{}) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
defaultAsserter.JSONMarshaling(t, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AssertJSONMarshalingP asserts that the given "v" value JSON marshals to an
|
||||||
|
// expected value fetched from a golden file on disk, and then verifies that the
|
||||||
|
// marshaled result produces a value that is equal to "want" when unmarshaled.
|
||||||
|
//
|
||||||
|
// Used for objects that change when they are marshaled and unmarshaled.
|
||||||
|
func AssertJSONMarshalingP(t TestingT, v, want interface{}) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
defaultAsserter.JSONMarshalingP(t, v, want)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AssertXMLMarshaling asserts that the given "v" value XML marshals to an
|
||||||
|
// expected value fetched from a golden file on disk, and then verifies that the
|
||||||
|
// marshaled result produces a value that is equal to "v" when unmarshaled.
|
||||||
|
//
|
||||||
|
// Used for objects that do NOT change when they are marshaled and unmarshaled.
|
||||||
|
func AssertXMLMarshaling(t TestingT, v interface{}) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
defaultAsserter.XMLMarshaling(t, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AssertXMLMarshalingP asserts that the given "v" value XML marshals to an
|
||||||
|
// expected value fetched from a golden file on disk, and then verifies that the
|
||||||
|
// marshaled result produces a value that is equal to "want" when unmarshaled.
|
||||||
|
//
|
||||||
|
// Used for objects that change when they are marshaled and unmarshaled.
|
||||||
|
func AssertXMLMarshalingP(t TestingT, v, want interface{}) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
defaultAsserter.XMLMarshalingP(t, v, want)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AssertYAMLMarshaling asserts that the given "v" value YAML marshals to an
|
||||||
|
// expected value fetched from a golden file on disk, and then verifies that the
|
||||||
|
// marshaled result produces a value that is equal to "v" when unmarshaled.
|
||||||
|
//
|
||||||
|
// Used for objects that do NOT change when they are marshaled and unmarshaled.
|
||||||
|
func AssertYAMLMarshaling(t TestingT, v interface{}) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
defaultAsserter.YAMLMarshaling(t, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AssertYAMLMarshalingP asserts that the given "v" value YAML marshals to an
|
||||||
|
// expected value fetched from a golden file on disk, and then verifies that the
|
||||||
|
// marshaled result produces a value that is equal to "want" when unmarshaled.
|
||||||
|
//
|
||||||
|
// Used for objects that change when they are marshaled and unmarshaled.
|
||||||
|
func AssertYAMLMarshalingP(t TestingT, v, want interface{}) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
defaultAsserter.YAMLMarshalingP(t, v, want)
|
||||||
|
}
|
||||||
68
assert_example_test.go
Normal file
68
assert_example_test.go
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
package golden_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/jimeh/go-golden"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MyBook struct {
|
||||||
|
FooBar string `json:"foo_bar,omitempty" yaml:"fooBar,omitempty" xml:"Foo_Bar,omitempty"`
|
||||||
|
Bar string `json:"-" yaml:"-" xml:"-"`
|
||||||
|
baz string
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestExampleMyBookMarshaling reads/writes the following golden files:
|
||||||
|
//
|
||||||
|
// testdata/TestExampleMyBookMarshaling/marshaled_json.golden
|
||||||
|
// testdata/TestExampleMyBookMarshaling/marshaled_xml.golden
|
||||||
|
// testdata/TestExampleMyBookMarshaling/marshaled_yaml.golden
|
||||||
|
//
|
||||||
|
func TestExampleMyBookMarshaling(t *testing.T) {
|
||||||
|
obj := &MyBook{FooBar: "Hello World!"}
|
||||||
|
|
||||||
|
golden.AssertJSONMarshaling(t, obj)
|
||||||
|
golden.AssertYAMLMarshaling(t, obj)
|
||||||
|
golden.AssertXMLMarshaling(t, obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestExampleMyBookMarshalingP reads/writes the following golden files:
|
||||||
|
//
|
||||||
|
// testdata/TestExampleMyBookMarshalingP/marshaled_json.golden
|
||||||
|
// testdata/TestExampleMyBookMarshalingP/marshaled_xml.golden
|
||||||
|
// testdata/TestExampleMyBookMarshalingP/marshaled_yaml.golden
|
||||||
|
//
|
||||||
|
func TestExampleMyBookMarshalingP(t *testing.T) {
|
||||||
|
obj := &MyBook{FooBar: "Hello World!", Bar: "Oops", baz: "nope!"}
|
||||||
|
want := &MyBook{FooBar: "Hello World!"}
|
||||||
|
|
||||||
|
golden.AssertJSONMarshalingP(t, obj, want)
|
||||||
|
golden.AssertYAMLMarshalingP(t, obj, want)
|
||||||
|
golden.AssertXMLMarshalingP(t, obj, want)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestExampleMyBookMarshalingTabular reads/writes the following golden files:
|
||||||
|
//
|
||||||
|
// testdata/TestExampleMyBookMarshalingTabular/empty/marshaled_json.golden
|
||||||
|
// testdata/TestExampleMyBookMarshalingTabular/empty/marshaled_xml.golden
|
||||||
|
// testdata/TestExampleMyBookMarshalingTabular/empty/marshaled_yaml.golden
|
||||||
|
// testdata/TestExampleMyBookMarshalingTabular/full/marshaled_json.golden
|
||||||
|
// testdata/TestExampleMyBookMarshalingTabular/full/marshaled_xml.golden
|
||||||
|
// testdata/TestExampleMyBookMarshalingTabular/full/marshaled_yaml.golden
|
||||||
|
//
|
||||||
|
func TestExampleMyBookMarshalingTabular(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
obj *MyBook
|
||||||
|
}{
|
||||||
|
{name: "empty", obj: &MyBook{}},
|
||||||
|
{name: "full", obj: &MyBook{FooBar: "Hello World!"}},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
golden.AssertJSONMarshaling(t, tt.obj)
|
||||||
|
golden.AssertYAMLMarshaling(t, tt.obj)
|
||||||
|
golden.AssertXMLMarshaling(t, tt.obj)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
559
assert_test.go
Normal file
559
assert_test.go
Normal file
@@ -0,0 +1,559 @@
|
|||||||
|
package golden
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/xml"
|
||||||
|
"fmt"
|
||||||
|
"regexp"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/jimeh/go-golden/marshal"
|
||||||
|
"github.com/jimeh/go-golden/unmarshal"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"gopkg.in/yaml.v3"
|
||||||
|
)
|
||||||
|
|
||||||
|
//
|
||||||
|
// Helpers
|
||||||
|
//
|
||||||
|
|
||||||
|
type author struct {
|
||||||
|
FirstName string `json:"first_name" yaml:"first_name" xml:"first_name"`
|
||||||
|
LastName string `json:"last_name" yaml:"last_name" xml:"last_name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type book struct {
|
||||||
|
ID string `json:"id" yaml:"id" xml:"id"`
|
||||||
|
Title string `json:"title" yaml:"title" xml:"title"`
|
||||||
|
Author *author `json:"author,omitempty" yaml:"author,omitempty" xml:"author,omitempty"`
|
||||||
|
Year int `json:"year,omitempty" yaml:"year,omitempty" xml:"year,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type article struct {
|
||||||
|
ID string `json:"id" yaml:"id" xml:"id"`
|
||||||
|
Title string `json:"title" yaml:"title" xml:"title"`
|
||||||
|
Author *author `json:"author" yaml:"author" xml:"author"`
|
||||||
|
Date *time.Time `json:"date,omitempty" yaml:"date,omitempty" xml:"date,omitempty"`
|
||||||
|
|
||||||
|
Rank int `json:"-" yaml:"-" xml:"-"`
|
||||||
|
order int
|
||||||
|
}
|
||||||
|
|
||||||
|
// comic is used for testing custom marshal/unmarshal functions on a type.
|
||||||
|
type comic struct {
|
||||||
|
ID string
|
||||||
|
Name string
|
||||||
|
Issue string
|
||||||
|
Ignored string
|
||||||
|
}
|
||||||
|
|
||||||
|
type xmlComic struct {
|
||||||
|
ID string `xml:"id,attr"`
|
||||||
|
Name string `xml:",chardata"`
|
||||||
|
Issue string `xml:"issue,attr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *comic) MarshalJSON() ([]byte, error) {
|
||||||
|
return []byte(fmt.Sprintf(`{"%s":"%s=%s"}`, s.ID, s.Name, s.Issue)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *comic) UnmarshalJSON(data []byte) error {
|
||||||
|
m := regexp.MustCompile(`^{\s*"(.*?)":\s*"(.*?)=(.*)"\s*}$`)
|
||||||
|
matches := m.FindSubmatch(bytes.TrimSpace(data))
|
||||||
|
if matches == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
s.ID = string(matches[1])
|
||||||
|
s.Name = string(matches[2])
|
||||||
|
s.Issue = string(matches[3])
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *comic) MarshalYAML() (interface{}, error) {
|
||||||
|
return map[string]map[string]string{s.ID: {s.Name: s.Issue}}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *comic) UnmarshalYAML(value *yaml.Node) error {
|
||||||
|
// Horribly hacky code, but it works and specifically only needs to extract
|
||||||
|
// these specific three values.
|
||||||
|
if len(value.Content) == 2 {
|
||||||
|
s.ID = value.Content[0].Value
|
||||||
|
if len(value.Content[1].Content) == 2 {
|
||||||
|
s.Name = value.Content[1].Content[0].Value
|
||||||
|
s.Issue = value.Content[1].Content[1].Value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *comic) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
|
||||||
|
return e.EncodeElement(
|
||||||
|
&xmlComic{ID: s.ID, Name: s.Name, Issue: s.Issue},
|
||||||
|
start,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *comic) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
|
||||||
|
x := &xmlComic{}
|
||||||
|
_ = d.DecodeElement(x, &start)
|
||||||
|
|
||||||
|
v := comic{ID: x.ID, Name: x.Name, Issue: x.Issue}
|
||||||
|
|
||||||
|
*s = v
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func boolPtr(b bool) *bool {
|
||||||
|
return &b
|
||||||
|
}
|
||||||
|
|
||||||
|
func intPtr(i int) *int {
|
||||||
|
return &i
|
||||||
|
}
|
||||||
|
|
||||||
|
func stringPtr(s string) *string {
|
||||||
|
return &s
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Test cases
|
||||||
|
//
|
||||||
|
|
||||||
|
var marhalingTestCases = []struct {
|
||||||
|
name string
|
||||||
|
v interface{}
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "true bool pointer",
|
||||||
|
v: boolPtr(true),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "false bool pointer",
|
||||||
|
v: boolPtr(false),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "int pointer",
|
||||||
|
v: intPtr(42),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "string pointer",
|
||||||
|
v: stringPtr("hello world"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "empty struct",
|
||||||
|
v: &book{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "partial struct",
|
||||||
|
v: &book{
|
||||||
|
ID: "cfda163c-d5c1-44a2-909b-5d2ce3a31979",
|
||||||
|
Title: "The Traveler",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "full struct",
|
||||||
|
v: &book{
|
||||||
|
ID: "cfda163c-d5c1-44a2-909b-5d2ce3a31979",
|
||||||
|
Title: "The Traveler",
|
||||||
|
Author: &author{
|
||||||
|
FirstName: "John",
|
||||||
|
LastName: "Twelve Hawks",
|
||||||
|
},
|
||||||
|
Year: 2005,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "custom marshaling",
|
||||||
|
v: &comic{
|
||||||
|
ID: "2fd5af35-b85e-4f03-8eba-524be28d7a5b",
|
||||||
|
Name: "Hello World!",
|
||||||
|
Issue: "Forty Two",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var articleDate = time.Date(
|
||||||
|
2021, time.October, 27, 23, 30, 34, 0, time.FixedZone("", 1*60*60),
|
||||||
|
).UTC()
|
||||||
|
|
||||||
|
var marshalingPTestCases = []struct {
|
||||||
|
name string
|
||||||
|
v interface{}
|
||||||
|
want interface{}
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "true bool pointer",
|
||||||
|
v: boolPtr(true),
|
||||||
|
want: boolPtr(true),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "false bool pointer",
|
||||||
|
v: boolPtr(false),
|
||||||
|
want: boolPtr(false),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "int pointer",
|
||||||
|
v: intPtr(42),
|
||||||
|
want: intPtr(42),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "string pointer",
|
||||||
|
v: stringPtr("hello world"),
|
||||||
|
want: stringPtr("hello world"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "empty struct",
|
||||||
|
v: &article{},
|
||||||
|
want: &article{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "partial struct",
|
||||||
|
v: &book{
|
||||||
|
ID: "10eec54d-e30a-4428-be18-01095d889126",
|
||||||
|
Title: "Time Travel",
|
||||||
|
},
|
||||||
|
want: &book{
|
||||||
|
ID: "10eec54d-e30a-4428-be18-01095d889126",
|
||||||
|
Title: "Time Travel",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "full struct",
|
||||||
|
v: &article{
|
||||||
|
ID: "10eec54d-e30a-4428-be18-01095d889126",
|
||||||
|
Title: "Time Travel",
|
||||||
|
Author: &author{
|
||||||
|
FirstName: "Doc",
|
||||||
|
LastName: "Brown",
|
||||||
|
},
|
||||||
|
Date: &articleDate,
|
||||||
|
Rank: 8,
|
||||||
|
order: 16,
|
||||||
|
},
|
||||||
|
want: &article{
|
||||||
|
ID: "10eec54d-e30a-4428-be18-01095d889126",
|
||||||
|
Title: "Time Travel",
|
||||||
|
Author: &author{
|
||||||
|
FirstName: "Doc",
|
||||||
|
LastName: "Brown",
|
||||||
|
},
|
||||||
|
Date: &articleDate,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "custom marshaling",
|
||||||
|
v: &comic{
|
||||||
|
ID: "2fd5af35-b85e-4f03-8eba-524be28d7a5b",
|
||||||
|
Name: "Hello World!",
|
||||||
|
Issue: "Forty Two",
|
||||||
|
Ignored: "don't pay attention to this :)",
|
||||||
|
},
|
||||||
|
want: &comic{
|
||||||
|
ID: "2fd5af35-b85e-4f03-8eba-524be28d7a5b",
|
||||||
|
Name: "Hello World!",
|
||||||
|
Issue: "Forty Two",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Tests
|
||||||
|
//
|
||||||
|
|
||||||
|
func TestWithGolden(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
golden Golden
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "nil",
|
||||||
|
args: args{golden: nil},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "non-nil",
|
||||||
|
args: args{golden: New(WithSuffix(".my-custom-golden"))},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
original := New(WithSuffix(".original-golden"))
|
||||||
|
o := &asserterOptions{golden: original}
|
||||||
|
|
||||||
|
fn := WithGolden(tt.args.golden)
|
||||||
|
fn.apply(o)
|
||||||
|
|
||||||
|
if tt.args.golden == nil {
|
||||||
|
assert.Equal(t, original, o.golden)
|
||||||
|
} else {
|
||||||
|
assert.Equal(t, tt.args.golden, o.golden)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNormalizedLineBreaks(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
value bool
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "true",
|
||||||
|
args: args{value: true},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "false",
|
||||||
|
args: args{value: false},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
o := &asserterOptions{normalizeLineBreaks: !tt.args.value}
|
||||||
|
|
||||||
|
fn := WithNormalizedLineBreaks(tt.args.value)
|
||||||
|
fn.apply(o)
|
||||||
|
|
||||||
|
assert.Equal(t, tt.args.value, o.normalizeLineBreaks)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewAssert(t *testing.T) {
|
||||||
|
otherGolden := New(WithSuffix(".other-golden"))
|
||||||
|
|
||||||
|
type args struct {
|
||||||
|
options []AsserterOption
|
||||||
|
}
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
want *asserter
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "no options",
|
||||||
|
args: args{options: []AsserterOption{}},
|
||||||
|
want: &asserter{
|
||||||
|
json: NewMarshalingAsserter(
|
||||||
|
defaultGolden, "JSON",
|
||||||
|
marshal.JSON, unmarshal.JSON,
|
||||||
|
true,
|
||||||
|
),
|
||||||
|
xml: NewMarshalingAsserter(
|
||||||
|
defaultGolden, "XML",
|
||||||
|
marshal.XML, unmarshal.XML,
|
||||||
|
true,
|
||||||
|
),
|
||||||
|
yaml: NewMarshalingAsserter(
|
||||||
|
defaultGolden, "YAML",
|
||||||
|
marshal.YAML, unmarshal.YAML,
|
||||||
|
true,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "WithGlobal option",
|
||||||
|
args: args{
|
||||||
|
options: []AsserterOption{
|
||||||
|
WithGolden(otherGolden),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: &asserter{
|
||||||
|
json: NewMarshalingAsserter(
|
||||||
|
otherGolden, "JSON",
|
||||||
|
marshal.JSON, unmarshal.JSON,
|
||||||
|
true,
|
||||||
|
),
|
||||||
|
xml: NewMarshalingAsserter(
|
||||||
|
otherGolden, "XML",
|
||||||
|
marshal.XML, unmarshal.XML,
|
||||||
|
true,
|
||||||
|
),
|
||||||
|
yaml: NewMarshalingAsserter(
|
||||||
|
otherGolden, "YAML",
|
||||||
|
marshal.YAML, unmarshal.YAML,
|
||||||
|
true,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "WithNormalizedLineBreaks option",
|
||||||
|
args: args{
|
||||||
|
options: []AsserterOption{
|
||||||
|
WithNormalizedLineBreaks(false),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: &asserter{
|
||||||
|
json: NewMarshalingAsserter(
|
||||||
|
defaultGolden, "JSON",
|
||||||
|
marshal.JSON, unmarshal.JSON,
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
xml: NewMarshalingAsserter(
|
||||||
|
defaultGolden, "XML",
|
||||||
|
marshal.XML, unmarshal.XML,
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
yaml: NewMarshalingAsserter(
|
||||||
|
defaultGolden, "YAML",
|
||||||
|
marshal.YAML, unmarshal.YAML,
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
a := NewAsserter(tt.args.options...)
|
||||||
|
assert.Implements(t, (*Asserter)(nil), a)
|
||||||
|
|
||||||
|
got, ok := a.(*asserter)
|
||||||
|
require.True(
|
||||||
|
t, ok, "failed to type assert return value to a *asserter",
|
||||||
|
)
|
||||||
|
|
||||||
|
assert.Equal(t, tt.want.json.Golden, got.json.Golden)
|
||||||
|
assert.Equal(t, tt.want.json.Format, got.json.Format)
|
||||||
|
assert.Equal(t,
|
||||||
|
tt.want.json.NormalizeLineBreaks, got.json.NormalizeLineBreaks,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert.Equal(t, tt.want.xml.Golden, got.xml.Golden)
|
||||||
|
assert.Equal(t, tt.want.xml.Format, got.xml.Format)
|
||||||
|
assert.Equal(t,
|
||||||
|
tt.want.xml.NormalizeLineBreaks, got.xml.NormalizeLineBreaks,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert.Equal(t, tt.want.yaml.Golden, got.yaml.Golden)
|
||||||
|
assert.Equal(t, tt.want.yaml.Format, got.yaml.Format)
|
||||||
|
assert.Equal(t,
|
||||||
|
tt.want.yaml.NormalizeLineBreaks, got.yaml.NormalizeLineBreaks,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_asserter(t *testing.T) {
|
||||||
|
a := &asserter{}
|
||||||
|
|
||||||
|
assert.Implements(t, (*Asserter)(nil), a)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAssertJSONMarshaling(t *testing.T) {
|
||||||
|
for _, tt := range marhalingTestCases {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
AssertJSONMarshaling(t, tt.v)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAssertJSONMarshalingP(t *testing.T) {
|
||||||
|
for _, tt := range marshalingPTestCases {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
AssertJSONMarshalingP(t, tt.v, tt.want)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAssertXMLMarshaling(t *testing.T) {
|
||||||
|
for _, tt := range marhalingTestCases {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
AssertXMLMarshaling(t, tt.v)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAssertXMLMarshalingP(t *testing.T) {
|
||||||
|
for _, tt := range marshalingPTestCases {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
AssertXMLMarshalingP(t, tt.v, tt.want)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAssertYAMLMarshaling(t *testing.T) {
|
||||||
|
for _, tt := range marhalingTestCases {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
AssertYAMLMarshaling(t, tt.v)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAssertYAMLMarshalingP(t *testing.T) {
|
||||||
|
for _, tt := range marshalingPTestCases {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
AssertYAMLMarshalingP(t, tt.v, tt.want)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAssert_JSONMarshaling(t *testing.T) {
|
||||||
|
for _, tt := range marhalingTestCases {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
assert := NewAsserter()
|
||||||
|
|
||||||
|
assert.JSONMarshaling(t, tt.v)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAssert_JSONMarshalingP(t *testing.T) {
|
||||||
|
for _, tt := range marshalingPTestCases {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
assert := NewAsserter()
|
||||||
|
|
||||||
|
assert.JSONMarshalingP(t, tt.v, tt.want)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAssert_XMLMarshaling(t *testing.T) {
|
||||||
|
for _, tt := range marhalingTestCases {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
assert := NewAsserter()
|
||||||
|
|
||||||
|
assert.XMLMarshaling(t, tt.v)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAssert_XMLMarshalingP(t *testing.T) {
|
||||||
|
for _, tt := range marshalingPTestCases {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
assert := NewAsserter()
|
||||||
|
|
||||||
|
assert.XMLMarshalingP(t, tt.v, tt.want)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAssert_YAMLMarshaling(t *testing.T) {
|
||||||
|
for _, tt := range marhalingTestCases {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
assert := NewAsserter()
|
||||||
|
|
||||||
|
assert.YAMLMarshaling(t, tt.v)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAssert_YAMLMarshalingP(t *testing.T) {
|
||||||
|
for _, tt := range marshalingPTestCases {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
assert := NewAsserter()
|
||||||
|
|
||||||
|
assert.YAMLMarshalingP(t, tt.v, tt.want)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
178
asserter.go
Normal file
178
asserter.go
Normal file
@@ -0,0 +1,178 @@
|
|||||||
|
package golden
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/jimeh/go-golden/marshal"
|
||||||
|
"github.com/jimeh/go-golden/unmarshal"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Asserter exposes a series of JSON, YAML, and XML marshaling assertion
|
||||||
|
// helpers.
|
||||||
|
type Asserter interface {
|
||||||
|
// JSONMarshaling asserts that the given "v" value JSON marshals to an
|
||||||
|
// expected value fetched from a golden file on disk, and then verifies that
|
||||||
|
// the marshaled result produces a value that is equal to "v" when
|
||||||
|
// unmarshaled.
|
||||||
|
//
|
||||||
|
// Used for objects that do NOT change when they are marshaled and
|
||||||
|
// unmarshaled.
|
||||||
|
JSONMarshaling(t TestingT, v interface{})
|
||||||
|
|
||||||
|
// JSONMarshalingP asserts that the given "v" value JSON marshals to an
|
||||||
|
// expected value fetched from a golden file on disk, and then verifies that
|
||||||
|
// the marshaled result produces a value that is equal to "want" when
|
||||||
|
// unmarshaled.
|
||||||
|
//
|
||||||
|
// Used for objects that change when they are marshaled and unmarshaled.
|
||||||
|
JSONMarshalingP(t TestingT, v interface{}, want interface{})
|
||||||
|
|
||||||
|
// XMLMarshaling asserts that the given "v" value XML marshals to an
|
||||||
|
// expected value fetched from a golden file on disk, and then verifies that
|
||||||
|
// the marshaled result produces a value that is equal to "v" when
|
||||||
|
// unmarshaled.
|
||||||
|
//
|
||||||
|
// Used for objects that do NOT change when they are marshaled and
|
||||||
|
// unmarshaled.
|
||||||
|
XMLMarshaling(t TestingT, v interface{})
|
||||||
|
|
||||||
|
// XMLMarshalingP asserts that the given "v" value XML marshals to an
|
||||||
|
// expected value fetched from a golden file on disk, and then verifies that
|
||||||
|
// the marshaled result produces a value that is equal to "want" when
|
||||||
|
// unmarshaled.
|
||||||
|
//
|
||||||
|
// Used for objects that change when they are marshaled and unmarshaled.
|
||||||
|
XMLMarshalingP(t TestingT, v interface{}, want interface{})
|
||||||
|
|
||||||
|
// YAMLMarshaling asserts that the given "v" value YAML marshals to an
|
||||||
|
// expected value fetched from a golden file on disk, and then verifies that
|
||||||
|
// the marshaled result produces a value that is equal to "v" when
|
||||||
|
// unmarshaled.
|
||||||
|
//
|
||||||
|
// Used for objects that do NOT change when they are marshaled and
|
||||||
|
// unmarshaled.
|
||||||
|
YAMLMarshaling(t TestingT, v interface{})
|
||||||
|
|
||||||
|
// YAMLMarshalingP asserts that the given "v" value YAML marshals to an
|
||||||
|
// expected value fetched from a golden file on disk, and then verifies that
|
||||||
|
// the marshaled result produces a value that is equal to "want" when
|
||||||
|
// unmarshaled.
|
||||||
|
//
|
||||||
|
// Used for objects that change when they are marshaled and unmarshaled.
|
||||||
|
YAMLMarshalingP(t TestingT, v interface{}, want interface{})
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewAsserter returns a new Asserter which exposes a number of marshaling
|
||||||
|
// assertion helpers for JSON, YAML and XML.
|
||||||
|
//
|
||||||
|
// The default encoders all specify indentation of two spaces, essentially
|
||||||
|
// enforcing pretty formatting for JSON and XML.
|
||||||
|
//
|
||||||
|
// The default decoders for JSON and YAML prohibit unknown fields which are not
|
||||||
|
// present on the provided struct.
|
||||||
|
func NewAsserter(options ...AsserterOption) Asserter {
|
||||||
|
o := &asserterOptions{
|
||||||
|
golden: defaultGolden,
|
||||||
|
normalizeLineBreaks: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, opt := range options {
|
||||||
|
opt.apply(o)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &asserter{
|
||||||
|
json: NewMarshalingAsserter(
|
||||||
|
o.golden, "JSON",
|
||||||
|
marshal.JSON, unmarshal.JSON,
|
||||||
|
o.normalizeLineBreaks,
|
||||||
|
),
|
||||||
|
xml: NewMarshalingAsserter(
|
||||||
|
o.golden, "XML",
|
||||||
|
marshal.XML, unmarshal.XML,
|
||||||
|
o.normalizeLineBreaks,
|
||||||
|
),
|
||||||
|
yaml: NewMarshalingAsserter(
|
||||||
|
o.golden, "YAML",
|
||||||
|
marshal.YAML, unmarshal.YAML,
|
||||||
|
o.normalizeLineBreaks,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type asserterOptions struct {
|
||||||
|
golden Golden
|
||||||
|
normalizeLineBreaks bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type AsserterOption interface {
|
||||||
|
apply(*asserterOptions)
|
||||||
|
}
|
||||||
|
|
||||||
|
type asserterOptionFunc func(*asserterOptions)
|
||||||
|
|
||||||
|
func (fn asserterOptionFunc) apply(c *asserterOptions) {
|
||||||
|
fn(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithGolden allows setting a custom *Golden instance when calling NewAssert().
|
||||||
|
func WithGolden(golden Golden) AsserterOption {
|
||||||
|
return asserterOptionFunc(func(a *asserterOptions) {
|
||||||
|
if golden != nil {
|
||||||
|
a.golden = golden
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithNormalizedLineBreaks allows turning off line-break normalization which
|
||||||
|
// replaces Windows' CRLF (\r\n) and Mac Classic CR (\r) line breaks with Unix's
|
||||||
|
// LF (\n) line breaks.
|
||||||
|
func WithNormalizedLineBreaks(value bool) AsserterOption {
|
||||||
|
return asserterOptionFunc(func(a *asserterOptions) {
|
||||||
|
a.normalizeLineBreaks = value
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// asserter implements the Assert interface.
|
||||||
|
type asserter struct {
|
||||||
|
json *MarshalingAsserter
|
||||||
|
xml *MarshalingAsserter
|
||||||
|
yaml *MarshalingAsserter
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *asserter) JSONMarshaling(t TestingT, v interface{}) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
s.json.Marshaling(t, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *asserter) JSONMarshalingP(
|
||||||
|
t TestingT,
|
||||||
|
v interface{},
|
||||||
|
want interface{},
|
||||||
|
) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
s.json.MarshalingP(t, v, want)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *asserter) XMLMarshaling(t TestingT, v interface{}) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
s.xml.Marshaling(t, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *asserter) XMLMarshalingP(t TestingT, v, want interface{}) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
s.xml.MarshalingP(t, v, want)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *asserter) YAMLMarshaling(t TestingT, v interface{}) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
s.yaml.Marshaling(t, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *asserter) YAMLMarshalingP(t TestingT, v, want interface{}) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
s.yaml.MarshalingP(t, v, want)
|
||||||
|
}
|
||||||
3
go.mod
3
go.mod
@@ -4,5 +4,8 @@ go 1.15
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/jimeh/envctl v0.1.0
|
github.com/jimeh/envctl v0.1.0
|
||||||
|
github.com/jimeh/go-mocktesting v0.1.0
|
||||||
|
github.com/spf13/afero v1.6.0
|
||||||
github.com/stretchr/testify v1.7.0
|
github.com/stretchr/testify v1.7.0
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
|
||||||
)
|
)
|
||||||
|
|||||||
21
go.sum
21
go.sum
@@ -2,13 +2,32 @@ 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/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 h1:KTv3D+pi5M4/PgFVE/W8ssWqiZP3pDJ8Cga50L+1avo=
|
||||||
github.com/jimeh/envctl v0.1.0/go.mod h1:aM27ffBbO1yUBKUzgJGCUorS4z+wyh+qhQe1ruxXZZo=
|
github.com/jimeh/envctl v0.1.0/go.mod h1:aM27ffBbO1yUBKUzgJGCUorS4z+wyh+qhQe1ruxXZZo=
|
||||||
|
github.com/jimeh/go-mocktesting v0.1.0 h1:y0tLABo3V4i9io7m6TiXdXbU3IVMjtPvWkr+A0+aLTM=
|
||||||
|
github.com/jimeh/go-mocktesting v0.1.0/go.mod h1:xnekQ6yP/ull2ewkOp1CbgH7Dym7nbKa/t96XWrIiH8=
|
||||||
|
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
||||||
|
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
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/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/spf13/afero v1.6.0 h1:xoax2sJ2DT8S8xA2paPFjDCScCNeWsg75VG0DLRreiY=
|
||||||
|
github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
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 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
|
||||||
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
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/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.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
|||||||
407
golden.go
407
golden.go
@@ -1,14 +1,68 @@
|
|||||||
// Package golden is yet another package for working with *.golden test files,
|
// Package golden is yet another package for working with *.golden test files,
|
||||||
// with a focus on simplicity through it's default behavior.
|
// with a focus on simplicity through it's default behavior, and marshaling
|
||||||
|
// assertion helpers to validate the JSON, YAML, and XML formats of structs.
|
||||||
//
|
//
|
||||||
// Golden file names are based on the name of the test function and any subtest
|
// Golden file names are based on the name of the test function and any subtest
|
||||||
// names by calling t.Name(). File names are sanitized to ensure they're
|
// names by calling t.Name(). File names are sanitized to ensure they're
|
||||||
// compatible with Linux, macOS and Windows systems regardless of what crazy
|
// compatible with Linux, macOS and Windows systems regardless of what
|
||||||
// characters might be in a subtest's name.
|
// characters might be in a subtest's name.
|
||||||
//
|
//
|
||||||
// Usage
|
// Assertion Helpers
|
||||||
//
|
//
|
||||||
// Typical usage should look something like this:
|
// There are marshaling assertion helpers for JSON, YAML, and XML. Each helper
|
||||||
|
// operates in two stages:
|
||||||
|
//
|
||||||
|
// 1. Marshal the provided object to a byte slice and read the corresponding
|
||||||
|
// golden file from disk followed by verifying both are identical.
|
||||||
|
//
|
||||||
|
// 2. Unmarshal the content of the golden file, verifying that the result is
|
||||||
|
// identical to the original given object.
|
||||||
|
//
|
||||||
|
// Typical usage of a assertion helper would look something like this in a
|
||||||
|
// tabular test:
|
||||||
|
//
|
||||||
|
// type MyStruct struct {
|
||||||
|
// FooBar string `json:"foo_bar" yaml:"fooBar" xml:"Foo_Bar"`
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// func TestMyStructMarshaling(t *testing.T) {
|
||||||
|
// tests := []struct {
|
||||||
|
// name string
|
||||||
|
// obj *MyStruct
|
||||||
|
// }{
|
||||||
|
// {name: "empty", obj: &MyStruct{}},
|
||||||
|
// {name: "full", obj: &MyStruct{FooBar: "Hello World!"}},
|
||||||
|
// }
|
||||||
|
// for _, tt := range tests {
|
||||||
|
// t.Run(tt.name, func(t *testing.T) {
|
||||||
|
// golden.AssertJSONMarshaling(t, tt.obj)
|
||||||
|
// golden.AssertYAMLMarshaling(t, tt.obj)
|
||||||
|
// golden.AssertXMLMarshaling(t, tt.obj)
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// The above example will read from the following golden files:
|
||||||
|
//
|
||||||
|
// testdata/TestMyStructMarshaling/empty/marshaled_json.golden
|
||||||
|
// testdata/TestMyStructMarshaling/empty/marshaled_xml.golden
|
||||||
|
// testdata/TestMyStructMarshaling/empty/marshaled_yaml.golden
|
||||||
|
// testdata/TestMyStructMarshaling/full/marshaled_json.golden
|
||||||
|
// testdata/TestMyStructMarshaling/full/marshaled_xml.golden
|
||||||
|
// testdata/TestMyStructMarshaling/full/marshaled_yaml.golden
|
||||||
|
//
|
||||||
|
// If a corresponding golden file cannot be found on disk, the test will fail.
|
||||||
|
// To create/update golden files, simply set the GOLDEN_UPDATE environment
|
||||||
|
// variable to one of "1", "y", "t", "yes", "on", or "true" when running the
|
||||||
|
// tests.
|
||||||
|
//
|
||||||
|
// Golden files should be committed to source control, as it allow tests to fail
|
||||||
|
// when the marshaling results for an object changes.
|
||||||
|
//
|
||||||
|
// Golden Usage
|
||||||
|
//
|
||||||
|
// If you need manual control over golden file reading and writing, this is a
|
||||||
|
// typical example:
|
||||||
//
|
//
|
||||||
// func TestExampleMyStruct(t *testing.T) {
|
// func TestExampleMyStruct(t *testing.T) {
|
||||||
// got, err := json.Marshal(&MyStruct{Foo: "Bar"})
|
// got, err := json.Marshal(&MyStruct{Foo: "Bar"})
|
||||||
@@ -123,30 +177,37 @@
|
|||||||
package golden
|
package golden
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/jimeh/go-golden/sanitize"
|
||||||
|
"github.com/spf13/afero"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
// TestingT is a interface describing a sub-set of methods of *testing.T which
|
||||||
DefaultDirMode = 0o755
|
// golden uses.
|
||||||
DefaultFileMode = 0o644
|
type TestingT interface {
|
||||||
DefaultSuffix = ".golden"
|
Error(args ...interface{})
|
||||||
DefaultDirname = "testdata"
|
Errorf(format string, args ...interface{})
|
||||||
)
|
FailNow()
|
||||||
|
Fatal(args ...interface{})
|
||||||
|
Fatalf(format string, args ...interface{})
|
||||||
|
Helper()
|
||||||
|
Log(args ...interface{})
|
||||||
|
Logf(format string, args ...interface{})
|
||||||
|
Name() string
|
||||||
|
}
|
||||||
|
|
||||||
var DefaultUpdateFunc = EnvUpdateFunc
|
var defaultGolden = New()
|
||||||
|
|
||||||
var global = New()
|
|
||||||
|
|
||||||
// File returns the filename of the golden file for the given *testing.T
|
// File returns the filename of the golden file for the given *testing.T
|
||||||
// instance as determined by t.Name().
|
// instance as determined by t.Name().
|
||||||
func File(t *testing.T) string {
|
func File(t *testing.T) string {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
return global.File(t)
|
return defaultGolden.File(t)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get returns the content of the golden file for the given *testing.T instance
|
// Get returns the content of the golden file for the given *testing.T instance
|
||||||
@@ -155,7 +216,7 @@ func File(t *testing.T) string {
|
|||||||
func Get(t *testing.T) []byte {
|
func Get(t *testing.T) []byte {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
return global.Get(t)
|
return defaultGolden.Get(t)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set writes given data to the golden file for the given *testing.T instance as
|
// Set writes given data to the golden file for the given *testing.T instance as
|
||||||
@@ -164,7 +225,7 @@ func Get(t *testing.T) []byte {
|
|||||||
func Set(t *testing.T, data []byte) {
|
func Set(t *testing.T, data []byte) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
global.Set(t, data)
|
defaultGolden.Set(t, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
// FileP returns the filename of the specifically named golden file for the
|
// FileP returns the filename of the specifically named golden file for the
|
||||||
@@ -172,7 +233,7 @@ func Set(t *testing.T, data []byte) {
|
|||||||
func FileP(t *testing.T, name string) string {
|
func FileP(t *testing.T, name string) string {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
return global.FileP(t, name)
|
return defaultGolden.FileP(t, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetP returns the content of the specifically named golden file belonging
|
// GetP returns the content of the specifically named golden file belonging
|
||||||
@@ -184,7 +245,7 @@ func FileP(t *testing.T, name string) string {
|
|||||||
func GetP(t *testing.T, name string) []byte {
|
func GetP(t *testing.T, name string) []byte {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
return global.GetP(t, name)
|
return defaultGolden.GetP(t, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetP writes given data of the specifically named golden file belonging to
|
// SetP writes given data of the specifically named golden file belonging to
|
||||||
@@ -196,7 +257,7 @@ func GetP(t *testing.T, name string) []byte {
|
|||||||
func SetP(t *testing.T, name string, data []byte) {
|
func SetP(t *testing.T, name string, data []byte) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
global.SetP(t, name, data)
|
defaultGolden.SetP(t, name, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update returns true when golden is set to update golden files. Should be used
|
// Update returns true when golden is set to update golden files. Should be used
|
||||||
@@ -206,104 +267,228 @@ func SetP(t *testing.T, name string, data []byte) {
|
|||||||
// environment variable is set to a truthy value. To customize create a custom
|
// environment variable is set to a truthy value. To customize create a custom
|
||||||
// *Golden instance with New() and set a new UpdateFunc value.
|
// *Golden instance with New() and set a new UpdateFunc value.
|
||||||
func Update() bool {
|
func Update() bool {
|
||||||
return global.Update()
|
return defaultGolden.Update()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Golden handles all interactions with golden files. The top-level package
|
// Golden handles all interactions with golden files. The top-level package
|
||||||
// functions all just proxy through to a default global *Golden instance.
|
// functions proxy through to a default global Golden instance.
|
||||||
type Golden struct {
|
type Golden interface {
|
||||||
// DirMode determines the file system permissions of any folders created to
|
// File returns the filename of the golden file for the given testing.TB
|
||||||
// hold golden files.
|
// instance as determined by t.Name().
|
||||||
DirMode os.FileMode
|
File(t TestingT) string
|
||||||
|
|
||||||
// FileMode determines the file system permissions of any created or updated
|
// Get returns the content of the golden file for the given TestingT
|
||||||
// golden files written to disk.
|
// instance as determined by t.Name(). If no golden file can be found/read,
|
||||||
FileMode os.FileMode
|
// it will fail the test by calling t.Fatal().
|
||||||
|
Get(t TestingT) []byte
|
||||||
|
|
||||||
// Suffix determines the filename suffix for all golden files. Typically
|
// Set writes given data to the golden file for the given TestingT
|
||||||
// this should be ".golden", but can be changed here if needed.
|
// instance as determined by t.Name(). If writing fails it will fail the
|
||||||
Suffix string
|
// test by calling t.Fatal() with error details.
|
||||||
|
Set(t TestingT, data []byte)
|
||||||
|
|
||||||
// Dirname is the name of the top-level directory at the root of the package
|
// FileP returns the filename of the specifically named golden file for the
|
||||||
// which holds all golden files. Typically this should "testdata", but can
|
// given TestingT instance as determined by t.Name().
|
||||||
// be changed here if needed.
|
FileP(t TestingT, name string) string
|
||||||
Dirname string
|
|
||||||
|
|
||||||
// UpdateFunc is used to determine if golden files should be updated or
|
// GetP returns the content of the specifically named golden file belonging
|
||||||
// not. Its boolean return value is returned by Update().
|
// to the given TestingT instance as determined by t.Name(). If no golden
|
||||||
UpdateFunc UpdateFunc
|
// file can be found/read, it will fail the test with t.Fatal().
|
||||||
|
//
|
||||||
|
// This is very similar to Get(), but it allows multiple different golden
|
||||||
|
// files to be used within the same one TestingT instance.
|
||||||
|
GetP(t TestingT, name string) []byte
|
||||||
|
|
||||||
|
// SetP writes given data of the specifically named golden file belonging to
|
||||||
|
// the given TestingT instance as determined by t.Name(). If writing fails
|
||||||
|
// it will fail the test with t.Fatal() detailing the error.
|
||||||
|
//
|
||||||
|
// This is very similar to Set(), but it allows multiple different golden
|
||||||
|
// files to be used within the same one TestingT instance.
|
||||||
|
SetP(t TestingT, name string, data []byte)
|
||||||
|
|
||||||
|
// Update returns true when golden is set to update golden files. Should be
|
||||||
|
// used to determine if golden.Set() or golden.SetP() should be called or
|
||||||
|
// not.
|
||||||
|
//
|
||||||
|
// Default behavior uses EnvUpdateFunc() to check if the "GOLDEN_UPDATE"
|
||||||
|
// environment variable is set to a truthy value. To customize set a new
|
||||||
|
// UpdateFunc value on *Golden.
|
||||||
|
Update() bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// New returns a new *Golden instance with default values correctly
|
// New returns a new Golden instance. Used to create custom Golden instances.
|
||||||
// populated. This is ideally how you should create a custom *Golden, and then
|
// See the the various Option functions for details of what can be customized.
|
||||||
// modify the relevant fields as you see fit.
|
func New(options ...Option) Golden {
|
||||||
func New() *Golden {
|
g := &golden{
|
||||||
return &Golden{
|
dirMode: 0o755,
|
||||||
DirMode: DefaultDirMode,
|
fileMode: 0o644,
|
||||||
FileMode: DefaultFileMode,
|
suffix: ".golden",
|
||||||
Suffix: DefaultSuffix,
|
dirname: "testdata",
|
||||||
Dirname: DefaultDirname,
|
updateFunc: EnvUpdateFunc,
|
||||||
UpdateFunc: DefaultUpdateFunc,
|
fs: afero.NewOsFs(),
|
||||||
|
logOnWrite: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, opt := range options {
|
||||||
|
opt.apply(g)
|
||||||
|
}
|
||||||
|
|
||||||
|
return g
|
||||||
}
|
}
|
||||||
|
|
||||||
// File returns the filename of the golden file for the given *testing.T
|
type Option interface {
|
||||||
// instance as determined by t.Name().
|
apply(*golden)
|
||||||
func (s *Golden) File(t *testing.T) string {
|
}
|
||||||
|
|
||||||
|
type optionFunc func(*golden)
|
||||||
|
|
||||||
|
func (fn optionFunc) apply(g *golden) {
|
||||||
|
fn(g)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithDirMode sets the file system permissions used for any folders created to
|
||||||
|
// hold golden files.
|
||||||
|
//
|
||||||
|
// When this option is not provided, the default value is 0o755.
|
||||||
|
func WithDirMode(mode os.FileMode) Option {
|
||||||
|
return optionFunc(func(g *golden) {
|
||||||
|
g.dirMode = mode
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithFileMode sets the file system permissions used for any created or updated
|
||||||
|
// golden files written to.
|
||||||
|
//
|
||||||
|
// When this option is not provided, the default value is 0o644.
|
||||||
|
func WithFileMode(mode os.FileMode) Option {
|
||||||
|
return optionFunc(func(g *golden) {
|
||||||
|
g.fileMode = mode
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithSuffix sets the filename suffix used for all golden files.
|
||||||
|
//
|
||||||
|
// When this option is not provided, the default value is ".golden".
|
||||||
|
func WithSuffix(suffix string) Option {
|
||||||
|
return optionFunc(func(g *golden) {
|
||||||
|
g.suffix = suffix
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithDirname sets the name of the top-level directory used to hold golden
|
||||||
|
// files.
|
||||||
|
//
|
||||||
|
// When this option is not provided, the default value is "testdata".
|
||||||
|
func WithDirname(name string) Option {
|
||||||
|
return optionFunc(func(g *golden) {
|
||||||
|
g.dirname = name
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithUpdateFunc sets the function used to determine if golden files should be
|
||||||
|
// updated or not. Essentially the provided UpdateFunc is called by Update().
|
||||||
|
//
|
||||||
|
// When this option is not provided, the default value is EnvUpdateFunc.
|
||||||
|
func WithUpdateFunc(fn UpdateFunc) Option {
|
||||||
|
return optionFunc(func(g *golden) {
|
||||||
|
g.updateFunc = fn
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithFs sets s afero.Fs instance which is used to read/write all golden files.
|
||||||
|
//
|
||||||
|
// When this option is not provided, the default value is afero.NewOsFs().
|
||||||
|
func WithFs(fs afero.Fs) Option {
|
||||||
|
return optionFunc(func(g *golden) {
|
||||||
|
g.fs = fs
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithSilentWrites silences the "golden: writing [...]" log messages whenever
|
||||||
|
// set functions write a golden file to disk.
|
||||||
|
func WithSilentWrites() Option {
|
||||||
|
return optionFunc(func(g *golden) {
|
||||||
|
g.logOnWrite = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// golden is the underlying struct that implements the Golden interface.
|
||||||
|
type golden struct {
|
||||||
|
// dirMode determines the file system permissions of any folders created to
|
||||||
|
// hold golden files.
|
||||||
|
dirMode os.FileMode
|
||||||
|
|
||||||
|
// fileMode determines the file system permissions of any created or updated
|
||||||
|
// golden files written to disk.
|
||||||
|
fileMode os.FileMode
|
||||||
|
|
||||||
|
// suffix determines the filename suffix for all golden files. Typically
|
||||||
|
// this should be ".golden", but can be changed here if needed.
|
||||||
|
suffix string
|
||||||
|
|
||||||
|
// dirname is the name of the top-level directory at the root of the package
|
||||||
|
// which holds all golden files. Typically this should be "testdata", but
|
||||||
|
// can be changed here if needed.
|
||||||
|
dirname string
|
||||||
|
|
||||||
|
// updateFunc is used to determine if golden files should be updated or
|
||||||
|
// not. Its boolean return value is returned by Update().
|
||||||
|
updateFunc UpdateFunc
|
||||||
|
|
||||||
|
// fs is used for all file system operations. This enables providing custom
|
||||||
|
// afero.fs instances which can be useful for testing purposes.
|
||||||
|
fs afero.Fs
|
||||||
|
|
||||||
|
// logOnWrite determines if a message is logged with t.Logf when a golden
|
||||||
|
// file is written to with either of the set methods.
|
||||||
|
logOnWrite bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure golden satisfies Golden interface.
|
||||||
|
var _ Golden = &golden{}
|
||||||
|
|
||||||
|
func (s *golden) File(t TestingT) string {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
return s.file(t, "")
|
return s.file(t, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get returns the content of the golden file for the given *testing.T instance
|
func (s *golden) Get(t TestingT) []byte {
|
||||||
// as determined by t.Name(). If no golden file can be found/read, it will fail
|
t.Helper()
|
||||||
// the test by calling t.Fatal().
|
|
||||||
func (s *Golden) Get(t *testing.T) []byte {
|
|
||||||
return s.get(t, "")
|
return s.get(t, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set writes given data to the golden file for the given *testing.T instance as
|
func (s *golden) Set(t TestingT, data []byte) {
|
||||||
// determined by t.Name(). If writing fails it will fail the test by calling
|
t.Helper()
|
||||||
// t.Fatal() with error details.
|
|
||||||
func (s *Golden) Set(t *testing.T, data []byte) {
|
|
||||||
s.set(t, "", data)
|
s.set(t, "", data)
|
||||||
}
|
}
|
||||||
|
|
||||||
// FileP returns the filename of the specifically named golden file for the
|
func (s *golden) FileP(t TestingT, name string) string {
|
||||||
// given *testing.T instance as determined by t.Name().
|
t.Helper()
|
||||||
func (s *Golden) FileP(t *testing.T, name string) string {
|
|
||||||
if name == "" {
|
|
||||||
if t != nil {
|
|
||||||
t.Fatal("golden: name cannot be empty")
|
|
||||||
}
|
|
||||||
|
|
||||||
return ""
|
if name == "" {
|
||||||
|
t.Fatalf("golden: test name cannot be empty")
|
||||||
}
|
}
|
||||||
|
|
||||||
return s.file(t, name)
|
return s.file(t, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetP returns the content of the specifically named golden file belonging
|
func (s *golden) GetP(t TestingT, name string) []byte {
|
||||||
// to the given *testing.T instance as determined by t.Name(). If no golden file
|
t.Helper()
|
||||||
// can be found/read, it will fail the test with t.Fatal().
|
|
||||||
//
|
|
||||||
// This is very similar to Get(), but it allows multiple different golden files
|
|
||||||
// to be used within the same one *testing.T instance.
|
|
||||||
func (s *Golden) GetP(t *testing.T, name string) []byte {
|
|
||||||
if name == "" {
|
if name == "" {
|
||||||
t.Fatal("golden: name cannot be empty")
|
t.Fatal("golden: name cannot be empty")
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return s.get(t, name)
|
return s.get(t, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetP writes given data of the specifically named golden file belonging to
|
func (s *golden) SetP(t TestingT, name string, data []byte) {
|
||||||
// the given *testing.T instance as determined by t.Name(). If writing fails it
|
t.Helper()
|
||||||
// will fail the test with t.Fatal() detailing the error.
|
|
||||||
//
|
|
||||||
// This is very similar to Set(), but it allows multiple different golden files
|
|
||||||
// to be used within the same one *testing.T instance.
|
|
||||||
func (s *Golden) SetP(t *testing.T, name string, data []byte) {
|
|
||||||
if name == "" {
|
if name == "" {
|
||||||
t.Fatal("golden: name cannot be empty")
|
t.Fatal("golden: name cannot be empty")
|
||||||
}
|
}
|
||||||
@@ -311,65 +496,65 @@ func (s *Golden) SetP(t *testing.T, name string, data []byte) {
|
|||||||
s.set(t, name, data)
|
s.set(t, name, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Golden) file(t *testing.T, name string) string {
|
func (s *golden) file(t TestingT, name string) string {
|
||||||
if t.Name() == "" {
|
t.Helper()
|
||||||
t.Fatalf("golden: could not determine filename for: %+v", t)
|
|
||||||
|
|
||||||
return ""
|
if t.Name() == "" {
|
||||||
|
t.Fatalf(
|
||||||
|
"golden: could not determine filename for given %T instance", t,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
base := []string{s.Dirname, filepath.FromSlash(t.Name())}
|
base := []string{s.dirname, filepath.FromSlash(t.Name())}
|
||||||
if name != "" {
|
if name != "" {
|
||||||
base = append(base, name)
|
base = append(base, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
f := filepath.Clean(filepath.Join(base...) + s.Suffix)
|
f := filepath.Clean(filepath.Join(base...) + s.suffix)
|
||||||
|
|
||||||
dirty := strings.Split(f, string(os.PathSeparator))
|
dirty := strings.Split(f, string(os.PathSeparator))
|
||||||
clean := make([]string, 0, len(dirty))
|
clean := make([]string, 0, len(dirty))
|
||||||
for _, s := range dirty {
|
for _, s := range dirty {
|
||||||
clean = append(clean, sanitizeFilename(s))
|
clean = append(clean, sanitize.Filename(s))
|
||||||
}
|
}
|
||||||
|
|
||||||
return strings.Join(clean, string(os.PathSeparator))
|
return strings.Join(clean, string(os.PathSeparator))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Golden) get(t *testing.T, name string) []byte {
|
func (s *golden) get(t TestingT, name string) []byte {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
f := s.file(t, name)
|
f := s.file(t, name)
|
||||||
|
|
||||||
b, err := ioutil.ReadFile(f)
|
b, err := afero.ReadFile(s.fs, f)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("golden: failed reading %s: %s", f, err.Error())
|
t.Fatalf("golden: %s", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Golden) set(t *testing.T, name string, data []byte) {
|
func (s *golden) set(t TestingT, name string, data []byte) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
f := s.file(t, name)
|
f := s.file(t, name)
|
||||||
dir := filepath.Dir(f)
|
dir := filepath.Dir(f)
|
||||||
|
|
||||||
t.Logf("golden: writing .golden file: %s", f)
|
if s.logOnWrite {
|
||||||
|
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)
|
err := s.fs.MkdirAll(dir, s.dirMode)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("golden: failed to create directory: %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
err = afero.WriteFile(s.fs, f, data, s.fileMode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("golden: filed to write file: %s", err.Error())
|
t.Fatalf("golden: filed to write file: %s", err.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update returns true when golden is set to update golden files. Should be used
|
func (s *golden) Update() bool {
|
||||||
// to determine if golden.Set() or golden.SetP() should be called or not.
|
return s.updateFunc()
|
||||||
//
|
|
||||||
// Default behavior uses EnvUpdateFunc() to check if the "GOLDEN_UPDATE"
|
|
||||||
// environment variable is set to a truthy value. To customize set a new
|
|
||||||
// UpdateFunc value on *Golden.
|
|
||||||
func (s *Golden) Update() bool {
|
|
||||||
return s.UpdateFunc()
|
|
||||||
}
|
}
|
||||||
|
|||||||
626
golden_test.go
626
golden_test.go
@@ -4,9 +4,13 @@ import (
|
|||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"reflect"
|
||||||
|
"runtime"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/jimeh/envctl"
|
"github.com/jimeh/envctl"
|
||||||
|
"github.com/jimeh/go-mocktesting"
|
||||||
|
"github.com/spf13/afero"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
@@ -122,6 +126,7 @@ func TestGet(t *testing.T) {
|
|||||||
|
|
||||||
func TestSet(t *testing.T) {
|
func TestSet(t *testing.T) {
|
||||||
t.Cleanup(func() {
|
t.Cleanup(func() {
|
||||||
|
t.Log("cleaning up golden files")
|
||||||
err := os.RemoveAll(filepath.Join("testdata", "TestSet"))
|
err := os.RemoveAll(filepath.Join("testdata", "TestSet"))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
err = os.Remove(filepath.Join("testdata", "TestSet.golden"))
|
err = os.Remove(filepath.Join("testdata", "TestSet.golden"))
|
||||||
@@ -344,6 +349,7 @@ func TestGetP(t *testing.T) {
|
|||||||
|
|
||||||
func TestSetP(t *testing.T) {
|
func TestSetP(t *testing.T) {
|
||||||
t.Cleanup(func() {
|
t.Cleanup(func() {
|
||||||
|
t.Log("cleaning up golden files")
|
||||||
err := os.RemoveAll(filepath.Join("testdata", "TestSetP"))
|
err := os.RemoveAll(filepath.Join("testdata", "TestSetP"))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
})
|
})
|
||||||
@@ -441,3 +447,623 @@ func TestUpdate(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestNew(t *testing.T) {
|
||||||
|
myUpdateFunc := func() bool { return false }
|
||||||
|
|
||||||
|
type args struct {
|
||||||
|
options []Option
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
want *golden
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "no options",
|
||||||
|
args: args{options: nil},
|
||||||
|
want: &golden{
|
||||||
|
dirMode: 0o755,
|
||||||
|
fileMode: 0o644,
|
||||||
|
suffix: ".golden",
|
||||||
|
dirname: "testdata",
|
||||||
|
updateFunc: EnvUpdateFunc,
|
||||||
|
fs: afero.NewOsFs(),
|
||||||
|
logOnWrite: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "all options",
|
||||||
|
args: args{
|
||||||
|
options: []Option{
|
||||||
|
WithDirMode(0o777),
|
||||||
|
WithFileMode(0o666),
|
||||||
|
WithSuffix(".gold"),
|
||||||
|
WithDirname("goldstuff"),
|
||||||
|
WithUpdateFunc(myUpdateFunc),
|
||||||
|
WithFs(afero.NewMemMapFs()),
|
||||||
|
WithSilentWrites(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: &golden{
|
||||||
|
dirMode: 0o777,
|
||||||
|
fileMode: 0o666,
|
||||||
|
suffix: ".gold",
|
||||||
|
dirname: "goldstuff",
|
||||||
|
updateFunc: myUpdateFunc,
|
||||||
|
fs: afero.NewMemMapFs(),
|
||||||
|
logOnWrite: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
g := New(tt.args.options...)
|
||||||
|
got, ok := g.(*golden)
|
||||||
|
require.True(t, ok, "New did not returns a *golden instance")
|
||||||
|
|
||||||
|
gotUpdateFunc := runtime.FuncForPC(
|
||||||
|
reflect.ValueOf(got.updateFunc).Pointer(),
|
||||||
|
).Name()
|
||||||
|
wantUpdateFunc := runtime.FuncForPC(
|
||||||
|
reflect.ValueOf(tt.want.updateFunc).Pointer(),
|
||||||
|
).Name()
|
||||||
|
|
||||||
|
assert.Equal(t, tt.want.dirMode, got.dirMode)
|
||||||
|
assert.Equal(t, tt.want.fileMode, got.fileMode)
|
||||||
|
assert.Equal(t, tt.want.suffix, got.suffix)
|
||||||
|
assert.Equal(t, tt.want.dirname, got.dirname)
|
||||||
|
assert.Equal(t, tt.want.logOnWrite, got.logOnWrite)
|
||||||
|
assert.Equal(t, wantUpdateFunc, gotUpdateFunc)
|
||||||
|
assert.IsType(t, tt.want.fs, got.fs)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_golden_File(t *testing.T) {
|
||||||
|
type fields struct {
|
||||||
|
suffix *string
|
||||||
|
dirname *string
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
testName string
|
||||||
|
fields fields
|
||||||
|
want string
|
||||||
|
wantAborted bool
|
||||||
|
wantFailCount int
|
||||||
|
wantTestOutput []string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "top-level",
|
||||||
|
testName: "TestFooBar",
|
||||||
|
want: filepath.Join("testdata", "TestFooBar.golden"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "sub-test",
|
||||||
|
testName: "TestFooBar/it_is_here",
|
||||||
|
want: filepath.Join(
|
||||||
|
"testdata", "TestFooBar", "it_is_here.golden",
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "blank test name",
|
||||||
|
testName: "",
|
||||||
|
wantAborted: true,
|
||||||
|
wantFailCount: 1,
|
||||||
|
wantTestOutput: []string{
|
||||||
|
"golden: could not determine filename for given " +
|
||||||
|
"*mocktesting.T instance\n",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "custom dirname",
|
||||||
|
testName: "TestFozBar",
|
||||||
|
fields: fields{
|
||||||
|
dirname: stringPtr("goldenfiles"),
|
||||||
|
},
|
||||||
|
want: filepath.Join("goldenfiles", "TestFozBar.golden"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "custom suffix",
|
||||||
|
testName: "TestFozBaz",
|
||||||
|
fields: fields{
|
||||||
|
suffix: stringPtr(".goldfile"),
|
||||||
|
},
|
||||||
|
want: filepath.Join("testdata", "TestFozBaz.goldfile"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "custom dirname and suffix",
|
||||||
|
testName: "TestFozBar",
|
||||||
|
fields: fields{
|
||||||
|
dirname: stringPtr("goldenfiles"),
|
||||||
|
suffix: stringPtr(".goldfile"),
|
||||||
|
},
|
||||||
|
want: filepath.Join("goldenfiles", "TestFozBar.goldfile"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "invalid chars in test name",
|
||||||
|
testName: `TestFooBar/foo?<>:*|"bar`,
|
||||||
|
want: filepath.Join(
|
||||||
|
"testdata", "TestFooBar", "foo_______bar.golden",
|
||||||
|
),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
if tt.fields.suffix == nil {
|
||||||
|
tt.fields.suffix = stringPtr(".golden")
|
||||||
|
}
|
||||||
|
if tt.fields.dirname == nil {
|
||||||
|
tt.fields.dirname = stringPtr("testdata")
|
||||||
|
}
|
||||||
|
|
||||||
|
g := &golden{
|
||||||
|
suffix: *tt.fields.suffix,
|
||||||
|
dirname: *tt.fields.dirname,
|
||||||
|
}
|
||||||
|
|
||||||
|
mt := mocktesting.NewT(tt.testName)
|
||||||
|
|
||||||
|
var got string
|
||||||
|
mocktesting.Go(func() {
|
||||||
|
got = g.File(mt)
|
||||||
|
})
|
||||||
|
|
||||||
|
assert.Equal(t, tt.want, got)
|
||||||
|
assert.Equal(t, tt.wantAborted, mt.Aborted(), "aborted")
|
||||||
|
assert.Equal(t,
|
||||||
|
tt.wantFailCount, mt.FailedCount(), "failed count",
|
||||||
|
)
|
||||||
|
assert.Equal(t, tt.wantTestOutput, mt.Output(), "test output")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_golden_Get(t *testing.T) {
|
||||||
|
type fields struct {
|
||||||
|
suffix *string
|
||||||
|
dirname *string
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
testName string
|
||||||
|
fields fields
|
||||||
|
files map[string][]byte
|
||||||
|
want []byte
|
||||||
|
wantAborted bool
|
||||||
|
wantFailCount int
|
||||||
|
wantTestOutput []string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "file exists",
|
||||||
|
testName: "TestFooBar",
|
||||||
|
files: map[string][]byte{
|
||||||
|
filepath.Join("testdata", "TestFooBar.golden"): []byte(
|
||||||
|
"foo: bar\nhello: world",
|
||||||
|
),
|
||||||
|
},
|
||||||
|
want: []byte("foo: bar\nhello: world"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "file is missing",
|
||||||
|
testName: "TestFooBar",
|
||||||
|
files: map[string][]byte{},
|
||||||
|
wantAborted: true,
|
||||||
|
wantFailCount: 1,
|
||||||
|
wantTestOutput: []string{
|
||||||
|
"golden: open " + filepath.Join(
|
||||||
|
"testdata", "TestFooBar.golden",
|
||||||
|
) + ": file does not exist\n",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "sub-test file exists",
|
||||||
|
testName: "TestFooBar/it_is_here",
|
||||||
|
files: map[string][]byte{
|
||||||
|
filepath.Join(
|
||||||
|
"testdata", "TestFooBar", "it_is_here.golden",
|
||||||
|
): []byte("this is really here ^_^\n"),
|
||||||
|
},
|
||||||
|
want: []byte("this is really here ^_^\n"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "sub-test file is missing",
|
||||||
|
testName: "TestFooBar/not_really_here",
|
||||||
|
files: map[string][]byte{},
|
||||||
|
wantAborted: true,
|
||||||
|
wantFailCount: 1,
|
||||||
|
wantTestOutput: []string{
|
||||||
|
"golden: open " + filepath.Join(
|
||||||
|
"testdata", "TestFooBar", "not_really_here.golden",
|
||||||
|
) + ": file does not exist\n",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "blank test name",
|
||||||
|
testName: "",
|
||||||
|
wantAborted: true,
|
||||||
|
wantFailCount: 1,
|
||||||
|
wantTestOutput: []string{
|
||||||
|
"golden: could not determine filename for given " +
|
||||||
|
"*mocktesting.T instance\n",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "custom dirname",
|
||||||
|
testName: "TestFozBar",
|
||||||
|
fields: fields{
|
||||||
|
dirname: stringPtr("goldenfiles"),
|
||||||
|
},
|
||||||
|
files: map[string][]byte{
|
||||||
|
filepath.Join("goldenfiles", "TestFozBar.golden"): []byte(
|
||||||
|
"foo: bar\nhello: world",
|
||||||
|
),
|
||||||
|
},
|
||||||
|
want: []byte("foo: bar\nhello: world"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "custom suffix",
|
||||||
|
testName: "TestFozBaz",
|
||||||
|
fields: fields{
|
||||||
|
suffix: stringPtr(".goldfile"),
|
||||||
|
},
|
||||||
|
files: map[string][]byte{
|
||||||
|
filepath.Join("testdata", "TestFozBaz.goldfile"): []byte(
|
||||||
|
"foo: bar\nhello: world",
|
||||||
|
),
|
||||||
|
},
|
||||||
|
want: []byte("foo: bar\nhello: world"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "custom dirname and suffix",
|
||||||
|
testName: "TestFozBar",
|
||||||
|
fields: fields{
|
||||||
|
dirname: stringPtr("goldenfiles"),
|
||||||
|
suffix: stringPtr(".goldfile"),
|
||||||
|
},
|
||||||
|
files: map[string][]byte{
|
||||||
|
filepath.Join("goldenfiles", "TestFozBar.goldfile"): []byte(
|
||||||
|
"foo: bar\nhello: world",
|
||||||
|
),
|
||||||
|
},
|
||||||
|
want: []byte("foo: bar\nhello: world"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "invalid chars in test name",
|
||||||
|
testName: `TestFooBar/foo?<>:*|"bar`,
|
||||||
|
files: map[string][]byte{
|
||||||
|
filepath.Join(
|
||||||
|
"testdata", "TestFooBar", "foo_______bar.golden",
|
||||||
|
): []byte("foo: bar\nhello: world"),
|
||||||
|
},
|
||||||
|
want: []byte("foo: bar\nhello: world"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
fs := afero.NewMemMapFs()
|
||||||
|
for f, b := range tt.files {
|
||||||
|
_ = afero.WriteFile(fs, f, b, 0o644)
|
||||||
|
}
|
||||||
|
|
||||||
|
if tt.fields.suffix == nil {
|
||||||
|
tt.fields.suffix = stringPtr(".golden")
|
||||||
|
}
|
||||||
|
if tt.fields.dirname == nil {
|
||||||
|
tt.fields.dirname = stringPtr("testdata")
|
||||||
|
}
|
||||||
|
|
||||||
|
g := &golden{
|
||||||
|
suffix: *tt.fields.suffix,
|
||||||
|
dirname: *tt.fields.dirname,
|
||||||
|
fs: fs,
|
||||||
|
}
|
||||||
|
|
||||||
|
mt := mocktesting.NewT(tt.testName)
|
||||||
|
|
||||||
|
var got []byte
|
||||||
|
mocktesting.Go(func() {
|
||||||
|
got = g.Get(mt)
|
||||||
|
})
|
||||||
|
|
||||||
|
assert.Equal(t, tt.want, got)
|
||||||
|
assert.Equal(t, tt.wantAborted, mt.Aborted(), "aborted")
|
||||||
|
assert.Equal(t,
|
||||||
|
tt.wantFailCount, mt.FailedCount(), "failed count",
|
||||||
|
)
|
||||||
|
assert.Equal(t, tt.wantTestOutput, mt.Output(), "test output")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_golden_FileP(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
name string
|
||||||
|
}
|
||||||
|
type fields struct {
|
||||||
|
suffix *string
|
||||||
|
dirname *string
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
testName string
|
||||||
|
args args
|
||||||
|
fields fields
|
||||||
|
want string
|
||||||
|
wantAborted bool
|
||||||
|
wantFailCount int
|
||||||
|
wantTestOutput []string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "top-level",
|
||||||
|
testName: "TestFooBar",
|
||||||
|
args: args{name: "yaml"},
|
||||||
|
want: filepath.Join("testdata", "TestFooBar", "yaml.golden"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "sub-test",
|
||||||
|
testName: "TestFooBar/it_is_here",
|
||||||
|
args: args{name: "json"},
|
||||||
|
want: filepath.Join(
|
||||||
|
"testdata", "TestFooBar", "it_is_here", "json.golden",
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "blank test name",
|
||||||
|
testName: "",
|
||||||
|
args: args{name: "json"},
|
||||||
|
wantAborted: true,
|
||||||
|
wantFailCount: 1,
|
||||||
|
wantTestOutput: []string{
|
||||||
|
"golden: could not determine filename for given " +
|
||||||
|
"*mocktesting.T instance\n",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "custom dirname",
|
||||||
|
testName: "TestFozBar",
|
||||||
|
args: args{name: "xml"},
|
||||||
|
fields: fields{
|
||||||
|
dirname: stringPtr("goldenfiles"),
|
||||||
|
},
|
||||||
|
want: filepath.Join("goldenfiles", "TestFozBar", "xml.golden"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "custom suffix",
|
||||||
|
testName: "TestFozBaz",
|
||||||
|
args: args{name: "toml"},
|
||||||
|
fields: fields{
|
||||||
|
suffix: stringPtr(".goldfile"),
|
||||||
|
},
|
||||||
|
want: filepath.Join("testdata", "TestFozBaz", "toml.goldfile"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "custom dirname and suffix",
|
||||||
|
testName: "TestFozBar",
|
||||||
|
args: args{name: "json"},
|
||||||
|
fields: fields{
|
||||||
|
dirname: stringPtr("goldenfiles"),
|
||||||
|
suffix: stringPtr(".goldfile"),
|
||||||
|
},
|
||||||
|
want: filepath.Join("goldenfiles", "TestFozBar", "json.goldfile"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "invalid chars in test name",
|
||||||
|
testName: `TestFooBar/foo?<>:*|"bar`,
|
||||||
|
args: args{name: "yml"},
|
||||||
|
want: filepath.Join(
|
||||||
|
"testdata", "TestFooBar", "foo_______bar", "yml.golden",
|
||||||
|
),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
if tt.fields.suffix == nil {
|
||||||
|
tt.fields.suffix = stringPtr(".golden")
|
||||||
|
}
|
||||||
|
if tt.fields.dirname == nil {
|
||||||
|
tt.fields.dirname = stringPtr("testdata")
|
||||||
|
}
|
||||||
|
|
||||||
|
g := &golden{
|
||||||
|
suffix: *tt.fields.suffix,
|
||||||
|
dirname: *tt.fields.dirname,
|
||||||
|
}
|
||||||
|
|
||||||
|
mt := mocktesting.NewT(tt.testName)
|
||||||
|
|
||||||
|
var got string
|
||||||
|
mocktesting.Go(func() {
|
||||||
|
got = g.FileP(mt, tt.args.name)
|
||||||
|
})
|
||||||
|
|
||||||
|
assert.Equal(t, tt.want, got)
|
||||||
|
assert.Equal(t, tt.wantAborted, mt.Aborted(), "aborted")
|
||||||
|
assert.Equal(t,
|
||||||
|
tt.wantFailCount, mt.FailedCount(), "failed count",
|
||||||
|
)
|
||||||
|
assert.Equal(t, tt.wantTestOutput, mt.Output(), "test output")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_golden_GetP(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
name string
|
||||||
|
}
|
||||||
|
type fields struct {
|
||||||
|
suffix *string
|
||||||
|
dirname *string
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
testName string
|
||||||
|
args args
|
||||||
|
fields fields
|
||||||
|
files map[string][]byte
|
||||||
|
want []byte
|
||||||
|
wantAborted bool
|
||||||
|
wantFailCount int
|
||||||
|
wantTestOutput []string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "file exists",
|
||||||
|
testName: "TestFooBar",
|
||||||
|
args: args{name: "yaml"},
|
||||||
|
files: map[string][]byte{
|
||||||
|
filepath.Join("testdata", "TestFooBar", "yaml.golden"): []byte(
|
||||||
|
"foo: bar\nhello: world",
|
||||||
|
),
|
||||||
|
},
|
||||||
|
want: []byte("foo: bar\nhello: world"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "file is missing",
|
||||||
|
testName: "TestFooBar",
|
||||||
|
args: args{name: "yaml"},
|
||||||
|
files: map[string][]byte{},
|
||||||
|
wantAborted: true,
|
||||||
|
wantFailCount: 1,
|
||||||
|
wantTestOutput: []string{
|
||||||
|
"golden: open " + filepath.Join(
|
||||||
|
"testdata", "TestFooBar", "yaml.golden",
|
||||||
|
) + ": file does not exist\n",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "sub-test file exists",
|
||||||
|
testName: "TestFooBar/it_is_here",
|
||||||
|
args: args{name: "plain"},
|
||||||
|
files: map[string][]byte{
|
||||||
|
filepath.Join(
|
||||||
|
"testdata", "TestFooBar", "it_is_here", "plain.golden",
|
||||||
|
): []byte("this is really here ^_^\n"),
|
||||||
|
},
|
||||||
|
want: []byte("this is really here ^_^\n"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "sub-test file is missing",
|
||||||
|
testName: "TestFooBar/not_really_here",
|
||||||
|
args: args{name: "plain"},
|
||||||
|
files: map[string][]byte{},
|
||||||
|
wantAborted: true,
|
||||||
|
wantFailCount: 1,
|
||||||
|
wantTestOutput: []string{
|
||||||
|
"golden: open " + filepath.Join(
|
||||||
|
"testdata", "TestFooBar", "not_really_here", "plain.golden",
|
||||||
|
) + ": file does not exist\n",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "blank test name",
|
||||||
|
testName: "",
|
||||||
|
args: args{name: "plain"},
|
||||||
|
wantAborted: true,
|
||||||
|
wantFailCount: 1,
|
||||||
|
wantTestOutput: []string{
|
||||||
|
"golden: could not determine filename for given " +
|
||||||
|
"*mocktesting.T instance\n",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "blank name",
|
||||||
|
testName: "TestFooBar",
|
||||||
|
args: args{name: ""},
|
||||||
|
wantAborted: true,
|
||||||
|
wantFailCount: 1,
|
||||||
|
wantTestOutput: []string{
|
||||||
|
"golden: name cannot be empty\n",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "custom dirname",
|
||||||
|
testName: "TestFozBar",
|
||||||
|
args: args{name: "yaml"},
|
||||||
|
fields: fields{
|
||||||
|
dirname: stringPtr("goldenfiles"),
|
||||||
|
},
|
||||||
|
files: map[string][]byte{
|
||||||
|
filepath.Join(
|
||||||
|
"goldenfiles", "TestFozBar", "yaml.golden",
|
||||||
|
): []byte("foo: bar\nhello: world"),
|
||||||
|
},
|
||||||
|
want: []byte("foo: bar\nhello: world"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "custom suffix",
|
||||||
|
testName: "TestFozBaz",
|
||||||
|
args: args{name: "yaml"},
|
||||||
|
fields: fields{
|
||||||
|
suffix: stringPtr(".goldfile"),
|
||||||
|
},
|
||||||
|
files: map[string][]byte{
|
||||||
|
filepath.Join(
|
||||||
|
"testdata", "TestFozBaz", "yaml.goldfile",
|
||||||
|
): []byte("foo: bar\nhello: world"),
|
||||||
|
},
|
||||||
|
want: []byte("foo: bar\nhello: world"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "custom dirname and suffix",
|
||||||
|
testName: "TestFozBar",
|
||||||
|
args: args{name: "yaml"},
|
||||||
|
fields: fields{
|
||||||
|
dirname: stringPtr("goldenfiles"),
|
||||||
|
suffix: stringPtr(".goldfile"),
|
||||||
|
},
|
||||||
|
files: map[string][]byte{
|
||||||
|
filepath.Join(
|
||||||
|
"goldenfiles", "TestFozBar", "yaml.goldfile",
|
||||||
|
): []byte("foo: bar\nhello: world"),
|
||||||
|
},
|
||||||
|
want: []byte("foo: bar\nhello: world"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "invalid chars in test name",
|
||||||
|
testName: `TestFooBar/foo?<>:*|"bar`,
|
||||||
|
args: args{name: "trash"},
|
||||||
|
files: map[string][]byte{
|
||||||
|
filepath.Join(
|
||||||
|
"testdata", "TestFooBar", "foo_______bar", "trash.golden",
|
||||||
|
): []byte("foo: bar\nhello: world"),
|
||||||
|
},
|
||||||
|
want: []byte("foo: bar\nhello: world"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
fs := afero.NewMemMapFs()
|
||||||
|
for f, b := range tt.files {
|
||||||
|
_ = afero.WriteFile(fs, f, b, 0o644)
|
||||||
|
}
|
||||||
|
|
||||||
|
if tt.fields.suffix == nil {
|
||||||
|
tt.fields.suffix = stringPtr(".golden")
|
||||||
|
}
|
||||||
|
if tt.fields.dirname == nil {
|
||||||
|
tt.fields.dirname = stringPtr("testdata")
|
||||||
|
}
|
||||||
|
|
||||||
|
g := &golden{
|
||||||
|
suffix: *tt.fields.suffix,
|
||||||
|
dirname: *tt.fields.dirname,
|
||||||
|
fs: fs,
|
||||||
|
}
|
||||||
|
|
||||||
|
mt := mocktesting.NewT(tt.testName)
|
||||||
|
|
||||||
|
var got []byte
|
||||||
|
mocktesting.Go(func() {
|
||||||
|
got = g.GetP(mt, tt.args.name)
|
||||||
|
})
|
||||||
|
|
||||||
|
assert.Equal(t, tt.want, got)
|
||||||
|
assert.Equal(t, tt.wantAborted, mt.Aborted(), "aborted")
|
||||||
|
assert.Equal(t,
|
||||||
|
tt.wantFailCount, mt.FailedCount(), "failed count",
|
||||||
|
)
|
||||||
|
assert.Equal(t, tt.wantTestOutput, mt.Output(), "test output")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
45
marshal/marshal.go
Normal file
45
marshal/marshal.go
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
package marshal
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"encoding/xml"
|
||||||
|
|
||||||
|
"gopkg.in/yaml.v3"
|
||||||
|
)
|
||||||
|
|
||||||
|
// JSON returns the JSON encoding of v. Returned JSON is intended by two spaces
|
||||||
|
// (pretty formatted), and is not HTML escaped.
|
||||||
|
func JSON(v interface{}) ([]byte, error) {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
enc := json.NewEncoder(&buf)
|
||||||
|
enc.SetIndent("", " ")
|
||||||
|
enc.SetEscapeHTML(false)
|
||||||
|
|
||||||
|
err := enc.Encode(v)
|
||||||
|
|
||||||
|
return buf.Bytes(), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// XML returns the XML encoding of v. Returned XML is intended by two spaces
|
||||||
|
// (pretty formatted).
|
||||||
|
func XML(v interface{}) ([]byte, error) {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
enc := xml.NewEncoder(&buf)
|
||||||
|
enc.Indent("", " ")
|
||||||
|
|
||||||
|
err := enc.Encode(v)
|
||||||
|
|
||||||
|
return buf.Bytes(), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// YAML returns the YAML encoding of v. Returned YAML is intended by two spaces.
|
||||||
|
func YAML(v interface{}) ([]byte, error) {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
enc := yaml.NewEncoder(&buf)
|
||||||
|
enc.SetIndent(2)
|
||||||
|
|
||||||
|
err := enc.Encode(v)
|
||||||
|
|
||||||
|
return buf.Bytes(), err
|
||||||
|
}
|
||||||
318
marshal/marshal_test.go
Normal file
318
marshal/marshal_test.go
Normal file
@@ -0,0 +1,318 @@
|
|||||||
|
package marshal_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/jimeh/go-golden/marshal"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
type book struct {
|
||||||
|
Title string `json:"title" yaml:"title" xml:"title"`
|
||||||
|
Author string `json:"author,omitempty" yaml:"author,omitempty" xml:"author,omitempty"`
|
||||||
|
Price int `json:"price" yaml:"price" xml:"price"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type shoe struct {
|
||||||
|
Make string `json:"make" yaml:"make" xml:"make"`
|
||||||
|
Model string `json:"model,omitempty" yaml:"model,omitempty" xml:"model,omitempty"`
|
||||||
|
Size int `json:"size" yaml:"size" xml:"size"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestJSON(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
v interface{}
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
want []byte
|
||||||
|
wantErr string
|
||||||
|
wantErrIs error
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "nil",
|
||||||
|
args: args{v: nil},
|
||||||
|
want: []byte("null\n"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "empty struct (1)",
|
||||||
|
args: args{v: &book{}},
|
||||||
|
want: []byte(`{
|
||||||
|
"title": "",
|
||||||
|
"price": 0
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "empty struct (2)",
|
||||||
|
args: args{v: &shoe{}},
|
||||||
|
want: []byte(`{
|
||||||
|
"make": "",
|
||||||
|
"size": 0
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "full struct (1)",
|
||||||
|
args: args{
|
||||||
|
v: &book{
|
||||||
|
Title: "a",
|
||||||
|
Author: "b",
|
||||||
|
Price: 499,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: []byte(`{
|
||||||
|
"title": "a",
|
||||||
|
"author": "b",
|
||||||
|
"price": 499
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "empty struct (2)",
|
||||||
|
args: args{
|
||||||
|
v: &shoe{
|
||||||
|
Make: "a",
|
||||||
|
Model: "b",
|
||||||
|
Size: 42,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: []byte(`{
|
||||||
|
"make": "a",
|
||||||
|
"model": "b",
|
||||||
|
"size": 42
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "channel",
|
||||||
|
args: args{
|
||||||
|
v: make(chan int),
|
||||||
|
},
|
||||||
|
wantErr: "json: unsupported type: chan int",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
got, err := marshal.JSON(tt.args.v)
|
||||||
|
|
||||||
|
if tt.wantErr != "" {
|
||||||
|
assert.EqualError(t, err, tt.wantErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
if tt.wantErrIs != nil {
|
||||||
|
assert.ErrorIs(t, err, tt.wantErrIs)
|
||||||
|
}
|
||||||
|
|
||||||
|
if tt.wantErr == "" && tt.wantErrIs == nil {
|
||||||
|
require.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, string(tt.want), string(got))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestYAML(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
v interface{}
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
want []byte
|
||||||
|
wantErr string
|
||||||
|
wantErrIs error
|
||||||
|
wantPanic interface{}
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "nil",
|
||||||
|
args: args{v: nil},
|
||||||
|
want: []byte("null\n"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "empty struct (1)",
|
||||||
|
args: args{v: &book{}},
|
||||||
|
want: []byte(`title: ""
|
||||||
|
price: 0
|
||||||
|
`,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "empty struct (2)",
|
||||||
|
args: args{v: &shoe{}},
|
||||||
|
want: []byte(`make: ""
|
||||||
|
size: 0
|
||||||
|
`,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "full struct (1)",
|
||||||
|
args: args{
|
||||||
|
v: &book{
|
||||||
|
Title: "a",
|
||||||
|
Author: "b",
|
||||||
|
Price: 499,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: []byte(`title: a
|
||||||
|
author: b
|
||||||
|
price: 499
|
||||||
|
`,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "empty struct (2)",
|
||||||
|
args: args{
|
||||||
|
v: &shoe{
|
||||||
|
Make: "a",
|
||||||
|
Model: "b",
|
||||||
|
Size: 42,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: []byte(`make: a
|
||||||
|
model: b
|
||||||
|
size: 42
|
||||||
|
`,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "channel",
|
||||||
|
args: args{
|
||||||
|
v: make(chan int),
|
||||||
|
},
|
||||||
|
wantPanic: "cannot marshal type: chan int",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
f := func() (got []byte, err error, p interface{}) {
|
||||||
|
defer func() { p = recover() }()
|
||||||
|
got, err = marshal.YAML(tt.args.v)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
got, err, p := f()
|
||||||
|
|
||||||
|
if tt.wantErr != "" {
|
||||||
|
assert.EqualError(t, err, tt.wantErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
if tt.wantErrIs != nil {
|
||||||
|
assert.ErrorIs(t, err, tt.wantErrIs)
|
||||||
|
}
|
||||||
|
|
||||||
|
if tt.wantPanic != nil {
|
||||||
|
assert.Equal(t, tt.wantPanic, p)
|
||||||
|
}
|
||||||
|
|
||||||
|
if tt.wantErr == "" && tt.wantErrIs == nil {
|
||||||
|
require.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, string(tt.want), string(got))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestXML(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
v interface{}
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
want []byte
|
||||||
|
wantErr string
|
||||||
|
wantErrIs error
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "nil",
|
||||||
|
args: args{v: nil},
|
||||||
|
want: []byte(""),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "empty struct (1)",
|
||||||
|
args: args{v: &book{}},
|
||||||
|
want: []byte(`<book>
|
||||||
|
<title></title>
|
||||||
|
<price>0</price>
|
||||||
|
</book>`,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "empty struct (2)",
|
||||||
|
args: args{v: &shoe{}},
|
||||||
|
want: []byte(`<shoe>
|
||||||
|
<make></make>
|
||||||
|
<size>0</size>
|
||||||
|
</shoe>`,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "full struct (1)",
|
||||||
|
args: args{
|
||||||
|
v: &book{
|
||||||
|
Title: "a",
|
||||||
|
Author: "b",
|
||||||
|
Price: 499,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: []byte(`<book>
|
||||||
|
<title>a</title>
|
||||||
|
<author>b</author>
|
||||||
|
<price>499</price>
|
||||||
|
</book>`,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "empty struct (2)",
|
||||||
|
args: args{
|
||||||
|
v: &shoe{
|
||||||
|
Make: "a",
|
||||||
|
Model: "b",
|
||||||
|
Size: 42,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: []byte(`<shoe>
|
||||||
|
<make>a</make>
|
||||||
|
<model>b</model>
|
||||||
|
<size>42</size>
|
||||||
|
</shoe>`,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "channel",
|
||||||
|
args: args{
|
||||||
|
v: make(chan int),
|
||||||
|
},
|
||||||
|
wantErr: "xml: unsupported type: chan int",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
got, err := marshal.XML(tt.args.v)
|
||||||
|
|
||||||
|
if tt.wantErr != "" {
|
||||||
|
assert.EqualError(t, err, tt.wantErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
if tt.wantErrIs != nil {
|
||||||
|
assert.ErrorIs(t, err, tt.wantErrIs)
|
||||||
|
}
|
||||||
|
|
||||||
|
if tt.wantErr == "" && tt.wantErrIs == nil {
|
||||||
|
require.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, string(tt.want), string(got))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
133
marshaling_asserter.go
Normal file
133
marshaling_asserter.go
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
package golden
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/jimeh/go-golden/sanitize"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
MarshalFunc func(interface{}) ([]byte, error)
|
||||||
|
UnmarshalFunc func([]byte, interface{}) error
|
||||||
|
)
|
||||||
|
|
||||||
|
// MarshalingAsserter allows building marshaling asserters by providing
|
||||||
|
// functions which marshal and unmarshal objects.
|
||||||
|
type MarshalingAsserter struct {
|
||||||
|
// Golden is the *Golden instance used to read/write golden files.
|
||||||
|
Golden Golden
|
||||||
|
|
||||||
|
// Name of the format the MarshalAsserter handles.
|
||||||
|
Format string
|
||||||
|
|
||||||
|
// GoldName is the name of the golden file used when marshaling. This is by
|
||||||
|
// default set based on Format using NewMarshalAsserter. For example if
|
||||||
|
// Format is set to "JSON", GoldName will be set to "marshaled_json" by
|
||||||
|
// default.
|
||||||
|
GoldName string
|
||||||
|
|
||||||
|
// MarshalFunc is the function used to marshal given objects.
|
||||||
|
MarshalFunc MarshalFunc
|
||||||
|
|
||||||
|
// UnmarshalFunc is the function used to unmarshal given objects.
|
||||||
|
UnmarshalFunc UnmarshalFunc
|
||||||
|
|
||||||
|
// NormalizeLineBreaks determines if Windows' CRLF (\r\n) and MacOS Classic
|
||||||
|
// CR (\r) line breaks are replaced with Unix's LF (\n) line breaks. This
|
||||||
|
// ensures marshaling assertions work across different platforms.
|
||||||
|
NormalizeLineBreaks bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// New returns a new MarshalingAsserter.
|
||||||
|
func NewMarshalingAsserter(
|
||||||
|
golden Golden,
|
||||||
|
format string,
|
||||||
|
marshalFunc MarshalFunc,
|
||||||
|
unmarshalFunc UnmarshalFunc,
|
||||||
|
normalizeLineBreaks bool,
|
||||||
|
) *MarshalingAsserter {
|
||||||
|
goldName := "marshaled_" + strings.ToLower(sanitize.Filename(format))
|
||||||
|
|
||||||
|
return &MarshalingAsserter{
|
||||||
|
Golden: golden,
|
||||||
|
Format: format,
|
||||||
|
GoldName: goldName,
|
||||||
|
MarshalFunc: marshalFunc,
|
||||||
|
UnmarshalFunc: unmarshalFunc,
|
||||||
|
NormalizeLineBreaks: normalizeLineBreaks,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Marshaling asserts that the given "v" value marshals via the provided encoder
|
||||||
|
// to an expected value fetched from a golden file on disk, and then verifies
|
||||||
|
// that the marshaled result produces a value that is equal to "v" when
|
||||||
|
// unmarshaled.
|
||||||
|
//
|
||||||
|
// Used for objects that do NOT change when they are marshaled and unmarshaled.
|
||||||
|
func (s *MarshalingAsserter) Marshaling(t TestingT, v interface{}) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
s.MarshalingP(t, v, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalingP asserts that the given "v" value marshals via the provided
|
||||||
|
// encoder to an expected value fetched from a golden file on disk, and then
|
||||||
|
// verifies that the marshaled result produces a value that is equal to "want"
|
||||||
|
// when unmarshaled.
|
||||||
|
//
|
||||||
|
// Used for objects that change when they are marshaled and unmarshaled.
|
||||||
|
func (s *MarshalingAsserter) MarshalingP(
|
||||||
|
t TestingT,
|
||||||
|
v interface{},
|
||||||
|
want interface{},
|
||||||
|
) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
if reflect.ValueOf(want).Kind() != reflect.Ptr {
|
||||||
|
require.FailNowf(t,
|
||||||
|
"golden: only pointer types can be asserted",
|
||||||
|
"%T is not a pointer type", want,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
marshaled, err := s.MarshalFunc(v)
|
||||||
|
require.NoErrorf(t,
|
||||||
|
err, "golden: failed to %s marshal %T: %+v", s.Format, v, v,
|
||||||
|
)
|
||||||
|
if s.NormalizeLineBreaks {
|
||||||
|
marshaled = sanitize.LineBreaks(marshaled)
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.Golden.Update() {
|
||||||
|
s.Golden.SetP(t, s.GoldName, marshaled)
|
||||||
|
}
|
||||||
|
|
||||||
|
gold := s.Golden.GetP(t, s.GoldName)
|
||||||
|
if s.NormalizeLineBreaks {
|
||||||
|
gold = sanitize.LineBreaks(gold)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch strings.ToLower(s.Format) {
|
||||||
|
case "json":
|
||||||
|
assert.JSONEq(t, string(gold), string(marshaled))
|
||||||
|
case "yaml", "yml":
|
||||||
|
assert.YAMLEq(t, string(gold), string(marshaled))
|
||||||
|
default:
|
||||||
|
assert.Equal(t, string(gold), string(marshaled))
|
||||||
|
}
|
||||||
|
|
||||||
|
got := reflect.New(reflect.TypeOf(want).Elem()).Interface()
|
||||||
|
err = s.UnmarshalFunc(gold, got)
|
||||||
|
|
||||||
|
f := s.Golden.FileP(t, s.GoldName)
|
||||||
|
require.NoErrorf(t, err,
|
||||||
|
"golden: failed to %s unmarshal %T from %s", s.Format, got, f,
|
||||||
|
)
|
||||||
|
assert.Equalf(t, want, got,
|
||||||
|
"golden: unmarshaling from golden file does not match "+
|
||||||
|
"expected object; golden file: %s", f,
|
||||||
|
)
|
||||||
|
}
|
||||||
98
marshaling_asserter_test.go
Normal file
98
marshaling_asserter_test.go
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
package golden
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/jimeh/go-golden/marshal"
|
||||||
|
"github.com/jimeh/go-golden/unmarshal"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNewMarshalingAsserter(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
golden Golden
|
||||||
|
format string
|
||||||
|
marshalFunc MarshalFunc
|
||||||
|
unmarshalFunc UnmarshalFunc
|
||||||
|
normalizeLineBreaks bool
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
want *MarshalingAsserter
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "json",
|
||||||
|
args: args{
|
||||||
|
nil,
|
||||||
|
"JSON",
|
||||||
|
marshal.JSON,
|
||||||
|
unmarshal.JSON,
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
want: &MarshalingAsserter{
|
||||||
|
Golden: nil,
|
||||||
|
Format: "JSON",
|
||||||
|
GoldName: "marshaled_json",
|
||||||
|
MarshalFunc: marshal.JSON,
|
||||||
|
UnmarshalFunc: unmarshal.JSON,
|
||||||
|
NormalizeLineBreaks: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "xml",
|
||||||
|
args: args{
|
||||||
|
nil,
|
||||||
|
"XML",
|
||||||
|
marshal.XML,
|
||||||
|
unmarshal.XML,
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
want: &MarshalingAsserter{
|
||||||
|
Golden: nil,
|
||||||
|
Format: "XML",
|
||||||
|
GoldName: "marshaled_xml",
|
||||||
|
MarshalFunc: marshal.XML,
|
||||||
|
UnmarshalFunc: unmarshal.XML,
|
||||||
|
NormalizeLineBreaks: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "yaml",
|
||||||
|
args: args{
|
||||||
|
nil,
|
||||||
|
"YAML",
|
||||||
|
marshal.YAML,
|
||||||
|
unmarshal.YAML,
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
want: &MarshalingAsserter{
|
||||||
|
Golden: nil,
|
||||||
|
Format: "YAML",
|
||||||
|
GoldName: "marshaled_yaml",
|
||||||
|
MarshalFunc: marshal.YAML,
|
||||||
|
UnmarshalFunc: unmarshal.YAML,
|
||||||
|
NormalizeLineBreaks: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
got := NewMarshalingAsserter(
|
||||||
|
tt.args.golden,
|
||||||
|
tt.args.format,
|
||||||
|
tt.args.marshalFunc,
|
||||||
|
tt.args.unmarshalFunc,
|
||||||
|
tt.args.normalizeLineBreaks,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert.Equal(t, tt.want.Golden, got.Golden)
|
||||||
|
assert.Equal(t, tt.want.Format, got.Format)
|
||||||
|
assert.Equal(t, tt.want.GoldName, got.GoldName)
|
||||||
|
assert.Equal(t,
|
||||||
|
tt.want.NormalizeLineBreaks,
|
||||||
|
got.NormalizeLineBreaks,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package golden
|
package sanitize
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"regexp"
|
"regexp"
|
||||||
@@ -15,7 +15,7 @@ var (
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
func sanitizeFilename(name string) string {
|
func Filename(name string) string {
|
||||||
if reservedNames.MatchString(name) || winReserved.MatchString(name) {
|
if reservedNames.MatchString(name) || winReserved.MatchString(name) {
|
||||||
var b []byte
|
var b []byte
|
||||||
for i := 0; i < len(name); i++ {
|
for i := 0; i < len(name); i++ {
|
||||||
@@ -1,12 +1,13 @@
|
|||||||
package golden
|
package sanitize_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/jimeh/go-golden/sanitize"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test_sanitizeFilename(t *testing.T) {
|
func TestFilename(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
filename string
|
filename string
|
||||||
@@ -69,6 +70,7 @@ func Test_sanitizeFilename(t *testing.T) {
|
|||||||
filename: "foobar.golden .. .. .. ",
|
filename: "foobar.golden .. .. .. ",
|
||||||
want: "foobar.golden",
|
want: "foobar.golden",
|
||||||
},
|
},
|
||||||
|
// Protected Windows filenames.
|
||||||
{name: "con", filename: "con", want: "___"},
|
{name: "con", filename: "con", want: "___"},
|
||||||
{name: "prn", filename: "prn", want: "___"},
|
{name: "prn", filename: "prn", want: "___"},
|
||||||
{name: "aux", filename: "aux", want: "___"},
|
{name: "aux", filename: "aux", want: "___"},
|
||||||
@@ -116,7 +118,7 @@ func Test_sanitizeFilename(t *testing.T) {
|
|||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
got := sanitizeFilename(tt.filename)
|
got := sanitize.Filename(tt.filename)
|
||||||
|
|
||||||
assert.Equal(t, tt.want, got)
|
assert.Equal(t, tt.want, got)
|
||||||
})
|
})
|
||||||
21
sanitize/line_breaks.go
Normal file
21
sanitize/line_breaks.go
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
package sanitize
|
||||||
|
|
||||||
|
import "bytes"
|
||||||
|
|
||||||
|
var (
|
||||||
|
lf = []byte{10}
|
||||||
|
cr = []byte{13}
|
||||||
|
crlf = []byte{13, 10}
|
||||||
|
)
|
||||||
|
|
||||||
|
// LineBreaks replaces Windows CRLF (\r\n) and MacOS Classic CR (\r)
|
||||||
|
// line-breaks with Unix LF (\n) line breaks.
|
||||||
|
func LineBreaks(data []byte) []byte {
|
||||||
|
// Replace Windows CRLF (\r\n) with Unix LF (\n)
|
||||||
|
result := bytes.ReplaceAll(data, crlf, lf)
|
||||||
|
|
||||||
|
// Replace Classic MacOS CR (\r) with Unix LF (\n)
|
||||||
|
result = bytes.ReplaceAll(result, cr, lf)
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
67
sanitize/line_breaks_test.go
Normal file
67
sanitize/line_breaks_test.go
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
package sanitize_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/jimeh/go-golden/sanitize"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestLineBreaks(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
data []byte
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
want []byte
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "nil",
|
||||||
|
args: args{data: nil},
|
||||||
|
want: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "empty",
|
||||||
|
args: args{data: []byte{}},
|
||||||
|
want: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "no line breaks",
|
||||||
|
args: args{data: []byte("hello world")},
|
||||||
|
want: []byte("hello world"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "UNIX line breaks",
|
||||||
|
args: args{data: []byte("hello\nworld\nhow are you?")},
|
||||||
|
want: []byte("hello\nworld\nhow are you?"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Windows line breaks",
|
||||||
|
args: args{data: []byte("hello\r\nworld\r\nhow are you?")},
|
||||||
|
want: []byte("hello\nworld\nhow are you?"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "MacOS Classic line breaks",
|
||||||
|
args: args{data: []byte("hello\rworld\rhow are you?")},
|
||||||
|
want: []byte("hello\nworld\nhow are you?"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Windows and MacOS Classic line breaks",
|
||||||
|
args: args{data: []byte("hello\r\nworld\rhow are you?")},
|
||||||
|
want: []byte("hello\nworld\nhow are you?"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Windows, MacOS Classic, and UNIX line breaks",
|
||||||
|
args: args{data: []byte("hello\r\nworld\rhow are you?\nGood!")},
|
||||||
|
want: []byte("hello\nworld\nhow are you?\nGood!"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
got := sanitize.LineBreaks(tt.args.data)
|
||||||
|
|
||||||
|
assert.Equal(t, tt.want, got)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
3
testdata/TestAssertJSONMarshaling/custom_marshaling/marshaled_json.golden
vendored
Normal file
3
testdata/TestAssertJSONMarshaling/custom_marshaling/marshaled_json.golden
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"2fd5af35-b85e-4f03-8eba-524be28d7a5b": "Hello World!=Forty Two"
|
||||||
|
}
|
||||||
4
testdata/TestAssertJSONMarshaling/empty_struct/marshaled_json.golden
vendored
Normal file
4
testdata/TestAssertJSONMarshaling/empty_struct/marshaled_json.golden
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"id": "",
|
||||||
|
"title": ""
|
||||||
|
}
|
||||||
1
testdata/TestAssertJSONMarshaling/false_bool_pointer/marshaled_json.golden
vendored
Normal file
1
testdata/TestAssertJSONMarshaling/false_bool_pointer/marshaled_json.golden
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
false
|
||||||
9
testdata/TestAssertJSONMarshaling/full_struct/marshaled_json.golden
vendored
Normal file
9
testdata/TestAssertJSONMarshaling/full_struct/marshaled_json.golden
vendored
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"id": "cfda163c-d5c1-44a2-909b-5d2ce3a31979",
|
||||||
|
"title": "The Traveler",
|
||||||
|
"author": {
|
||||||
|
"first_name": "John",
|
||||||
|
"last_name": "Twelve Hawks"
|
||||||
|
},
|
||||||
|
"year": 2005
|
||||||
|
}
|
||||||
1
testdata/TestAssertJSONMarshaling/int_pointer/marshaled_json.golden
vendored
Normal file
1
testdata/TestAssertJSONMarshaling/int_pointer/marshaled_json.golden
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
42
|
||||||
4
testdata/TestAssertJSONMarshaling/partial_struct/marshaled_json.golden
vendored
Normal file
4
testdata/TestAssertJSONMarshaling/partial_struct/marshaled_json.golden
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"id": "cfda163c-d5c1-44a2-909b-5d2ce3a31979",
|
||||||
|
"title": "The Traveler"
|
||||||
|
}
|
||||||
1
testdata/TestAssertJSONMarshaling/string_pointer/marshaled_json.golden
vendored
Normal file
1
testdata/TestAssertJSONMarshaling/string_pointer/marshaled_json.golden
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
"hello world"
|
||||||
1
testdata/TestAssertJSONMarshaling/true_bool_pointer/marshaled_json.golden
vendored
Normal file
1
testdata/TestAssertJSONMarshaling/true_bool_pointer/marshaled_json.golden
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
true
|
||||||
3
testdata/TestAssertJSONMarshalingP/custom_marshaling/marshaled_json.golden
vendored
Normal file
3
testdata/TestAssertJSONMarshalingP/custom_marshaling/marshaled_json.golden
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"2fd5af35-b85e-4f03-8eba-524be28d7a5b": "Hello World!=Forty Two"
|
||||||
|
}
|
||||||
5
testdata/TestAssertJSONMarshalingP/empty_struct/marshaled_json.golden
vendored
Normal file
5
testdata/TestAssertJSONMarshalingP/empty_struct/marshaled_json.golden
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"id": "",
|
||||||
|
"title": "",
|
||||||
|
"author": null
|
||||||
|
}
|
||||||
1
testdata/TestAssertJSONMarshalingP/false_bool_pointer/marshaled_json.golden
vendored
Normal file
1
testdata/TestAssertJSONMarshalingP/false_bool_pointer/marshaled_json.golden
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
false
|
||||||
9
testdata/TestAssertJSONMarshalingP/full_struct/marshaled_json.golden
vendored
Normal file
9
testdata/TestAssertJSONMarshalingP/full_struct/marshaled_json.golden
vendored
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"id": "10eec54d-e30a-4428-be18-01095d889126",
|
||||||
|
"title": "Time Travel",
|
||||||
|
"author": {
|
||||||
|
"first_name": "Doc",
|
||||||
|
"last_name": "Brown"
|
||||||
|
},
|
||||||
|
"date": "2021-10-27T22:30:34Z"
|
||||||
|
}
|
||||||
1
testdata/TestAssertJSONMarshalingP/int_pointer/marshaled_json.golden
vendored
Normal file
1
testdata/TestAssertJSONMarshalingP/int_pointer/marshaled_json.golden
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
42
|
||||||
4
testdata/TestAssertJSONMarshalingP/partial_struct/marshaled_json.golden
vendored
Normal file
4
testdata/TestAssertJSONMarshalingP/partial_struct/marshaled_json.golden
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"id": "10eec54d-e30a-4428-be18-01095d889126",
|
||||||
|
"title": "Time Travel"
|
||||||
|
}
|
||||||
1
testdata/TestAssertJSONMarshalingP/string_pointer/marshaled_json.golden
vendored
Normal file
1
testdata/TestAssertJSONMarshalingP/string_pointer/marshaled_json.golden
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
"hello world"
|
||||||
1
testdata/TestAssertJSONMarshalingP/true_bool_pointer/marshaled_json.golden
vendored
Normal file
1
testdata/TestAssertJSONMarshalingP/true_bool_pointer/marshaled_json.golden
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
true
|
||||||
1
testdata/TestAssertXMLMarshaling/custom_marshaling/marshaled_xml.golden
vendored
Normal file
1
testdata/TestAssertXMLMarshaling/custom_marshaling/marshaled_xml.golden
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<comic id="2fd5af35-b85e-4f03-8eba-524be28d7a5b" issue="Forty Two">Hello World!</comic>
|
||||||
4
testdata/TestAssertXMLMarshaling/empty_struct/marshaled_xml.golden
vendored
Normal file
4
testdata/TestAssertXMLMarshaling/empty_struct/marshaled_xml.golden
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<book>
|
||||||
|
<id></id>
|
||||||
|
<title></title>
|
||||||
|
</book>
|
||||||
1
testdata/TestAssertXMLMarshaling/false_bool_pointer/marshaled_xml.golden
vendored
Normal file
1
testdata/TestAssertXMLMarshaling/false_bool_pointer/marshaled_xml.golden
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<bool>false</bool>
|
||||||
9
testdata/TestAssertXMLMarshaling/full_struct/marshaled_xml.golden
vendored
Normal file
9
testdata/TestAssertXMLMarshaling/full_struct/marshaled_xml.golden
vendored
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<book>
|
||||||
|
<id>cfda163c-d5c1-44a2-909b-5d2ce3a31979</id>
|
||||||
|
<title>The Traveler</title>
|
||||||
|
<author>
|
||||||
|
<first_name>John</first_name>
|
||||||
|
<last_name>Twelve Hawks</last_name>
|
||||||
|
</author>
|
||||||
|
<year>2005</year>
|
||||||
|
</book>
|
||||||
1
testdata/TestAssertXMLMarshaling/int_pointer/marshaled_xml.golden
vendored
Normal file
1
testdata/TestAssertXMLMarshaling/int_pointer/marshaled_xml.golden
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<int>42</int>
|
||||||
4
testdata/TestAssertXMLMarshaling/partial_struct/marshaled_xml.golden
vendored
Normal file
4
testdata/TestAssertXMLMarshaling/partial_struct/marshaled_xml.golden
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<book>
|
||||||
|
<id>cfda163c-d5c1-44a2-909b-5d2ce3a31979</id>
|
||||||
|
<title>The Traveler</title>
|
||||||
|
</book>
|
||||||
1
testdata/TestAssertXMLMarshaling/string_pointer/marshaled_xml.golden
vendored
Normal file
1
testdata/TestAssertXMLMarshaling/string_pointer/marshaled_xml.golden
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<string>hello world</string>
|
||||||
1
testdata/TestAssertXMLMarshaling/true_bool_pointer/marshaled_xml.golden
vendored
Normal file
1
testdata/TestAssertXMLMarshaling/true_bool_pointer/marshaled_xml.golden
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<bool>true</bool>
|
||||||
1
testdata/TestAssertXMLMarshalingP/custom_marshaling/marshaled_xml.golden
vendored
Normal file
1
testdata/TestAssertXMLMarshalingP/custom_marshaling/marshaled_xml.golden
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<comic id="2fd5af35-b85e-4f03-8eba-524be28d7a5b" issue="Forty Two">Hello World!</comic>
|
||||||
4
testdata/TestAssertXMLMarshalingP/empty_struct/marshaled_xml.golden
vendored
Normal file
4
testdata/TestAssertXMLMarshalingP/empty_struct/marshaled_xml.golden
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<article>
|
||||||
|
<id></id>
|
||||||
|
<title></title>
|
||||||
|
</article>
|
||||||
1
testdata/TestAssertXMLMarshalingP/false_bool_pointer/marshaled_xml.golden
vendored
Normal file
1
testdata/TestAssertXMLMarshalingP/false_bool_pointer/marshaled_xml.golden
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<bool>false</bool>
|
||||||
9
testdata/TestAssertXMLMarshalingP/full_struct/marshaled_xml.golden
vendored
Normal file
9
testdata/TestAssertXMLMarshalingP/full_struct/marshaled_xml.golden
vendored
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<article>
|
||||||
|
<id>10eec54d-e30a-4428-be18-01095d889126</id>
|
||||||
|
<title>Time Travel</title>
|
||||||
|
<author>
|
||||||
|
<first_name>Doc</first_name>
|
||||||
|
<last_name>Brown</last_name>
|
||||||
|
</author>
|
||||||
|
<date>2021-10-27T22:30:34Z</date>
|
||||||
|
</article>
|
||||||
1
testdata/TestAssertXMLMarshalingP/int_pointer/marshaled_xml.golden
vendored
Normal file
1
testdata/TestAssertXMLMarshalingP/int_pointer/marshaled_xml.golden
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<int>42</int>
|
||||||
4
testdata/TestAssertXMLMarshalingP/partial_struct/marshaled_xml.golden
vendored
Normal file
4
testdata/TestAssertXMLMarshalingP/partial_struct/marshaled_xml.golden
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<book>
|
||||||
|
<id>10eec54d-e30a-4428-be18-01095d889126</id>
|
||||||
|
<title>Time Travel</title>
|
||||||
|
</book>
|
||||||
1
testdata/TestAssertXMLMarshalingP/string_pointer/marshaled_xml.golden
vendored
Normal file
1
testdata/TestAssertXMLMarshalingP/string_pointer/marshaled_xml.golden
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<string>hello world</string>
|
||||||
1
testdata/TestAssertXMLMarshalingP/true_bool_pointer/marshaled_xml.golden
vendored
Normal file
1
testdata/TestAssertXMLMarshalingP/true_bool_pointer/marshaled_xml.golden
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<bool>true</bool>
|
||||||
2
testdata/TestAssertYAMLMarshaling/custom_marshaling/marshaled_yaml.golden
vendored
Normal file
2
testdata/TestAssertYAMLMarshaling/custom_marshaling/marshaled_yaml.golden
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
2fd5af35-b85e-4f03-8eba-524be28d7a5b:
|
||||||
|
Hello World!: Forty Two
|
||||||
2
testdata/TestAssertYAMLMarshaling/empty_struct/marshaled_yaml.golden
vendored
Normal file
2
testdata/TestAssertYAMLMarshaling/empty_struct/marshaled_yaml.golden
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
id: ""
|
||||||
|
title: ""
|
||||||
1
testdata/TestAssertYAMLMarshaling/false_bool_pointer/marshaled_yaml.golden
vendored
Normal file
1
testdata/TestAssertYAMLMarshaling/false_bool_pointer/marshaled_yaml.golden
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
false
|
||||||
6
testdata/TestAssertYAMLMarshaling/full_struct/marshaled_yaml.golden
vendored
Normal file
6
testdata/TestAssertYAMLMarshaling/full_struct/marshaled_yaml.golden
vendored
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
id: cfda163c-d5c1-44a2-909b-5d2ce3a31979
|
||||||
|
title: The Traveler
|
||||||
|
author:
|
||||||
|
first_name: John
|
||||||
|
last_name: Twelve Hawks
|
||||||
|
year: 2005
|
||||||
1
testdata/TestAssertYAMLMarshaling/int_pointer/marshaled_yaml.golden
vendored
Normal file
1
testdata/TestAssertYAMLMarshaling/int_pointer/marshaled_yaml.golden
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
42
|
||||||
2
testdata/TestAssertYAMLMarshaling/partial_struct/marshaled_yaml.golden
vendored
Normal file
2
testdata/TestAssertYAMLMarshaling/partial_struct/marshaled_yaml.golden
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
id: cfda163c-d5c1-44a2-909b-5d2ce3a31979
|
||||||
|
title: The Traveler
|
||||||
1
testdata/TestAssertYAMLMarshaling/string_pointer/marshaled_yaml.golden
vendored
Normal file
1
testdata/TestAssertYAMLMarshaling/string_pointer/marshaled_yaml.golden
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
hello world
|
||||||
1
testdata/TestAssertYAMLMarshaling/true_bool_pointer/marshaled_yaml.golden
vendored
Normal file
1
testdata/TestAssertYAMLMarshaling/true_bool_pointer/marshaled_yaml.golden
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
true
|
||||||
2
testdata/TestAssertYAMLMarshalingP/custom_marshaling/marshaled_yaml.golden
vendored
Normal file
2
testdata/TestAssertYAMLMarshalingP/custom_marshaling/marshaled_yaml.golden
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
2fd5af35-b85e-4f03-8eba-524be28d7a5b:
|
||||||
|
Hello World!: Forty Two
|
||||||
3
testdata/TestAssertYAMLMarshalingP/empty_struct/marshaled_yaml.golden
vendored
Normal file
3
testdata/TestAssertYAMLMarshalingP/empty_struct/marshaled_yaml.golden
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
id: ""
|
||||||
|
title: ""
|
||||||
|
author: null
|
||||||
1
testdata/TestAssertYAMLMarshalingP/false_bool_pointer/marshaled_yaml.golden
vendored
Normal file
1
testdata/TestAssertYAMLMarshalingP/false_bool_pointer/marshaled_yaml.golden
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
false
|
||||||
6
testdata/TestAssertYAMLMarshalingP/full_struct/marshaled_yaml.golden
vendored
Normal file
6
testdata/TestAssertYAMLMarshalingP/full_struct/marshaled_yaml.golden
vendored
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
id: 10eec54d-e30a-4428-be18-01095d889126
|
||||||
|
title: Time Travel
|
||||||
|
author:
|
||||||
|
first_name: Doc
|
||||||
|
last_name: Brown
|
||||||
|
date: 2021-10-27T22:30:34Z
|
||||||
1
testdata/TestAssertYAMLMarshalingP/int_pointer/marshaled_yaml.golden
vendored
Normal file
1
testdata/TestAssertYAMLMarshalingP/int_pointer/marshaled_yaml.golden
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
42
|
||||||
2
testdata/TestAssertYAMLMarshalingP/partial_struct/marshaled_yaml.golden
vendored
Normal file
2
testdata/TestAssertYAMLMarshalingP/partial_struct/marshaled_yaml.golden
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
id: 10eec54d-e30a-4428-be18-01095d889126
|
||||||
|
title: Time Travel
|
||||||
1
testdata/TestAssertYAMLMarshalingP/string_pointer/marshaled_yaml.golden
vendored
Normal file
1
testdata/TestAssertYAMLMarshalingP/string_pointer/marshaled_yaml.golden
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
hello world
|
||||||
1
testdata/TestAssertYAMLMarshalingP/true_bool_pointer/marshaled_yaml.golden
vendored
Normal file
1
testdata/TestAssertYAMLMarshalingP/true_bool_pointer/marshaled_yaml.golden
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
true
|
||||||
3
testdata/TestAssert_JSONMarshaling/custom_marshaling/marshaled_json.golden
vendored
Normal file
3
testdata/TestAssert_JSONMarshaling/custom_marshaling/marshaled_json.golden
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"2fd5af35-b85e-4f03-8eba-524be28d7a5b": "Hello World!=Forty Two"
|
||||||
|
}
|
||||||
4
testdata/TestAssert_JSONMarshaling/empty_struct/marshaled_json.golden
vendored
Normal file
4
testdata/TestAssert_JSONMarshaling/empty_struct/marshaled_json.golden
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"id": "",
|
||||||
|
"title": ""
|
||||||
|
}
|
||||||
1
testdata/TestAssert_JSONMarshaling/false_bool_pointer/marshaled_json.golden
vendored
Normal file
1
testdata/TestAssert_JSONMarshaling/false_bool_pointer/marshaled_json.golden
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
false
|
||||||
9
testdata/TestAssert_JSONMarshaling/full_struct/marshaled_json.golden
vendored
Normal file
9
testdata/TestAssert_JSONMarshaling/full_struct/marshaled_json.golden
vendored
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"id": "cfda163c-d5c1-44a2-909b-5d2ce3a31979",
|
||||||
|
"title": "The Traveler",
|
||||||
|
"author": {
|
||||||
|
"first_name": "John",
|
||||||
|
"last_name": "Twelve Hawks"
|
||||||
|
},
|
||||||
|
"year": 2005
|
||||||
|
}
|
||||||
1
testdata/TestAssert_JSONMarshaling/int_pointer/marshaled_json.golden
vendored
Normal file
1
testdata/TestAssert_JSONMarshaling/int_pointer/marshaled_json.golden
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
42
|
||||||
4
testdata/TestAssert_JSONMarshaling/partial_struct/marshaled_json.golden
vendored
Normal file
4
testdata/TestAssert_JSONMarshaling/partial_struct/marshaled_json.golden
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"id": "cfda163c-d5c1-44a2-909b-5d2ce3a31979",
|
||||||
|
"title": "The Traveler"
|
||||||
|
}
|
||||||
1
testdata/TestAssert_JSONMarshaling/string_pointer/marshaled_json.golden
vendored
Normal file
1
testdata/TestAssert_JSONMarshaling/string_pointer/marshaled_json.golden
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
"hello world"
|
||||||
1
testdata/TestAssert_JSONMarshaling/true_bool_pointer/marshaled_json.golden
vendored
Normal file
1
testdata/TestAssert_JSONMarshaling/true_bool_pointer/marshaled_json.golden
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
true
|
||||||
3
testdata/TestAssert_JSONMarshalingP/custom_marshaling/marshaled_json.golden
vendored
Normal file
3
testdata/TestAssert_JSONMarshalingP/custom_marshaling/marshaled_json.golden
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"2fd5af35-b85e-4f03-8eba-524be28d7a5b": "Hello World!=Forty Two"
|
||||||
|
}
|
||||||
5
testdata/TestAssert_JSONMarshalingP/empty_struct/marshaled_json.golden
vendored
Normal file
5
testdata/TestAssert_JSONMarshalingP/empty_struct/marshaled_json.golden
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"id": "",
|
||||||
|
"title": "",
|
||||||
|
"author": null
|
||||||
|
}
|
||||||
1
testdata/TestAssert_JSONMarshalingP/false_bool_pointer/marshaled_json.golden
vendored
Normal file
1
testdata/TestAssert_JSONMarshalingP/false_bool_pointer/marshaled_json.golden
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
false
|
||||||
9
testdata/TestAssert_JSONMarshalingP/full_struct/marshaled_json.golden
vendored
Normal file
9
testdata/TestAssert_JSONMarshalingP/full_struct/marshaled_json.golden
vendored
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"id": "10eec54d-e30a-4428-be18-01095d889126",
|
||||||
|
"title": "Time Travel",
|
||||||
|
"author": {
|
||||||
|
"first_name": "Doc",
|
||||||
|
"last_name": "Brown"
|
||||||
|
},
|
||||||
|
"date": "2021-10-27T22:30:34Z"
|
||||||
|
}
|
||||||
1
testdata/TestAssert_JSONMarshalingP/int_pointer/marshaled_json.golden
vendored
Normal file
1
testdata/TestAssert_JSONMarshalingP/int_pointer/marshaled_json.golden
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
42
|
||||||
4
testdata/TestAssert_JSONMarshalingP/partial_struct/marshaled_json.golden
vendored
Normal file
4
testdata/TestAssert_JSONMarshalingP/partial_struct/marshaled_json.golden
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"id": "10eec54d-e30a-4428-be18-01095d889126",
|
||||||
|
"title": "Time Travel"
|
||||||
|
}
|
||||||
1
testdata/TestAssert_JSONMarshalingP/string_pointer/marshaled_json.golden
vendored
Normal file
1
testdata/TestAssert_JSONMarshalingP/string_pointer/marshaled_json.golden
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
"hello world"
|
||||||
1
testdata/TestAssert_JSONMarshalingP/true_bool_pointer/marshaled_json.golden
vendored
Normal file
1
testdata/TestAssert_JSONMarshalingP/true_bool_pointer/marshaled_json.golden
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
true
|
||||||
1
testdata/TestAssert_XMLMarshaling/custom_marshaling/marshaled_xml.golden
vendored
Normal file
1
testdata/TestAssert_XMLMarshaling/custom_marshaling/marshaled_xml.golden
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<comic id="2fd5af35-b85e-4f03-8eba-524be28d7a5b" issue="Forty Two">Hello World!</comic>
|
||||||
4
testdata/TestAssert_XMLMarshaling/empty_struct/marshaled_xml.golden
vendored
Normal file
4
testdata/TestAssert_XMLMarshaling/empty_struct/marshaled_xml.golden
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<book>
|
||||||
|
<id></id>
|
||||||
|
<title></title>
|
||||||
|
</book>
|
||||||
1
testdata/TestAssert_XMLMarshaling/false_bool_pointer/marshaled_xml.golden
vendored
Normal file
1
testdata/TestAssert_XMLMarshaling/false_bool_pointer/marshaled_xml.golden
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<bool>false</bool>
|
||||||
9
testdata/TestAssert_XMLMarshaling/full_struct/marshaled_xml.golden
vendored
Normal file
9
testdata/TestAssert_XMLMarshaling/full_struct/marshaled_xml.golden
vendored
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<book>
|
||||||
|
<id>cfda163c-d5c1-44a2-909b-5d2ce3a31979</id>
|
||||||
|
<title>The Traveler</title>
|
||||||
|
<author>
|
||||||
|
<first_name>John</first_name>
|
||||||
|
<last_name>Twelve Hawks</last_name>
|
||||||
|
</author>
|
||||||
|
<year>2005</year>
|
||||||
|
</book>
|
||||||
1
testdata/TestAssert_XMLMarshaling/int_pointer/marshaled_xml.golden
vendored
Normal file
1
testdata/TestAssert_XMLMarshaling/int_pointer/marshaled_xml.golden
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<int>42</int>
|
||||||
4
testdata/TestAssert_XMLMarshaling/partial_struct/marshaled_xml.golden
vendored
Normal file
4
testdata/TestAssert_XMLMarshaling/partial_struct/marshaled_xml.golden
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<book>
|
||||||
|
<id>cfda163c-d5c1-44a2-909b-5d2ce3a31979</id>
|
||||||
|
<title>The Traveler</title>
|
||||||
|
</book>
|
||||||
1
testdata/TestAssert_XMLMarshaling/string_pointer/marshaled_xml.golden
vendored
Normal file
1
testdata/TestAssert_XMLMarshaling/string_pointer/marshaled_xml.golden
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<string>hello world</string>
|
||||||
1
testdata/TestAssert_XMLMarshaling/true_bool_pointer/marshaled_xml.golden
vendored
Normal file
1
testdata/TestAssert_XMLMarshaling/true_bool_pointer/marshaled_xml.golden
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<bool>true</bool>
|
||||||
1
testdata/TestAssert_XMLMarshalingP/custom_marshaling/marshaled_xml.golden
vendored
Normal file
1
testdata/TestAssert_XMLMarshalingP/custom_marshaling/marshaled_xml.golden
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<comic id="2fd5af35-b85e-4f03-8eba-524be28d7a5b" issue="Forty Two">Hello World!</comic>
|
||||||
4
testdata/TestAssert_XMLMarshalingP/empty_struct/marshaled_xml.golden
vendored
Normal file
4
testdata/TestAssert_XMLMarshalingP/empty_struct/marshaled_xml.golden
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<article>
|
||||||
|
<id></id>
|
||||||
|
<title></title>
|
||||||
|
</article>
|
||||||
1
testdata/TestAssert_XMLMarshalingP/false_bool_pointer/marshaled_xml.golden
vendored
Normal file
1
testdata/TestAssert_XMLMarshalingP/false_bool_pointer/marshaled_xml.golden
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<bool>false</bool>
|
||||||
9
testdata/TestAssert_XMLMarshalingP/full_struct/marshaled_xml.golden
vendored
Normal file
9
testdata/TestAssert_XMLMarshalingP/full_struct/marshaled_xml.golden
vendored
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<article>
|
||||||
|
<id>10eec54d-e30a-4428-be18-01095d889126</id>
|
||||||
|
<title>Time Travel</title>
|
||||||
|
<author>
|
||||||
|
<first_name>Doc</first_name>
|
||||||
|
<last_name>Brown</last_name>
|
||||||
|
</author>
|
||||||
|
<date>2021-10-27T22:30:34Z</date>
|
||||||
|
</article>
|
||||||
1
testdata/TestAssert_XMLMarshalingP/int_pointer/marshaled_xml.golden
vendored
Normal file
1
testdata/TestAssert_XMLMarshalingP/int_pointer/marshaled_xml.golden
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<int>42</int>
|
||||||
4
testdata/TestAssert_XMLMarshalingP/partial_struct/marshaled_xml.golden
vendored
Normal file
4
testdata/TestAssert_XMLMarshalingP/partial_struct/marshaled_xml.golden
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<book>
|
||||||
|
<id>10eec54d-e30a-4428-be18-01095d889126</id>
|
||||||
|
<title>Time Travel</title>
|
||||||
|
</book>
|
||||||
1
testdata/TestAssert_XMLMarshalingP/string_pointer/marshaled_xml.golden
vendored
Normal file
1
testdata/TestAssert_XMLMarshalingP/string_pointer/marshaled_xml.golden
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<string>hello world</string>
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user