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

add stacktrace to chain and formatted data #396

Merged
merged 4 commits into from
Jun 2, 2023

Conversation

diPhantxm
Copy link
Contributor

@diPhantxm diPhantxm commented Apr 9, 2023

#160

  • add EnableStacktrace
  • add Stacktrace on fail in chain
  • add Stacktrace to FormatData

@diPhantxm
Copy link
Contributor Author

Hey @gavv. I decided to add Stacktrace to chain and fill it when fail() called. chain_test started failing because of comparing assertionFailure in tests. Should I remake tests to make it not compare Stacktrace or was it a bad idea to add to chain?

@gavv gavv added the ready for review Pull request can be reviewed label Apr 9, 2023
@gavv
Copy link
Owner

gavv commented Apr 10, 2023

Hey @gavv. I decided to add Stacktrace to chain and fill it when fail() called. chain_test started failing because of comparing assertionFailure in tests. Should I remake tests to make it not compare Stacktrace or was it a bad idea to add to chain?

This is a good idea indeed, this way trace would be much more convenient.

@gavv gavv added work in progress Pull request is still in progress and changing and removed ready for review Pull request can be reviewed labels Apr 10, 2023
@coveralls
Copy link
Collaborator

coveralls commented Apr 10, 2023

Coverage Status

Coverage: 95.65% (-0.004%) from 95.655% when pulling 71795fe on diPhantxm:feature/enable-stacktrace-flag into 2d6c5d7 on gavv:master.

@diPhantxm
Copy link
Contributor Author

I think it's done. Please check defaultFailureTemplate for formatting stacktrace.

Maybe there is a point to think about shortening stacktrace if it's too long.

@gavv gavv added ready for review Pull request can be reviewed and removed work in progress Pull request is still in progress and changing labels Apr 11, 2023
Copy link
Owner

@gavv gavv left a comment

Choose a reason for hiding this comment

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

Thanks for PR! Here are some comments.

assertion.go Outdated Show resolved Hide resolved
opChain.leave()

assert.NotNil(t, handler.failure)
assert.NotEmpty(t, handler.failure.Stacktrace)
Copy link
Owner

Choose a reason for hiding this comment

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

Can we do some simple sanity check here?

E.g. that Stacktrace contains "TestChain_Stacktrace" and "chain.fail".

formatter.go Outdated
Comment on lines 90 to 91
// Enables printing of stacktrace on failure
EnableStacktrace bool
Copy link
Owner

Choose a reason for hiding this comment

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

nit: let's move it upper, right after other flags (..., DisableRequests, DisableResponses)

