Skip to content
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

assert: handle TokenTooLong error scenario #1559

Open
wants to merge 9 commits into
base: master
Choose a base branch
from

Conversation

arjunmahishi
Copy link
Collaborator

Summary

As pointed out in #1525, when the assertion message is too long, it gets completely truncated in the final output. This is because bufio.Scanner.Scan() has a default MaxScanTokenSize set to 65536 characters (64 * 1024). The Scan() function returns false whenever the line being scanned exceeds that max limit. This leads to the final assertion message being truncated.

Changes

This commit fixes that by manually setting the internal scan buffer size to len(message) + 1 to make sure that the above scenario never occurs.

Related issues

Fixes #1525

… messages

As pointed out in stretchr#1525, when the assertion message is too long, it gets
completely truncated in the final output. This is because,
`bufio.Scanner.Scan()` has a default `MaxScanTokenSize` set to `65536`
characters (64 * 1024). The `Scan()` function return false whenever the
line being scanned exceeds that max limit. This leads to the final
assertion message being truncated.

This commit fixes that by manually setting the internal scan buffer size
to `len(message) + 1` to make sure that above scenario never occurs.

Fixes stretchr#1525
@arjunmahishi arjunmahishi changed the title assert: handle TokenTooLong error scenario while formatting assertion messages assert: handle TokenTooLong error scenario Mar 3, 2024
assert/assertions.go Outdated Show resolved Hide resolved
assert/assertions_test.go Outdated Show resolved Hide resolved
@dolmen dolmen added enhancement pkg-assert Change related to package testify/assert pkg-require Change related to package testify/require bug and removed enhancement pkg-require Change related to package testify/require labels Mar 4, 2024
@arjunmahishi arjunmahishi requested a review from dolmen March 4, 2024 18:04
assert/assertions.go Outdated Show resolved Hide resolved
assert/assertions.go Outdated Show resolved Hide resolved
assert/assertions_priv_test.go Outdated Show resolved Hide resolved
Instead of using an arbitrary value like 20000, we can just use the
value defined by bufio package (MaxScanTokenSize).
@arjunmahishi arjunmahishi requested a review from dolmen March 6, 2024 04:18
@arjunmahishi
Copy link
Collaborator Author

@dolmen @brackendawson Can you please take one last look at this?
I think it's ready to be merged

assert/assertions_priv_test.go Outdated Show resolved Hide resolved
assert/assertions.go Outdated Show resolved Hide resolved
@arjunmahishi arjunmahishi requested a review from dolmen March 7, 2024 14:40
@arjunmahishi
Copy link
Collaborator Author

@dolmen Are we good to merge?

@dolmen
Copy link
Collaborator

dolmen commented Mar 10, 2024

I'm not yet convinced that the tests provide enough coverage. I would like to see more clearly a check related to bufio boundaries.

I feel that we are testing with strings much longer than the boundary, and not just below and at the boundary.
I have to dig into the code more carefully.

},
{
name: "single line - just under the bufio default limit",
msg: strings.Repeat("hello ", bufio.MaxScanTokenSize-10),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The boundary is not "hello" repeated bufio.MaxScanTokenSize times, but a string which has a line whose length is bufio.MaxScanTokenSize bytes.

We probably need a utility function to build such an input string. At least strings.Repeat alone doesn't help.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ohh! that's right.

Changed the approach a little bit. The new commit generates the input as you suggested. And instead of matching against an exact output, it asserts the pattern of the output. Like the leading white spaces etc. Because for generating the expected output, we will end up rewriting the actual function logic in the test.

assert/assertions.go Outdated Show resolved Hide resolved
Also, fix the test cases for this function. This commit generates the
input based on parameters like bytes per line and number of lines. The
assertion is made against the pattern of the output rather than the
exact output.
// than the length of the message to avoid exceeding the default
// MaxScanTokenSize while scanning lines. This can happen when there is a
// single very long line. Refer to issue #1525
msgScanner.Buffer(nil, len(message)+1)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this safe? As we know from the assert.Len case this can contain a very large formatted slice. Having more than doubled the memory consumed by the large slice by creating a string containing the formatted version of it. Do we want to double the formatted version again? Plus all the intermediate buffers that were allocated on the heap by msgScanner before the next GC cycle?

We could at least pre-allocate the buffer for msgScanner rather than passing in nil. But I'd prefer that we either split or truncate lines that are unreasonably long, or we could just truncate the entire message to less than bufio.MaxScanTokenSize?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could at least pre-allocate the buffer for msgScanner rather than passing in nil.

Agreed. I've made this change.

But I'd prefer that we either split or truncate lines that are unreasonably long, or we could just truncate the entire message to less than bufio.MaxScanTokenSize?

If we decide to truncate, we could probably truncate based on the dimensions of the terminal? This makes more sense from a UX point of view.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug pkg-assert Change related to package testify/assert
Projects
None yet
Development

Successfully merging this pull request may close these issues.

assert/require.Len doesn't print anything if the slice is too long
4 participants