Merge 51a089712b
into d8f796af33
This commit is contained in:
commit
20c1c7f1b9
|
@ -67,6 +67,12 @@ type ConfigState struct {
|
||||||
// Google App Engine or with the "safe" build tag specified.
|
// Google App Engine or with the "safe" build tag specified.
|
||||||
DisablePointerMethods bool
|
DisablePointerMethods bool
|
||||||
|
|
||||||
|
// DisableNilValues specifies whether or not to include nil fields. This is
|
||||||
|
// useful in tests that rely on "golden" test data: Often one adds a new,
|
||||||
|
// optional field to a struct, and it's onerous to update all test data
|
||||||
|
// to reflect this field, which would now be nil everywhere.
|
||||||
|
DisableNilValues bool
|
||||||
|
|
||||||
// DisablePointerAddresses specifies whether to disable the printing of
|
// DisablePointerAddresses specifies whether to disable the printing of
|
||||||
// pointer addresses. This is useful when diffing data structures in tests.
|
// pointer addresses. This is useful when diffing data structures in tests.
|
||||||
DisablePointerAddresses bool
|
DisablePointerAddresses bool
|
||||||
|
@ -76,6 +82,11 @@ type ConfigState struct {
|
||||||
// data structures in tests.
|
// data structures in tests.
|
||||||
DisableCapacities bool
|
DisableCapacities bool
|
||||||
|
|
||||||
|
// AlwaysIncludeTrailingComma specifies whether to always include a trailing
|
||||||
|
// comma, Go-style. This is useful to avoid false positives when diffing data
|
||||||
|
// structures in tests.
|
||||||
|
AlwaysIncludeTrailingComma bool
|
||||||
|
|
||||||
// ContinueOnMethod specifies whether or not recursion should continue once
|
// ContinueOnMethod specifies whether or not recursion should continue once
|
||||||
// a custom error or Stringer interface is invoked. The default, false,
|
// a custom error or Stringer interface is invoked. The default, false,
|
||||||
// means it will print the results of invoking the custom error or Stringer
|
// means it will print the results of invoking the custom error or Stringer
|
||||||
|
|
104
spew/dump.go
104
spew/dump.go
|
@ -234,14 +234,16 @@ func (d *dumpState) dumpSlice(v reflect.Value) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Recursively call dump for each item.
|
// Recursively call dump for each item.
|
||||||
|
l := list{dumpState: d}
|
||||||
for i := 0; i < numEntries; i++ {
|
for i := 0; i < numEntries; i++ {
|
||||||
d.dump(d.unpackValue(v.Index(i)))
|
entryVal := d.unpackValue(v.Index(i))
|
||||||
if i < (numEntries - 1) {
|
if !d.filterValue(entryVal) {
|
||||||
d.w.Write(commaNewlineBytes)
|
continue
|
||||||
} else {
|
|
||||||
d.w.Write(newlineBytes)
|
|
||||||
}
|
}
|
||||||
|
l.beginEntry()
|
||||||
|
d.dump(entryVal)
|
||||||
}
|
}
|
||||||
|
l.end()
|
||||||
}
|
}
|
||||||
|
|
||||||
// dump is the main workhorse for dumping a value. It uses the passed reflect
|
// dump is the main workhorse for dumping a value. It uses the passed reflect
|
||||||
|
@ -382,22 +384,23 @@ func (d *dumpState) dump(v reflect.Value) {
|
||||||
d.indent()
|
d.indent()
|
||||||
d.w.Write(maxNewlineBytes)
|
d.w.Write(maxNewlineBytes)
|
||||||
} else {
|
} else {
|
||||||
numEntries := v.Len()
|
|
||||||
keys := v.MapKeys()
|
keys := v.MapKeys()
|
||||||
if d.cs.SortKeys {
|
if d.cs.SortKeys {
|
||||||
sortValues(keys, d.cs)
|
sortValues(keys, d.cs)
|
||||||
}
|
}
|
||||||
for i, key := range keys {
|
l := list{dumpState: d}
|
||||||
|
for _, key := range keys {
|
||||||
|
mapValue := d.unpackValue(v.MapIndex(key))
|
||||||
|
if !d.filterValue(mapValue) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
l.beginEntry()
|
||||||
d.dump(d.unpackValue(key))
|
d.dump(d.unpackValue(key))
|
||||||
d.w.Write(colonSpaceBytes)
|
d.w.Write(colonSpaceBytes)
|
||||||
d.ignoreNextIndent = true
|
d.ignoreNextIndent = true
|
||||||
d.dump(d.unpackValue(v.MapIndex(key)))
|
d.dump(mapValue)
|
||||||
if i < (numEntries - 1) {
|
|
||||||
d.w.Write(commaNewlineBytes)
|
|
||||||
} else {
|
|
||||||
d.w.Write(newlineBytes)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
l.end()
|
||||||
}
|
}
|
||||||
d.depth--
|
d.depth--
|
||||||
d.indent()
|
d.indent()
|
||||||
|
@ -412,19 +415,20 @@ func (d *dumpState) dump(v reflect.Value) {
|
||||||
} else {
|
} else {
|
||||||
vt := v.Type()
|
vt := v.Type()
|
||||||
numFields := v.NumField()
|
numFields := v.NumField()
|
||||||
|
l := list{dumpState: d, indented: true}
|
||||||
for i := 0; i < numFields; i++ {
|
for i := 0; i < numFields; i++ {
|
||||||
d.indent()
|
fieldValue := d.unpackValue(v.Field(i))
|
||||||
|
if !d.filterValue(fieldValue) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
l.beginEntry()
|
||||||
vtf := vt.Field(i)
|
vtf := vt.Field(i)
|
||||||
d.w.Write([]byte(vtf.Name))
|
d.w.Write([]byte(vtf.Name))
|
||||||
d.w.Write(colonSpaceBytes)
|
d.w.Write(colonSpaceBytes)
|
||||||
d.ignoreNextIndent = true
|
d.ignoreNextIndent = true
|
||||||
d.dump(d.unpackValue(v.Field(i)))
|
d.dump(fieldValue)
|
||||||
if i < (numFields - 1) {
|
|
||||||
d.w.Write(commaNewlineBytes)
|
|
||||||
} else {
|
|
||||||
d.w.Write(newlineBytes)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
l.end()
|
||||||
}
|
}
|
||||||
d.depth--
|
d.depth--
|
||||||
d.indent()
|
d.indent()
|
||||||
|
@ -448,15 +452,67 @@ func (d *dumpState) dump(v reflect.Value) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// writeDelimiter emits a comma if hasMoreElements is true, or if trailing
|
||||||
|
// commas are always enabled.
|
||||||
|
func (d *dumpState) writeDelimiter(hasMoreElements bool) {
|
||||||
|
if hasMoreElements || d.cs.AlwaysIncludeTrailingComma {
|
||||||
|
d.w.Write(commaNewlineBytes)
|
||||||
|
} else {
|
||||||
|
d.w.Write(newlineBytes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// filterValue returns true if a value should be dumped.
|
||||||
|
func (d *dumpState) filterValue(value reflect.Value) bool {
|
||||||
|
if !d.cs.DisableNilValues {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return !isNil(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// list is a small helper to write lists and ensure that each item is
|
||||||
|
// correctly delimited, and that nothing is emitted if no entires were added.
|
||||||
|
type list struct {
|
||||||
|
count int
|
||||||
|
indented bool
|
||||||
|
*dumpState
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *list) beginEntry() {
|
||||||
|
if l.count > 0 {
|
||||||
|
l.writeDelimiter(true)
|
||||||
|
}
|
||||||
|
l.count++
|
||||||
|
if l.indented {
|
||||||
|
l.indent()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *list) end() {
|
||||||
|
if l.count > 0 {
|
||||||
|
l.writeDelimiter(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func isNil(v reflect.Value) bool {
|
||||||
|
switch v.Kind() {
|
||||||
|
case reflect.Interface, reflect.Map, reflect.Slice, reflect.Ptr:
|
||||||
|
return v.IsNil()
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// fdump is a helper function to consolidate the logic from the various public
|
// fdump is a helper function to consolidate the logic from the various public
|
||||||
// methods which take varying writers and config states.
|
// methods which take varying writers and config states.
|
||||||
func fdump(cs *ConfigState, w io.Writer, a ...interface{}) {
|
func fdump(cs *ConfigState, w io.Writer, a ...interface{}) {
|
||||||
for _, arg := range a {
|
for _, arg := range a {
|
||||||
if arg == nil {
|
if arg == nil {
|
||||||
w.Write(interfaceBytes)
|
if !cs.DisableNilValues {
|
||||||
w.Write(spaceBytes)
|
w.Write(interfaceBytes)
|
||||||
w.Write(nilAngleBytes)
|
w.Write(spaceBytes)
|
||||||
w.Write(newlineBytes)
|
w.Write(nilAngleBytes)
|
||||||
|
w.Write(newlineBytes)
|
||||||
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -132,6 +132,8 @@ func initSpewTests() {
|
||||||
scsContinue := &spew.ConfigState{Indent: " ", ContinueOnMethod: true}
|
scsContinue := &spew.ConfigState{Indent: " ", ContinueOnMethod: true}
|
||||||
scsNoPtrAddr := &spew.ConfigState{DisablePointerAddresses: true}
|
scsNoPtrAddr := &spew.ConfigState{DisablePointerAddresses: true}
|
||||||
scsNoCap := &spew.ConfigState{DisableCapacities: true}
|
scsNoCap := &spew.ConfigState{DisableCapacities: true}
|
||||||
|
scsTrailingComma := &spew.ConfigState{Indent: " ", AlwaysIncludeTrailingComma: true}
|
||||||
|
scsNoNils := &spew.ConfigState{Indent: " ", DisableNilValues: true}
|
||||||
|
|
||||||
// Variables for tests on types which implement Stringer interface with and
|
// Variables for tests on types which implement Stringer interface with and
|
||||||
// without a pointer receiver.
|
// without a pointer receiver.
|
||||||
|
@ -143,6 +145,17 @@ func initSpewTests() {
|
||||||
}
|
}
|
||||||
tptr := &ptrTester{s: &struct{}{}}
|
tptr := &ptrTester{s: &struct{}{}}
|
||||||
|
|
||||||
|
type testIntf interface {
|
||||||
|
TestyTesty()
|
||||||
|
}
|
||||||
|
|
||||||
|
type nilTester struct {
|
||||||
|
s *struct{}
|
||||||
|
slice []interface{}
|
||||||
|
m map[string]interface{}
|
||||||
|
intf testIntf
|
||||||
|
}
|
||||||
|
|
||||||
// depthTester is used to test max depth handling for structs, array, slices
|
// depthTester is used to test max depth handling for structs, array, slices
|
||||||
// and maps.
|
// and maps.
|
||||||
type depthTester struct {
|
type depthTester struct {
|
||||||
|
@ -154,6 +167,12 @@ func initSpewTests() {
|
||||||
dt := depthTester{indirCir1{nil}, [1]string{"arr"}, []string{"slice"},
|
dt := depthTester{indirCir1{nil}, [1]string{"arr"}, []string{"slice"},
|
||||||
map[string]int{"one": 1}}
|
map[string]int{"one": 1}}
|
||||||
|
|
||||||
|
// commatester is to test trailing commas
|
||||||
|
type commaTester struct {
|
||||||
|
slice []interface{}
|
||||||
|
m map[string]int
|
||||||
|
}
|
||||||
|
|
||||||
// Variable for tests on types which implement error interface.
|
// Variable for tests on types which implement error interface.
|
||||||
te := customError(10)
|
te := customError(10)
|
||||||
|
|
||||||
|
@ -203,6 +222,31 @@ func initSpewTests() {
|
||||||
{scsNoPtrAddr, fCSSdump, "", tptr, "(*spew_test.ptrTester)({\ns: (*struct {})({\n})\n})\n"},
|
{scsNoPtrAddr, fCSSdump, "", tptr, "(*spew_test.ptrTester)({\ns: (*struct {})({\n})\n})\n"},
|
||||||
{scsNoCap, fCSSdump, "", make([]string, 0, 10), "([]string) {\n}\n"},
|
{scsNoCap, fCSSdump, "", make([]string, 0, 10), "([]string) {\n}\n"},
|
||||||
{scsNoCap, fCSSdump, "", make([]string, 1, 10), "([]string) (len=1) {\n(string) \"\"\n}\n"},
|
{scsNoCap, fCSSdump, "", make([]string, 1, 10), "([]string) (len=1) {\n(string) \"\"\n}\n"},
|
||||||
|
{scsTrailingComma, fCSFdump, "", commaTester{
|
||||||
|
slice: []interface{}{
|
||||||
|
map[string]int{"one": 1},
|
||||||
|
},
|
||||||
|
m: map[string]int{"one": 1},
|
||||||
|
},
|
||||||
|
"(spew_test.commaTester) {\n" +
|
||||||
|
" slice: ([]interface {}) (len=1 cap=1) {\n" +
|
||||||
|
" (map[string]int) (len=1) {\n" +
|
||||||
|
" (string) (len=3) \"one\": (int) 1,\n" +
|
||||||
|
" },\n" +
|
||||||
|
" },\n" +
|
||||||
|
" m: (map[string]int) (len=1) {\n" +
|
||||||
|
" (string) (len=3) \"one\": (int) 1,\n" +
|
||||||
|
" },\n" +
|
||||||
|
"}\n"},
|
||||||
|
{scsNoNils, fCSSdump, "", nilTester{}, "(spew_test.nilTester) {\n}\n"},
|
||||||
|
{scsNoNils, fCSSdump, "", nilTester{
|
||||||
|
slice: []interface{}{nil, "foo"},
|
||||||
|
},
|
||||||
|
"(spew_test.nilTester) {\n" +
|
||||||
|
" slice: ([]interface {}) (len=2 cap=2) {\n" +
|
||||||
|
" (string) (len=3) \"foo\"\n" +
|
||||||
|
" }\n" +
|
||||||
|
"}\n"},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue