-
Notifications
You must be signed in to change notification settings - Fork 1.7k
assert: truncate very long objects in test failure messages #1646
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -325,13 +325,15 @@ func messageFromMsgAndArgs(msgAndArgs ...interface{}) string { | |
func indentMessageLines(message string, longestLabelLen int) string { | ||
outBuf := new(bytes.Buffer) | ||
|
||
for i, scanner := 0, bufio.NewScanner(strings.NewReader(message)); scanner.Scan(); i++ { | ||
// no need to align first line because it starts at the correct location (after the label) | ||
if i != 0 { | ||
// append alignLen+1 spaces to align with "{{longestLabel}}:" before adding tab | ||
outBuf.WriteString("\n\t" + strings.Repeat(" ", longestLabelLen+1) + "\t") | ||
scanner := bufio.NewScanner(strings.NewReader(message)) | ||
for firstLine := true; scanner.Scan(); firstLine = false { | ||
if !firstLine { | ||
fmt.Fprint(outBuf, "\n\t"+strings.Repeat(" ", longestLabelLen+1)+"\t") | ||
} | ||
outBuf.WriteString(scanner.Text()) | ||
fmt.Fprint(outBuf, scanner.Text()) | ||
} | ||
if err := scanner.Err(); err != nil { | ||
return fmt.Sprintf("cannot display message: %s", err) | ||
} | ||
|
||
return outBuf.String() | ||
|
@@ -544,9 +546,8 @@ func Same(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) b | |
if !same { | ||
// both are pointers but not the same type & pointing to the same address | ||
return Fail(t, fmt.Sprintf("Not same: \n"+ | ||
"expected: %#[1]v (%[1]T)(%[1]p)\n"+ | ||
"actual : %#[2]v (%[2]T)(%[2]p)", | ||
expected, actual), msgAndArgs...) | ||
"expected: %[2]s (%[1]T)(%[1]p)\n"+ | ||
"actual : %[4]s (%[3]T)(%[3]p)", expected, truncatingFormat("%#v", expected), actual, truncatingFormat("%#v", actual)), msgAndArgs...) | ||
} | ||
|
||
return true | ||
|
@@ -571,8 +572,8 @@ func NotSame(t TestingT, expected, actual interface{}, msgAndArgs ...interface{} | |
|
||
if same { | ||
return Fail(t, fmt.Sprintf( | ||
"Expected and actual point to the same object: %p %#[1]v", | ||
expected), msgAndArgs...) | ||
"Expected and actual point to the same object: %p %s", | ||
expected, truncatingFormat("%#v", expected)), msgAndArgs...) | ||
} | ||
return true | ||
} | ||
|
@@ -604,25 +605,26 @@ func samePointers(first, second interface{}) (same bool, ok bool) { | |
// to a type conversion in the Go grammar. | ||
func formatUnequalValues(expected, actual interface{}) (e string, a string) { | ||
if reflect.TypeOf(expected) != reflect.TypeOf(actual) { | ||
return fmt.Sprintf("%T(%s)", expected, truncatingFormat(expected)), | ||
fmt.Sprintf("%T(%s)", actual, truncatingFormat(actual)) | ||
return fmt.Sprintf("%T(%s)", expected, truncatingFormat("%#v", expected)), | ||
fmt.Sprintf("%T(%s)", actual, truncatingFormat("%#v", actual)) | ||
} | ||
switch expected.(type) { | ||
case time.Duration: | ||
return fmt.Sprintf("%v", expected), fmt.Sprintf("%v", actual) | ||
} | ||
return truncatingFormat(expected), truncatingFormat(actual) | ||
return truncatingFormat("%#v", expected), truncatingFormat("%#v", actual) | ||
} | ||
|
||
// truncatingFormat formats the data and truncates it if it's too long. | ||
// | ||
// This helps keep formatted error messages lines from exceeding the | ||
// bufio.MaxScanTokenSize max line length that the go testing framework imposes. | ||
func truncatingFormat(data interface{}) string { | ||
value := fmt.Sprintf("%#v", data) | ||
max := bufio.MaxScanTokenSize - 100 // Give us some space the type info too if needed. | ||
if len(value) > max { | ||
value = value[0:max] + "<... truncated>" | ||
func truncatingFormat(format string, data interface{}) string { | ||
value := fmt.Sprintf(format, data) | ||
// Give us space for two truncated objects and the surrounding sentence. | ||
maxMessageSize := bufio.MaxScanTokenSize/2 - 100 | ||
if len(value) > maxMessageSize { | ||
value = value[0:maxMessageSize] + "<... truncated>" | ||
} | ||
return value | ||
} | ||
|
@@ -743,7 +745,7 @@ func Nil(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { | |
if h, ok := t.(tHelper); ok { | ||
h.Helper() | ||
} | ||
return Fail(t, fmt.Sprintf("Expected nil, but got: %#v", object), msgAndArgs...) | ||
return Fail(t, fmt.Sprintf("Expected nil, but got: %s", truncatingFormat("%#v", object)), msgAndArgs...) | ||
} | ||
|
||
// isEmpty gets whether the specified object is considered empty or not. | ||
|
@@ -793,7 +795,7 @@ func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { | |
if h, ok := t.(tHelper); ok { | ||
h.Helper() | ||
} | ||
Fail(t, fmt.Sprintf("Should be empty, but was %v", object), msgAndArgs...) | ||
Fail(t, fmt.Sprintf("Should be empty, but was %s", truncatingFormat("%v", object)), msgAndArgs...) | ||
} | ||
|
||
return pass | ||
|
@@ -836,11 +838,11 @@ func Len(t TestingT, object interface{}, length int, msgAndArgs ...interface{}) | |
} | ||
l, ok := getLen(object) | ||
if !ok { | ||
return Fail(t, fmt.Sprintf("\"%v\" could not be applied builtin len()", object), msgAndArgs...) | ||
return Fail(t, fmt.Sprintf("%q could not be applied builtin len()", truncatingFormat("%v", object)), msgAndArgs...) | ||
ccoVeille marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
if l != length { | ||
return Fail(t, fmt.Sprintf("\"%v\" should have %d item(s), but has %d", object, length, l), msgAndArgs...) | ||
return Fail(t, fmt.Sprintf("%q should have %d item(s), but has %d", truncatingFormat("%v", object), length, l), msgAndArgs...) | ||
ccoVeille marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
return true | ||
} | ||
|
@@ -889,7 +891,7 @@ func NotEqual(t TestingT, expected, actual interface{}, msgAndArgs ...interface{ | |
} | ||
|
||
if ObjectsAreEqual(expected, actual) { | ||
return Fail(t, fmt.Sprintf("Should not be: %#v\n", actual), msgAndArgs...) | ||
return Fail(t, fmt.Sprintf("Should not be: %s\n", truncatingFormat("%#v", actual)), msgAndArgs...) | ||
} | ||
|
||
return true | ||
|
@@ -904,7 +906,7 @@ func NotEqualValues(t TestingT, expected, actual interface{}, msgAndArgs ...inte | |
} | ||
|
||
if ObjectsAreEqualValues(expected, actual) { | ||
return Fail(t, fmt.Sprintf("Should not be: %#v\n", actual), msgAndArgs...) | ||
return Fail(t, fmt.Sprintf("Should not be: %s\n", truncatingFormat("%#v", actual)), msgAndArgs...) | ||
} | ||
|
||
return true | ||
|
@@ -964,10 +966,10 @@ func Contains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) bo | |
|
||
ok, found := containsElement(s, contains) | ||
if !ok { | ||
return Fail(t, fmt.Sprintf("%#v could not be applied builtin len()", s), msgAndArgs...) | ||
return Fail(t, fmt.Sprintf("%s could not be applied builtin len()", truncatingFormat("%#v", s)), msgAndArgs...) | ||
} | ||
if !found { | ||
return Fail(t, fmt.Sprintf("%#v does not contain %#v", s, contains), msgAndArgs...) | ||
return Fail(t, fmt.Sprintf("%s does not contain %#v", truncatingFormat("%#v", s), contains), msgAndArgs...) | ||
} | ||
|
||
return true | ||
|
@@ -986,10 +988,10 @@ func NotContains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) | |
|
||
ok, found := containsElement(s, contains) | ||
if !ok { | ||
return Fail(t, fmt.Sprintf("%#v could not be applied builtin len()", s), msgAndArgs...) | ||
return Fail(t, fmt.Sprintf("%s could not be applied builtin len()", truncatingFormat("%#v", s)), msgAndArgs...) | ||
} | ||
if found { | ||
return Fail(t, fmt.Sprintf("%#v should not contain %#v", s, contains), msgAndArgs...) | ||
return Fail(t, fmt.Sprintf("%s should not contain %#v", truncatingFormat("%#v", s), contains), msgAndArgs...) | ||
} | ||
|
||
return true | ||
|
@@ -1031,10 +1033,10 @@ func Subset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok | |
av := actualMap.MapIndex(k) | ||
|
||
if !av.IsValid() { | ||
return Fail(t, fmt.Sprintf("%#v does not contain %#v", list, subset), msgAndArgs...) | ||
return Fail(t, fmt.Sprintf("%s does not contain %s", truncatingFormat("%#v", list), truncatingFormat("%#v", subset)), msgAndArgs...) | ||
} | ||
if !ObjectsAreEqual(ev.Interface(), av.Interface()) { | ||
return Fail(t, fmt.Sprintf("%#v does not contain %#v", list, subset), msgAndArgs...) | ||
return Fail(t, fmt.Sprintf("%s does not contain %s", truncatingFormat("%#v", list), truncatingFormat("%#v", subset)), msgAndArgs...) | ||
Comment on lines
+1036
to
+1039
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm a bit worried about these 2 We have 2 list that could be huge that would be truncated here, but displayed on the same line. Maybe it works because of the max/2-200, but I feel like the line might difficult to read because of the lack of \n that we have for almost all others failures There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree about the hard to read part, but that would be a different issue. Again the only aim here is to show strings which previously got swallowed. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I understand your point. But mine wasn't about readability. I just wanted to make sure that comparing two slices that very long won't lead to a line that cannot be displayed. I feel like that tge point of the max/2-200 but I wanted to make sure it would work. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, that doesn't sit 100% with me either. When we follow this up with #1235 and reduce the max length much further I will be very confident. |
||
} | ||
} | ||
|
||
|
@@ -1056,7 +1058,7 @@ func Subset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok | |
return Fail(t, fmt.Sprintf("%#v could not be applied builtin len()", list), msgAndArgs...) | ||
} | ||
if !found { | ||
return Fail(t, fmt.Sprintf("%#v does not contain %#v", list, element), msgAndArgs...) | ||
return Fail(t, fmt.Sprintf("%s does not contain %#v", truncatingFormat("%#v", list), element), msgAndArgs...) | ||
ccoVeille marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
} | ||
|
||
|
@@ -1106,7 +1108,7 @@ func NotSubset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) | |
} | ||
} | ||
|
||
return Fail(t, fmt.Sprintf("%q is a subset of %q", subset, list), msgAndArgs...) | ||
return Fail(t, fmt.Sprintf("%s is a subset of %s", truncatingFormat("%q", subset), truncatingFormat("%q", list)), msgAndArgs...) | ||
} | ||
|
||
subsetList := reflect.ValueOf(subset) | ||
|
@@ -1128,7 +1130,7 @@ func NotSubset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) | |
} | ||
} | ||
|
||
return Fail(t, fmt.Sprintf("%q is a subset of %q", subset, list), msgAndArgs...) | ||
return Fail(t, fmt.Sprintf("%s is a subset of %s", truncatingFormat("%q", subset), truncatingFormat("%q", list)), msgAndArgs...) | ||
} | ||
|
||
// ElementsMatch asserts that the specified listA(array, slice...) is equal to specified | ||
|
@@ -1641,7 +1643,7 @@ func NoError(t TestingT, err error, msgAndArgs ...interface{}) bool { | |
if h, ok := t.(tHelper); ok { | ||
h.Helper() | ||
} | ||
return Fail(t, fmt.Sprintf("Received unexpected error:\n%+v", err), msgAndArgs...) | ||
return Fail(t, fmt.Sprintf("Received unexpected error:\n%s", truncatingFormat("%+v", err)), msgAndArgs...) | ||
} | ||
|
||
return true | ||
|
@@ -1680,7 +1682,7 @@ func EqualError(t TestingT, theError error, errString string, msgAndArgs ...inte | |
if expected != actual { | ||
return Fail(t, fmt.Sprintf("Error message not equal:\n"+ | ||
"expected: %q\n"+ | ||
"actual : %q", expected, actual), msgAndArgs...) | ||
"actual : %s", expected, truncatingFormat("%q", actual)), msgAndArgs...) | ||
} | ||
return true | ||
} | ||
|
@@ -1700,7 +1702,7 @@ func ErrorContains(t TestingT, theError error, contains string, msgAndArgs ...in | |
|
||
actual := theError.Error() | ||
if !strings.Contains(actual, contains) { | ||
return Fail(t, fmt.Sprintf("Error %#v does not contain %#v", actual, contains), msgAndArgs...) | ||
return Fail(t, fmt.Sprintf("Error %s does not contain %#v", truncatingFormat("%#v", actual), contains), msgAndArgs...) | ||
ccoVeille marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
return true | ||
|
@@ -1766,7 +1768,7 @@ func Zero(t TestingT, i interface{}, msgAndArgs ...interface{}) bool { | |
h.Helper() | ||
} | ||
if i != nil && !reflect.DeepEqual(i, reflect.Zero(reflect.TypeOf(i)).Interface()) { | ||
return Fail(t, fmt.Sprintf("Should be zero, but was %v", i), msgAndArgs...) | ||
return Fail(t, fmt.Sprintf("Should be zero, but was %s", truncatingFormat("%v", i)), msgAndArgs...) | ||
} | ||
return true | ||
} | ||
|
@@ -2206,8 +2208,8 @@ func ErrorIs(t TestingT, err, target error, msgAndArgs ...interface{}) bool { | |
chain := buildErrorChainString(err, false) | ||
|
||
return Fail(t, fmt.Sprintf("Target error should be in err chain:\n"+ | ||
"expected: %q\n"+ | ||
"in chain: %s", expectedText, chain, | ||
"expected: %s\n"+ | ||
"in chain: %s", truncatingFormat("%q", expectedText), truncatingFormat("%s", chain), | ||
), msgAndArgs...) | ||
} | ||
|
||
|
@@ -2229,8 +2231,8 @@ func NotErrorIs(t TestingT, err, target error, msgAndArgs ...interface{}) bool { | |
chain := buildErrorChainString(err, false) | ||
|
||
return Fail(t, fmt.Sprintf("Target error should not be in err chain:\n"+ | ||
"found: %q\n"+ | ||
"in chain: %s", expectedText, chain, | ||
"found: %s\n"+ | ||
"in chain: %s", truncatingFormat("%q", expectedText), truncatingFormat("%s", chain), | ||
), msgAndArgs...) | ||
} | ||
|
||
|
@@ -2254,7 +2256,7 @@ func ErrorAs(t TestingT, err error, target interface{}, msgAndArgs ...interface{ | |
|
||
return Fail(t, fmt.Sprintf("Should be in error chain:\n"+ | ||
"expected: %s\n"+ | ||
"in chain: %s", expectedType, chain, | ||
"in chain: %s", expectedType, truncatingFormat("%s", chain), | ||
), msgAndArgs...) | ||
} | ||
|
||
|
@@ -2272,7 +2274,7 @@ func NotErrorAs(t TestingT, err error, target interface{}, msgAndArgs ...interfa | |
|
||
return Fail(t, fmt.Sprintf("Target error should not be in err chain:\n"+ | ||
"found: %s\n"+ | ||
"in chain: %s", reflect.TypeOf(target).Elem().String(), chain, | ||
"in chain: %s", reflect.TypeOf(target).Elem().String(), truncatingFormat("%s", chain), | ||
), msgAndArgs...) | ||
} | ||
|
||
|
Uh oh!
There was an error while loading. Please reload this page.