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 wrongSignature() (int, int) { return 0, 0 } func recursion(a int) int { if a == 0 { return 0 } s, err := noThrow() Check(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 TestAny_noError(t *testing.T) { d := Any(noThrow()) if d[0].(string) != "test" { t.Fail() } d = Any(twoStrNoThrow()) if d[0].(string) != "test" || d[1].(string) != "test" { t.Fail() } } func TestDefault_Error(t *testing.T) { var err Err defer Return(&err) Any(throw()) t.Fail() // If everything works we are never here } func TestAny_Error(t *testing.T) { var err Err defer Handle(&err, func() {}) Any(throw()) t.Fail() // If everything works we are never here } func panickingHandle() { var err Err defer Handle(&err, func() {}) Any(wrongSignature()) } func TestPanickingCarryOn_Handle(t *testing.T) { defer func() { if recover() == nil { t.Error("panics should went thru when not our errors") } }() panickingHandle() } func panickingCatchAll() { defer CatchAll(func(err Err) {}, func(v interface{}) {}) Any(wrongSignature()) } 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) Any(wrongSignature()) } 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) {}) Any(wrongSignature()) } 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) }) Any(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) String(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) Check(testErr) return } err := tf() if err != testErr { t.Fail() } } func ExampleReturn() { var err Err defer Return(&err) Any(noThrow()) // Output: } func ExampleAnnotate() { annotated := func() (err Err) { defer Annotate(&err, "annotated") Any(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") Any(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)) }) Any(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++ { Any(noThrow()) } } func BenchmarkAny_ErrVar(b *testing.B) { for n := 0; n < b.N; n++ { _, err := noThrow() Any(err) } } func BenchmarkCheckInsideCall(b *testing.B) { for n := 0; n < b.N; n++ { Check(noErr()) } } func BenchmarkCheckVarCall(b *testing.B) { for n := 0; n < b.N; n++ { err := noErr() Check(err) } } func BenchmarkCheck_ErrVar(b *testing.B) { for n := 0; n < b.N; n++ { _, err := noThrow() Check(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 } } }