Skip to content

Commit

Permalink
make format MaxDepth and UseStringerInterface configurable
Browse files Browse the repository at this point in the history
  • Loading branch information
Onsi Fakhouri committed May 28, 2014
1 parent 50bea9a commit 05c2b32
Show file tree
Hide file tree
Showing 2 changed files with 101 additions and 5 deletions.
62 changes: 59 additions & 3 deletions format/format.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
/*
Gomega's format package pretty-prints objects. It explores input objects recursively and generates formatted, indented output with type information.
*/
package format

import (
Expand All @@ -6,10 +9,37 @@ import (
"strings"
)

// 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

//The default indentation string emitted by the format package
var Indent = " "

var longFormThreshold = 20
var maxIndent = uint(10)

/*
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)
Expand All @@ -18,12 +48,24 @@ func Message(actual interface{}, message string, expected ...interface{}) string
}
}

/*
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.
*/
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 := ""
Expand Down Expand Up @@ -61,12 +103,26 @@ func formatType(object interface{}) string {
}

func formatValue(value reflect.Value, indentation uint) string {
if indentation > maxIndent {
return "Too deep for me, man..."
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()
}
}
}

switch value.Kind() {
case reflect.Bool:
return fmt.Sprintf("%v", value.Bool())
Expand Down
44 changes: 42 additions & 2 deletions format/format_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ package format_test

import (
"fmt"
"strings"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
. "github.com/onsi/gomega/format"
"strings"
)

//recursive struct
Expand Down Expand Up @@ -52,6 +52,24 @@ type SecretiveStruct struct {
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{}) OmegaMatcher {
if len(args) > 0 {
Expand Down Expand Up @@ -402,7 +420,29 @@ var _ = Describe("Format", func() {
m := map[string]interface{}{}
m["integer"] = 2
m["map"] = m
Ω(Object(m, 1)).Should(ContainSubstring("Too deep for me, man..."))
Ω(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"))
})
})
})
})

0 comments on commit 05c2b32

Please sign in to comment.