lab.nexedi.com/kirr/go123@v0.0.0-20240207185015-8299741fa871/exc/error_test.go (about) 1 // Copyright (C) 2015-2020 Nexedi SA and Contributors. 2 // Kirill Smelkov <kirr@nexedi.com> 3 // 4 // This program is free software: you can Use, Study, Modify and Redistribute 5 // it under the terms of the GNU General Public License version 3, or (at your 6 // option) any later version, as published by the Free Software Foundation. 7 // 8 // You can also Link and Combine this program with other software covered by 9 // the terms of any of the Free Software licenses or any of the Open Source 10 // Initiative approved licenses and Convey the resulting work. Corresponding 11 // source of such a combination shall include the source code for all other 12 // software used. 13 // 14 // This program is distributed WITHOUT ANY WARRANTY; without even the implied 15 // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 16 // 17 // See COPYING file for full licensing terms. 18 // See https://www.nexedi.com/licensing for rationale and options. 19 20 package exc 21 22 import ( 23 "errors" 24 "reflect" 25 "runtime" 26 "testing" 27 28 "lab.nexedi.com/kirr/go123/my" 29 ) 30 31 func do_raise1() { 32 Raise(1) 33 } 34 35 func TestErrRaiseCatch(t *testing.T) { 36 defer Catch(func(e *Error) { 37 if !(e.arg == 1 && e.link == nil) { 38 t.Fatalf("error caught but unexpected: %#v ; want {1, nil}", e) 39 } 40 }) 41 do_raise1() 42 t.Fatal("error not caught") 43 } 44 45 // verify err chain has .arg(s) as expected 46 func verifyErrChain(t *testing.T, e *Error, argv ...interface{}) { 47 i := 0 48 for ; e != nil; i, e = i+1, e.link { 49 if i >= len(argv) { 50 t.Fatal("too long error chain") 51 } 52 if e.arg != argv[i] { 53 t.Fatalf("error caught but unexpected %vth arg: %v ; want %v", i, e.arg, argv[i]) 54 } 55 } 56 if i < len(argv) { 57 t.Fatal("too small error chain") 58 } 59 } 60 61 func do_onunwind1(t *testing.T) { 62 defer Onunwind(func(e *Error) *Error { 63 t.Fatal("on unwind called without raise") 64 return nil 65 }) 66 } 67 68 func do_onunwind2() { 69 defer Onunwind(func(e *Error) *Error { 70 return &Error{2, e} 71 }) 72 do_raise1() 73 } 74 75 func TestErrOnUnwind(t *testing.T) { 76 defer Catch(func(e *Error) { 77 verifyErrChain(t, e, 2, 1) 78 }) 79 do_onunwind1(t) 80 do_onunwind2() 81 t.Fatal("error not caught") 82 } 83 84 func do_context1(t *testing.T) { 85 defer Context(func() interface{} { 86 t.Fatal("on context called without raise") 87 return nil 88 }) 89 } 90 91 func do_context2() { 92 defer Context(func() interface{} { 93 return 3 94 }) 95 do_raise1() 96 } 97 98 func TestErrContext(t *testing.T) { 99 defer Catch(func(e *Error) { 100 verifyErrChain(t, e, 3, 1) 101 }) 102 do_context1(t) 103 do_context2() 104 t.Fatal("error not caught") 105 } 106 107 func do_contextf1() { 108 defer Contextf("must not be added") 109 return // no exception 110 } 111 112 func do_contextf2() { 113 defer Contextf("hello %d world", 123) 114 do_raise1() 115 } 116 117 func TestErrContextf(t *testing.T) { 118 defer Catch(func(e *Error) { 119 verifyErrChain(t, e, "hello 123 world", 1) 120 }) 121 do_contextf1() 122 do_contextf2() 123 t.Fatal("error not caught") 124 } 125 126 func do_raise11() { 127 do_raise1() 128 } 129 130 func do_raise3if() { 131 Raiseif(errors.New("3")) 132 } 133 134 func do_raise3if1() { 135 do_raise3if() 136 } 137 138 func do_raise4f() { 139 Raisef("%d", 4) 140 } 141 142 func do_raise4f1() { 143 do_raise4f() 144 } 145 146 // get name of a function 147 func funcname(f interface{}) string { 148 fentry := reflect.ValueOf(f).Pointer() 149 ffunc := runtime.FuncForPC(fentry) 150 return ffunc.Name() 151 } 152 153 func TestErrAddCallingContext(t *testing.T) { 154 var tests = []struct { f func(); wanterrcontext string } { 155 {do_raise11, "do_raise11: do_raise1: 1"}, 156 {do_raise3if1, "do_raise3if1: do_raise3if: 3"}, 157 {do_raise4f1, "do_raise4f1: do_raise4f: 4"}, 158 } 159 160 for _, tt := range tests { 161 func() { 162 myfunc := my.FuncName() 163 defer Catch(func(e *Error) { 164 e = Addcallingcontext(myfunc, e) 165 msg := e.Error() 166 if msg != tt.wanterrcontext { 167 t.Fatalf("%v: err + calling context: %q ; want %q", funcname(tt.f), msg, tt.wanterrcontext) 168 } 169 }) 170 tt.f() 171 t.Fatalf("%v: error not caught", funcname(tt.f)) 172 }() 173 } 174 } 175 176 func TestRunx(t *testing.T) { 177 var tests = []struct { f func(); wanterr string } { 178 {func() {}, ""}, 179 {do_raise11, "do_raise11: do_raise1: 1"}, 180 } 181 182 for _, tt := range tests { 183 err := Runx(tt.f) 184 if err == nil { 185 if tt.wanterr != "" { 186 t.Errorf("runx(%v) -> nil ; want %q error", funcname(tt.f), tt.wanterr) 187 } 188 continue 189 } 190 msg := err.Error() 191 if msg != tt.wanterr { 192 t.Errorf("runx(%v) -> %q ; want %q", funcname(tt.f), msg, tt.wanterr) 193 } 194 } 195 } 196 197 func TestXRun(t *testing.T) { 198 var tests = []struct { f func() error; wanterr string } { 199 {func() error { return nil }, ""}, 200 {func() error { return errors.New("abc") }, "X abc"}, 201 } 202 203 for _, tt := range tests { 204 errStr := "" 205 func() { 206 defer Catch(func(e *Error) { 207 errStr = "X " + e.Error() 208 }) 209 XRun(tt.f) 210 }() 211 212 if errStr != tt.wanterr { 213 t.Errorf("xrun(%v) -> %q ; want %q", funcname(tt.f), errStr, tt.wanterr) 214 } 215 } 216 }