From 05792272d435a29ea9faec403056d352cb687321 Mon Sep 17 00:00:00 2001 From: Mike Auclair Date: Tue, 25 Jun 2024 12:24:31 -0400 Subject: [PATCH] Lazily render mock diff output on successful match (#8) * wip * wip * cleanup --- mock/mock.go | 69 ++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 51 insertions(+), 18 deletions(-) diff --git a/mock/mock.go b/mock/mock.go index d5eb1ef55..ba66ad5e6 100644 --- a/mock/mock.go +++ b/mock/mock.go @@ -923,6 +923,8 @@ 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. // @@ -930,7 +932,7 @@ func (args Arguments) Is(objects ...interface{}) bool { 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) @@ -938,24 +940,35 @@ func (args Arguments) Diff(objects []interface{}) (string, int) { 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 { @@ -963,16 +976,20 @@ func (args Arguments) Diff(objects []interface{}) (string, int) { 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) { @@ -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 @@ -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())) } } } @@ -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