summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/onsi/gomega/format
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/onsi/gomega/format')
-rw-r--r--vendor/github.com/onsi/gomega/format/format.go384
-rw-r--r--vendor/github.com/onsi/gomega/format/format_suite_test.go13
-rw-r--r--vendor/github.com/onsi/gomega/format/format_test.go590
3 files changed, 987 insertions, 0 deletions
diff --git a/vendor/github.com/onsi/gomega/format/format.go b/vendor/github.com/onsi/gomega/format/format.go
new file mode 100644
index 0000000..a9d2651
--- /dev/null
+++ b/vendor/github.com/onsi/gomega/format/format.go
@@ -0,0 +1,384 @@
+/*
+Gomega's format package pretty-prints objects. It explores input objects recursively and generates formatted, indented output with type information.
+*/
+package format
+
+import (
+ "fmt"
+ "reflect"
+ "strconv"
+ "strings"
+ "time"
+)
+
+// Use MaxDepth to set the maximum recursion depth when printing deeply nested objects
+var MaxDepth = uint(10)
+
+/*
+By default, all objects (even those that implement fmt.Stringer and fmt.GoStringer) are recursively inspected to generate output.
+
+Set UseStringerRepresentation = true to use GoString (for fmt.GoStringers) or String (for fmt.Stringer) instead.
+
+Note that GoString and String don't always have all the information you need to understand why a test failed!
+*/
+var UseStringerRepresentation = false
+
+/*
+Print the content of context objects. By default it will be suppressed.
+
+Set PrintContextObjects = true to enable printing of the context internals.
+*/
+var PrintContextObjects = false
+
+// Ctx interface defined here to keep backwards compatability with go < 1.7
+// It matches the context.Context interface
+type Ctx interface {
+ Deadline() (deadline time.Time, ok bool)
+ Done() <-chan struct{}
+ Err() error
+ Value(key interface{}) interface{}
+}
+
+var contextType = reflect.TypeOf((*Ctx)(nil)).Elem()
+var timeType = reflect.TypeOf(time.Time{})
+
+//The default indentation string emitted by the format package
+var Indent = " "
+
+var longFormThreshold = 20
+
+/*
+Generates a formatted matcher success/failure message of the form:
+
+ Expected
+ <pretty printed actual>
+ <message>
+ <pretty printed expected>
+
+If expected is omited, then the message looks like:
+
+ Expected
+ <pretty printed actual>
+ <message>
+*/
+func Message(actual interface{}, message string, expected ...interface{}) string {
+ if len(expected) == 0 {
+ return fmt.Sprintf("Expected\n%s\n%s", Object(actual, 1), message)
+ } else {
+ return fmt.Sprintf("Expected\n%s\n%s\n%s", Object(actual, 1), message, Object(expected[0], 1))
+ }
+}
+
+/*
+
+Generates a nicely formatted matcher success / failure message
+
+Much like Message(...), but it attempts to pretty print diffs in strings
+
+Expected
+ <string>: "...aaaaabaaaaa..."
+to equal |
+ <string>: "...aaaaazaaaaa..."
+
+*/
+
+func MessageWithDiff(actual, message, expected string) string {
+ if len(actual) >= truncateThreshold && len(expected) >= truncateThreshold {
+ diffPoint := findFirstMismatch(actual, expected)
+ formattedActual := truncateAndFormat(actual, diffPoint)
+ formattedExpected := truncateAndFormat(expected, diffPoint)
+
+ spacesBeforeFormattedMismatch := findFirstMismatch(formattedActual, formattedExpected)
+
+ tabLength := 4
+ spaceFromMessageToActual := tabLength + len("<string>: ") - len(message)
+ padding := strings.Repeat(" ", spaceFromMessageToActual+spacesBeforeFormattedMismatch) + "|"
+ return Message(formattedActual, message+padding, formattedExpected)
+ }
+ return Message(actual, message, expected)
+}
+
+func truncateAndFormat(str string, index int) string {
+ leftPadding := `...`
+ rightPadding := `...`
+
+ start := index - charactersAroundMismatchToInclude
+ if start < 0 {
+ start = 0
+ leftPadding = ""
+ }
+
+ // slice index must include the mis-matched character
+ lengthOfMismatchedCharacter := 1
+ end := index + charactersAroundMismatchToInclude + lengthOfMismatchedCharacter
+ if end > len(str) {
+ end = len(str)
+ rightPadding = ""
+
+ }
+ return fmt.Sprintf("\"%s\"", leftPadding+str[start:end]+rightPadding)
+}
+
+func findFirstMismatch(a, b string) int {
+ aSlice := strings.Split(a, "")
+ bSlice := strings.Split(b, "")
+
+ for index, str := range aSlice {
+ if index > len(b) - 1 {
+ return index
+ }
+ if str != bSlice[index] {
+ return index
+ }
+ }
+
+ if len(b) > len(a) {
+ return len(a) + 1
+ }
+
+ return 0
+}
+
+const (
+ truncateThreshold = 50
+ charactersAroundMismatchToInclude = 5
+)
+
+/*
+Pretty prints the passed in object at the passed in indentation level.
+
+Object recurses into deeply nested objects emitting pretty-printed representations of their components.
+
+Modify format.MaxDepth to control how deep the recursion is allowed to go
+Set format.UseStringerRepresentation to true to return object.GoString() or object.String() when available instead of
+recursing into the object.
+
+Set PrintContextObjects to true to print the content of objects implementing context.Context
+*/
+func Object(object interface{}, indentation uint) string {
+ indent := strings.Repeat(Indent, int(indentation))
+ value := reflect.ValueOf(object)
+ return fmt.Sprintf("%s<%s>: %s", indent, formatType(object), formatValue(value, indentation))
+}
+
+/*
+IndentString takes a string and indents each line by the specified amount.
+*/
+func IndentString(s string, indentation uint) string {
+ components := strings.Split(s, "\n")
+ result := ""
+ indent := strings.Repeat(Indent, int(indentation))
+ for i, component := range components {
+ result += indent + component
+ if i < len(components)-1 {
+ result += "\n"
+ }
+ }
+
+ return result
+}
+
+func formatType(object interface{}) string {
+ t := reflect.TypeOf(object)
+ if t == nil {
+ return "nil"
+ }
+ switch t.Kind() {
+ case reflect.Chan:
+ v := reflect.ValueOf(object)
+ return fmt.Sprintf("%T | len:%d, cap:%d", object, v.Len(), v.Cap())
+ case reflect.Ptr:
+ return fmt.Sprintf("%T | %p", object, object)
+ case reflect.Slice:
+ v := reflect.ValueOf(object)
+ return fmt.Sprintf("%T | len:%d, cap:%d", object, v.Len(), v.Cap())
+ case reflect.Map:
+ v := reflect.ValueOf(object)
+ return fmt.Sprintf("%T | len:%d", object, v.Len())
+ default:
+ return fmt.Sprintf("%T", object)
+ }
+}
+
+func formatValue(value reflect.Value, indentation uint) string {
+ if indentation > MaxDepth {
+ return "..."
+ }
+
+ if isNilValue(value) {
+ return "nil"
+ }
+
+ if UseStringerRepresentation {
+ if value.CanInterface() {
+ obj := value.Interface()
+ switch x := obj.(type) {
+ case fmt.GoStringer:
+ return x.GoString()
+ case fmt.Stringer:
+ return x.String()
+ }
+ }
+ }
+
+ if !PrintContextObjects {
+ if value.Type().Implements(contextType) && indentation > 1 {
+ return "<suppressed context>"
+ }
+ }
+
+ switch value.Kind() {
+ case reflect.Bool:
+ return fmt.Sprintf("%v", value.Bool())
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ return fmt.Sprintf("%v", value.Int())
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+ return fmt.Sprintf("%v", value.Uint())
+ case reflect.Uintptr:
+ return fmt.Sprintf("0x%x", value.Uint())
+ case reflect.Float32, reflect.Float64:
+ return fmt.Sprintf("%v", value.Float())
+ case reflect.Complex64, reflect.Complex128:
+ return fmt.Sprintf("%v", value.Complex())
+ case reflect.Chan:
+ return fmt.Sprintf("0x%x", value.Pointer())
+ case reflect.Func:
+ return fmt.Sprintf("0x%x", value.Pointer())
+ case reflect.Ptr:
+ return formatValue(value.Elem(), indentation)
+ case reflect.Slice:
+ return formatSlice(value, indentation)
+ case reflect.String:
+ return formatString(value.String(), indentation)
+ case reflect.Array:
+ return formatSlice(value, indentation)
+ case reflect.Map:
+ return formatMap(value, indentation)
+ case reflect.Struct:
+ if value.Type() == timeType && value.CanInterface() {
+ t, _ := value.Interface().(time.Time)
+ return t.Format(time.RFC3339Nano)
+ }
+ return formatStruct(value, indentation)
+ case reflect.Interface:
+ return formatValue(value.Elem(), indentation)
+ default:
+ if value.CanInterface() {
+ return fmt.Sprintf("%#v", value.Interface())
+ } else {
+ return fmt.Sprintf("%#v", value)
+ }
+ }
+}
+
+func formatString(object interface{}, indentation uint) string {
+ if indentation == 1 {
+ s := fmt.Sprintf("%s", object)
+ components := strings.Split(s, "\n")
+ result := ""
+ for i, component := range components {
+ if i == 0 {
+ result += component
+ } else {
+ result += Indent + component
+ }
+ if i < len(components)-1 {
+ result += "\n"
+ }
+ }
+
+ return fmt.Sprintf("%s", result)
+ } else {
+ return fmt.Sprintf("%q", object)
+ }
+}
+
+func formatSlice(v reflect.Value, indentation uint) string {
+ if v.Kind() == reflect.Slice && v.Type().Elem().Kind() == reflect.Uint8 && isPrintableString(string(v.Bytes())) {
+ return formatString(v.Bytes(), indentation)
+ }
+
+ l := v.Len()
+ result := make([]string, l)
+ longest := 0
+ for i := 0; i < l; i++ {
+ result[i] = formatValue(v.Index(i), indentation+1)
+ if len(result[i]) > longest {
+ longest = len(result[i])
+ }
+ }
+
+ if longest > longFormThreshold {
+ indenter := strings.Repeat(Indent, int(indentation))
+ return fmt.Sprintf("[\n%s%s,\n%s]", indenter+Indent, strings.Join(result, ",\n"+indenter+Indent), indenter)
+ } else {
+ return fmt.Sprintf("[%s]", strings.Join(result, ", "))
+ }
+}
+
+func formatMap(v reflect.Value, indentation uint) string {
+ l := v.Len()
+ result := make([]string, l)
+
+ longest := 0
+ for i, key := range v.MapKeys() {
+ value := v.MapIndex(key)
+ result[i] = fmt.Sprintf("%s: %s", formatValue(key, indentation+1), formatValue(value, indentation+1))
+ if len(result[i]) > longest {
+ longest = len(result[i])
+ }
+ }
+
+ if longest > longFormThreshold {
+ indenter := strings.Repeat(Indent, int(indentation))
+ return fmt.Sprintf("{\n%s%s,\n%s}", indenter+Indent, strings.Join(result, ",\n"+indenter+Indent), indenter)
+ } else {
+ return fmt.Sprintf("{%s}", strings.Join(result, ", "))
+ }
+}
+
+func formatStruct(v reflect.Value, indentation uint) string {
+ t := v.Type()
+
+ l := v.NumField()
+ result := []string{}
+ longest := 0
+ for i := 0; i < l; i++ {
+ structField := t.Field(i)
+ fieldEntry := v.Field(i)
+ representation := fmt.Sprintf("%s: %s", structField.Name, formatValue(fieldEntry, indentation+1))
+ result = append(result, representation)
+ if len(representation) > longest {
+ longest = len(representation)
+ }
+ }
+ if longest > longFormThreshold {
+ indenter := strings.Repeat(Indent, int(indentation))
+ return fmt.Sprintf("{\n%s%s,\n%s}", indenter+Indent, strings.Join(result, ",\n"+indenter+Indent), indenter)
+ } else {
+ return fmt.Sprintf("{%s}", strings.Join(result, ", "))
+ }
+}
+
+func isNilValue(a reflect.Value) bool {
+ switch a.Kind() {
+ case reflect.Invalid:
+ return true
+ case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
+ return a.IsNil()
+ }
+
+ return false
+}
+
+/*
+Returns true when the string is entirely made of printable runes, false otherwise.
+*/
+func isPrintableString(str string) bool {
+ for _, runeValue := range str {
+ if !strconv.IsPrint(runeValue) {
+ return false
+ }
+ }
+ return true
+}
diff --git a/vendor/github.com/onsi/gomega/format/format_suite_test.go b/vendor/github.com/onsi/gomega/format/format_suite_test.go
new file mode 100644
index 0000000..8e65a95
--- /dev/null
+++ b/vendor/github.com/onsi/gomega/format/format_suite_test.go
@@ -0,0 +1,13 @@
+package format_test
+
+import (
+ . "github.com/onsi/ginkgo"
+ . "github.com/onsi/gomega"
+
+ "testing"
+)
+
+func TestFormat(t *testing.T) {
+ RegisterFailHandler(Fail)
+ RunSpecs(t, "Format Suite")
+}
diff --git a/vendor/github.com/onsi/gomega/format/format_test.go b/vendor/github.com/onsi/gomega/format/format_test.go
new file mode 100644
index 0000000..a1a9031
--- /dev/null
+++ b/vendor/github.com/onsi/gomega/format/format_test.go
@@ -0,0 +1,590 @@
+package format_test
+
+import (
+ "fmt"
+ "strings"
+ "time"
+
+ . "github.com/onsi/ginkgo"
+ . "github.com/onsi/gomega"
+ . "github.com/onsi/gomega/format"
+ "github.com/onsi/gomega/types"
+)
+
+//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"
+}
+
+type ctx struct {
+}
+
+func (c *ctx) Deadline() (deadline time.Time, ok bool) {
+ return time.Time{}, false
+}
+
+func (c *ctx) Done() <-chan struct{} {
+ return nil
+}
+
+func (c *ctx) Err() error {
+ return nil
+}
+
+func (c *ctx) Value(key interface{}) interface{} {
+ return nil
+}
+
+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("MessageWithDiff", func() {
+ It("shows the exact point where two long strings differ", func() {
+ stringWithB := "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ stringWithZ := "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaazaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+
+ Ω(MessageWithDiff(stringWithB, "to equal", stringWithZ)).Should(Equal(expectedLongStringFailureMessage))
+ })
+
+ It("truncates the start of long strings that differ only at their end", func() {
+ stringWithB := "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab"
+ stringWithZ := "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaz"
+
+ Ω(MessageWithDiff(stringWithB, "to equal", stringWithZ)).Should(Equal(expectedTruncatedStartStringFailureMessage))
+ })
+
+ It("truncates the start of long strings that differ only in length", func() {
+ smallString := "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ largeString := "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+
+ Ω(MessageWithDiff(largeString, "to equal", smallString)).Should(Equal(expectedTruncatedStartSizeFailureMessage))
+ Ω(MessageWithDiff(smallString, "to equal", largeString)).Should(Equal(expectedTruncatedStartSizeSwappedFailureMessage))
+ })
+
+ It("truncates the end of long strings that differ only at their start", func() {
+ stringWithB := "baaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ stringWithZ := "zaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+
+ Ω(MessageWithDiff(stringWithB, "to equal", stringWithZ)).Should(Equal(expectedTruncatedEndStringFailureMessage))
+ })
+ })
+
+ 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() {
+ Context("when the slice is made of printable bytes", func() {
+ It("should present it as string", func() {
+ b := []byte("a b c")
+ Ω(Object(b, 1)).Should(matchRegexp(`\[\]uint8 \| len:5, cap:\d+`, `a b c`))
+ })
+ })
+ Context("when the slice contains non-printable bytes", func() {
+ It("should present it as slice", func() {
+ b := []byte("a b c\n\x01\x02\x03\xff\x1bH")
+ Ω(Object(b, 1)).Should(matchRegexp(`\[\]uint8 \| len:12, cap:\d+`, `\[97, 32, 98, 32, 99, 10, 1, 2, 3, 255, 27, 72\]`))
+ })
+ })
+ })
+
+ 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("formatting times", func() {
+ It("should format time as RFC3339", func() {
+ t := time.Date(2016, 10, 31, 9, 57, 23, 12345, time.UTC)
+ Ω(Object(t, 1)).Should(match("time.Time", `2016-10-31T09:57:23.000012345Z`))
+ })
+ })
+ })
+
+ 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("..."))
+ })
+
+ It("really should not go crazy...", func() {
+ type complexKey struct {
+ Value map[interface{}]int
+ }
+
+ complexObject := complexKey{}
+ complexObject.Value = make(map[interface{}]int)
+
+ complexObject.Value[&complexObject] = 2
+ Ω(Object(complexObject, 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"))
+ })
+ })
+ })
+
+ Describe("Printing a context.Context field", func() {
+
+ type structWithContext struct {
+ Context Ctx
+ Value string
+ }
+
+ context := ctx{}
+ objWithContext := structWithContext{Value: "some-value", Context: &context}
+
+ It("Suppresses the content by default", func() {
+ Ω(Object(objWithContext, 1)).Should(ContainSubstring("<suppressed context>"))
+ })
+
+ It("Doesn't supress the context if it's the object being printed", func() {
+ Ω(Object(context, 1)).ShouldNot(MatchRegexp("^.*<suppressed context>$"))
+ })
+
+ Context("PrintContextObjects is set", func() {
+ BeforeEach(func() {
+ PrintContextObjects = true
+ })
+
+ AfterEach(func() {
+ PrintContextObjects = false
+ })
+
+ It("Prints the context", func() {
+ Ω(Object(objWithContext, 1)).ShouldNot(ContainSubstring("<suppressed context>"))
+ })
+ })
+ })
+})
+
+var expectedLongStringFailureMessage = strings.TrimSpace(`
+Expected
+ <string>: "...aaaaabaaaaa..."
+to equal |
+ <string>: "...aaaaazaaaaa..."
+`)
+var expectedTruncatedEndStringFailureMessage = strings.TrimSpace(`
+Expected
+ <string>: "baaaaa..."
+to equal |
+ <string>: "zaaaaa..."
+`)
+var expectedTruncatedStartStringFailureMessage = strings.TrimSpace(`
+Expected
+ <string>: "...aaaaab"
+to equal |
+ <string>: "...aaaaaz"
+`)
+var expectedTruncatedStartSizeFailureMessage = strings.TrimSpace(`
+Expected
+ <string>: "...aaaaaa"
+to equal |
+ <string>: "...aaaaa"
+`)
+var expectedTruncatedStartSizeSwappedFailureMessage = strings.TrimSpace(`
+Expected
+ <string>: "...aaaa"
+to equal |
+ <string>: "...aaaaa"
+`)