formatter.go Outdated
@@ -183,7 +186,8 @@ type FormatData struct {
AssertType string
AssertSeverity string

Errors []string
Errors []string
Stacktrace []string
Copy link
Owner

Choose a reason for hiding this comment

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

nit: let's move it after Request and Response, and add HaveStacktrace, for consistency with other fields in FormatData. HaveXxx is not strictly necessary, but is descriptive when used in template.

formatter.go Outdated
Comment on lines 1009 to 1011
{{- range $n, $call := .Stacktrace }}
{{ $call | indent }}
{{- end -}}
Copy link
Owner

Choose a reason for hiding this comment

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

Let's guard it with HaveStacktrace check, for consistency with other stuff. We should ensure that if HaveStacktrace, no additional empty lines are inserted.

Copy link
Owner

Choose a reason for hiding this comment

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

Also let's move stacktrace between "test name" and "assertion" and add "trace" header. It will look like this:

        <error message>
        
        test name: TestBlah

        trace:
          <stacktrace here>
        
        assertion:
          Request("GET").Expect().JSON().Object()
        
        actual value:
          <blah>
        
        reference value:
         <blah>

This way, error message and test name, which are most important, go first; trace and assertion are grouped together, which makes sense because both of them are kind of traces; assertion and actual/expected/reference are also grouped which is also logical because all them describe assertion made.

formatter.go Outdated
Comment on lines 554 to 560
func (f *DefaultFormatter) fillStacktrace(
data *FormatData, ctx *AssertionContext, failure *AssertionFailure,
) {
if f.EnableStacktrace {
data.Stacktrace = strings.Split(failure.Stacktrace, "\n\t")
}
}
Copy link
Owner

Choose a reason for hiding this comment

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

Let's make fillStacktrace guarantee that data.Stacktrace is always a non-nil slice, like we do for Errors in fillErrors.

TBH I don't remember why, but for some reason it was important or useful for Errors slice, in the context of templates :)

@gavv gavv added needs revision Pull request should be revised by its author and removed ready for review Pull request can be reviewed labels Apr 26, 2023
@gavv
Copy link
Owner

gavv commented Apr 26, 2023

I've played a bit with this patch and I think it may be convenient to resemble behavior of testify to produce a more compact trace. It is implemented here: https://github.com/stretchr/testify/blob/c5fc9d6b6b21ea89be8480c0dc35e2977ab988f6/assert/assertions.go#L147

If we'll use assert.CallerInfo() instead of debug.Stack(), the stack will look like this:

        <error message>
        
        test name: TestBlah

        trace:
          chain.go:442
          array.go:719
          report_test.go:380

        assertion:
          Request("GET").Expect().JSON().Array().IsEmpty()
        
        actual value:
          <blah>
        
        reference value:
         <blah>

which is very compact, fits the style of httpexpect output, and looks usual to testify users.

However, it loses some information compared to original trace, and some users may prefer to see the full trace. So I'd make it configurable - replace EnableStacktrace boolean with enum of three values: disabled, full, and compact.

For compact mode, if feasible, I'd even reimplement assert.CallerInfo() and use the following format instead:

module/file:line struct.method()

so instead of:

array.go:719

it would be:

httpexpect/v2/array.go:719 Array.IsEmpty()

IMHO this is the best compromise between providing enough information and keeping it compact.


What do you think? Do you find these ideas useful? Would you like to implement some of them in this PR, or should I create a separate issue?

@diPhantxm
Copy link
Contributor Author

I've played a bit with this patch and I think it may be convenient to resemble behavior of testify to produce a more compact trace. It is implemented here: https://github.com/stretchr/testify/blob/c5fc9d6b6b21ea89be8480c0dc35e2977ab988f6/assert/assertions.go#L147

If we'll use assert.CallerInfo() instead of debug.Stack(), the stack will look like this:

        <error message>
        
        test name: TestBlah

        trace:
          chain.go:442
          array.go:719
          report_test.go:380

        assertion:
          Request("GET").Expect().JSON().Array().IsEmpty()
        
        actual value:
          <blah>
        
        reference value:
         <blah>

which is very compact, fits the style of httpexpect output, and looks usual to testify users.

However, it loses some information compared to original trace, and some users may prefer to see the full trace. So I'd make it configurable - replace EnableStacktrace boolean with enum of three values: disabled, full, and compact.

For compact mode, if feasible, I'd even reimplement assert.CallerInfo() and use the following format instead:

module/file:line struct.method()

so instead of:

array.go:719

it would be:

httpexpect/v2/array.go:719 Array.IsEmpty()

IMHO this is the best compromise between providing enough information and keeping it compact.

What do you think? Do you find these ideas useful? Would you like to implement some of them in this PR, or should I create a separate issue?

TBH I don't see much difference between compact and full modes. file:line format is not so useful, because you may have multiple files with the same name but in different packages, and I don't think we should make people worry about switching modes. I agree debug.Stack() provides too much info and it doesn't look nice, so I would stay only with full format you provided upper httpexpect/v2/array.go:719 Array.IsEmpty()
I can implement this feature in this pr.
Let me know what we're doing here

@gavv
Copy link
Owner

gavv commented Apr 26, 2023

file:line format is not so useful, because you may have multiple files with the same name but in different packages

Agree

I can implement this feature in this pr.
Let me know what we're doing here

Awesome, thanks!

I don't think we should make people worry about switching modes.

I feel that supporting stanard format makes sense just because it's common and complete. Someone may be just used to it, someone may want absolute paths, someone may want to use tools like panicparse or maybe their editor understands this trace format, etc. Also having unprocessed trace may be useful to debug httpexpect itself.

We could add it later. Could you use enum instead of boolean? This will allow us to add formats in future, doesn't matter if this pr will implement 1 or 2.

@gavv
Copy link
Owner

gavv commented Apr 26, 2023

Another little thought. Since we'll collect caller info and then format it, we can store caller info instead of formatted string in AssertionFailure, and do formatting in DefaultFormatter.

First, it's just logical, and second, it will give us additional flexibility for free: user will be able to implement their own formatting when using custom formatter.

@diPhantxm
Copy link
Contributor Author

I got your idea. I'll do it and ping you when it's ready for review

@diPhantxm diPhantxm force-pushed the feature/enable-stacktrace-flag branch from 1e7da05 to 71795fe Compare April 27, 2023 20:49
@diPhantxm
Copy link
Contributor Author

@gavv I think it's ready for review.
I made a struct CallerInfo that stores function name, file and line, and then slice of these objects is formatted in formatter.
I made such format at [function]([file]:[line]). Maybe that was a bad decision, I found such format on google. Maybe I should have kept default Golang format.

@gavv
Copy link
Owner

gavv commented Apr 28, 2023

Thanks, will look in upcoming days! BTW I don't remember if I mentioned - welcome to discord chat if you like that format.

@gavv gavv added ready for review Pull request can be reviewed and removed needs revision Pull request should be revised by its author labels May 3, 2023
@gavv gavv merged commit 7c797ad into gavv:master Jun 2, 2023
@gavv
Copy link
Owner

gavv commented Jun 2, 2023

Thanks for update!

Follow-up commit: ca5a5dd

renames, two stacktrace modes, changes in stacktrace format, and improvements in tests

@gavv gavv removed the ready for review Pull request can be reviewed label Jun 2, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants