From ef2b7fa5332ad49f61df8bd83679dcc80de809b0 Mon Sep 17 00:00:00 2001 From: Asman Malikov Date: Thu, 16 Jul 2026 16:53:22 +0200 Subject: [PATCH] assert: fix failure message hidden when a diff line exceeds 64KiB (fixes #746) indentMessageLines used bufio.Scanner, which errors out on lines longer than bufio.MaxScanTokenSize (64KiB) and replaced the whole failure message with "cannot display message: bufio.Scanner: token too long". This happened e.g. on assert.Equal of long single-line strings or of structs with a long string field, because diff() output is not truncated. Split the message manually instead, preserving bufio.ScanLines semantics (trailing final newline ignored, \r\n treated as a line ending). --- assert/assertions.go | 21 +++++++++++++-------- assert/assertions_test.go | 23 +++++++++++++++++++++++ 2 files changed, 36 insertions(+), 8 deletions(-) diff --git a/assert/assertions.go b/assert/assertions.go index 1419e4776..ec32ac127 100644 --- a/assert/assertions.go +++ b/assert/assertions.go @@ -324,15 +324,20 @@ func messageFromMsgAndArgs(msgAndArgs ...interface{}) string { func indentMessageLines(message string, longestLabelLen int) string { outBuf := new(bytes.Buffer) - 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") + // Splitting manually instead of using bufio.Scanner because the scanner + // errors out on lines longer than bufio.MaxScanTokenSize (64KiB), hiding + // the whole message. See issue #746. + lines := strings.Split(message, "\n") + // Like bufio.ScanLines, ignore the trailing empty line after a final "\n". + if len(lines) > 1 && lines[len(lines)-1] == "" { + lines = lines[:len(lines)-1] + } + for i, line := range lines { + if i != 0 { + outBuf.WriteString("\n\t" + strings.Repeat(" ", longestLabelLen+1) + "\t") } - fmt.Fprint(outBuf, scanner.Text()) - } - if err := scanner.Err(); err != nil { - return fmt.Sprintf("cannot display message: %s", err) + // Like bufio.ScanLines, treat "\r\n" as a line ending as well. + outBuf.WriteString(strings.TrimSuffix(line, "\r")) } return outBuf.String() diff --git a/assert/assertions_test.go b/assert/assertions_test.go index 11642e096..7cf7fe6f8 100644 --- a/assert/assertions_test.go +++ b/assert/assertions_test.go @@ -628,6 +628,29 @@ func TestEqual(t *testing.T) { } } +// Regression test for the issue #746: a diff line longer than +// bufio.MaxScanTokenSize replaced the whole failure message with +// "cannot display message: bufio.Scanner: token too long". +func TestEqualLongDiffLines(t *testing.T) { + t.Parallel() + + mockT := new(mockTestingT) + + longLine := strings.Repeat("a", bufio.MaxScanTokenSize) + Equal(mockT, longLine+"expected", longLine+"actual") + + errorString := mockT.errorString() + if strings.Contains(errorString, "cannot display message") { + t.Errorf("Equal failure message should be displayed, got: %.100q...", errorString) + } + if !strings.Contains(errorString, "--- Expected") { + t.Errorf("Equal failure message should contain the diff, got: %.100q...", errorString) + } + if !strings.Contains(errorString, longLine+"expected") { + t.Errorf("Equal failure message should contain the long expected line, got: %.100q...", errorString) + } +} + func ptr(i int) *int { return &i }