From bfa072b61e4d886e5a6d72e0eb14d38adc7ecc58 Mon Sep 17 00:00:00 2001 From: Harri Lainio My Date: Sun, 3 Nov 2024 20:21:51 +0200 Subject: [PATCH 01/11] optimize package description --- doc.go | 4 ++-- try/try.go | 9 +++++---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/doc.go b/doc.go index 7d7b007..9119fdd 100644 --- a/doc.go +++ b/doc.go @@ -1,6 +1,6 @@ /* -Package err2 provides three main functionality: - 1. err2 package includes helper functions for error handling & automatic error +Package err2 is error handling solution including three main functionality: + 1. err2 package offers helper functions for error handling & automatic error stack tracing 2. [github.com/lainio/err2/try] sub-package is for error checking 3. [github.com/lainio/err2/assert] sub-package is for design-by-contract and diff --git a/try/try.go b/try/try.go index 7ed1310..c8eaff1 100644 --- a/try/try.go +++ b/try/try.go @@ -1,8 +1,9 @@ /* -Package try is a package for [To], [To1], and [To2] functions that implement the error -checking. [To] functions check 'if err != nil' and if it throws the err to the -error handlers, which are implemented by the err2 package. More information -about err2 and try packager roles can be seen in the FileCopy example: +Package try is a package for error checking with the functions: [To], [To1], and +[To2]. These functions check the last given error argument isn't nil, i.e., 'if +err != nil' and if it is, it throws the err to the error handlers. More +information about err2 and try packages roles can be seen in the FileCopy +example: ... r := try.To1(os.Open(src)) From 879543f78d744a265b5d5e95f4b1168964dbcda7 Mon Sep 17 00:00:00 2001 From: Harri Lainio My Date: Sun, 3 Nov 2024 20:22:39 +0200 Subject: [PATCH 02/11] have sample of Try-prefix to use non-local error handling --- samples/main-play.go | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/samples/main-play.go b/samples/main-play.go index dd12042..9284034 100644 --- a/samples/main-play.go +++ b/samples/main-play.go @@ -64,10 +64,9 @@ func ClassicCopyFile(src, dst string) error { return nil } -// OrgCopyFile copies the source file to the given destination. If any error occurs it +// TryCopyFile copies the source file to the given destination. If any error occurs it // returns an error value describing the reason. -func OrgCopyFile(src, dst string) (err error) { - defer err2.Handle(&err) // automatic error message: see err2.Formatter +func TryCopyFile(src, dst string) { // You can out-comment above handler line(s) to see what happens. // You'll learn that call stacks are for every function level 'catch' @@ -79,17 +78,13 @@ func OrgCopyFile(src, dst string) (err error) { r := try.To1(os.Open(src)) defer r.Close() - w, err := os.Create(dst) - if err != nil { - return fmt.Errorf("mixing traditional error checking: %w", err) - } + w:= try.To1(os.Create(dst)) defer err2.Handle(&err, func(err error) error { try.Out(os.Remove(dst)).Logf("cleaning error") return err }) defer w.Close() try.To1(io.Copy(w, r)) - return nil } func CallRecur(d int) (ret int, err error) { @@ -161,13 +156,13 @@ func doMain() (err error) { // how err2 works. Especially interesting is automatic stack tracing. // // source file exists, but the destination is not in high probability - //try.To(OrgCopyFile("main.go", "/notfound/path/file.bak")) + //TryCopyFile("main.go", "/notfound/path/file.bak") // Both source and destination don't exist - //try.To(OrgCopyFile("/notfound/path/file.go", "/notfound/path/file.bak")) + //TryCopyFile("/notfound/path/file.go", "/notfound/path/file.bak") // to play with real args: - try.To(CopyFile(flag.Arg(0), flag.Arg(1))) + TryCopyFile(flag.Arg(0), flag.Arg(1)) if len(flag.Args()) > 0 { // Next fn demonstrates how error and panic traces work, comment out all @@ -177,7 +172,7 @@ func doMain() (err error) { fmt.Println("ret val:", ret) } else { // 2nd argument is empty to assert - try.To(OrgCopyFile("main.go", "")) + TryCopyFile("main.go", "") } fmt.Println("=== you cannot see this ===") From 33e54720161f55db1989419507f9cde6324fb5b2 Mon Sep 17 00:00:00 2001 From: Harri Lainio My Date: Tue, 5 Nov 2024 10:45:16 +0200 Subject: [PATCH 03/11] play-recursion flag added --- samples/main.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/samples/main.go b/samples/main.go index bb0ab8b..0ff4380 100644 --- a/samples/main.go +++ b/samples/main.go @@ -14,7 +14,8 @@ var ( "mode", "play", "runs the wanted playground: db, play, nil, assert,"+ - "\nassert-keep (= uses assert.Debug in GLS)", + "\nassert-keep (= uses assert.Debug in GLS),"+ + "\nplay-recursion (= runs recursion example)", ) isErr = flag.Bool("err", false, "tells if we want to have an error") ) @@ -40,7 +41,7 @@ func main() { doMain1() case "nil2": doMain2() - case "play": + case "play", "play-recursion": doPlayMain() case "assert": doAssertMainKeepGLSAsserter(false) From 9d7a288285683b1af8cd1ebb1a60c57210490265 Mon Sep 17 00:00:00 2001 From: Harri Lainio My Date: Thu, 7 Nov 2024 17:30:07 +0200 Subject: [PATCH 04/11] full error return tracing implementation (like in Zig) --- err2_test.go | 10 +++ internal/debug/debug.go | 19 ++++- internal/debug/debug_test.go | 152 ++++++++++++++++++++++++----------- internal/handler/handler.go | 30 +++++-- internal/tracer/tracer.go | 13 ++- samples/main-play.go | 53 ++++++++---- tracer.go | 40 +++++++-- 7 files changed, 237 insertions(+), 80 deletions(-) diff --git a/err2_test.go b/err2_test.go index 61c91fa..e3c1550 100644 --- a/err2_test.go +++ b/err2_test.go @@ -672,6 +672,16 @@ func TestSetErrorTracer(t *testing.T) { require.That(t, w == nil, "error tracer should be nil") } +func TestSetErrRetTracer(t *testing.T) { + t.Parallel() + w := err2.ErrRetTracer() + require.That(t, w == nil, "error return tracer should be nil") + var w1 io.Writer + err2.SetErrRetTracer(w1) + w = err2.ErrRetTracer() + require.That(t, w == nil, "error return tracer should be nil") +} + func ExampleCatch_withFmt() { // Set default logger to stdout for this example oldLogW := err2.LogTracer() diff --git a/internal/debug/debug.go b/internal/debug/debug.go index 8185dbe..f3197d5 100644 --- a/internal/debug/debug.go +++ b/internal/debug/debug.go @@ -26,6 +26,8 @@ type StackInfo struct { // these are used to filter out specific lines from output ExlRegexp []*regexp.Regexp + + PrintFirstOnly bool } var ( @@ -39,14 +41,19 @@ var ( // we want to check that this is not our package packageRegexp = regexp.MustCompile( - `^github\.com/lainio/err2[a-zA-Z0-9_/.\[\]]*\(`, + `^github\.com/lainio/err2[a-zA-Z0-9_/\.\[\]\@]*\(`, ) // testing package exluding regexps: testingPkgRegexp = regexp.MustCompile(`^testing\.`) testingFileRegexp = regexp.MustCompile(`^.*\/src\/testing\/testing\.go`) - exludeRegexps = []*regexp.Regexp{testingPkgRegexp, testingFileRegexp} + exludeRegexps = []*regexp.Regexp{testingPkgRegexp, testingFileRegexp} + exludeRegexpsAll = []*regexp.Regexp{ + testingPkgRegexp, + testingFileRegexp, + packageRegexp, + } ) func (si StackInfo) fullName() string { @@ -89,7 +96,11 @@ func (si StackInfo) canPrint(s string, anchorLine, i int) (ok bool) { // printed from call stack. anchorLine = 0 } - ok = i >= 2*si.Level+anchorLine + if si.PrintFirstOnly { + ok = i >= 2*si.Level+anchorLine && i < 2*si.Level+anchorLine+2 + } else { + ok = i >= 2*si.Level+anchorLine + } if si.ExlRegexp == nil { return ok @@ -271,7 +282,7 @@ func stackPrint(r io.Reader, w io.Writer, si StackInfo) { line := scanner.Text() // we can print a line if we didn't find anything, i.e. anchorLine is - // nilAnchor, which means that our start is not limited by then anchor + // nilAnchor, which means that our start is not limited by the anchor canPrint := anchorLine == nilAnchor // if it's not nilAnchor we need to check it more carefully if !canPrint { diff --git a/internal/debug/debug_test.go b/internal/debug/debug_test.go index a6b1748..c428198 100644 --- a/internal/debug/debug_test.go +++ b/internal/debug/debug_test.go @@ -20,23 +20,23 @@ func TestFullName(t *testing.T) { retval string } tests := []ttest{ - {"all empty", args{StackInfo{"", "", 0, nil, nil}}, ""}, + {"all empty", args{StackInfo{"", "", 0, nil, nil, false}}, ""}, { "namespaces", - args{StackInfo{"lainio/err2", "", 0, nil, nil}}, + args{StackInfo{"lainio/err2", "", 0, nil, nil, false}}, "lainio/err2", }, { "both", - args{StackInfo{"lainio/err2", "try", 0, nil, nil}}, + args{StackInfo{"lainio/err2", "try", 0, nil, nil, false}}, "lainio/err2.try", }, { "short both", - args{StackInfo{"err2", "Handle", 0, nil, nil}}, + args{StackInfo{"err2", "Handle", 0, nil, nil, false}}, "err2.Handle", }, - {"func", args{StackInfo{"", "try", 0, nil, nil}}, "try"}, + {"func", args{StackInfo{"", "try", 0, nil, nil, false}}, "try"}, } for _, ttv := range tests { tt := ttv @@ -61,34 +61,34 @@ func TestIsAnchor(t *testing.T) { tests := []ttest{ {"panic func and short regexp", args{ "github.com/lainio/err2.Return(0x14001c1ee20)", - StackInfo{"", "panic(", 0, PackageRegexp, nil}}, true}, + StackInfo{"", "panic(", 0, PackageRegexp, nil, false}}, true}, {"func hit and regexp on", args{ "github.com/lainioxx/err2_printStackIf({0x1545d2, 0x6}, 0x0, {0x12e3e0?, 0x188f50?})", - StackInfo{"", "printStackIf(", 0, noHitRegexp, nil}}, false}, + StackInfo{"", "printStackIf(", 0, noHitRegexp, nil, false}}, false}, {"short regexp no match", args{ "github.com/lainioxx/err2_printStackIf({0x1545d2, 0x6}, 0x0, {0x12e3e0?, 0x188f50?})", - StackInfo{"", "", 0, noHitRegexp, nil}}, false}, + StackInfo{"", "", 0, noHitRegexp, nil, false}}, false}, {"short regexp", args{ "github.com/lainio/err2/assert.That({0x1545d2, 0x6}, 0x0, {0x12e3e0?, 0x188f50?})", - StackInfo{"", "", 0, PackageRegexp, nil}}, true}, + StackInfo{"", "", 0, PackageRegexp, nil, false}}, true}, {"short", args{ "github.com/lainio/err2.printStackIf({0x1545d2, 0x6}, 0x0, {0x12e3e0?, 0x188f50?})", - StackInfo{"", "", 0, nil, nil}}, true}, + StackInfo{"", "", 0, nil, nil, false}}, true}, {"short-but-false", args{ "github.com/lainio/err2.printStackIf({0x1545d2, 0x6}, 0x0, {0x12e3e0?, 0x188f50?})", - StackInfo{"err2", "Handle", 0, nil, nil}}, false}, + StackInfo{"err2", "Handle", 0, nil, nil, false}}, false}, {"medium", args{ "github.com/lainio/err2.Returnw(0x40000b3e60, {0x0, 0x0}, {0x0, 0x0, 0x0})", - StackInfo{"err2", "Returnw", 0, nil, nil}}, true}, + StackInfo{"err2", "Returnw", 0, nil, nil, false}}, true}, {"medium-but-false", args{ "github.com/lainio/err2.Returnw(0x40000b3e60, {0x0, 0x0}, {0x0, 0x0, 0x0})", - StackInfo{"err2", "Return(", 0, nil, nil}}, false}, + StackInfo{"err2", "Return(", 0, nil, nil, false}}, false}, {"long", args{ "github.com/lainio/err2.Handle(0x40000b3ed8, 0x40000b3ef8)", - StackInfo{"err2", "Handle", 0, nil, nil}}, true}, + StackInfo{"err2", "Handle", 0, nil, nil, false}}, true}, {"package name only", args{ "github.com/lainio/err2/try.To1[...](...)", - StackInfo{"lainio/err2", "", 0, nil, nil}}, true}, + StackInfo{"lainio/err2", "", 0, nil, nil, false}}, true}, } for _, ttv := range tests { tt := ttv @@ -113,28 +113,28 @@ func TestIsFuncAnchor(t *testing.T) { tests := []ttest{ {"func hit and regexp on", args{ "github.com/lainioxx/err2_printStackIf({0x1545d2, 0x6}, 0x0, {0x12e3e0?, 0x188f50?})", - StackInfo{"", "printStackIf(", 0, noHitRegexp, nil}}, true}, + StackInfo{"", "printStackIf(", 0, noHitRegexp, nil, false}}, true}, {"short regexp", args{ "github.com/lainio/err2/assert.That({0x1545d2, 0x6}, 0x0, {0x12e3e0?, 0x188f50?})", - StackInfo{"", "", 0, PackageRegexp, nil}}, true}, + StackInfo{"", "", 0, PackageRegexp, nil, false}}, true}, {"short", args{ "github.com/lainio/err2.printStackIf({0x1545d2, 0x6}, 0x0, {0x12e3e0?, 0x188f50?})", - StackInfo{"", "", 0, nil, nil}}, true}, + StackInfo{"", "", 0, nil, nil, false}}, true}, {"short-but-false", args{ "github.com/lainio/err2.printStackIf({0x1545d2, 0x6}, 0x0, {0x12e3e0?, 0x188f50?})", - StackInfo{"err2", "Handle", 0, nil, nil}}, false}, + StackInfo{"err2", "Handle", 0, nil, nil, false}}, false}, {"medium", args{ "github.com/lainio/err2.Returnw(0x40000b3e60, {0x0, 0x0}, {0x0, 0x0, 0x0})", - StackInfo{"err2", "Returnw", 0, nil, nil}}, true}, + StackInfo{"err2", "Returnw", 0, nil, nil, false}}, true}, {"medium-but-false", args{ "github.com/lainio/err2.Returnw(0x40000b3e60, {0x0, 0x0}, {0x0, 0x0, 0x0})", - StackInfo{"err2", "Return(", 0, nil, nil}}, false}, + StackInfo{"err2", "Return(", 0, nil, nil, false}}, false}, {"long", args{ "github.com/lainio/err2.Handle(0x40000b3ed8, 0x40000b3ef8)", - StackInfo{"err2", "Handle", 0, nil, nil}}, true}, + StackInfo{"err2", "Handle", 0, nil, nil, false}}, true}, {"package name only", args{ "github.com/lainio/err2/try.To1[...](...)", - StackInfo{"lainio/err2", "", 0, nil, nil}}, true}, + StackInfo{"lainio/err2", "", 0, nil, nil, false}}, true}, } for _, ttv := range tests { tt := ttv @@ -285,50 +285,58 @@ func TestCalcAnchor(t *testing.T) { anchor int } tests := []ttest{ + { + "macOS from test using ALL regexp", + args{ + inputFromMac, + StackInfo{"", "StartPSM(", 1, nil, exludeRegexpsAll, false}, + }, + 16, + }, { "macOS from test using regexp", args{ inputFromMac, - StackInfo{"", "panic(", 1, PackageRegexp, nil}, + StackInfo{"", "panic(", 1, PackageRegexp, nil, false}, }, 12, }, - {"short", args{input, StackInfo{"", "panic(", 0, nil, nil}}, 6}, + {"short", args{input, StackInfo{"", "panic(", 0, nil, nil, false}}, 6}, { "short error stack", args{ inputByError, - StackInfo{"", "panic(", 0, PackageRegexp, nil}, + StackInfo{"", "panic(", 0, PackageRegexp, nil, false}, }, 4, }, { "short and nolimit", - args{input, StackInfo{"", "", 0, nil, nil}}, + args{input, StackInfo{"", "", 0, nil, nil, false}}, nilAnchor, }, { "short and only LVL is 2", - args{input, StackInfo{"", "", 2, nil, nil}}, + args{input, StackInfo{"", "", 2, nil, nil, false}}, 2, }, - {"medium", args{input1, StackInfo{"", "panic(", 0, nil, nil}}, 10}, + {"medium", args{input1, StackInfo{"", "panic(", 0, nil, nil, false}}, 10}, { "from test using panic", - args{inputFromTest, StackInfo{"", "panic(", 0, nil, nil}}, + args{inputFromTest, StackInfo{"", "panic(", 0, nil, nil, false}}, 8, }, { "from test", args{ inputFromTest, - StackInfo{"", "panic(", 0, PackageRegexp, nil}, + StackInfo{"", "panic(", 0, PackageRegexp, nil, false}, }, 14, }, { "macOS from test using panic", - args{inputFromMac, StackInfo{"", "panic(", 0, nil, nil}}, + args{inputFromMac, StackInfo{"", "panic(", 0, nil, nil, false}}, 12, }, } @@ -344,6 +352,48 @@ func TestCalcAnchor(t *testing.T) { } func TestStackPrint_limit(t *testing.T) { + t.Parallel() + type args struct { + input string + StackInfo + } + type ttest struct { + name string + args + output string + } + tests := []ttest{ + { + "find function with FRAME from test stack", + args{inputFromTest, + StackInfo{"", "", 8, nil, exludeRegexpsAll, true}}, + outputFromTestOnlyFunction, + }, + { + "find function with FRAME from mac stack", + args{inputFromMac, + StackInfo{"", "", 7, nil, exludeRegexpsAll, true}}, + outputFromMacOneFunction, + }, + } + for _, ttv := range tests { + tt := ttv + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + r := strings.NewReader(tt.input) + w := new(bytes.Buffer) + stackPrint(r, w, tt.StackInfo) + ins := strings.Split(tt.input, "\n") + outs := strings.Split(w.String(), "\n") + require.Thatf(t, len(ins) > len(outs), + "input length:%d should be greater:%d", len(ins), len(outs)) + b, a := tt.output, w.String() + require.Equal(t, a, b) + }) + } +} + +func TestStackPrint_OneFunction(t *testing.T) { t.Parallel() type args struct { input string @@ -357,47 +407,47 @@ func TestStackPrint_limit(t *testing.T) { tests := []ttest{ { "real test trace", - args{inputFromTest, StackInfo{"", "", 8, nil, exludeRegexps}}, + args{inputFromTest, StackInfo{"", "", 8, nil, exludeRegexps, false}}, outputFromTest, }, { "only level 4", - args{input1, StackInfo{"", "", 4, nil, nil}}, + args{input1, StackInfo{"", "", 4, nil, nil, false}}, output1, }, { "short", - args{input, StackInfo{"err2", "Returnw(", 0, nil, nil}}, + args{input, StackInfo{"err2", "Returnw(", 0, nil, nil, false}}, output, }, { "medium", - args{input1, StackInfo{"err2", "Returnw(", 0, nil, nil}}, + args{input1, StackInfo{"err2", "Returnw(", 0, nil, nil, false}}, output1, }, { "medium level 2", - args{input1, StackInfo{"err2", "Returnw(", 2, nil, nil}}, + args{input1, StackInfo{"err2", "Returnw(", 2, nil, nil, false}}, output12, }, { "medium level 0", - args{input1, StackInfo{"err2", "Returnw(", 0, nil, nil}}, + args{input1, StackInfo{"err2", "Returnw(", 0, nil, nil, false}}, output1, }, { "medium panic", - args{input1, StackInfo{"", "panic(", 0, nil, nil}}, + args{input1, StackInfo{"", "panic(", 0, nil, nil, false}}, output1panic, }, { "long", - args{input2, StackInfo{"err2", "Handle(", 0, nil, nil}}, + args{input2, StackInfo{"err2", "Handle(", 0, nil, nil, false}}, output2, }, { "long lvl 2", - args{input2, StackInfo{"err2", "Handle(", 3, nil, nil}}, + args{input2, StackInfo{"err2", "Handle(", 3, nil, nil, false}}, output23, }, } @@ -434,35 +484,35 @@ func TestFuncName(t *testing.T) { tests := []ttest{ { "basic", - args{input2, StackInfo{"", "Handle", 1, nil, nil}}, + args{input2, StackInfo{"", "Handle", 1, nil, nil, false}}, "err2.ReturnW", 214, 6, }, { "basic lvl 3", - args{input2, StackInfo{"", "Handle", 3, nil, nil}}, + args{input2, StackInfo{"", "Handle", 3, nil, nil, false}}, "err2.ReturnW", 214, 6, }, { "basic lvl 2", - args{input2, StackInfo{"lainio/err2", "Handle", 1, nil, nil}}, + args{input2, StackInfo{"lainio/err2", "Handle", 1, nil, nil, false}}, "err2.ReturnW", 214, 6, }, { "method", - args{inputFromTest, StackInfo{"", "Handle", 1, nil, nil}}, + args{inputFromTest, StackInfo{"", "Handle", 1, nil, nil, false}}, "ssi.(*DIDAgent).AssertWallet", 146, 8, }, { "pipeline", - args{inputPipelineStack, StackInfo{"", "Handle", -1, nil, nil}}, + args{inputPipelineStack, StackInfo{"", "Handle", -1, nil, nil, false}}, "CopyFile", 29, 9, @@ -517,6 +567,11 @@ created by github.com/findy-network/findy-agent/agent/prot.FindAndStartTask /Users/harrilainio/go/src/github.com/findy-network/findy-agent/agent/prot/processor.go:337 +0x21c ` + outputFromMacOneFunction = `goroutine 518 [running]: +github.com/findy-network/findy-agent/agent/cloud.(*Agent).PwPipe(0x1400024e2d0, {0x140017ad770, 0x24}) + /Users/harrilainio/go/src/github.com/findy-network/findy-agent/agent/cloud/agent.go:202 +0x324 +` + inputFromTest = `goroutine 31 [running]: testing.tRunner.func1.2({0xa8e0e0, 0x40001937d0}) /usr/local/go/src/testing/testing.go:1389 +0x1c8 @@ -557,6 +612,11 @@ github.com/findy-network/findy-agent/agent/sec_test.TestPipe_packPeer(0x4000106d /home/god/go/src/github.com/findy-network/findy-agent/agent/sec/pipe_test.go:355 +0x1b8 ` + outputFromTestOnlyFunction = `goroutine 31 [running]: +github.com/findy-network/findy-agent/agent/ssi.(*DIDAgent).AssertWallet(...) + /home/god/go/src/github.com/findy-network/findy-agent/agent/ssi/agent.go:146 +` + inputByError = `goroutine 1 [running]: panic({0x137b20, 0x400007ac60}) /usr/local/go/src/runtime/panic.go:838 +0x20c diff --git a/internal/handler/handler.go b/internal/handler/handler.go index 235dc23..3e173c3 100644 --- a/internal/handler/handler.go +++ b/internal/handler/handler.go @@ -94,11 +94,20 @@ func (i *Info) callNilHandler() { } func (i *Info) checkErrorTracer() { + errRet := false + if i.ErrorTracer == nil { + i.ErrorTracer = tracer.ErrRet.Tracer() + errRet = true + } if i.ErrorTracer == nil { i.ErrorTracer = tracer.Error.Tracer() + errRet = false } if i.ErrorTracer != nil { - si := stackPrologueError + si := stackPrologueErrRet + if !errRet { + si = stackPrologueError + } if i.Any == nil { i.Any = i.safeErr() } @@ -405,18 +414,29 @@ see 'err2/scripts/README.md' and run auto-migration scripts for your repo func printStack(w io.Writer, si debug.StackInfo, msg any) { fmt.Fprintf(w, "---\n%v\n---\n", msg) debug.FprintStack(w, si) + if si.PrintFirstOnly { + fmt.Fprintln(w, "") + } } var ( - // stackPrologueRuntime = newSI("", "panic(", 1) - stackPrologueError = newErrSI() - stackProloguePanic = newSI("", "panic(", 1) + stackPrologueError = newErrSIOld() + stackPrologueErrRet = newErrSI() + stackProloguePanic = newSI("", "panic(", 1) ) -func newErrSI() debug.StackInfo { +func newErrSIOld() debug.StackInfo { return debug.StackInfo{Regexp: debug.PackageRegexp, Level: 1} } +func newErrSI() debug.StackInfo { + return debug.StackInfo{ + Level: 1, + Regexp: debug.PackageRegexp, + PrintFirstOnly: true, + } +} + func newSI(pn, fn string, lvl int) debug.StackInfo { return debug.StackInfo{ PackageName: pn, diff --git a/internal/tracer/tracer.go b/internal/tracer/tracer.go index aa10296..53f8fbe 100644 --- a/internal/tracer/tracer.go +++ b/internal/tracer/tracer.go @@ -19,12 +19,14 @@ type writer struct { } var ( - Error value - Panic value - Log value + Error value + Panic value + Log value + ErrRet value ) func init() { + ErrRet.SetTracer(nil) Error.SetTracer(nil) // Because we stop panics as default, we need to output as default Panic.SetTracer(os.Stderr) @@ -39,6 +41,11 @@ func init() { "`stream` for error tracing: stderr, stdout", ) flag.Var(&Panic, "err2-panic-trace", "`stream` for panic tracing") + flag.Var( + &ErrRet, + "err2-ret-trace", + "`stream` for error return tracing: stderr, stdout", + ) } func (v *value) Tracer() io.Writer { diff --git a/samples/main-play.go b/samples/main-play.go index 9284034..f06dc81 100644 --- a/samples/main-play.go +++ b/samples/main-play.go @@ -78,7 +78,7 @@ func TryCopyFile(src, dst string) { r := try.To1(os.Open(src)) defer r.Close() - w:= try.To1(os.Create(dst)) + w := try.To1(os.Create(dst)) defer err2.Handle(&err, func(err error) error { try.Out(os.Remove(dst)).Logf("cleaning error") return err @@ -94,18 +94,37 @@ func CallRecur(d int) (ret int, err error) { } func doRecur(d int) (ret int, err error) { - d-- if d >= 0 { // Keep below to show how asserts work //assert.NotZero(d) // Comment out the above assert statement to simulate runtime-error ret = 10 / d - fmt.Println(ret) + fmt.Println("ret:", ret) //return doRecur(d) } return ret, fmt.Errorf("root error") } +type runMode int + +const ( + runModePlay runMode = iota + runModePlayRec +) + +func (rm runMode) String() string { + return []string{"play", "play-recursion"}[rm] +} + +var rMode runMode + +func setRunMode() { + playRec := runModePlayRec + if *mode == playRec.String() { + rMode = runModePlayRec + } +} + func doPlayMain() { // Keep here that you can play without changing imports assert.That(true) @@ -122,11 +141,7 @@ func doPlayMain() { // errors are caught without specific handlers. defer err2.Catch(err2.Stderr) - // If you don't want to use tracers or you just need a proper error handler - // here. - // defer err2.Catch(func(err error) { - // fmt.Println("ERROR:", err) - // }) + setRunMode() // by calling one of these you can test how automatic logging in above // catch works correctly: the last source of error check is shown in line @@ -161,18 +176,22 @@ func doMain() (err error) { // Both source and destination don't exist //TryCopyFile("/notfound/path/file.go", "/notfound/path/file.bak") - // to play with real args: - TryCopyFile(flag.Arg(0), flag.Arg(1)) - if len(flag.Args()) > 0 { - // Next fn demonstrates how error and panic traces work, comment out all - // above CopyFile calls to play with: - argument := try.To1(strconv.Atoi(flag.Arg(0))) - ret := try.To1(CallRecur(argument)) - fmt.Println("ret val:", ret) + if rMode == runModePlayRec { + // Next fn demonstrates how error and panic traces work, comment + // out all above CopyFile calls to play with: + argument := try.To1(strconv.Atoi(flag.Arg(0))) + ret := try.To1(CallRecur(argument)) + fmt.Println("ret val:", ret) + } else { + // to play with real args: + //TryCopyFile(flag.Arg(0), flag.Arg(1)) + try.To(CopyFile(flag.Arg(0), flag.Arg(1))) + } } else { // 2nd argument is empty to assert - TryCopyFile("main.go", "") + //TryCopyFile("main.go", "") + try.To(CopyFile("main.go", "")) } fmt.Println("=== you cannot see this ===") diff --git a/tracer.go b/tracer.go index 954768f..f5a96f3 100644 --- a/tracer.go +++ b/tracer.go @@ -8,10 +8,22 @@ import ( // ErrorTracer returns current [io.Writer] for automatic error stack tracing. // The default value is nil. +// +// See [SetErrRetTracer] and [SetErrorTracer] for more information of the two +// different error tracing systems. func ErrorTracer() io.Writer { return tracer.Error.Tracer() } +// ErrRetTracer returns current [io.Writer] for automatic error return stack +// tracing. The default value is nil. +// +// See [SetErrRetTracer] and [SetErrorTracer] for more information of the two +// different error tracing systems. +func ErrRetTracer() io.Writer { + return tracer.Error.Tracer() +} + // PanicTracer returns current [io.Writer] for automatic panic stack tracing. Note // that [runtime.Error] types which are transported by panics are controlled by // this. The default value is [os.Stderr]. @@ -36,18 +48,36 @@ func LogTracer() io.Writer { // Error trace is almost the same format as Go's standard call stack but it may // have multiple sections because every [Handle] and [Catch] prints it. If an // error happens in a deep call stack, the error trace includes various parts. -// The principle is similar to [Zig Error Return Traces], where you see how -// error bubbles up. However, our error trace is a combination of error return -// traces and stack traces because we get all the needed information at once. +// If you prefer similar to [Zig Error Return Traces], where you see how +// error bubbles up, you should use [SetErrRetTrace]. // // Remember that you can reset these with [flag] package support. See // documentation of err2 package's flag section. -// -// [Zig Error Return Traces]: https://ziglang.org/documentation/master/#Error-Return-Traces func SetErrorTracer(w io.Writer) { tracer.Error.SetTracer(w) } +// SetErrRetTracer sets a [io.Writer] for automatic error return stack tracing. +// The err2 default is nil. Note that any function that has deferred [Handle] or +// [Catch] is capable to print error return stack trace: +// +// func CopyFile(src, dst string) (err error) { +// defer err2.Handle(&err) // <- makes error trace printing decision +// +// Error return trace is almost the same format as Go's standard call stack but +// it may have multiple sections because every [Handle] and [Catch] prints it. +// If an error happens in a deep call stack, the error return trace includes +// various parts. The principle is similar to [Zig Error Return Traces], where +// you see how error bubbles up. +// +// Remember that you can reset these with [flag] package support. See +// documentation of err2 package's flag section. +// +// [Zig Error Return Traces]: https://ziglang.org/documentation/master/#Error-Return-Traces +func SetErrRetTracer(w io.Writer) { + tracer.ErrRet.SetTracer(w) +} + // SetPanicTracer sets a [io.Writer] for automatic panic stack tracing. The err2 // default is [os.Stderr]. Note that [runtime.Error] types which are transported by // panics are controlled by this. Note also that the current function is capable From e023bd001b6a8bc9705ad719a172f9a9f7cea7ba Mon Sep 17 00:00:00 2001 From: Harri Lainio Date: Sun, 10 Nov 2024 16:23:10 +0200 Subject: [PATCH 05/11] add formatter.DecamelAndRmTryPrefix to support TryCopy -> copy: annot. --- formatter/formatter.go | 20 +++++++++- internal/str/str.go | 4 ++ internal/str/str_test.go | 82 ++++++++++++++++++++++++++++++++++++++++ samples/main-play.go | 4 +- samples/main.go | 5 +++ 5 files changed, 111 insertions(+), 4 deletions(-) diff --git a/formatter/formatter.go b/formatter/formatter.go index 73ae476..db27913 100644 --- a/formatter/formatter.go +++ b/formatter/formatter.go @@ -24,8 +24,24 @@ type Formatter struct { DoFmt } -// Decamel is preimplemented and default formatter to produce human -// readable error strings from function names. +// DecamelAndRmTryPrefix is pre-implemented formatter to produce human readable +// error strings from function names. It's similar to [Decamel] but also removes +// try-prefixes from function names: +// +// func TryCopyFile(..) -> "copy file: file not exists" +// ^-------^ -> generated from 'func TryCopyFile' +// +// It's convenient helper for those who wants to write compact functions by +// following convention to always add 'Try' prefix to those functions that can +// throw errors thru panics. Fox example, if you're using helpers like +// [github.com/lainio/err2/assert.That] and [github.com/lainio/err2/try.To] but +// you don't want to handle errors in your current function, it's still good +// practice to use convention to mark that function to throw errors. However, we +// suggest that you don't do that in your packages public API functions. +var DecamelAndRmTryPrefix = &Formatter{DoFmt: str.DecamelRmTryPrefix} + +// Decamel is pre-implemented and default formatter to produce human readable +// error strings from function names. // // func CopyFile(..) -> "copy file: file not exists" // ^-------^ -> generated from 'func CopyFile' diff --git a/internal/str/str.go b/internal/str/str.go index c9d9d13..5259c81 100644 --- a/internal/str/str.go +++ b/internal/str/str.go @@ -25,6 +25,10 @@ func DecamelRegexp(str string) string { return str } +func DecamelRmTryPrefix(s string) string { + return strings.ReplaceAll(Decamel(s), "try ", "") +} + // Decamel return the given string as space delimeted. It's optimized to split // and decamel function names returned from Go call stacks. For more information // see its test cases. diff --git a/internal/str/str_test.go b/internal/str/str_test.go index 9bf8160..56da177 100644 --- a/internal/str/str_test.go +++ b/internal/str/str_test.go @@ -23,6 +23,12 @@ func BenchmarkDecamel(b *testing.B) { } } +func BenchmarkDecamelRmTryPrefix(b *testing.B) { + for n := 0; n < b.N; n++ { + _ = str.DecamelRmTryPrefix(camelStr) + } +} + func TestCamel(t *testing.T) { t.Parallel() type args struct { @@ -104,3 +110,79 @@ func TestDecamel(t *testing.T) { }) } } + +func TestDecamelRmTryPrefix(t *testing.T) { + t.Parallel() + type args struct { + s string + } + tests := []struct { + name string + args args + want string + }{ + {"simple", args{"CamelString"}, "camel string"}, + {"simple try", args{"TryCamelString"}, "camel string"}, + {"underscore", args{"CamelString_error"}, "camel string error"}, + {"underscore and try", args{"TryCamelString_error"}, "camel string error"}, + { + "our contant", + args{camelStr}, + "benchmark recursion with old error if check and defer", + }, + {"number", args{"CamelString2Testing"}, "camel string2 testing"}, + {"acronym", args{"ARMCamelString"}, "armcamel string"}, + {"acronym and try at END so it left", args{"ARMCamelStringTry"}, "armcamel string try"}, + {"acronym and try", args{"TryARMCamelString"}, "armcamel string"}, + {"acronym at end", args{"archIsARM"}, "arch is arm"}, + { + "simple method", + args{"(*DIDAgent).AssertWallet"}, + "didagent assert wallet", + }, + { + "package name and simple method", + args{"ssi.(*DIDAgent).CreateWallet"}, + "ssi: didagent create wallet", + }, + { + "package name and simple method and Function start try", + args{"ssi.(*DIDAgent).TryCreateWallet"}, + "ssi: didagent create wallet", + }, + { + "simple method and anonym", + args{"(*DIDAgent).AssertWallet.Func1"}, + "didagent assert wallet: func1", + }, + { + "complex method and anonym", + args{"(**DIDAgent).AssertWallet.Func1"}, + "didagent assert wallet: func1", + }, + { + "complex method and anonym AND try", + args{"(**DIDAgent).TryAssertWallet.Func1"}, + "didagent assert wallet: func1", + }, + { + "unnatural method and anonym", + args{"(**DIDAgent)...AssertWallet...Func1"}, + "didagent assert wallet: func1", + }, + { + "unnatural method and anonym AND try", + args{"(**DIDAgent)...TryAssertWallet...TryFunc1"}, + "didagent assert wallet: func1", + }, + {"from spf13 cobra", args{"bot.glob..func5"}, "bot: glob: func5"}, + } + for _, ttv := range tests { + tt := ttv + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + got := str.DecamelRmTryPrefix(tt.args.s) + require.Equal(t, got, tt.want) + }) + } +} diff --git a/samples/main-play.go b/samples/main-play.go index f06dc81..79a8e21 100644 --- a/samples/main-play.go +++ b/samples/main-play.go @@ -190,8 +190,8 @@ func doMain() (err error) { } } else { // 2nd argument is empty to assert - //TryCopyFile("main.go", "") - try.To(CopyFile("main.go", "")) + TryCopyFile("main.go", "") + //try.To(CopyFile("main.go", "")) } fmt.Println("=== you cannot see this ===") diff --git a/samples/main.go b/samples/main.go index 0ff4380..db79f4b 100644 --- a/samples/main.go +++ b/samples/main.go @@ -7,6 +7,7 @@ import ( "github.com/lainio/err2" "github.com/lainio/err2/assert" + "github.com/lainio/err2/formatter" ) var ( @@ -24,6 +25,10 @@ func init() { // highlight that this is before flag.Parse to allow it to work properly. err2.SetLogTracer(os.Stderr) // for import err2.SetLogTracer(nil) + + // select which one you want to play with + err2.SetFormatter(formatter.DecamelAndRmTryPrefix) + // err2.SetFormatter(formatter.Decamel) } func main() { From 19612775fca5b08a6576e33e734f344261ea2626 Mon Sep 17 00:00:00 2001 From: Harri Lainio Date: Sun, 10 Nov 2024 19:21:49 +0200 Subject: [PATCH 06/11] use log in sample --- samples/main-play.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/samples/main-play.go b/samples/main-play.go index 79a8e21..fabe80f 100644 --- a/samples/main-play.go +++ b/samples/main-play.go @@ -12,6 +12,7 @@ import ( "flag" "fmt" "io" + "log" "os" "strconv" @@ -99,7 +100,7 @@ func doRecur(d int) (ret int, err error) { //assert.NotZero(d) // Comment out the above assert statement to simulate runtime-error ret = 10 / d - fmt.Println("ret:", ret) + log.Println("ret:", ret) //return doRecur(d) } return ret, fmt.Errorf("root error") From e3fa9c2fd542a6d2237537ef7d5f01fe24ef30e0 Mon Sep 17 00:00:00 2001 From: Harri Lainio Date: Sun, 10 Nov 2024 19:22:02 +0200 Subject: [PATCH 07/11] refactor readability --- internal/debug/debug_test.go | 12 ++++++------ internal/handler/handler.go | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/internal/debug/debug_test.go b/internal/debug/debug_test.go index c428198..e0a70b4 100644 --- a/internal/debug/debug_test.go +++ b/internal/debug/debug_test.go @@ -380,15 +380,15 @@ func TestStackPrint_limit(t *testing.T) { tt := ttv t.Run(tt.name, func(t *testing.T) { t.Parallel() - r := strings.NewReader(tt.input) - w := new(bytes.Buffer) - stackPrint(r, w, tt.StackInfo) + readStack := strings.NewReader(tt.input) + writeStack := new(bytes.Buffer) + stackPrint(readStack, writeStack, tt.StackInfo) ins := strings.Split(tt.input, "\n") - outs := strings.Split(w.String(), "\n") + outs := strings.Split(writeStack.String(), "\n") require.Thatf(t, len(ins) > len(outs), "input length:%d should be greater:%d", len(ins), len(outs)) - b, a := tt.output, w.String() - require.Equal(t, a, b) + wantResult, gotResult := tt.output, writeStack.String() + require.Equal(t, gotResult, wantResult) }) } } diff --git a/internal/handler/handler.go b/internal/handler/handler.go index 3e173c3..e80300d 100644 --- a/internal/handler/handler.go +++ b/internal/handler/handler.go @@ -104,9 +104,9 @@ func (i *Info) checkErrorTracer() { errRet = false } if i.ErrorTracer != nil { - si := stackPrologueErrRet - if !errRet { - si = stackPrologueError + si := stackPrologueError + if errRet { + si = stackPrologueErrRet } if i.Any == nil { i.Any = i.safeErr() From dfe31579b8300690765397fcd8d8d6853b67dc77 Mon Sep 17 00:00:00 2001 From: Harri Lainio Date: Mon, 11 Nov 2024 16:04:32 +0200 Subject: [PATCH 08/11] fix SetDefault docs --- assert/assert.go | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/assert/assert.go b/assert/assert.go index b33c653..13b78f1 100644 --- a/assert/assert.go +++ b/assert/assert.go @@ -1021,24 +1021,17 @@ func current() (curAsserter asserter) { return curAsserter } -// SetDefault sets the current default [Asserter] for assert pkg. It also -// returns the previous [Asserter]. +// SetDefault sets the new default [Asserter] for the assert pkg instance you're +// currently using. It also returns the previous [Asserter]. The default +// asserter is [Production] that's best for most use cases and packages. // -// Note that you should use this in TestMain function, and use [flag] package to -// set it for the app. For the tests you can set it to panic about every -// assertion fault, or to throw an error, or/and print the call stack -// immediately when assert occurs. The err2 package helps you to catch and -// report all types of the asserts. +// Note that for most cases [PushAsserter] is most suitable. [PushAsserter] +// allows you to set [Asserter] per goroutine or function, i.e., until you pop +// the asserter out, and the assert package uses the current default [Asserter] +// set by [SetDefault]. // -// Note that if you are using tracers you might get two call stacks, so test -// what's best for your case. -// -// Tip. If our own packages (client packages for assert) have lots of parallel -// testing and race detection, please try to use same [Asserter] for all of them -// and set [Asserter] only one in TestMain, or in init. -// -// func TestMain(m *testing.M) { -// SetDefault(assert.TestFull) +// Note that if you are using tracers you might get overlapping call stacks, so +// test what's best for your case. func SetDefault(i Asserter) (old Asserter) { // pkg lvl lock to allow only one pkg client call this at one of the time // together with the indexing, i.e we don't need to switch asserter From 20bdd1471175f5d265b624640607b8f34010ed2f Mon Sep 17 00:00:00 2001 From: Harri Lainio Date: Mon, 11 Nov 2024 16:05:02 +0200 Subject: [PATCH 09/11] release 1.2.0 documentation --- CHANGELOG.md | 12 ++++++++++++ README.md | 41 ++++++++++++++++++++++++++--------------- 2 files changed, 38 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9d8507a..92e71e3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,18 @@ ### Version history +##### 1.1.0 +- `assert` package: + - bug fix: call stack traversal during unit testing in some situations + - **all generics-based functions are inline expansed** + - *performance* is now *same as if-statements for all functions* + - new assert functions: `MNil`, `CNil`, `Less`, `Greater`, etc. + - all assert messages follow Go idiom: `got, want` + - `Asserter` can be set per goroutine: `PushAsserter` +- `try` package: + - new check functions: `T`, `T1`, `T2`, `T3`, for quick refactoring from `To` functions to annotate an error locally + - **all functions are inline expansed**: if-statement equal performance + ##### 1.0.0 - **Finally! We are very happy, and thanks to all who have helped!** - Lots of documentation updates and cleanups for version 1.0.0 diff --git a/README.md b/README.md index 0565455..b5278cb 100644 --- a/README.md +++ b/README.md @@ -103,7 +103,8 @@ tolerant*. And, of course, it helps to make your code error-safe. handler. 3. It helps us use design-by-contract type preconditions. 4. It offers automatic stack tracing for every error, runtime error, or panic. - If you are familiar with Zig, the `err2` error traces are same as Zig's. + If you are familiar with Zig, the `err2` error return traces are same as + Zig's. You can use all of them or just the other. However, if you use `try` for error checks, you must remember to use Go's `recover()` by yourself, or your error @@ -152,7 +153,11 @@ own purposes. #### Error Stack Tracing -The err2 offers optional stack tracing. It's *automatic* and *optimized*. +The err2 offers optional stack tracing in two different formats: +1. Optimized call stacks (`-err2-trace`) +1. Error return traces similar to Zig (`-err2-ret-trace`) + +Both are *automatic* and fully *optimized*.
The example of the optimized call stack: @@ -177,12 +182,14 @@ main.main()
-Just set the `err2.SetErrorTracer` or `err2.SetPanicTracer` to the stream you -want traces to be written: +Just set the `err2.SetErrorTracer`, `err2.SetErrRetTracer` or +`err2.SetPanicTracer` to the stream you want traces to be written: ```go err2.SetErrorTracer(os.Stderr) // write error stack trace to stderr // or, for example: +err2.SetErrRetTracer(os.Stderr) // write error return trace (like Zig) +// or, for example: err2.SetPanicTracer(log.Writer()) // stack panic trace to std logger ``` @@ -592,6 +599,15 @@ been much easier.** There is an excellent [blog post](https://jesseduffield.com/ about the issues you are facing with Go's error handling without the help of the err2 package. +- If you don't want to bubble up error from every function, we have learned that +`Try` prefix convention is pretty cool way to solve limitations of Go +programming language help to make your code more skimmable. If your internal +functions normally would be something like `func CopyFile(s, t string) (err +error)`, you can replace them with `func TryCopyFile(s, t string)`, where `Try` +prefix remind you that the function throws errors. You can decide at what level +of the call stack you will catch them with `err2.Handle` or `err2.Catch`, +depending your case and API. + ## Support And Contributions @@ -607,14 +623,9 @@ Please see the full version history from [CHANGELOG](./CHANGELOG.md). ### Latest Release -##### 1.1.0 -- `assert` package: - - bug fix: call stack traversal during unit testing in some situations - - **all generics-based functions are inline expansed** - - *performance* is now *same as if-statements for all functions* - - new assert functions: `MNil`, `CNil`, `Less`, `Greater`, etc. - - all assert messages follow Go idiom: `got, want` - - `Asserter` can be set per goroutine: `PushAsserter` -- `try` package: - - new check functions: `T`, `T1`, `T2`, `T3`, for quick refactoring from `To` functions to annotate an error locally - - **all functions are inline expansed**: if-statement equal performance +##### 1.2.0 +- Now `-err2-ret-trace` and `err2.SetErrRetTracer` gives us *error return traces* + which are even more readable than `-err2-trace`, `err2.SetErrorTracer` with + long error return traces +- A new automatic error formatter/generator added for `TryCopyFile` convention +- Better documentation From 674e7d7f116a670f08604336a20ac354943eb8d2 Mon Sep 17 00:00:00 2001 From: Harri Lainio Date: Mon, 11 Nov 2024 17:19:51 +0200 Subject: [PATCH 10/11] fix assert sample code, add mention of samle/ dir --- README.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index b5278cb..628b141 100644 --- a/README.md +++ b/README.md @@ -389,12 +389,12 @@ func TestWebOfTrustInfo(t *testing.T) { // And if there's violations during the test run they are reported as // test failures for this TestWebOfTrustInfo -test. - assert.Equal(0, wot.CommonInvider) - assert.Equal(1, wot.Hops) + assert.Equal(wot.CommonInvider, 0) + assert.Equal(wot.Hops, 1) wot = NewWebOfTrust(bob.Node, carol.Node) - assert.Equal(-1, wot.CommonInvider) - assert.Equal(-1, wot.Hops) + assert.Equal(wot.CommonInvider, hop.NotConnected) + assert.Equal(wot.Hops, hop.NotConnected) ... ``` @@ -628,4 +628,5 @@ Please see the full version history from [CHANGELOG](./CHANGELOG.md). which are even more readable than `-err2-trace`, `err2.SetErrorTracer` with long error return traces - A new automatic error formatter/generator added for `TryCopyFile` convention -- Better documentation +- New features for `sample/` to demonstrate latest features +- Extended documentation From 7b66b1ba4e359251064a16be4bfb10af126e60e6 Mon Sep 17 00:00:00 2001 From: Harri Lainio Date: Mon, 11 Nov 2024 17:39:12 +0200 Subject: [PATCH 11/11] require -> except --- err2_test.go | 92 +++++++++---------- internal/debug/debug_test.go | 36 ++++---- .../{require/test.go => except/except.go} | 2 +- internal/handler/handler_test.go | 26 +++--- internal/handler/handlers_test.go | 12 +-- internal/str/str_test.go | 8 +- internal/x/x_test.go | 22 ++--- try/copy_test.go | 14 +-- try/out_test.go | 10 +- 9 files changed, 111 insertions(+), 111 deletions(-) rename internal/{require/test.go => except/except.go} (98%) diff --git a/err2_test.go b/err2_test.go index e3c1550..09dc69a 100644 --- a/err2_test.go +++ b/err2_test.go @@ -9,7 +9,7 @@ import ( "testing" "github.com/lainio/err2" - "github.com/lainio/err2/internal/require" + "github.com/lainio/err2/internal/except" "github.com/lainio/err2/try" ) @@ -70,7 +70,7 @@ func TestHandle_noerrHandler(t *testing.T) { var err error var handlerCalled bool defer func() { - require.That(t, handlerCalled) + except.That(t, handlerCalled) }() // This is the handler we are testing! defer err2.Handle(&err, func(noerr bool) { @@ -88,12 +88,12 @@ func TestHandle_noerrHandler(t *testing.T) { var err error var handlerCalled bool defer func() { - require.That(t, handlerCalled) + except.That(t, handlerCalled) }() defer err2.Handle(&err, func(err error) error { // this should not be called, so lets try to fuckup things... handlerCalled = false - require.That(t, false) + except.That(t, false) return err }) @@ -111,20 +111,20 @@ func TestHandle_noerrHandler(t *testing.T) { var err error var handlerCalled bool defer func() { - require.ThatNot(t, handlerCalled) + except.ThatNot(t, handlerCalled) }() // This is the handler we are testing! defer err2.Handle(&err, func(err error) error { - require.ThatNot(t, handlerCalled) + except.ThatNot(t, handlerCalled) handlerCalled = false - require.That(t, true, "error should be handled") + except.That(t, true, "error should be handled") return err }) // This is the handler we are testing! AND it's not called in error. defer err2.Handle(&err, func(bool) { - require.That(t, false, "when error this is not called") + except.That(t, false, "when error this is not called") }) try.To1(throw()) @@ -141,14 +141,14 @@ func TestHandle_noerrHandler(t *testing.T) { callCount int ) defer func() { - require.ThatNot(t, handlerCalled) - require.Equal(t, callCount, 2) - require.Equal(t, err.Error(), finalAnnotatedErr.Error()) + except.ThatNot(t, handlerCalled) + except.Equal(t, callCount, 2) + except.Equal(t, err.Error(), finalAnnotatedErr.Error()) }() // This is the handler we are testing! AND it's not called in error. defer err2.Handle(&err, func(noerr bool) { - require.That( + except.That( t, false, "if error occurs/reset, this cannot happen", @@ -159,18 +159,18 @@ func TestHandle_noerrHandler(t *testing.T) { // important! test that our handler doesn't change the current error // and it's not nil defer err2.Handle(&err, func(er error) error { - require.That(t, er != nil, "er val: ", er, err) - require.Equal(t, callCount, 1, "this is called in sencond") + except.That(t, er != nil, "er val: ", er, err) + except.Equal(t, callCount, 1, "this is called in sencond") callCount++ return er }) defer err2.Handle(&err, func(err error) error { // this should not be called, so lets try to fuckup things... - require.Equal(t, callCount, 0, "this is called in first") + except.Equal(t, callCount, 0, "this is called in first") callCount++ handlerCalled = false - require.That(t, err != nil) + except.That(t, err != nil) return finalAnnotatedErr }) try.To1(throw()) @@ -182,17 +182,17 @@ func TestHandle_noerrHandler(t *testing.T) { var err error var handlerCalled bool defer func() { - require.That(t, handlerCalled) + except.That(t, handlerCalled) }() // This is the handler we are testing! defer err2.Handle(&err, func(noerr bool) { - require.That(t, noerr) + except.That(t, noerr) handlerCalled = noerr }) defer err2.Handle(&err, func(err error) error { - require.That(t, false, "no error to handle!") + except.That(t, false, "no error to handle!") // this should not be called, so lets try to fuckup things... handlerCalled = false // see first deferred function return err @@ -207,27 +207,27 @@ func TestHandle_noerrHandler(t *testing.T) { var err error var handlerCalled bool defer func() { - require.That(t, handlerCalled) + except.That(t, handlerCalled) }() // This is the handler we are testing! defer err2.Handle(&err, func(noerr bool) { - require.That(t, true) - require.That(t, noerr) + except.That(t, true) + except.That(t, noerr) handlerCalled = noerr }) defer err2.Handle(&err) defer err2.Handle(&err, func(err error) error { - require.That(t, false, "no error to handle!") + except.That(t, false, "no error to handle!") // this should not be called, so lets try to fuckup things... handlerCalled = false // see first deferred function return err }) defer err2.Handle(&err, func(err error) error { - require.That(t, false, "no error to handle!") + except.That(t, false, "no error to handle!") // this should not be called, so lets try to fuckup things... handlerCalled = false // see first deferred function return err @@ -243,20 +243,20 @@ func TestHandle_noerrHandler(t *testing.T) { var err error var noerrHandlerCalled, errHandlerCalled bool defer func() { - require.That(t, noerrHandlerCalled) - require.That(t, errHandlerCalled) + except.That(t, noerrHandlerCalled) + except.That(t, errHandlerCalled) }() // This is the handler we are testing! defer err2.Handle(&err, func(noerr bool) { - require.That(t, true) // we are here, for debugging - require.That(t, noerr) + except.That(t, true) // we are here, for debugging + except.That(t, noerr) noerrHandlerCalled = noerr }) // this is the err handler that -- RESETS -- the error to nil defer err2.Handle(&err, func(err error) error { - require.That(t, err != nil) // helps fast debugging + except.That(t, err != nil) // helps fast debugging // this should not be called, so lets try to fuckup things... noerrHandlerCalled = false // see first deferred function @@ -266,7 +266,7 @@ func TestHandle_noerrHandler(t *testing.T) { }) defer err2.Handle(&err, func(err error) error { - require.That(t, err != nil) // helps fast debugging + except.That(t, err != nil) // helps fast debugging // this should not be called, so lets try to fuckup things... noerrHandlerCalled = false // see first deferred function @@ -284,14 +284,14 @@ func TestHandle_noerrHandler(t *testing.T) { var err error var handlerCalled bool defer func() { - require.That(t, handlerCalled) + except.That(t, handlerCalled) }() defer err2.Handle(&err) defer err2.Handle(&err) defer err2.Handle(&err, func(err error) error { - require.That(t, false, "no error to handle!") + except.That(t, false, "no error to handle!") // this should not be called, so lets try to fuckup things... handlerCalled = false // see first deferred function return err @@ -299,13 +299,13 @@ func TestHandle_noerrHandler(t *testing.T) { // This is the handler we are testing! defer err2.Handle(&err, func(noerr bool) { - require.That(t, true, "this must be called") - require.That(t, noerr) + except.That(t, true, "this must be called") + except.That(t, noerr) handlerCalled = noerr }) defer err2.Handle(&err, func(err error) error { - require.That(t, false, "no error to handle!") + except.That(t, false, "no error to handle!") // this should not be called, so lets try to fuckup things... handlerCalled = false // see first deferred function return err @@ -376,7 +376,7 @@ func TestPanickingCatchAll(t *testing.T) { t.Run(tt.name, func(t *testing.T) { t.Parallel() defer func() { - require.That( + except.That( t, recover() == nil, "panics should NOT carry on", @@ -424,7 +424,7 @@ func TestPanickingCarryOn_Handle(t *testing.T) { t.Run(tt.name, func(t *testing.T) { t.Parallel() defer func() { - require.That( + except.That( t, recover() != nil, "panics should went thru when not our errors", @@ -554,12 +554,12 @@ func TestPanicking_Handle(t *testing.T) { defer func() { r := recover() if tt.wants == nil { - require.That(t, r != nil, "wants err, then panic") + except.That(t, r != nil, "wants err, then panic") } }() err := tt.args.f() if err != nil { - require.Equal(t, err.Error(), tt.wants.Error()) + except.Equal(t, err.Error(), tt.wants.Error()) } }) } @@ -600,7 +600,7 @@ func TestPanicking_Catch(t *testing.T) { t.Run(tt.name, func(t *testing.T) { t.Parallel() defer func() { - require.That( + except.That( t, recover() == nil, "panics should NOT carry on", @@ -623,7 +623,7 @@ func TestCatch_Error(t *testing.T) { func Test_TryOutError(t *testing.T) { t.Parallel() defer err2.Catch(func(err error) error { - require.Equal(t, err.Error(), "fails: test: this is an ERROR", + except.Equal(t, err.Error(), "fails: test: this is an ERROR", "=> we should catch right error str here") return err }) @@ -633,7 +633,7 @@ func Test_TryOutError(t *testing.T) { // let's test try.Out1() and it's throw capabilities here, even try.To1() // is the preferred way. retVal = try.Out1(noThrow()).Handle().Val1 - require.Equal(t, retVal, "test", "if no error happens, we get value") + except.Equal(t, retVal, "test", "if no error happens, we get value") _ = try.Out1(throw()).Handle("fails: %v", retVal).Val1 t.Fail() // If everything works in Handle we are never here. @@ -665,21 +665,21 @@ func TestCatch_Panic(t *testing.T) { func TestSetErrorTracer(t *testing.T) { t.Parallel() w := err2.ErrorTracer() - require.That(t, w == nil, "error tracer should be nil") + except.That(t, w == nil, "error tracer should be nil") var w1 io.Writer err2.SetErrorTracer(w1) w = err2.ErrorTracer() - require.That(t, w == nil, "error tracer should be nil") + except.That(t, w == nil, "error tracer should be nil") } func TestSetErrRetTracer(t *testing.T) { t.Parallel() w := err2.ErrRetTracer() - require.That(t, w == nil, "error return tracer should be nil") + except.That(t, w == nil, "error return tracer should be nil") var w1 io.Writer err2.SetErrRetTracer(w1) w = err2.ErrRetTracer() - require.That(t, w == nil, "error return tracer should be nil") + except.That(t, w == nil, "error return tracer should be nil") } func ExampleCatch_withFmt() { diff --git a/internal/debug/debug_test.go b/internal/debug/debug_test.go index e0a70b4..501458e 100644 --- a/internal/debug/debug_test.go +++ b/internal/debug/debug_test.go @@ -6,7 +6,7 @@ import ( "strings" "testing" - "github.com/lainio/err2/internal/require" + "github.com/lainio/err2/internal/except" ) func TestFullName(t *testing.T) { @@ -42,7 +42,7 @@ func TestFullName(t *testing.T) { tt := ttv t.Run(tt.name, func(t *testing.T) { t.Parallel() - require.Equal(t, tt.retval, tt.fullName()) + except.Equal(t, tt.retval, tt.fullName()) }) } } @@ -94,7 +94,7 @@ func TestIsAnchor(t *testing.T) { tt := ttv t.Run(tt.name, func(t *testing.T) { t.Parallel() - require.Equal(t, tt.retval, tt.isAnchor(tt.input)) + except.Equal(t, tt.retval, tt.isAnchor(tt.input)) }) } } @@ -140,7 +140,7 @@ func TestIsFuncAnchor(t *testing.T) { tt := ttv t.Run(tt.name, func(t *testing.T) { t.Parallel() - require.Equal(t, tt.retval, tt.isFuncAnchor(tt.input)) + except.Equal(t, tt.retval, tt.isFuncAnchor(tt.input)) }) } } @@ -161,7 +161,7 @@ func TestFnLNro(t *testing.T) { t.Run(tt.name, func(t *testing.T) { t.Parallel() output := fnLNro(tt.input) - require.Equal(t, output, tt.output) + except.Equal(t, output, tt.output) }) } } @@ -212,7 +212,7 @@ func TestFnName(t *testing.T) { t.Run(tt.name, func(t *testing.T) { t.Parallel() output := fnName(tt.input) - require.Equal(t, output, tt.output) + except.Equal(t, output, tt.output) }) } } @@ -239,7 +239,7 @@ func TestStackPrint_noLimits(t *testing.T) { FuncName: "", Level: 0, }) - require.Equal(t, tt.input, w.String()) + except.Equal(t, tt.input, w.String()) }) } } @@ -267,8 +267,8 @@ func TestStackPrintForTest(t *testing.T) { // print(tt.output) // println("------") // print(w.String()) - require.Equal(t, a, b) - require.Equal(t, tt.output, w.String()) + except.Equal(t, a, b) + except.Equal(t, tt.output, w.String()) }) } } @@ -346,7 +346,7 @@ func TestCalcAnchor(t *testing.T) { t.Parallel() r := strings.NewReader(tt.input) anchor := calcAnchor(r, tt.StackInfo) - require.Equal(t, tt.anchor, anchor) + except.Equal(t, tt.anchor, anchor) }) } } @@ -385,10 +385,10 @@ func TestStackPrint_limit(t *testing.T) { stackPrint(readStack, writeStack, tt.StackInfo) ins := strings.Split(tt.input, "\n") outs := strings.Split(writeStack.String(), "\n") - require.Thatf(t, len(ins) > len(outs), + except.Thatf(t, len(ins) > len(outs), "input length:%d should be greater:%d", len(ins), len(outs)) wantResult, gotResult := tt.output, writeStack.String() - require.Equal(t, gotResult, wantResult) + except.Equal(t, gotResult, wantResult) }) } } @@ -460,10 +460,10 @@ func TestStackPrint_OneFunction(t *testing.T) { stackPrint(r, w, tt.StackInfo) ins := strings.Split(tt.input, "\n") outs := strings.Split(w.String(), "\n") - require.Thatf(t, len(ins) > len(outs), + except.Thatf(t, len(ins) > len(outs), "input length:%d should be greater:%d", len(ins), len(outs)) b, a := tt.output, w.String() - require.Equal(t, a, b) + except.Equal(t, a, b) }) } } @@ -528,10 +528,10 @@ func TestFuncName(t *testing.T) { FuncName: tt.FuncName, Level: tt.Level, }) - require.That(t, found) - require.Equal(t, tt.output, name) - require.Equal(t, ln, tt.outln) - require.Equal(t, fr, tt.outFrame) + except.That(t, found) + except.Equal(t, tt.output, name) + except.Equal(t, ln, tt.outln) + except.Equal(t, fr, tt.outFrame) }) } } diff --git a/internal/require/test.go b/internal/except/except.go similarity index 98% rename from internal/require/test.go rename to internal/except/except.go index c1a87fa..932099f 100644 --- a/internal/require/test.go +++ b/internal/except/except.go @@ -1,4 +1,4 @@ -package require +package except import ( "fmt" diff --git a/internal/handler/handler_test.go b/internal/handler/handler_test.go index dd72499..0e8476b 100644 --- a/internal/handler/handler_test.go +++ b/internal/handler/handler_test.go @@ -6,8 +6,8 @@ import ( "fmt" "testing" + "github.com/lainio/err2/internal/except" "github.com/lainio/err2/internal/handler" - "github.com/lainio/err2/internal/require" "github.com/lainio/err2/internal/x" ) @@ -121,11 +121,11 @@ func TestProcess(t *testing.T) { if handler.WorkToDo(tt.args.Any, tt.args.Err) { handler.Process(&tt.args.Info) - require.Equal(t, panicHandlerCalled, tt.want.panicCalled) - require.Equal(t, errorHandlerCalled, tt.want.errorCalled) - require.Equal(t, nilHandlerCalled, tt.want.nilCalled) + except.Equal(t, panicHandlerCalled, tt.want.panicCalled) + except.Equal(t, errorHandlerCalled, tt.want.errorCalled) + except.Equal(t, nilHandlerCalled, tt.want.nilCalled) - require.Equal(t, myErrVal.Error(), tt.want.errStr) + except.Equal(t, myErrVal.Error(), tt.want.errStr) } resetCalled() }) @@ -152,13 +152,13 @@ func TestPreProcess_debug(t *testing.T) { // and that's what error stack tracing is all about Handle() - require.ThatNot(t, panicHandlerCalled) - require.ThatNot(t, errorHandlerCalled) - require.ThatNot(t, nilHandlerCalled) + except.ThatNot(t, panicHandlerCalled) + except.ThatNot(t, errorHandlerCalled) + except.ThatNot(t, nilHandlerCalled) // See the name of this test function. Decamel it + error const want = "testing: t runner: error" - require.Equal(t, myErrVal.Error(), want) + except.Equal(t, myErrVal.Error(), want) resetCalled() } @@ -243,11 +243,11 @@ func TestPreProcess(t *testing.T) { err = handler.PreProcess(&err, &tt.args.Info, tt.args.a) - require.Equal(t, panicHandlerCalled, tt.want.panicCalled) - require.Equal(t, errorHandlerCalled, tt.want.errorCalled) - require.Equal(t, nilHandlerCalled, tt.want.nilCalled) + except.Equal(t, panicHandlerCalled, tt.want.panicCalled) + except.Equal(t, errorHandlerCalled, tt.want.errorCalled) + except.Equal(t, nilHandlerCalled, tt.want.nilCalled) - require.Equal(t, err.Error(), tt.want.errStr) + except.Equal(t, err.Error(), tt.want.errStr) } resetCalled() }) diff --git a/internal/handler/handlers_test.go b/internal/handler/handlers_test.go index cd76762..a98586c 100644 --- a/internal/handler/handlers_test.go +++ b/internal/handler/handlers_test.go @@ -4,8 +4,8 @@ import ( "testing" "github.com/lainio/err2" + "github.com/lainio/err2/internal/except" "github.com/lainio/err2/internal/handler" - "github.com/lainio/err2/internal/require" ) func TestHandlers(t *testing.T) { @@ -93,17 +93,17 @@ func TestHandlers(t *testing.T) { t.Parallel() anys := tt.args.f - require.That(t, anys != nil, "cannot be nil") + except.That(t, anys != nil, "cannot be nil") fns, dis := handler.ToErrorFns(anys) - require.That(t, fns != nil, "cannot be nil") - require.Equal(t, dis, tt.dis, "disabled wanted") + except.That(t, fns != nil, "cannot be nil") + except.Equal(t, dis, tt.dis, "disabled wanted") errHandler := handler.Pipeline(fns) err := errHandler(err2.ErrNotFound) if err == nil { - require.That(t, tt.want == nil) + except.That(t, tt.want == nil) } else { - require.Equal(t, err.Error(), tt.want.Error()) + except.Equal(t, err.Error(), tt.want.Error()) } }) } diff --git a/internal/str/str_test.go b/internal/str/str_test.go index 56da177..c013d0b 100644 --- a/internal/str/str_test.go +++ b/internal/str/str_test.go @@ -3,7 +3,7 @@ package str_test import ( "testing" - "github.com/lainio/err2/internal/require" + "github.com/lainio/err2/internal/except" "github.com/lainio/err2/internal/str" ) @@ -49,7 +49,7 @@ func TestCamel(t *testing.T) { t.Run(tt.name, func(t *testing.T) { t.Parallel() got := str.DecamelRegexp(tt.args.s) - require.Equal(t, got, tt.want) + except.Equal(t, got, tt.want) }) } } @@ -106,7 +106,7 @@ func TestDecamel(t *testing.T) { t.Run(tt.name, func(t *testing.T) { t.Parallel() got := str.Decamel(tt.args.s) - require.Equal(t, got, tt.want) + except.Equal(t, got, tt.want) }) } } @@ -182,7 +182,7 @@ func TestDecamelRmTryPrefix(t *testing.T) { t.Run(tt.name, func(t *testing.T) { t.Parallel() got := str.DecamelRmTryPrefix(tt.args.s) - require.Equal(t, got, tt.want) + except.Equal(t, got, tt.want) }) } } diff --git a/internal/x/x_test.go b/internal/x/x_test.go index 1140013..b18fc78 100644 --- a/internal/x/x_test.go +++ b/internal/x/x_test.go @@ -4,7 +4,7 @@ import ( "reflect" "testing" - "github.com/lainio/err2/internal/require" + "github.com/lainio/err2/internal/except" ) var ( @@ -52,30 +52,30 @@ func TestSwap(t *testing.T) { var ( lhs, rhs = 1, 2 // these are ints as default ) - require.Equal(t, lhs, 1) - require.Equal(t, rhs, 2) + except.Equal(t, lhs, 1) + except.Equal(t, rhs, 2) Swap(&lhs, &rhs) - require.Equal(t, lhs, 2) - require.Equal(t, rhs, 1) + except.Equal(t, lhs, 2) + except.Equal(t, rhs, 1) } { var ( lhs, rhs float64 = 1, 2 ) - require.Equal(t, lhs, 1) - require.Equal(t, rhs, 2) + except.Equal(t, lhs, 1) + except.Equal(t, rhs, 2) Swap(&lhs, &rhs) - require.Equal(t, lhs, 2) - require.Equal(t, rhs, 1) + except.Equal(t, lhs, 2) + except.Equal(t, rhs, 1) } } func TestSReverse(t *testing.T) { t.Parallel() SReverse(lengths) - require.That(t, reflect.DeepEqual(lengths, reverseLengths)) + except.That(t, reflect.DeepEqual(lengths, reverseLengths)) SReverse(lengths) // it's reverse now turn it to original - require.That(t, reflect.DeepEqual(lengths, original)) + except.That(t, reflect.DeepEqual(lengths, original)) } func BenchmarkSSReverse(b *testing.B) { diff --git a/try/copy_test.go b/try/copy_test.go index 7e50099..1498538 100644 --- a/try/copy_test.go +++ b/try/copy_test.go @@ -8,7 +8,7 @@ import ( "os" "testing" - "github.com/lainio/err2/internal/require" + "github.com/lainio/err2/internal/except" "github.com/lainio/err2/try" ) @@ -16,8 +16,8 @@ const dataFile = "./try.go" func Benchmark_CopyBufferMy(b *testing.B) { all, err := os.ReadFile(dataFile) - require.Thatf(b, err == nil, "error: %v", err) - require.That(b, all != nil) + except.Thatf(b, err == nil, "error: %v", err) + except.That(b, all != nil) buf := make([]byte, 4) dst := bufio.NewWriter(bytes.NewBuffer(make([]byte, 0, len(all)))) @@ -29,8 +29,8 @@ func Benchmark_CopyBufferMy(b *testing.B) { func Benchmark_CopyBufferStd(b *testing.B) { all, err := os.ReadFile(dataFile) - require.Thatf(b, err == nil, "error: %v", err) - require.That(b, all != nil) + except.Thatf(b, err == nil, "error: %v", err) + except.That(b, all != nil) buf := make([]byte, 4) dst := bufio.NewWriter(bytes.NewBuffer(make([]byte, 0, len(all)))) @@ -42,8 +42,8 @@ func Benchmark_CopyBufferStd(b *testing.B) { func Benchmark_CopyBufferOur(b *testing.B) { all, err := os.ReadFile(dataFile) - require.Thatf(b, err == nil, "error: %v", err) - require.That(b, all != nil) + except.Thatf(b, err == nil, "error: %v", err) + except.That(b, all != nil) tmp := make([]byte, 4) dst := bufio.NewWriter(bytes.NewBuffer(make([]byte, 0, len(all)))) diff --git a/try/out_test.go b/try/out_test.go index 60ea173..1ce0946 100644 --- a/try/out_test.go +++ b/try/out_test.go @@ -11,7 +11,7 @@ import ( "testing" "github.com/lainio/err2" - "github.com/lainio/err2/internal/require" + "github.com/lainio/err2/internal/except" "github.com/lainio/err2/try" ) @@ -102,8 +102,8 @@ func TestResult2_Logf(t *testing.T) { return v1 + v2, v2 } num1, num2 := countSomething("1", "bad") - require.Equal(t, num2, 2) - require.Equal(t, num1, 3) + except.Equal(t, num2, 2) + except.Equal(t, num1, 3) } func TestResult_Handle(t *testing.T) { @@ -121,10 +121,10 @@ func TestResult_Handle(t *testing.T) { return nil } err := callFn(1) - require.That(t, err == nil, "no error when Out.Handle sets it nil") + except.That(t, err == nil, "no error when Out.Handle sets it nil") err = callFn(0) - require.That(t, err != nil, "want error when Out.Handle sets it the same") + except.That(t, err != nil, "want error when Out.Handle sets it the same") } func ExampleResult1_Handle() {