Skip to content

Commit

Permalink
Lazily render mock diff output on successful match (#8)
Browse files Browse the repository at this point in the history
* wip

* wip

* cleanup
  • Loading branch information
mikeauclair authored Jun 25, 2024
1 parent 7ecde95 commit 0579227
Showing 1 changed file with 51 additions and 18 deletions.
69 changes: 51 additions & 18 deletions mock/mock.go
Original file line number Diff line number Diff line change
Expand Up @@ -923,56 +923,73 @@ func (args Arguments) Is(objects ...interface{}) bool {
return true
}

type outputRenderer interface{}

// Diff gets a string describing the differences between the arguments
// and the specified objects.
//
// Returns the diff string and number of differences found.
func (args Arguments) Diff(objects []interface{}) (string, int) {
// TODO: could return string as error and nil for No difference

output := "\n"
var outputBuilder strings.Builder
var differences int

maxArgCount := len(args)
if len(objects) > maxArgCount {
maxArgCount = len(objects)
}

for i := 0; i < maxArgCount; i++ {
outputRenderers := []outputRenderer{}

for j := 0; j < maxArgCount; j++ {
i := j
var actual, expected interface{}
var actualFmt, expectedFmt string
var actualFmt, expectedFmt func() string

if len(objects) <= i {
actual = "(Missing)"
actualFmt = "(Missing)"
actualFmt = func() string {
return "(Missing)"
}
} else {
actual = objects[i]
actualFmt = fmt.Sprintf("(%[1]T=%[1]v)", actual)
actualFmt = func() string {
return fmt.Sprintf("(%[1]T=%[1]v)", actual)
}
}

if len(args) <= i {
expected = "(Missing)"
expectedFmt = "(Missing)"
expectedFmt = func() string {
return "(Missing)"
}
} else {
expected = args[i]
expectedFmt = fmt.Sprintf("(%[1]T=%[1]v)", expected)
expectedFmt = func() string {
return fmt.Sprintf("(%[1]T=%[1]v)", expected)
}
}

if matcher, ok := expected.(argumentMatcher); ok {
var matches bool
func() {
defer func() {
if r := recover(); r != nil {
actualFmt = fmt.Sprintf("panic in argument matcher: %v", r)
actualFmt = func() string {
return fmt.Sprintf("panic in argument matcher: %v", r)
}
}
}()
matches = matcher.Matches(actual)
}()
if matches {
output = fmt.Sprintf("%s\t%d: PASS: %s matched by %s\n", output, i, actualFmt, matcher)
outputRenderers = append(outputRenderers, func() string {
return fmt.Sprintf("\t%d: PASS: %s matched by %s\n", i, actualFmt(), matcher)
})
} else {
differences++
output = fmt.Sprintf("%s\t%d: FAIL: %s not matched by %s\n", output, i, actualFmt, matcher)
outputRenderers = append(outputRenderers, fmt.Sprintf("\t%d: FAIL: %s not matched by %s\n", i, actualFmt(), matcher))
}
} else {
switch expected := expected.(type) {
Expand All @@ -981,13 +998,13 @@ func (args Arguments) Diff(objects []interface{}) (string, int) {
if reflect.TypeOf(actual).Name() != string(expected) && reflect.TypeOf(actual).String() != string(expected) {
// not match
differences++
output = fmt.Sprintf("%s\t%d: FAIL: type %s != type %s - %s\n", output, i, expected, reflect.TypeOf(actual).Name(), actualFmt)
outputRenderers = append(outputRenderers, fmt.Sprintf("\t%d: FAIL: type %s != type %s - %s\n", i, expected, reflect.TypeOf(actual).Name(), actualFmt()))
}
case *IsTypeArgument:
actualT := reflect.TypeOf(actual)
if actualT != expected.t {
differences++
output = fmt.Sprintf("%s\t%d: FAIL: type %s != type %s - %s\n", output, i, expected.t.Name(), actualT.Name(), actualFmt)
outputRenderers = append(outputRenderers, fmt.Sprintf("\t%d: FAIL: type %s != type %s - %s\n", i, expected.t.Name(), actualT.Name(), actualFmt()))
}
case *FunctionalOptionsArgument:
t := expected.value
Expand All @@ -1001,26 +1018,30 @@ func (args Arguments) Diff(objects []interface{}) (string, int) {
tName := reflect.TypeOf(t).Name()
if name != reflect.TypeOf(actual).String() && tValue.Len() != 0 {
differences++
output = fmt.Sprintf("%s\t%d: FAIL: type %s != type %s - %s\n", output, i, tName, reflect.TypeOf(actual).Name(), actualFmt)
outputRenderers = append(outputRenderers, fmt.Sprintf("\t%d: FAIL: type %s != type %s - %s\n", i, tName, reflect.TypeOf(actual).Name(), actualFmt()))
} else {
if ef, af := assertOpts(t, actual); ef == "" && af == "" {
// match
output = fmt.Sprintf("%s\t%d: PASS: %s == %s\n", output, i, tName, tName)
outputRenderers = append(outputRenderers, func() string {
return fmt.Sprintf("\t%d: PASS: %s == %s\n", i, tName, tName)
})
} else {
// not match
differences++
output = fmt.Sprintf("%s\t%d: FAIL: %s != %s\n", output, i, af, ef)
outputRenderers = append(outputRenderers, fmt.Sprintf("\t%d: FAIL: %s != %s\n", i, af, ef))
}
}

default:
if assert.ObjectsAreEqual(expected, Anything) || assert.ObjectsAreEqual(actual, Anything) || assert.ObjectsAreEqual(actual, expected) {
// match
output = fmt.Sprintf("%s\t%d: PASS: %s == %s\n", output, i, actualFmt, expectedFmt)
outputRenderers = append(outputRenderers, func() string {
return fmt.Sprintf("\t%d: PASS: %s == %s\n", i, actualFmt(), expectedFmt())
})
} else {
// not match
differences++
output = fmt.Sprintf("%s\t%d: FAIL: %s != %s\n", output, i, actualFmt, expectedFmt)
outputRenderers = append(outputRenderers, fmt.Sprintf("\t%d: FAIL: %s != %s\n", i, actualFmt(), expectedFmt()))
}
}
}
Expand All @@ -1031,7 +1052,19 @@ func (args Arguments) Diff(objects []interface{}) (string, int) {
return "No differences.", differences
}

return output, differences
outputBuilder.WriteString("\n")
for _, renderer := range outputRenderers {
switch r := renderer.(type) {
case string:
outputBuilder.WriteString(r)
case func() string:
outputBuilder.WriteString(r())
default:
panic("Invalid Output Renderer")
}
}

return outputBuilder.String(), differences
}

// Assert compares the arguments with the specified objects and fails if
Expand Down

0 comments on commit 0579227

Please sign in to comment.