package try /* try is a fork of lainio's err2 package Copyright (c) 2019 Lainio, MIT License https://github.com/lainio/err2 */ import ( "errors" "fmt" "strings" "testing" ) func throw() (string, error) { return "", fmt.Errorf("this is an ERROR") } func twoStrNoThrow() (string, string, error) { return "test", "test", nil } func noThrow() (string, error) { return "test", nil } func recursion(a int) int { if a == 0 { return 0 } s, err := noThrow() To(err) _ = s return a + recursion(a-1) } func recursionWithErrorCheck(a int) (int, error) { if a == 0 { return 0, nil } s, err := noThrow() if err != nil { return 0, err } _ = s v, err := recursionWithErrorCheck(a - 1) if err != nil { return 0, err } return a + v, nil } func noErr() error { return nil } func Test_noError(t *testing.T) { x := To1(noThrow()) if x != "test" { t.Fail() } x, y := To2(twoStrNoThrow()) if x != "test" || y != "test" { t.Fail() } } func TestDefault_Error(t *testing.T) { var err Err defer Return(&err) To1(throw()) t.Fail() // If everything works we are never here } func TestAny_Error(t *testing.T) { var err Err defer Handle(&err, func() {}) To1(throw()) t.Fail() // If everything works we are never here } func panickingHandle() { var err Err defer Handle(&err, func() {}) panic("test") } func TestPanickingCarryOn_Handle(t *testing.T) { defer func() { if recover() == nil { t.Error("panics should go through when not our errors") } }() panickingHandle() } func panickingCatchAll() { defer CatchAll(func(err Err) {}, func(v interface{}) {}) panickingHandle() } func TestPanickingCatchAll(t *testing.T) { defer func() { if recover() != nil { t.Error("panics should not fly thru") } }() panickingCatchAll() } func panickingReturn() { var err Err defer Return(&err) panickingHandle() } func TestPanicking_Return(t *testing.T) { defer func() { if recover() == nil { t.Error("panics should carry on") } }() panickingReturn() } func panickingCatch() { defer Catch(func(err Err) {}) panickingHandle() } func TestPanicking_Catch(t *testing.T) { defer func() { if recover() == nil { t.Error("panics should carry on") } }() panickingCatch() } func TestCatch_Error(t *testing.T) { defer Catch(func(err Err) { // fmt.Printf("error and defer handling:%s\n", err) }) To1(throw()) t.Fail() // If everything works we are never here } func TestNewTryErr(t *testing.T) { tryErr := newErr("I f*cked up") callStack := tryErr.CallStackString() if !strings.HasPrefix(callStack, "ERROR: I f*cked up\n") { fmt.Println("Call stack does not have prefix.\n" + callStack) t.Fail() } if strings.Count(callStack, "\n") != 9 { fmt.Println("Call stack is not 9 lines long.\n" + fmt.Sprint(strings.Count(callStack, "\n"))) t.Fail() } } func TestGetData(t *testing.T) { tryErr := newErr("I f*cked up") tryErr.Annotate("test1") tryErr.Annotate("test2") data := tryErr.GetData() if data.Msg != "I f*cked up" { fmt.Println("wrong msg") t.Fail() } if data.Annotations[0] != "test1" { fmt.Println("wrong annotation#0") t.Fail() } if data.Annotations[1] != "test2" { fmt.Println("wrong annotation#1") t.Fail() } if len(data.CallStack) != 3 { fmt.Println("call stack length != 3, " + fmt.Sprint(len(data.CallStack))) t.Fail() } } func TestErrCompare(t *testing.T) { err := errors.New("TestError") tryErr := fromErr(err) if !errors.Is(tryErr, err) { t.Fail() } } func TestReturnStd(t *testing.T) { tf := func() (err error) { defer ReturnStd(&err) To1(throw()) return } err := tf() if err.Error() != "this is an ERROR" { t.Fail() } } func TestCheckTryErr(t *testing.T) { testErr := newErr("TestErr") tf := func() (err Err) { defer Return(&err) To(testErr) return } err := tf() if err != testErr { t.Fail() } } func ExampleReturn() { var err Err defer Return(&err) To1(noThrow()) // Output: } func ExampleAnnotate() { annotated := func() (err Err) { defer Annotate(&err, "annotated") To1(throw()) return err } err := annotated() fmt.Printf("%v", err) // Output: annotated: this is an ERROR } func ExampleAnnotate_deferStack() { annotated := func() (err Err) { defer Annotate(&err, "annotated 2nd") defer Annotate(&err, "annotated 1st") To1(throw()) return err } err := annotated() fmt.Printf("%v", err) // Output: annotated 1st: annotated 2nd: this is an ERROR } func ExampleHandle() { doSomething := func(a, b int) (err Err) { defer Handle(&err, func() { err.Annotate(fmt.Sprintf("error with (%d, %d)", a, b)) }) To1(throw()) return err } err := doSomething(1, 2) fmt.Printf("%v", err) // Output: error with (1, 2): this is an ERROR } func BenchmarkOldErrorCheckingWithIfClause(b *testing.B) { for n := 0; n < b.N; n++ { _, err := noThrow() if err != nil { return } } } func BenchmarkAny(b *testing.B) { for n := 0; n < b.N; n++ { To1(noThrow()) } } func BenchmarkAny_ErrVar(b *testing.B) { for n := 0; n < b.N; n++ { _, err := noThrow() To(err) } } func BenchmarkCheckInsideCall(b *testing.B) { for n := 0; n < b.N; n++ { To(noErr()) } } func BenchmarkCheckVarCall(b *testing.B) { for n := 0; n < b.N; n++ { err := noErr() To(err) } } func BenchmarkCheck_ErrVar(b *testing.B) { for n := 0; n < b.N; n++ { _, err := noThrow() To(err) } } func BenchmarkRecursionNoCheck_NotRelated(b *testing.B) { for n := 0; n < b.N; n++ { _ = recursion(100) } } func BenchmarkRecursionWithErrorCheck_NotRelated(b *testing.B) { for n := 0; n < b.N; n++ { _, err := recursionWithErrorCheck(100) if err != nil { return } } }