blob: 59517fe7a4f6c62804cbdb92f16409f6f581020c [file] [log] [blame]
package format_test
import (
"fmt"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
. "github.com/onsi/gomega/format"
"github.com/onsi/gomega/types"
"strings"
)
//recursive struct
type StringAlias string
type ByteAlias []byte
type IntAlias int
type AStruct struct {
Exported string
}
type SimpleStruct struct {
Name string
Enumeration int
Veritas bool
Data []byte
secret uint32
}
type ComplexStruct struct {
Strings []string
SimpleThings []*SimpleStruct
DataMaps map[int]ByteAlias
}
type SecretiveStruct struct {
boolValue bool
intValue int
uintValue uint
uintptrValue uintptr
floatValue float32
complexValue complex64
chanValue chan bool
funcValue func()
pointerValue *int
sliceValue []string
byteSliceValue []byte
stringValue string
arrValue [3]int
byteArrValue [3]byte
mapValue map[string]int
structValue AStruct
interfaceValue interface{}
}
type GoStringer struct {
}
func (g GoStringer) GoString() string {
return "go-string"
}
func (g GoStringer) String() string {
return "string"
}
type Stringer struct {
}
func (g Stringer) String() string {
return "string"
}
var _ = Describe("Format", func() {
match := func(typeRepresentation string, valueRepresentation string, args ...interface{}) types.GomegaMatcher {
if len(args) > 0 {
valueRepresentation = fmt.Sprintf(valueRepresentation, args...)
}
return Equal(fmt.Sprintf("%s<%s>: %s", Indent, typeRepresentation, valueRepresentation))
}
matchRegexp := func(typeRepresentation string, valueRepresentation string, args ...interface{}) types.GomegaMatcher {
if len(args) > 0 {
valueRepresentation = fmt.Sprintf(valueRepresentation, args...)
}
return MatchRegexp(fmt.Sprintf("%s<%s>: %s", Indent, typeRepresentation, valueRepresentation))
}
hashMatchingRegexp := func(entries ...string) string {
entriesSwitch := "(" + strings.Join(entries, "|") + ")"
arr := make([]string, len(entries))
for i := range arr {
arr[i] = entriesSwitch
}
return "{" + strings.Join(arr, ", ") + "}"
}
Describe("Message", func() {
Context("with only an actual value", func() {
It("should print out an indented formatted representation of the value and the message", func() {
Ω(Message(3, "to be three.")).Should(Equal("Expected\n <int>: 3\nto be three."))
})
})
Context("with an actual and an expected value", func() {
It("should print out an indented formatted representatino of both values, and the message", func() {
Ω(Message(3, "to equal", 4)).Should(Equal("Expected\n <int>: 3\nto equal\n <int>: 4"))
})
})
})
Describe("IndentString", func() {
It("should indent the string", func() {
Ω(IndentString("foo\n bar\nbaz", 2)).Should(Equal(" foo\n bar\n baz"))
})
})
Describe("Object", func() {
Describe("formatting boolean values", func() {
It("should give the type and format values correctly", func() {
Ω(Object(true, 1)).Should(match("bool", "true"))
Ω(Object(false, 1)).Should(match("bool", "false"))
})
})
Describe("formatting numbers", func() {
It("should give the type and format values correctly", func() {
Ω(Object(int(3), 1)).Should(match("int", "3"))
Ω(Object(int8(3), 1)).Should(match("int8", "3"))
Ω(Object(int16(3), 1)).Should(match("int16", "3"))
Ω(Object(int32(3), 1)).Should(match("int32", "3"))
Ω(Object(int64(3), 1)).Should(match("int64", "3"))
Ω(Object(uint(3), 1)).Should(match("uint", "3"))
Ω(Object(uint8(3), 1)).Should(match("uint8", "3"))
Ω(Object(uint16(3), 1)).Should(match("uint16", "3"))
Ω(Object(uint32(3), 1)).Should(match("uint32", "3"))
Ω(Object(uint64(3), 1)).Should(match("uint64", "3"))
})
It("should handle uintptr differently", func() {
Ω(Object(uintptr(3), 1)).Should(match("uintptr", "0x3"))
})
})
Describe("formatting channels", func() {
It("should give the type and format values correctly", func() {
c := make(chan<- bool, 3)
c <- true
c <- false
Ω(Object(c, 1)).Should(match("chan<- bool | len:2, cap:3", "%v", c))
})
})
Describe("formatting strings", func() {
It("should give the type and format values correctly", func() {
s := "a\nb\nc"
Ω(Object(s, 1)).Should(match("string", `a
b
c`))
})
})
Describe("formatting []byte slices", func() {
It("should present them as strings", func() {
b := []byte("a\nb\nc")
Ω(Object(b, 1)).Should(matchRegexp(`\[\]uint8 \| len:5, cap:\d+`, `a
b
c`))
})
})
Describe("formatting functions", func() {
It("should give the type and format values correctly", func() {
f := func(a string, b []int) ([]byte, error) {
return []byte("abc"), nil
}
Ω(Object(f, 1)).Should(match("func(string, []int) ([]uint8, error)", "%v", f))
})
})
Describe("formatting pointers", func() {
It("should give the type and dereference the value to format it correctly", func() {
a := 3
Ω(Object(&a, 1)).Should(match(fmt.Sprintf("*int | %p", &a), "3"))
})
Context("when there are pointers to pointers...", func() {
It("should recursively deference the pointer until it gets to a value", func() {
a := 3
var b *int
var c **int
var d ***int
b = &a
c = &b
d = &c
Ω(Object(d, 1)).Should(match(fmt.Sprintf("***int | %p", d), "3"))
})
})
Context("when the pointer points to nil", func() {
It("should say nil and not explode", func() {
var a *AStruct
Ω(Object(a, 1)).Should(match("*format_test.AStruct | 0x0", "nil"))
})
})
})
Describe("formatting arrays", func() {
It("should give the type and format values correctly", func() {
w := [3]string{"Jed Bartlet", "Toby Ziegler", "CJ Cregg"}
Ω(Object(w, 1)).Should(match("[3]string", `["Jed Bartlet", "Toby Ziegler", "CJ Cregg"]`))
})
Context("with byte arrays", func() {
It("should give the type and format values correctly", func() {
w := [3]byte{17, 28, 19}
Ω(Object(w, 1)).Should(match("[3]uint8", `[17, 28, 19]`))
})
})
})
Describe("formatting slices", func() {
It("should include the length and capacity in the type information", func() {
s := make([]bool, 3, 4)
Ω(Object(s, 1)).Should(match("[]bool | len:3, cap:4", "[false, false, false]"))
})
Context("when the slice contains long entries", func() {
It("should format the entries with newlines", func() {
w := []string{"Josiah Edward Bartlet", "Toby Ziegler", "CJ Cregg"}
expected := `[
"Josiah Edward Bartlet",
"Toby Ziegler",
"CJ Cregg",
]`
Ω(Object(w, 1)).Should(match("[]string | len:3, cap:3", expected))
})
})
})
Describe("formatting maps", func() {
It("should include the length in the type information", func() {
m := make(map[int]bool, 5)
m[3] = true
m[4] = false
Ω(Object(m, 1)).Should(matchRegexp(`map\[int\]bool \| len:2`, hashMatchingRegexp("3: true", "4: false")))
})
Context("when the slice contains long entries", func() {
It("should format the entries with newlines", func() {
m := map[string][]byte{}
m["Josiah Edward Bartlet"] = []byte("Martin Sheen")
m["Toby Ziegler"] = []byte("Richard Schiff")
m["CJ Cregg"] = []byte("Allison Janney")
expected := `{
("Josiah Edward Bartlet": "Martin Sheen"|"Toby Ziegler": "Richard Schiff"|"CJ Cregg": "Allison Janney"),
("Josiah Edward Bartlet": "Martin Sheen"|"Toby Ziegler": "Richard Schiff"|"CJ Cregg": "Allison Janney"),
("Josiah Edward Bartlet": "Martin Sheen"|"Toby Ziegler": "Richard Schiff"|"CJ Cregg": "Allison Janney"),
}`
Ω(Object(m, 1)).Should(matchRegexp(`map\[string\]\[\]uint8 \| len:3`, expected))
})
})
})
Describe("formatting structs", func() {
It("should include the struct name and the field names", func() {
s := SimpleStruct{
Name: "Oswald",
Enumeration: 17,
Veritas: true,
Data: []byte("datum"),
secret: 1983,
}
Ω(Object(s, 1)).Should(match("format_test.SimpleStruct", `{Name: "Oswald", Enumeration: 17, Veritas: true, Data: "datum", secret: 1983}`))
})
Context("when the struct contains long entries", func() {
It("should format the entries with new lines", func() {
s := &SimpleStruct{
Name: "Mithrandir Gandalf Greyhame",
Enumeration: 2021,
Veritas: true,
Data: []byte("wizard"),
secret: 3,
}
Ω(Object(s, 1)).Should(match(fmt.Sprintf("*format_test.SimpleStruct | %p", s), `{
Name: "Mithrandir Gandalf Greyhame",
Enumeration: 2021,
Veritas: true,
Data: "wizard",
secret: 3,
}`))
})
})
})
Describe("formatting nil values", func() {
It("should print out nil", func() {
Ω(Object(nil, 1)).Should(match("nil", "nil"))
var typedNil *AStruct
Ω(Object(typedNil, 1)).Should(match("*format_test.AStruct | 0x0", "nil"))
var c chan<- bool
Ω(Object(c, 1)).Should(match("chan<- bool | len:0, cap:0", "nil"))
var s []string
Ω(Object(s, 1)).Should(match("[]string | len:0, cap:0", "nil"))
var m map[string]bool
Ω(Object(m, 1)).Should(match("map[string]bool | len:0", "nil"))
})
})
Describe("formatting aliased types", func() {
It("should print out the correct alias type", func() {
Ω(Object(StringAlias("alias"), 1)).Should(match("format_test.StringAlias", `alias`))
Ω(Object(ByteAlias("alias"), 1)).Should(matchRegexp(`format_test\.ByteAlias \| len:5, cap:\d+`, `alias`))
Ω(Object(IntAlias(3), 1)).Should(match("format_test.IntAlias", "3"))
})
})
Describe("handling nested things", func() {
It("should produce a correctly nested representation", func() {
s := ComplexStruct{
Strings: []string{"lots", "of", "short", "strings"},
SimpleThings: []*SimpleStruct{
{"short", 7, true, []byte("succinct"), 17},
{"something longer", 427, true, []byte("designed to wrap around nicely"), 30},
},
DataMaps: map[int]ByteAlias{
17: ByteAlias("some substantially longer chunks of data"),
1138: ByteAlias("that should make things wrap"),
},
}
expected := `{
Strings: \["lots", "of", "short", "strings"\],
SimpleThings: \[
{Name: "short", Enumeration: 7, Veritas: true, Data: "succinct", secret: 17},
{
Name: "something longer",
Enumeration: 427,
Veritas: true,
Data: "designed to wrap around nicely",
secret: 30,
},
\],
DataMaps: {
(17: "some substantially longer chunks of data"|1138: "that should make things wrap"),
(17: "some substantially longer chunks of data"|1138: "that should make things wrap"),
},
}`
Ω(Object(s, 1)).Should(matchRegexp(`format_test\.ComplexStruct`, expected))
})
})
})
Describe("Handling unexported fields in structs", func() {
It("should handle all the various types correctly", func() {
a := int(5)
s := SecretiveStruct{
boolValue: true,
intValue: 3,
uintValue: 4,
uintptrValue: 5,
floatValue: 6.0,
complexValue: complex(5.0, 3.0),
chanValue: make(chan bool, 2),
funcValue: func() {},
pointerValue: &a,
sliceValue: []string{"string", "slice"},
byteSliceValue: []byte("bytes"),
stringValue: "a string",
arrValue: [3]int{11, 12, 13},
byteArrValue: [3]byte{17, 20, 32},
mapValue: map[string]int{"a key": 20, "b key": 30},
structValue: AStruct{"exported"},
interfaceValue: map[string]int{"a key": 17},
}
expected := fmt.Sprintf(`{
boolValue: true,
intValue: 3,
uintValue: 4,
uintptrValue: 0x5,
floatValue: 6,
complexValue: \(5\+3i\),
chanValue: %p,
funcValue: %p,
pointerValue: 5,
sliceValue: \["string", "slice"\],
byteSliceValue: "bytes",
stringValue: "a string",
arrValue: \[11, 12, 13\],
byteArrValue: \[17, 20, 32\],
mapValue: %s,
structValue: {Exported: "exported"},
interfaceValue: {"a key": 17},
}`, s.chanValue, s.funcValue, hashMatchingRegexp(`"a key": 20`, `"b key": 30`))
Ω(Object(s, 1)).Should(matchRegexp(`format_test\.SecretiveStruct`, expected))
})
})
Describe("Handling interfaces", func() {
It("should unpack the interface", func() {
outerHash := map[string]interface{}{}
innerHash := map[string]int{}
innerHash["inner"] = 3
outerHash["integer"] = 2
outerHash["map"] = innerHash
expected := hashMatchingRegexp(`"integer": 2`, `"map": {"inner": 3}`)
Ω(Object(outerHash, 1)).Should(matchRegexp(`map\[string\]interface {} \| len:2`, expected))
})
})
Describe("Handling recursive things", func() {
It("should not go crazy...", func() {
m := map[string]interface{}{}
m["integer"] = 2
m["map"] = m
Ω(Object(m, 1)).Should(ContainSubstring("..."))
})
})
Describe("When instructed to use the Stringer representation", func() {
BeforeEach(func() {
UseStringerRepresentation = true
})
AfterEach(func() {
UseStringerRepresentation = false
})
Context("when passed a GoStringer", func() {
It("should use what GoString() returns", func() {
Ω(Object(GoStringer{}, 1)).Should(ContainSubstring("<format_test.GoStringer>: go-string"))
})
})
Context("when passed a stringer", func() {
It("should use what String() returns", func() {
Ω(Object(Stringer{}, 1)).Should(ContainSubstring("<format_test.Stringer>: string"))
})
})
})
})