From bcafd916826a6ae9c3a81d1cfc439c7a0dcb87e8 Mon Sep 17 00:00:00 2001 From: sha1n Date: Fri, 16 Feb 2024 09:33:17 +0200 Subject: [PATCH] slightly improved abortion error ui handling --- cmd/main.go | 2 +- go.mod | 1 + go.sum | 1 + internal/cli/abor_on_error_listener.go | 40 +++++++++++++ internal/cli/abor_on_error_listener_test.go | 47 +++++++++++++++ internal/cli/abortion_listener.go | 38 ------------- internal/cli/abortion_listener_test.go | 63 --------------------- internal/cli/main_runner.go | 2 +- 8 files changed, 91 insertions(+), 103 deletions(-) create mode 100644 internal/cli/abor_on_error_listener.go create mode 100644 internal/cli/abor_on_error_listener_test.go delete mode 100644 internal/cli/abortion_listener.go delete mode 100644 internal/cli/abortion_listener_test.go diff --git a/cmd/main.go b/cmd/main.go index ff6dd29..2a8083c 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -66,7 +66,7 @@ func handlePanics(exitFn func(int)) { log.Fatal(err) exitFn(1) } - if err, ok := o.(cli.ExecutionAbortedError); ok { + if err, ok := o.(cli.AbortionError); ok { log.Error(err) exitFn(0) } diff --git a/go.mod b/go.mod index 2c7b45f..d8e8361 100644 --- a/go.mod +++ b/go.mod @@ -31,6 +31,7 @@ require ( github.com/mattn/go-isatty v0.0.20 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/spf13/pflag v1.0.5 // indirect + github.com/stretchr/objx v0.5.0 // indirect golang.org/x/crypto v0.17.0 // indirect golang.org/x/mod v0.12.0 // indirect golang.org/x/net v0.17.0 // indirect diff --git a/go.sum b/go.sum index d1f0c30..a731739 100644 --- a/go.sum +++ b/go.sum @@ -53,6 +53,7 @@ github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= diff --git a/internal/cli/abor_on_error_listener.go b/internal/cli/abor_on_error_listener.go new file mode 100644 index 0000000..cc33def --- /dev/null +++ b/internal/cli/abor_on_error_listener.go @@ -0,0 +1,40 @@ +package cli + +import ( + "fmt" + + "github.com/sha1n/bert/api" +) + +// AbortionError a marker type for fatal user errors. +// This type of errors is treated differently when user feedback is provided. +type AbortionError struct { + message string +} + +func (e AbortionError) Error() string { + return e.message +} + +// NewAbortionError creates a new abortion error with the specified message. +func NewAbortionError(id api.ID, err error) AbortionError { + return AbortionError{ + message: fmt.Sprintf("'%s' reported an error. %s", id, err), + } +} + +type abortOnErrorListener struct { + api.Listener +} + +// NewAbortOnErrorListener creates a new listener that logs abortion events. +func NewAbortOnErrorListener(delegate api.Listener) api.Listener { + return &abortOnErrorListener{Listener: delegate} +} + +// OnError logs an error message with the specified ID and error details +func (l abortOnErrorListener) OnError(id api.ID, err error) { + defer panic(NewAbortionError(id, err)) + l.Listener.OnError(id, err) + l.Listener.OnScenarioEnd(id) +} diff --git a/internal/cli/abor_on_error_listener_test.go b/internal/cli/abor_on_error_listener_test.go new file mode 100644 index 0000000..cbc855a --- /dev/null +++ b/internal/cli/abor_on_error_listener_test.go @@ -0,0 +1,47 @@ +package cli + +import ( + "errors" + "testing" + + "github.com/sha1n/bert/api" + "github.com/stretchr/testify/mock" +) + +func TestAbortOnErrorListener_OnError(t *testing.T) { + mockID := api.ID("mockID") + mockError := errors.New("mock error") + mockListener := new(MockListener) + mockListener.On("OnError", mockID, mockError).Once() + mockListener.On("OnScenarioEnd", mockID).Once() + abortOnErrorListener := NewAbortOnErrorListener(mockListener) + + defer func() { + actual := recover() + if actual == nil { + t.Errorf("The code did not panic") + } + + _, ok := actual.(AbortionError) + if !ok { + t.Errorf("Unexpected panic type: %T", actual) + } + + mockListener.AssertExpectations(t) + }() + + abortOnErrorListener.OnError(mockID, mockError) +} + +type MockListener struct { + api.Listener + mock.Mock +} + +func (m *MockListener) OnScenarioEnd(id string) { + m.Called(id) +} + +func (m *MockListener) OnError(id api.ID, err error) { + m.Called(id, err) +} diff --git a/internal/cli/abortion_listener.go b/internal/cli/abortion_listener.go deleted file mode 100644 index 4fde151..0000000 --- a/internal/cli/abortion_listener.go +++ /dev/null @@ -1,38 +0,0 @@ -package cli - -import ( - "fmt" - - "github.com/sha1n/bert/api" -) - -// ExecutionAbortedError a marker type for fatal user errors. -// This type of errors is treated differently when user feedback is provided. -type ExecutionAbortedError struct { - message string -} - -func (e ExecutionAbortedError) Error() string { - return e.message -} - -// NewExecutionAbortedError creates a new abortion error with the specified message. -func NewExecutionAbortedError(id api.ID, err error) ExecutionAbortedError { - return ExecutionAbortedError{ - message: fmt.Sprintf("'%s' reported an error. %s", id, err), - } -} - -type failFastListener struct { - api.Listener -} - -// NewFailFastListener creates a new listener that logs abortion events. -func NewFailFastListener(delegate api.Listener) api.Listener { - return &failFastListener{Listener: delegate} -} - -// OnError logs an error message with the specified ID and error details -func (l failFastListener) OnError(id api.ID, err error) { - panic(NewExecutionAbortedError(id, err)) -} diff --git a/internal/cli/abortion_listener_test.go b/internal/cli/abortion_listener_test.go deleted file mode 100644 index 0869231..0000000 --- a/internal/cli/abortion_listener_test.go +++ /dev/null @@ -1,63 +0,0 @@ -package cli - -import ( - "errors" - "testing" - - "github.com/sha1n/bert/api" -) - -func TestFailFastListener_OnError(t *testing.T) { - mockID := api.ID("mockID") - mockError := errors.New("mock error") - - failFastListener := NewFailFastListener(&mockListener{}) - - defer func() { - actual := recover() - if actual == nil { - t.Errorf("The code did not panic") - } - - _, ok := actual.(ExecutionAbortedError) - if !ok { - t.Errorf("Unexpected panic type: %T", actual) - } - }() - - failFastListener.OnError(mockID, mockError) -} - -type mockListener struct{} - -// OnBenchmarkEnd implements api.Listener. -func (*mockListener) OnBenchmarkEnd() { - panic("unimplemented") -} - -// OnBenchmarkStart implements api.Listener. -func (*mockListener) OnBenchmarkStart() { - panic("unimplemented") -} - -// OnMessage implements api.Listener. -func (*mockListener) OnMessage(id string, message string) { - panic("unimplemented") -} - -// OnMessagef implements api.Listener. -func (*mockListener) OnMessagef(id string, format string, args ...interface{}) { - panic("unimplemented") -} - -// OnScenarioEnd implements api.Listener. -func (*mockListener) OnScenarioEnd(id string) { - panic("unimplemented") -} - -// OnScenarioStart implements api.Listener. -func (*mockListener) OnScenarioStart(id string) { - panic("unimplemented") -} - -func (m *mockListener) OnError(id api.ID, err error) {} diff --git a/internal/cli/main_runner.go b/internal/cli/main_runner.go index b2196b7..9039a88 100644 --- a/internal/cli/main_runner.go +++ b/internal/cli/main_runner.go @@ -240,7 +240,7 @@ func resolveExecutionListener(cmd *cobra.Command, spec api.BenchmarkSpec, ctx ap } if spec.FailFast { - listener = NewFailFastListener(listener) + listener = NewAbortOnErrorListener(listener) } return listener