8 . "github.com/onsi/ginkgo"
9 . "github.com/onsi/gomega"
10 . "github.com/onsi/gomega/format"
11 "github.com/onsi/gomega/types"
16 type StringAlias string
24 type SimpleStruct struct {
32 type ComplexStruct struct {
34 SimpleThings []*SimpleStruct
35 DataMaps map[int]ByteAlias
38 type SecretiveStruct struct {
44 complexValue complex64
53 mapValue map[string]int
55 interfaceValue interface{}
58 type GoStringer struct {
61 func (g GoStringer) GoString() string {
65 func (g GoStringer) String() string {
69 type Stringer struct {
72 func (g Stringer) String() string {
79 func (c *ctx) Deadline() (deadline time.Time, ok bool) {
80 return time.Time{}, false
83 func (c *ctx) Done() <-chan struct{} {
87 func (c *ctx) Err() error {
91 func (c *ctx) Value(key interface{}) interface{} {
95 var _ = Describe("Format", func() {
96 match := func(typeRepresentation string, valueRepresentation string, args ...interface{}) types.GomegaMatcher {
98 valueRepresentation = fmt.Sprintf(valueRepresentation, args...)
100 return Equal(fmt.Sprintf("%s<%s>: %s", Indent, typeRepresentation, valueRepresentation))
103 matchRegexp := func(typeRepresentation string, valueRepresentation string, args ...interface{}) types.GomegaMatcher {
105 valueRepresentation = fmt.Sprintf(valueRepresentation, args...)
107 return MatchRegexp(fmt.Sprintf("%s<%s>: %s", Indent, typeRepresentation, valueRepresentation))
110 hashMatchingRegexp := func(entries ...string) string {
111 entriesSwitch := "(" + strings.Join(entries, "|") + ")"
112 arr := make([]string, len(entries))
114 arr[i] = entriesSwitch
116 return "{" + strings.Join(arr, ", ") + "}"
119 Describe("Message", func() {
120 Context("with only an actual value", func() {
121 It("should print out an indented formatted representation of the value and the message", func() {
122 Ω(Message(3, "to be three.")).Should(Equal("Expected\n <int>: 3\nto be three."))
126 Context("with an actual and an expected value", func() {
127 It("should print out an indented formatted representatino of both values, and the message", func() {
128 Ω(Message(3, "to equal", 4)).Should(Equal("Expected\n <int>: 3\nto equal\n <int>: 4"))
133 Describe("MessageWithDiff", func() {
134 It("shows the exact point where two long strings differ", func() {
135 stringWithB := "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
136 stringWithZ := "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaazaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
138 Ω(MessageWithDiff(stringWithB, "to equal", stringWithZ)).Should(Equal(expectedLongStringFailureMessage))
141 It("truncates the start of long strings that differ only at their end", func() {
142 stringWithB := "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab"
143 stringWithZ := "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaz"
145 Ω(MessageWithDiff(stringWithB, "to equal", stringWithZ)).Should(Equal(expectedTruncatedStartStringFailureMessage))
148 It("truncates the start of long strings that differ only in length", func() {
149 smallString := "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
150 largeString := "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
152 Ω(MessageWithDiff(largeString, "to equal", smallString)).Should(Equal(expectedTruncatedStartSizeFailureMessage))
153 Ω(MessageWithDiff(smallString, "to equal", largeString)).Should(Equal(expectedTruncatedStartSizeSwappedFailureMessage))
156 It("truncates the end of long strings that differ only at their start", func() {
157 stringWithB := "baaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
158 stringWithZ := "zaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
160 Ω(MessageWithDiff(stringWithB, "to equal", stringWithZ)).Should(Equal(expectedTruncatedEndStringFailureMessage))
164 Describe("IndentString", func() {
165 It("should indent the string", func() {
166 Ω(IndentString("foo\n bar\nbaz", 2)).Should(Equal(" foo\n bar\n baz"))
170 Describe("Object", func() {
171 Describe("formatting boolean values", func() {
172 It("should give the type and format values correctly", func() {
173 Ω(Object(true, 1)).Should(match("bool", "true"))
174 Ω(Object(false, 1)).Should(match("bool", "false"))
178 Describe("formatting numbers", func() {
179 It("should give the type and format values correctly", func() {
180 Ω(Object(int(3), 1)).Should(match("int", "3"))
181 Ω(Object(int8(3), 1)).Should(match("int8", "3"))
182 Ω(Object(int16(3), 1)).Should(match("int16", "3"))
183 Ω(Object(int32(3), 1)).Should(match("int32", "3"))
184 Ω(Object(int64(3), 1)).Should(match("int64", "3"))
186 Ω(Object(uint(3), 1)).Should(match("uint", "3"))
187 Ω(Object(uint8(3), 1)).Should(match("uint8", "3"))
188 Ω(Object(uint16(3), 1)).Should(match("uint16", "3"))
189 Ω(Object(uint32(3), 1)).Should(match("uint32", "3"))
190 Ω(Object(uint64(3), 1)).Should(match("uint64", "3"))
193 It("should handle uintptr differently", func() {
194 Ω(Object(uintptr(3), 1)).Should(match("uintptr", "0x3"))
198 Describe("formatting channels", func() {
199 It("should give the type and format values correctly", func() {
200 c := make(chan<- bool, 3)
203 Ω(Object(c, 1)).Should(match("chan<- bool | len:2, cap:3", "%v", c))
207 Describe("formatting strings", func() {
208 It("should give the type and format values correctly", func() {
210 Ω(Object(s, 1)).Should(match("string", `a
216 Describe("formatting []byte slices", func() {
217 Context("when the slice is made of printable bytes", func() {
218 It("should present it as string", func() {
220 Ω(Object(b, 1)).Should(matchRegexp(`\[\]uint8 \| len:5, cap:\d+`, `a b c`))
223 Context("when the slice contains non-printable bytes", func() {
224 It("should present it as slice", func() {
225 b := []byte("a b c\n\x01\x02\x03\xff\x1bH")
226 Ω(Object(b, 1)).Should(matchRegexp(`\[\]uint8 \| len:12, cap:\d+`, `\[97, 32, 98, 32, 99, 10, 1, 2, 3, 255, 27, 72\]`))
231 Describe("formatting functions", func() {
232 It("should give the type and format values correctly", func() {
233 f := func(a string, b []int) ([]byte, error) {
234 return []byte("abc"), nil
236 Ω(Object(f, 1)).Should(match("func(string, []int) ([]uint8, error)", "%v", f))
240 Describe("formatting pointers", func() {
241 It("should give the type and dereference the value to format it correctly", func() {
243 Ω(Object(&a, 1)).Should(match(fmt.Sprintf("*int | %p", &a), "3"))
246 Context("when there are pointers to pointers...", func() {
247 It("should recursively deference the pointer until it gets to a value", func() {
256 Ω(Object(d, 1)).Should(match(fmt.Sprintf("***int | %p", d), "3"))
260 Context("when the pointer points to nil", func() {
261 It("should say nil and not explode", func() {
263 Ω(Object(a, 1)).Should(match("*format_test.AStruct | 0x0", "nil"))
268 Describe("formatting arrays", func() {
269 It("should give the type and format values correctly", func() {
270 w := [3]string{"Jed Bartlet", "Toby Ziegler", "CJ Cregg"}
271 Ω(Object(w, 1)).Should(match("[3]string", `["Jed Bartlet", "Toby Ziegler", "CJ Cregg"]`))
274 Context("with byte arrays", func() {
275 It("should give the type and format values correctly", func() {
276 w := [3]byte{17, 28, 19}
277 Ω(Object(w, 1)).Should(match("[3]uint8", `[17, 28, 19]`))
282 Describe("formatting slices", func() {
283 It("should include the length and capacity in the type information", func() {
284 s := make([]bool, 3, 4)
285 Ω(Object(s, 1)).Should(match("[]bool | len:3, cap:4", "[false, false, false]"))
288 Context("when the slice contains long entries", func() {
289 It("should format the entries with newlines", func() {
290 w := []string{"Josiah Edward Bartlet", "Toby Ziegler", "CJ Cregg"}
292 "Josiah Edward Bartlet",
296 Ω(Object(w, 1)).Should(match("[]string | len:3, cap:3", expected))
301 Describe("formatting maps", func() {
302 It("should include the length in the type information", func() {
303 m := make(map[int]bool, 5)
306 Ω(Object(m, 1)).Should(matchRegexp(`map\[int\]bool \| len:2`, hashMatchingRegexp("3: true", "4: false")))
309 Context("when the slice contains long entries", func() {
310 It("should format the entries with newlines", func() {
311 m := map[string][]byte{}
312 m["Josiah Edward Bartlet"] = []byte("Martin Sheen")
313 m["Toby Ziegler"] = []byte("Richard Schiff")
314 m["CJ Cregg"] = []byte("Allison Janney")
316 ("Josiah Edward Bartlet": "Martin Sheen"|"Toby Ziegler": "Richard Schiff"|"CJ Cregg": "Allison Janney"),
317 ("Josiah Edward Bartlet": "Martin Sheen"|"Toby Ziegler": "Richard Schiff"|"CJ Cregg": "Allison Janney"),
318 ("Josiah Edward Bartlet": "Martin Sheen"|"Toby Ziegler": "Richard Schiff"|"CJ Cregg": "Allison Janney"),
320 Ω(Object(m, 1)).Should(matchRegexp(`map\[string\]\[\]uint8 \| len:3`, expected))
325 Describe("formatting structs", func() {
326 It("should include the struct name and the field names", func() {
331 Data: []byte("datum"),
335 Ω(Object(s, 1)).Should(match("format_test.SimpleStruct", `{Name: "Oswald", Enumeration: 17, Veritas: true, Data: "datum", secret: 1983}`))
338 Context("when the struct contains long entries", func() {
339 It("should format the entries with new lines", func() {
341 Name: "Mithrandir Gandalf Greyhame",
344 Data: []byte("wizard"),
348 Ω(Object(s, 1)).Should(match(fmt.Sprintf("*format_test.SimpleStruct | %p", s), `{
349 Name: "Mithrandir Gandalf Greyhame",
359 Describe("formatting nil values", func() {
360 It("should print out nil", func() {
361 Ω(Object(nil, 1)).Should(match("nil", "nil"))
362 var typedNil *AStruct
363 Ω(Object(typedNil, 1)).Should(match("*format_test.AStruct | 0x0", "nil"))
365 Ω(Object(c, 1)).Should(match("chan<- bool | len:0, cap:0", "nil"))
367 Ω(Object(s, 1)).Should(match("[]string | len:0, cap:0", "nil"))
368 var m map[string]bool
369 Ω(Object(m, 1)).Should(match("map[string]bool | len:0", "nil"))
373 Describe("formatting aliased types", func() {
374 It("should print out the correct alias type", func() {
375 Ω(Object(StringAlias("alias"), 1)).Should(match("format_test.StringAlias", `alias`))
376 Ω(Object(ByteAlias("alias"), 1)).Should(matchRegexp(`format_test\.ByteAlias \| len:5, cap:\d+`, `alias`))
377 Ω(Object(IntAlias(3), 1)).Should(match("format_test.IntAlias", "3"))
381 Describe("handling nested things", func() {
382 It("should produce a correctly nested representation", func() {
384 Strings: []string{"lots", "of", "short", "strings"},
385 SimpleThings: []*SimpleStruct{
386 {"short", 7, true, []byte("succinct"), 17},
387 {"something longer", 427, true, []byte("designed to wrap around nicely"), 30},
389 DataMaps: map[int]ByteAlias{
390 17: ByteAlias("some substantially longer chunks of data"),
391 1138: ByteAlias("that should make things wrap"),
395 Strings: \["lots", "of", "short", "strings"\],
397 {Name: "short", Enumeration: 7, Veritas: true, Data: "succinct", secret: 17},
399 Name: "something longer",
402 Data: "designed to wrap around nicely",
407 (17: "some substantially longer chunks of data"|1138: "that should make things wrap"),
408 (17: "some substantially longer chunks of data"|1138: "that should make things wrap"),
411 Ω(Object(s, 1)).Should(matchRegexp(`format_test\.ComplexStruct`, expected))
415 Describe("formatting times", func() {
416 It("should format time as RFC3339", func() {
417 t := time.Date(2016, 10, 31, 9, 57, 23, 12345, time.UTC)
418 Ω(Object(t, 1)).Should(match("time.Time", `2016-10-31T09:57:23.000012345Z`))
423 Describe("Handling unexported fields in structs", func() {
424 It("should handle all the various types correctly", func() {
426 s := SecretiveStruct{
432 complexValue: complex(5.0, 3.0),
433 chanValue: make(chan bool, 2),
434 funcValue: func() {},
436 sliceValue: []string{"string", "slice"},
437 byteSliceValue: []byte("bytes"),
438 stringValue: "a string",
439 arrValue: [3]int{11, 12, 13},
440 byteArrValue: [3]byte{17, 20, 32},
441 mapValue: map[string]int{"a key": 20, "b key": 30},
442 structValue: AStruct{"exported"},
443 interfaceValue: map[string]int{"a key": 17},
446 expected := fmt.Sprintf(`{
452 complexValue: \(5\+3i\),
456 sliceValue: \["string", "slice"\],
457 byteSliceValue: "bytes",
458 stringValue: "a string",
459 arrValue: \[11, 12, 13\],
460 byteArrValue: \[17, 20, 32\],
462 structValue: {Exported: "exported"},
463 interfaceValue: {"a key": 17},
464 }`, s.chanValue, s.funcValue, hashMatchingRegexp(`"a key": 20`, `"b key": 30`))
466 Ω(Object(s, 1)).Should(matchRegexp(`format_test\.SecretiveStruct`, expected))
470 Describe("Handling interfaces", func() {
471 It("should unpack the interface", func() {
472 outerHash := map[string]interface{}{}
473 innerHash := map[string]int{}
475 innerHash["inner"] = 3
476 outerHash["integer"] = 2
477 outerHash["map"] = innerHash
479 expected := hashMatchingRegexp(`"integer": 2`, `"map": {"inner": 3}`)
480 Ω(Object(outerHash, 1)).Should(matchRegexp(`map\[string\]interface {} \| len:2`, expected))
484 Describe("Handling recursive things", func() {
485 It("should not go crazy...", func() {
486 m := map[string]interface{}{}
489 Ω(Object(m, 1)).Should(ContainSubstring("..."))
492 It("really should not go crazy...", func() {
493 type complexKey struct {
494 Value map[interface{}]int
497 complexObject := complexKey{}
498 complexObject.Value = make(map[interface{}]int)
500 complexObject.Value[&complexObject] = 2
501 Ω(Object(complexObject, 1)).Should(ContainSubstring("..."))
505 Describe("When instructed to use the Stringer representation", func() {
507 UseStringerRepresentation = true
511 UseStringerRepresentation = false
514 Context("when passed a GoStringer", func() {
515 It("should use what GoString() returns", func() {
516 Ω(Object(GoStringer{}, 1)).Should(ContainSubstring("<format_test.GoStringer>: go-string"))
520 Context("when passed a stringer", func() {
521 It("should use what String() returns", func() {
522 Ω(Object(Stringer{}, 1)).Should(ContainSubstring("<format_test.Stringer>: string"))
527 Describe("Printing a context.Context field", func() {
529 type structWithContext struct {
535 objWithContext := structWithContext{Value: "some-value", Context: &context}
537 It("Suppresses the content by default", func() {
538 Ω(Object(objWithContext, 1)).Should(ContainSubstring("<suppressed context>"))
541 It("Doesn't supress the context if it's the object being printed", func() {
542 Ω(Object(context, 1)).ShouldNot(MatchRegexp("^.*<suppressed context>$"))
545 Context("PrintContextObjects is set", func() {
547 PrintContextObjects = true
551 PrintContextObjects = false
554 It("Prints the context", func() {
555 Ω(Object(objWithContext, 1)).ShouldNot(ContainSubstring("<suppressed context>"))
561 var expectedLongStringFailureMessage = strings.TrimSpace(`
563 <string>: "...aaaaabaaaaa..."
565 <string>: "...aaaaazaaaaa..."
567 var expectedTruncatedEndStringFailureMessage = strings.TrimSpace(`
569 <string>: "baaaaa..."
571 <string>: "zaaaaa..."
573 var expectedTruncatedStartStringFailureMessage = strings.TrimSpace(`
575 <string>: "...aaaaab"
577 <string>: "...aaaaaz"
579 var expectedTruncatedStartSizeFailureMessage = strings.TrimSpace(`
581 <string>: "...aaaaaa"
585 var expectedTruncatedStartSizeSwappedFailureMessage = strings.TrimSpace(`