-
Notifications
You must be signed in to change notification settings - Fork 125
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
deadlock on unexpected call with mocked argument implementing fmt.Stringer
#116
Comments
nbgraham
added a commit
to nbgraham/mock
that referenced
this issue
Jan 25, 2024
JacobOaks
pushed a commit
that referenced
this issue
Feb 6, 2024
Fixes #116 # Problem If your mock an interface that matches the fmt.Stringer interface: ```go type Stringer interface { String() string } ``` Then your unit tests can deadlock if you provide that mock as an expected argument to a call that is not matched (i.e. fails the test). Because, when printing the error message for the call that was not matched, it calls `String()` on all arguments that support it, including the mock. But each call to a mock is protected with a mutex, and the previous call (that was not matched) has not yet exited. # Solution The solution has two parts 1. During mock code generation (an existing part of this library) add a unique method on mocks (ISGOMOCK) 1. During test execution, whenever we are stringifying something that might be a mock, check if it is a mock (ISGOMOCK) that implements the String() method. If it is, just use the type name as the string value, instead of calling String() (which would cause the deadlock).
|
JacobOaks
added a commit
to JacobOaks/mock
that referenced
this issue
Sep 18, 2024
A deadlock related to controller calling Stringer on mocks themselves was revealed in uber-go#116. uber-go#114 solved this deadlock by adding a generated `ISGOMOCK()` method to all generated mocks, and then checking for it before calling `.String()` on arguments. This reveals an exported method on each generated mock that: * Bloats the generated code * Can be taken dependency on in strange ways via Hyrum's Law * Technically opens up another route for naming collision. This PR attempts to clean up this type of check by instead generating an unexported field in generated mock structs instead, and checks for it using reflect. This hides this implementation detail of gomock/mockgen better, and produces less generated bloat. Ref: uber-go#116
JacobOaks
added a commit
to JacobOaks/mock
that referenced
this issue
Sep 18, 2024
A deadlock related to controller calling Stringer on mocks themselves was revealed in uber-go#116. uber-go#114 solved this deadlock by adding a generated `ISGOMOCK()` method to all generated mocks, and then checking for it before calling `.String()` on arguments. This reveals an exported method on each generated mock that: * Bloats the generated code * Can be taken dependency on in strange ways via Hyrum's Law * Technically opens up another route for naming collision. This PR attempts to clean up this type of check by instead generating an unexported field in generated mock structs instead, and checks for it using reflect. This hides this implementation detail of gomock/mockgen better, and produces less generated bloat. Ref: uber-go#116
JacobOaks
added a commit
to JacobOaks/mock
that referenced
this issue
Sep 18, 2024
A deadlock related to controller calling Stringer on mocks themselves was revealed in uber-go#116. uber-go#114 solved this deadlock by adding a generated `ISGOMOCK()` method to all generated mocks, and then checking for it before calling `.String()` on arguments. This reveals an exported method on each generated mock that: * Bloats the generated code * Can be taken dependency on in strange ways via Hyrum's Law * Technically opens up another route for naming collision. This PR attempts to clean up this type of check by instead generating an unexported field in generated mock structs instead, and checks for it using reflect. This hides this implementation detail of gomock/mockgen better, and produces less generated bloat. This PR then regenerated all generated code for tests/example via `go generate`. Ref: uber-go#116
JacobOaks
added a commit
to JacobOaks/mock
that referenced
this issue
Sep 18, 2024
A deadlock related to controller calling Stringer on mocks themselves was revealed in uber-go#116. uber-go#144 solved this deadlock by adding a generated `ISGOMOCK()` method to all generated mocks, and then checking for it before calling `.String()` on arguments. This reveals an exported method on each generated mock that: * Bloats the generated code * Can be taken dependency on in strange ways via Hyrum's Law * Technically opens up another route for naming collision. This PR attempts to clean up this type of check by instead generating an unexported field in generated mock structs instead, and checks for it using reflect. This hides this implementation detail of gomock/mockgen better, and produces less generated bloat. This PR then regenerated all generated code for tests/example via `go generate`. Note that, importantly, the regression test added in uber-go#144 still passes with this PR. Ref: uber-go#193
JacobOaks
added a commit
to JacobOaks/mock
that referenced
this issue
Sep 18, 2024
A deadlock related to controller calling Stringer on mocks themselves was revealed in uber-go#116. uber-go#144 solved this deadlock by adding a generated `ISGOMOCK()` method to all generated mocks, and then checking for it before calling `.String()` on arguments. This reveals an exported method on each generated mock that: * Bloats the generated code * Can be taken dependency on in strange ways via Hyrum's Law * Technically opens up another route for naming collision. This PR attempts to clean up this type of check by instead generating an unexported field in generated mock structs instead, and checks for it using reflect. This hides this implementation detail of gomock/mockgen better, and produces less generated bloat. Note that, importantly, the regression test added in uber-go#144 still passes with this PR. Ref: uber-go#193
JacobOaks
added a commit
that referenced
this issue
Sep 18, 2024
A deadlock related to controller calling Stringer on mocks themselves was revealed in #116. #144 solved this deadlock by adding a generated `ISGOMOCK()` method to all generated mocks, and then checking for it before calling `.String()` on arguments. This reveals an exported method on each generated mock that: * Bloats the generated code * Can be taken dependency on in strange ways via Hyrum's Law * Technically opens up another route for naming collision. This PR attempts to clean up this type of check by instead generating an unexported field in generated mock structs instead, and checks for it using reflect. This hides this implementation detail of gomock/mockgen better, and produces less generated bloat. Note that, importantly, the regression test added in #144 still passes with this PR. Ref: #193
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Actual behavior Deadlock on unexpected call to function where one or more arguments are a generated mock that implements
fmt.Stringer
.Expected behavior No deadlock on an unexpected call
To Reproduce Steps to reproduce the behavior
mockgen -package=deadlock -source=interfaces.go -destination=mocks.go
interfaces.go
deadlock_test.go
Additional Information
source
v0.3.0
go version go1.21.3 linux/amd64
Triage Notes for the Maintainers
ctrl.T.Fatalf
in controller.go:209 is holdingctrl.mu
Fatalf
will call theString()
method on the mock (also unexpectedly)backtrace
The text was updated successfully, but these errors were encountered: