Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 18 additions & 4 deletions assert/assertions.go
Original file line number Diff line number Diff line change
Expand Up @@ -614,14 +614,28 @@ func formatUnequalValues(expected, actual interface{}) (e string, a string) {
return truncatingFormat("%#v", expected), truncatingFormat("%#v", actual)
}

// maxMessageSize is the maximum length, in bytes, of a single formatted
// value that truncatingFormat will print before appending "<... truncated>".
//
// This used to be derived from bufio.MaxScanTokenSize, sized just small
// enough that two truncated values plus their surrounding sentence couldn't
// exceed the line-length limit go test's output scanner imposes; that
// avoided losing failure output entirely (#1525), but a limit of ~32KB per
// value still let a single assertion swamp the console with output no one
// can read (#1801). 4000 keeps failure output readable, matches the default
// MaxLength used by Gomega's format package (github.com/onsi/gomega/format)
// for the same purpose, and remains comfortably below bufio.MaxScanTokenSize
// so the original line-length guarantee still holds. Anyone who needs the
// untruncated value can print it themselves, e.g. via t.Logf.
const maxMessageSize = 4000

// 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.
// This helps keep formatted error messages readable and ensures they don't
// exceed the bufio.MaxScanTokenSize max line length that the go testing
// framework imposes.
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>"
}
Expand Down
22 changes: 20 additions & 2 deletions assert/assertions_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package assert

import (
"bufio"
"bytes"
"encoding/json"
"errors"
Expand Down Expand Up @@ -3653,7 +3652,7 @@ func Test_validateEqualArgs(t *testing.T) {
func Test_truncatingFormat(t *testing.T) {
t.Parallel()

original := strings.Repeat("a", bufio.MaxScanTokenSize/2-102)
original := strings.Repeat("a", maxMessageSize-2)
result := truncatingFormat("%#v", original)
Equal(t, fmt.Sprintf("%#v", original), result, "string should not be truncated")

Expand Down Expand Up @@ -3981,6 +3980,25 @@ func TestLenWithSliceTooLongToPrint(t *testing.T) {
Contains(t, mockT.errorString(), `<... truncated>" should have 1 item(s), but has 1000000`)
}

// TestLenWithSliceTooLongToPrintIsReadable is a regression test for
// https://github.com/stretchr/testify/issues/1801. Truncating the printed
// value at all (TestLenWithSliceTooLongToPrint above) is not enough on its
// own: the pre-#1801 limit still let a single assertion dump tens of
// kilobytes into the console, which is just as unreadable as no output at
// all. The failure message must be truncated to a size a human can actually
// read in a terminal.
func TestLenWithSliceTooLongToPrintIsReadable(t *testing.T) {
t.Parallel()
mockT := new(mockTestingT)
longSlice := make([]int, 1_000_000)
Len(mockT, longSlice, 1)
errStr := mockT.errorString()
Contains(t, errStr, "<... truncated>")
if len(errStr) > 8000 {
t.Errorf("Len failure message on a very large slice is %d bytes, want a readable size (<=8000 bytes):\n%s", len(errStr), errStr)
}
}

func TestContainsWithSliceTooLongToPrint(t *testing.T) {
t.Parallel()
mockT := new(mockTestingT)
Expand Down