-
Notifications
You must be signed in to change notification settings - Fork 1.2k
panic test hooks if not enabled #8726
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
Merged
Merged
Changes from 3 commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,63 +3,45 @@ | |
| package testhooks | ||
|
|
||
| import ( | ||
| "context" | ||
| "sync" | ||
|
|
||
| "go.uber.org/fx" | ||
| ) | ||
|
|
||
| var Module = fx.Options( | ||
| fx.Provide(NewTestHooksImpl), | ||
| fx.Provide(NewTestHooks), | ||
| ) | ||
|
|
||
| type ( | ||
| // TestHooks holds a registry of active test hooks. It should be obtained through fx and | ||
| // used with Get and Set. | ||
| // | ||
| // TestHooks are an inherently unclean way of writing tests. They require mixing test-only | ||
| // concerns into production code. In general you should prefer other ways of writing tests | ||
| // wherever possible, and only use TestHooks sparingly, as a last resort. | ||
| TestHooks interface { | ||
| // private accessors; access must go through package-level Get/Set | ||
| get(Key) (any, bool) | ||
| set(Key, any) | ||
| del(Key) | ||
| } | ||
| type testHooksData struct { | ||
| m sync.Map | ||
| } | ||
|
|
||
| // testHooksImpl is an implementation of TestHooks. | ||
| testHooksImpl struct { | ||
| m sync.Map | ||
| } | ||
| // TestHooks holds a registry of active test hooks. It should be obtained through fx and | ||
| // used with Get and Set. | ||
| // | ||
| // TestHooks are an inherently unclean way of writing tests. They require mixing test-only | ||
| // concerns into production code. In general you should prefer other ways of writing tests | ||
| // wherever possible, and only use TestHooks sparingly, as a last resort. | ||
| type TestHooks struct { | ||
| data *testHooksData | ||
|
||
| } | ||
|
|
||
| contextKey struct{} | ||
| ) | ||
| func NewTestHooks() TestHooks { | ||
| return TestHooks{data: &testHooksData{}} | ||
| } | ||
|
|
||
| // Get gets the value of a test hook from the registry. | ||
| // | ||
| // TestHooks should be used sparingly, see comment on TestHooks. | ||
| func Get[T any](th TestHooks, key Key) (T, bool) { | ||
| var zero T | ||
| if th == nil { | ||
| if th.data == nil { | ||
| return zero, false | ||
| } | ||
| if val, ok := th.get(key); ok { | ||
| if val, ok := th.data.m.Load(key); ok { | ||
| // this is only used in test so we want to panic on type mismatch: | ||
| return val.(T), ok // nolint:revive | ||
| } | ||
| return zero, false | ||
| } | ||
|
|
||
| // GetCtx gets the value of a test hook from the registry embedded in the | ||
| // context chain. | ||
| // | ||
| // TestHooks should be used sparingly, see comment on TestHooks. | ||
| func GetCtx[T any](ctx context.Context, key Key) (T, bool) { | ||
| hooks := ctx.Value(contextKey{}) | ||
| if hooks, ok := hooks.(TestHooks); ok { | ||
| return Get[T](hooks, key) | ||
| return val.(T), ok //nolint:revive | ||
| } | ||
| var zero T | ||
| return zero, false | ||
| } | ||
|
|
||
|
|
@@ -72,57 +54,9 @@ func Call(th TestHooks, key Key) { | |
| } | ||
| } | ||
|
|
||
| // CallCtx calls a func() hook if present and a TestHooks implementation | ||
| // exists in the context chain. | ||
| // | ||
| // TestHooks should be used sparingly, see comment on TestHooks. | ||
| func CallCtx(ctx context.Context, key Key) { | ||
| hooks := ctx.Value(contextKey{}) | ||
| if hooks, ok := hooks.(TestHooks); ok { | ||
| Call(hooks, key) | ||
| } | ||
| } | ||
|
|
||
| // Set sets a test hook to a value and returns a cleanup function to unset it. | ||
| // Calls to Set and the cleanup function should form a stack. | ||
| func Set[T any](th TestHooks, key Key, val T) func() { | ||
| th.set(key, val) | ||
| return func() { th.del(key) } | ||
| } | ||
|
|
||
| // SetCtx sets a test hook to a value on the provided context and returns a | ||
| // cleanup function to unset it. Calls to SetCtx and the cleanup function | ||
| // should form a stack. | ||
| func SetCtx[T any](ctx context.Context, key Key, val T) func() { | ||
| hooks := ctx.Value(contextKey{}) | ||
| if hooks, ok := hooks.(TestHooks); ok { | ||
| return Set(hooks, key, val) | ||
| } | ||
| return func() {} | ||
| } | ||
|
|
||
| // NewTestHooksImpl returns a new instance of a test hook registry. This is provided and used | ||
| // in the main "resource" module as a default, but in functional tests, it's overridden by an | ||
| // explicitly constructed instance. | ||
| func NewTestHooksImpl() TestHooks { | ||
| return &testHooksImpl{} | ||
| } | ||
|
|
||
| // NewInjectedTestHooksImpl returns a new instance of a test hook registry and a context with the | ||
| // registry injected. | ||
| func NewInjectedTestHooksImpl(parent context.Context) (context.Context, TestHooks) { | ||
| hooks := NewTestHooksImpl() | ||
| return context.WithValue(parent, contextKey{}, hooks), hooks | ||
| } | ||
|
|
||
| func (th *testHooksImpl) get(key Key) (any, bool) { | ||
| return th.m.Load(key) | ||
| } | ||
|
|
||
| func (th *testHooksImpl) set(key Key, val any) { | ||
| th.m.Store(key, val) | ||
| } | ||
|
|
||
| func (th *testHooksImpl) del(key Key) { | ||
| th.m.Delete(key) | ||
| th.data.m.Store(key, val) | ||
| return func() { th.data.m.Delete(key) } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what was wrong with the interface/concrete switch? an interface takes 16 bytes, a concrete struct{} takes 0. yeah, it may not be noticeable but why change it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was playing around with some options; the size wasn't top of mind for me here. I'll revert that, no problem.