Skip to content
This repository was archived by the owner on Dec 1, 2021. It is now read-only.

Commit 5a0856f

Browse files
committed
fixed tests, add coverage for Wrap and Fprintf
2 parents c978824 + 7b7bdcf commit 5a0856f

File tree

2 files changed

+130
-102
lines changed

2 files changed

+130
-102
lines changed

errors.go

Lines changed: 72 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -3,107 +3,110 @@ package errors
33

44
import (
55
"fmt"
6+
"io"
7+
"os"
68
"runtime"
79
)
810

911
// New returns an error that formats as the given text.
10-
func New(message string) error {
11-
pc, _, _, _ := runtime.Caller(1) // the caller of New
12+
func New(text string) error {
1213
return struct {
1314
error
1415
pc uintptr
1516
}{
16-
fmt.Errorf(message),
17-
pc,
17+
fmt.Errorf(text),
18+
pc(),
1819
}
1920
}
2021

21-
// Errorf returns a formatted error.
22-
func Errorf(format string, args ...interface{}) error {
23-
pc, _, _, _ := runtime.Caller(1) // the caller of Errorf
24-
return struct {
25-
error
26-
pc uintptr
27-
}{
28-
fmt.Errorf(format, args...),
29-
pc,
22+
type e struct {
23+
cause error
24+
message string
25+
pc uintptr
26+
}
27+
28+
func (e *e) Error() string {
29+
return e.message + ": " + e.cause.Error()
30+
}
31+
32+
func (e *e) Cause() error {
33+
return e.cause
34+
}
35+
36+
// Wrap returns an error annotating the cause with message.
37+
// If cause is nil, Wrap returns nil.
38+
func Wrap(cause error, message string) error {
39+
if cause == nil {
40+
return nil
41+
}
42+
return &e{
43+
cause: cause,
44+
message: message,
45+
pc: pc(),
3046
}
3147
}
3248

49+
type causer interface {
50+
Cause() error
51+
}
52+
3353
// Cause returns the underlying cause of the error, if possible.
3454
// An error value has a cause if it implements the following
35-
// method:
36-
//
37-
// Cause() error
55+
// interface:
3856
//
57+
// type Causer interface {
58+
// Cause() error
59+
// }
3960
//
4061
// If the error does not implement Cause, the original error will
4162
// be returned. If the error is nil, nil will be returned without further
4263
// investigation.
4364
func Cause(err error) error {
44-
if err == nil {
45-
return nil
46-
}
47-
type causer interface {
48-
Cause() error
49-
}
50-
if err, ok := err.(causer); ok {
51-
return err.Cause()
65+
for err != nil {
66+
cause, ok := err.(causer)
67+
if !ok {
68+
break
69+
}
70+
err = cause.Cause()
5271
}
5372
return err
5473
}
5574

56-
func underlying(err error) (error, bool) {
57-
if err == nil {
58-
return nil, false
59-
}
60-
type underlying interface {
61-
underlying() error
62-
}
63-
if err, ok := err.(underlying); ok {
64-
return err.underlying(), true
65-
}
66-
return nil, false
75+
type locationer interface {
76+
Location() (string, int)
6777
}
6878

69-
type traced struct {
70-
error // underlying error
71-
pc uintptr
79+
// Print prints the error to Stderr.
80+
func Print(err error) {
81+
Fprint(os.Stderr, err)
7282
}
7383

74-
func (t *traced) underlying() error { return t.error }
84+
// Fprint prints the error to the supplied writer.
85+
// The format of the output is the same as Print.
86+
// If err is nil, nothing is printed.
87+
func Fprint(w io.Writer, err error) {
88+
for err != nil {
89+
location, ok := err.(locationer)
90+
if ok {
91+
file, line := location.Location()
92+
fmt.Fprint(w, "%s:%d: ", file, line)
93+
}
94+
switch err := err.(type) {
95+
case *e:
96+
fmt.Fprintln(w, err.message)
97+
default:
98+
fmt.Fprintln(w, err.Error())
99+
}
75100

76-
// Trace adds caller information to the error.
77-
// If error is nil, nil will be returned.
78-
func Trace(err error) error {
79-
if err == nil {
80-
return nil
101+
cause, ok := err.(causer)
102+
if !ok {
103+
break
104+
}
105+
err = cause.Cause()
81106
}
82-
pc, _, _, _ := runtime.Caller(1) // the caller of Trace
83-
return traced{
84-
error: err,
85-
pc: pc,
86-
}
87-
}
88-
89-
type annotated struct {
90-
error // underlying error
91-
pc uintptr
92107
}
93108

94-
func (a *annotated) Cause() error { return a.error }
95-
96-
// Annotate returns a new error annotating the error provided
97-
// with the message, and the location of the caller of Annotate.
98-
// The underlying error can be recovered by calling Cause.
99-
// If err is nil, nil will be returned.
100-
func Annotate(err error, message string) error {
101-
if err == nil {
102-
return nil
103-
}
104-
pc, _, _, _ := runtime.Caller(1) // the caller of Annotate
105-
return annotated{
106-
error: err,
107-
pc: pc,
108-
}
109+
func pc() uintptr {
110+
pc, _, _, _ := runtime.Caller(2)
111+
return pc
109112
}

errors_test.go

Lines changed: 58 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
package errors
22

33
import (
4+
"bytes"
45
"fmt"
56
"io"
67
"reflect"
78
"testing"
89
)
910

10-
func TestNewError(t *testing.T) {
11+
func TestNew(t *testing.T) {
1112
tests := []struct {
1213
err string
1314
want error
@@ -25,23 +26,28 @@ func TestNewError(t *testing.T) {
2526
}
2627
}
2728

28-
func TestNewEqualNew(t *testing.T) {
29-
// test that two calls to New return the same error when called from the same location
30-
var errs []error
31-
for i := 0; i < 2; i++ {
32-
errs = append(errs, New("error"))
33-
}
34-
a, b := errs[0], errs[1]
35-
if !reflect.DeepEqual(a, b) {
36-
t.Errorf("Expected two calls to New from the same location to give the same error: %#v, %#v", a, b)
29+
func TestWrapNil(t *testing.T) {
30+
got := Wrap(nil, "no error")
31+
if got != nil {
32+
t.Errorf("Wrap(nil, \"no error\"): got %#v, expected nil", got)
3733
}
3834
}
3935

40-
func TestNewNotEqualNew(t *testing.T) {
41-
// test that two calls to New return different errors when called from different locations
42-
a, b := New("error"), New("error")
43-
if reflect.DeepEqual(a, b) {
44-
t.Errorf("Expected two calls to New from the different locations give the same error: %#v, %#v", a, b)
36+
func TestWrap(t *testing.T) {
37+
tests := []struct {
38+
err error
39+
message string
40+
want string
41+
}{
42+
{io.EOF, "read error", "read error: EOF"},
43+
{Wrap(io.EOF, "read error"), "client error", "client error: read error: EOF"},
44+
}
45+
46+
for _, tt := range tests {
47+
got := Wrap(tt.err, tt.message).Error()
48+
if got != tt.want {
49+
t.Errorf("Wrap(%v, %q): got: %v, want %v", tt.err, tt.message, got, tt.want)
50+
}
4551
}
4652
}
4753

@@ -94,23 +100,42 @@ func TestCause(t *testing.T) {
94100
}
95101
}
96102

97-
func TestTraceNotEqual(t *testing.T) {
98-
// test that two calls to trace do not return identical errors
99-
err := New("error")
100-
a := err
101-
var errs []error
102-
for i := 0; i < 2; i++ {
103-
err = Trace(err)
104-
errs = append(errs, err)
105-
}
106-
b, c := errs[0], errs[1]
107-
if reflect.DeepEqual(a, b) {
108-
t.Errorf("a and b equal: %#v, %#v", a, b)
109-
}
110-
if reflect.DeepEqual(b, c) {
111-
t.Errorf("b and c equal: %#v, %#v", b, c)
112-
}
113-
if reflect.DeepEqual(a, c) {
114-
t.Errorf("a and c equal: %#v, %#v", a, c)
103+
func TestFprint(t *testing.T) {
104+
x := New("error")
105+
tests := []struct {
106+
err error
107+
want string
108+
}{{
109+
// nil error is nil
110+
err: nil,
111+
}, {
112+
// explicit nil error is nil
113+
err: (error)(nil),
114+
}, {
115+
// uncaused error is unaffected
116+
err: io.EOF,
117+
want: "EOF\n",
118+
}, {
119+
// caused error returns cause
120+
err: &causeError{cause: io.EOF},
121+
want: "cause error\nEOF\n",
122+
}, {
123+
err: x, // return from errors.New
124+
want: "error\n",
125+
}, {
126+
err: Wrap(x, "message"),
127+
want: "message\nerror\n",
128+
}, {
129+
err: Wrap(Wrap(x, "message"), "another message"),
130+
want: "another message\nmessage\nerror\n",
131+
}}
132+
133+
for i, tt := range tests {
134+
var w bytes.Buffer
135+
Fprint(&w, tt.err)
136+
got := w.String()
137+
if got != tt.want {
138+
t.Errorf("test %d: Fprint(w, %q): got %q, want %q", i+1, tt.err, got, tt.want)
139+
}
115140
}
116141
}

0 commit comments

Comments
 (0)