Skip to content

Commit 40e3932

Browse files
committed
Initial version: Panic, Fatal, Exit, and Log.
0 parents  commit 40e3932

File tree

12 files changed

+717
-0
lines changed

12 files changed

+717
-0
lines changed

LICENSE

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
Copyright (c) 2023 Patrick Smith
2+
3+
Permission is hereby granted, free of charge, to any person obtaining a copy
4+
of this software and associated documentation files (the "Software"), to deal
5+
in the Software without restriction, including without limitation the rights
6+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7+
copies of the Software, and to permit persons to whom the Software is
8+
furnished to do so, subject to the following conditions:
9+
10+
The above copyright notice and this permission notice shall be included in all
11+
copies or substantial portions of the Software.
12+
13+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19+
SOFTWARE.

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Go tests
2+
3+
[![Go Reference](https://pkg.go.dev/badge/github.com/pat42smith/or.svg)](https://pkg.go.dev/github.com/pat42smith/or)
4+
5+
This repository provides some simple utilities for handling errors in Go code.

error_test.go

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
// Copyright 2023 Patrick Smith
2+
// Use of this source code is subject to the MIT-style license in the LICENSE file.
3+
4+
package or
5+
6+
import (
7+
"errors"
8+
"testing"
9+
10+
"github.com/pat42smith/gotest"
11+
)
12+
13+
// Check that non-nil errors are handled correctly.
14+
// Exit and Log need to be tested in a separate process,
15+
// so they are not included here.
16+
17+
var disaster = errors.New("disaster")
18+
19+
func jabber0() error {
20+
return disaster
21+
}
22+
23+
func jabber1() (string, error) {
24+
return "'Twas", disaster
25+
}
26+
27+
func jabber2() (string, string, error) {
28+
return "brillig,", "and", disaster
29+
}
30+
31+
func jabber3() (string, string, string, error) {
32+
return "the", "slithy", "toves", disaster
33+
}
34+
35+
func jabber4() (string, string, string, string, error) {
36+
return "Did", "gyre", "and", "gimble", disaster
37+
}
38+
39+
func jabber5() (string, string, string, string, string, error) {
40+
return "in", "the", "wabe;", "All", "mimsy", disaster
41+
}
42+
43+
func checkPanic(t *testing.T, f func()) {
44+
t.Helper()
45+
p := gotest.MustPanic(t, f)
46+
gotest.Expect(t, disaster, p.(error))
47+
}
48+
49+
func TestErrorPanic(t *testing.T) {
50+
checkPanic(t, func() {
51+
Panic0(jabber0())
52+
})
53+
checkPanic(t, func() {
54+
Panic1(jabber1())
55+
})
56+
checkPanic(t, func() {
57+
Panic2(jabber2())
58+
})
59+
checkPanic(t, func() {
60+
Panic3(jabber3())
61+
})
62+
checkPanic(t, func() {
63+
Panic4(jabber4())
64+
})
65+
checkPanic(t, func() {
66+
Panic5(jabber5())
67+
})
68+
}
69+
70+
func checkFatal(t *testing.T, f func(*gotest.StubReporter)) {
71+
t.Helper()
72+
var sr gotest.StubReporter
73+
// StubReporter.Fatal actually returns, so Fataln should panic.
74+
gotest.MustPanic(t, func() {
75+
f(&sr)
76+
})
77+
sr.Expect(t, true, true, "disaster\n")
78+
}
79+
80+
func TestErrorFatal(t *testing.T) {
81+
checkFatal(t, func(sr *gotest.StubReporter) {
82+
Fatal0(jabber0())(sr)
83+
})
84+
checkFatal(t, func(sr *gotest.StubReporter) {
85+
Fatal1(jabber1())(sr)
86+
})
87+
checkFatal(t, func(sr *gotest.StubReporter) {
88+
Fatal2(jabber2())(sr)
89+
})
90+
checkFatal(t, func(sr *gotest.StubReporter) {
91+
Fatal3(jabber3())(sr)
92+
})
93+
checkFatal(t, func(sr *gotest.StubReporter) {
94+
Fatal4(jabber4())(sr)
95+
})
96+
checkFatal(t, func(sr *gotest.StubReporter) {
97+
Fatal5(jabber5())(sr)
98+
})
99+
}
100+
101+
func checkHandle(t *testing.T, f func(Handler)) {
102+
var saved error
103+
p := gotest.MustPanic(t, func() {
104+
f(func(e error) {
105+
saved = e
106+
panic(e)
107+
})
108+
})
109+
gotest.Require(t, saved == disaster && p.(error) == disaster)
110+
111+
saved = nil
112+
p = gotest.MustPanic(t, func() {
113+
f(func(e error) {
114+
saved = e
115+
})
116+
})
117+
gotest.Require(t, saved == disaster && p.(string) == "Error handler returned")
118+
}
119+
120+
func TestErrorHandle(t *testing.T) {
121+
checkHandle(t, func(h Handler) {
122+
Handle0(jabber0())(h)
123+
})
124+
checkHandle(t, func(h Handler) {
125+
Handle1(jabber1())(h)
126+
})
127+
checkHandle(t, func(h Handler) {
128+
Handle2(jabber2())(h)
129+
})
130+
checkHandle(t, func(h Handler) {
131+
Handle3(jabber3())(h)
132+
})
133+
checkHandle(t, func(h Handler) {
134+
Handle4(jabber4())(h)
135+
})
136+
checkHandle(t, func(h Handler) {
137+
Handle5(jabber5())(h)
138+
})
139+
}

exit.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// Copyright 2023 Patrick Smith
2+
// Use of this source code is subject to the MIT-style license in the LICENSE file.
3+
4+
package or
5+
6+
import (
7+
"fmt"
8+
"os"
9+
)
10+
11+
// Exit0, if e is not nil, prints e to standard error and then calls os.Exit(1).
12+
func Exit0(e error) {
13+
if e != nil {
14+
fmt.Fprintln(os.Stderr, e)
15+
os.Exit(1)
16+
}
17+
}
18+
19+
// Exit1 is like Exit0, but returns t1 when e is nil.
20+
func Exit1[T1 any](t1 T1, e error) T1 {
21+
Exit0(e)
22+
return t1
23+
}
24+
25+
// Exit2 is like Exit0, but returns (t1, t2) when e is nil.
26+
func Exit2[T1, T2 any](t1 T1, t2 T2, e error) (T1, T2) {
27+
Exit0(e)
28+
return t1, t2
29+
}
30+
31+
// Exit3 is like Exit0, but returns (t1, t2, t3) when e is nil.
32+
func Exit3[T1, T2, T3 any](t1 T1, t2 T2, t3 T3, e error) (T1, T2, T3) {
33+
Exit0(e)
34+
return t1, t2, t3
35+
}
36+
37+
// Exit4 is like Exit0, but returns (t1, t2, t3, t4) when e is nil.
38+
func Exit4[T1, T2, T3, T4 any](t1 T1, t2 T2, t3 T3, t4 T4, e error) (T1, T2, T3, T4) {
39+
Exit0(e)
40+
return t1, t2, t3, t4
41+
}
42+
43+
// Exit5 is like Exit0, but returns (t1, t2, t3, t4, t5) when e is nil.
44+
func Exit5[T1, T2, T3, T4, T5 any](t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, e error) (T1, T2, T3, T4, T5) {
45+
Exit0(e)
46+
return t1, t2, t3, t4, t5
47+
}

exitlog_test.go

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
// Copyright 2023 Patrick Smith
2+
// Use of this source code is subject to the MIT-style license in the LICENSE file.
3+
4+
package or
5+
6+
import (
7+
"os"
8+
"path/filepath"
9+
"testing"
10+
11+
"github.com/pat42smith/gotest"
12+
)
13+
14+
// Check that Exitn and Logn handle non-nil errors correctly.
15+
// This requires a separate process.
16+
17+
var test_source = `package main
18+
19+
import (
20+
"errors"
21+
"log"
22+
"os"
23+
"github.com/pat42smith/or"
24+
)
25+
26+
func main() {
27+
log.SetFlags(0)
28+
log.SetPrefix("logged: ")
29+
30+
switch os.Args[1] {
31+
case "exit":
32+
switch os.Args[2] {
33+
case "0":
34+
or.Exit0(errors.New("zero"))
35+
case "1":
36+
or.Exit1(1, errors.New("one"))
37+
case "2":
38+
or.Exit2(1, 2, errors.New("two"))
39+
case "3":
40+
or.Exit3(1, 2, 3, errors.New("three"))
41+
case "4":
42+
or.Exit4(1, 2, 3, 4, errors.New("four"))
43+
case "5":
44+
or.Exit5(1, 2, 3, 4, 5, errors.New("five"))
45+
}
46+
case "log":
47+
switch os.Args[2] {
48+
case "0":
49+
or.Log0(errors.New("zero"))
50+
case "1":
51+
or.Log1(1, errors.New("one"))
52+
case "2":
53+
or.Log2(1, 2, errors.New("two"))
54+
case "3":
55+
or.Log3(1, 2, 3, errors.New("three"))
56+
case "4":
57+
or.Log4(1, 2, 3, 4, errors.New("four"))
58+
case "5":
59+
or.Log5(1, 2, 3, 4, 5, errors.New("five"))
60+
}
61+
}
62+
}`
63+
64+
func checkExitLog(t *testing.T, program, which, n, expect string) {
65+
t.Helper()
66+
cmd := gotest.Command(program, which, n)
67+
cmd.WantStderr(expect + "\n")
68+
cmd.WantCode(1)
69+
cmd.Run(t, "")
70+
}
71+
72+
func TestExitLog(t *testing.T) {
73+
tmp := t.TempDir()
74+
testgo := filepath.Join(tmp, "test.go")
75+
testexe := filepath.Join(tmp, "test.exe")
76+
if e := os.WriteFile(testgo, []byte(test_source), 0444); e != nil {
77+
t.Fatal(e)
78+
}
79+
gotest.Command("go", "build", "-o", testexe, testgo).Run(t, "")
80+
81+
checkExitLog(t, testexe, "exit", "0", "zero")
82+
checkExitLog(t, testexe, "exit", "1", "one")
83+
checkExitLog(t, testexe, "exit", "2", "two")
84+
checkExitLog(t, testexe, "exit", "3", "three")
85+
checkExitLog(t, testexe, "exit", "4", "four")
86+
checkExitLog(t, testexe, "exit", "5", "five")
87+
88+
checkExitLog(t, testexe, "log", "0", "logged: zero")
89+
checkExitLog(t, testexe, "log", "1", "logged: one")
90+
checkExitLog(t, testexe, "log", "2", "logged: two")
91+
checkExitLog(t, testexe, "log", "3", "logged: three")
92+
checkExitLog(t, testexe, "log", "4", "logged: four")
93+
checkExitLog(t, testexe, "log", "5", "logged: five")
94+
}

0 commit comments

Comments
 (0)