|  | 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")) | 
|  | }) | 
|  | }) | 
|  | }) | 
|  | }) |