OLD | NEW |
1 // Copyright 2016 The LUCI Authors. All rights reserved. | 1 // Copyright 2016 The LUCI Authors. All rights reserved. |
2 // Use of this source code is governed under the Apache License, Version 2.0 | 2 // Use of this source code is governed under the Apache License, Version 2.0 |
3 // that can be found in the LICENSE file. | 3 // that can be found in the LICENSE file. |
4 | 4 |
5 package errors | 5 package errors |
6 | 6 |
7 import ( | 7 import ( |
8 "fmt" | |
9 "regexp" | 8 "regexp" |
10 "strings" | 9 "strings" |
11 "testing" | 10 "testing" |
12 | 11 |
13 . "github.com/smartystreets/goconvey/convey" | 12 . "github.com/smartystreets/goconvey/convey" |
14 ) | 13 ) |
15 | 14 |
16 var ( | 15 var ( |
17 fixSkip = regexp.MustCompile(`skipped \d+ frames`) | 16 fixSkip = regexp.MustCompile(`skipped \d+ frames`) |
18 fixNum = regexp.MustCompile(`^#\d+`) | 17 fixNum = regexp.MustCompile(`^#\d+`) |
19 fixTestingLine = regexp.MustCompile(`(testing/\w+.go):\d+`) | 18 fixTestingLine = regexp.MustCompile(`(testing/\w+.go):\d+`) |
20 ) | 19 ) |
21 | 20 |
22 func FixForTest(lines Lines) Lines { | 21 func FixForTest(lines []string) []string { |
23 for i, l := range lines { | 22 for i, l := range lines { |
24 switch { | 23 switch { |
25 case strings.HasPrefix(l, "goroutine"): | 24 case strings.HasPrefix(l, "goroutine"): |
26 l = "GOROUTINE LINE" | 25 l = "GOROUTINE LINE" |
27 case strings.HasPrefix(l, "... skipped"): | 26 case strings.HasPrefix(l, "... skipped"): |
28 l = fixSkip.ReplaceAllLiteralString(l, "skipped SOME fra
mes") | 27 l = fixSkip.ReplaceAllLiteralString(l, "skipped SOME fra
mes") |
29 } | 28 } |
30 l = fixNum.ReplaceAllLiteralString(l, "#?") | 29 l = fixNum.ReplaceAllLiteralString(l, "#?") |
31 if strings.HasPrefix(l, "#? testing/") { | 30 if strings.HasPrefix(l, "#? testing/") { |
32 l = fixTestingLine.ReplaceAllString(l, "$1:XXX") | 31 l = fixTestingLine.ReplaceAllString(l, "$1:XXX") |
33 } | 32 } |
34 lines[i] = l | 33 lines[i] = l |
35 } | 34 } |
36 return lines | 35 return lines |
37 } | 36 } |
38 | 37 |
39 func TestAnnotation(t *testing.T) { | 38 func TestAnnotation(t *testing.T) { |
40 t.Parallel() | 39 t.Parallel() |
41 | 40 |
42 Convey("Test annotation struct", t, func() { | 41 Convey("Test annotation struct", t, func() { |
43 » » e := (Annotate(New("bad thing")). | 42 » » e := Annotate(New("bad thing"), "%d some error: %q", 20, "string
y"). |
44 » » » Reason("%(first)d some error: %(second)q"). | 43 » » » InternalReason("extra(%.3f)", 8.2).Err() |
45 » » » D("extra", 8.2, "%.3f"). | |
46 » » » D("first", 20, "0x%08x"). | |
47 » » » D("second", "stringy").Err()) | |
48 ae := e.(*annotatedError) | 44 ae := e.(*annotatedError) |
49 | 45 |
50 Convey("annotation can render itself for public usage", func() { | 46 Convey("annotation can render itself for public usage", func() { |
51 So(ae.Error(), ShouldEqual, `20 some error: "stringy": b
ad thing`) | 47 So(ae.Error(), ShouldEqual, `20 some error: "stringy": b
ad thing`) |
52 }) | 48 }) |
53 | 49 |
54 Convey("annotation can render itself for internal usage", func()
{ | 50 Convey("annotation can render itself for internal usage", func()
{ |
55 » » » lines := RenderStack(e).ToLines( | 51 » » » lines := RenderStack(e, `runtime`, `github.com/jtolds/gl
s`, |
56 » » » » `runtime`, `github.com/jtolds/gls`, | |
57 `github.com/smartystreets/goconvey/convey`) | 52 `github.com/smartystreets/goconvey/convey`) |
58 FixForTest(lines) | 53 FixForTest(lines) |
59 | 54 |
60 » » » So(lines, ShouldResemble, Lines{ | 55 » » » So(lines, ShouldResemble, []string{ |
61 `original error: bad thing`, | 56 `original error: bad thing`, |
62 ``, | 57 ``, |
63 `GOROUTINE LINE`, | 58 `GOROUTINE LINE`, |
64 » » » » `#? github.com/luci/luci-go/common/errors/annota
te_test.go:47 - errors.TestAnnotation.func1()`, | 59 » » » » `#? github.com/luci/luci-go/common/errors/annota
te_test.go:43 - errors.TestAnnotation.func1()`, |
65 » » » » ` reason: "20 some error: \"stringy\""`, | 60 » » » » ` reason: 20 some error: "stringy"`, |
66 » » » » ` "extra" = 8.200`, | 61 » » » » ` internal reason: extra(8.200)`, |
67 » » » » ` "first" = 0x00000014`, | |
68 » » » » ` "second" = "stringy"`, | |
69 ``, | 62 ``, |
70 `... skipped SOME frames in pkg "github.com/smar
tystreets/goconvey/convey"...`, | 63 `... skipped SOME frames in pkg "github.com/smar
tystreets/goconvey/convey"...`, |
71 `... skipped SOME frames in pkg "github.com/jtol
ds/gls"...`, | 64 `... skipped SOME frames in pkg "github.com/jtol
ds/gls"...`, |
72 `... skipped SOME frames in pkg "github.com/smar
tystreets/goconvey/convey"...`, | 65 `... skipped SOME frames in pkg "github.com/smar
tystreets/goconvey/convey"...`, |
73 ``, | 66 ``, |
74 » » » » `#? github.com/luci/luci-go/common/errors/annota
te_test.go:110 - errors.TestAnnotation()`, | 67 » » » » `#? github.com/luci/luci-go/common/errors/annota
te_test.go:99 - errors.TestAnnotation()`, |
75 `#? testing/testing.go:XXX - testing.tRunner()`, | 68 `#? testing/testing.go:XXX - testing.tRunner()`, |
76 `... skipped SOME frames in pkg "runtime"...`, | 69 `... skipped SOME frames in pkg "runtime"...`, |
77 }) | 70 }) |
78 }) | 71 }) |
79 | 72 |
80 Convey("can render whole stack", func() { | 73 Convey("can render whole stack", func() { |
81 » » » e = Annotate(e).Reason("outer frame %(first)s").D("first
", "outer").Err() | 74 » » » e = Annotate(e, "outer frame %s", "outer").Err() |
82 » » » lines := RenderStack(e).ToLines( | 75 » » » lines := RenderStack(e, `runtime`, `github.com/jtolds/gl
s`, |
83 » » » » `runtime`, `github.com/jtolds/gls`, | |
84 `github.com/smartystreets/goconvey/convey`) | 76 `github.com/smartystreets/goconvey/convey`) |
85 FixForTest(lines) | 77 FixForTest(lines) |
86 | 78 |
87 » » » So(lines, ShouldResemble, Lines{ | 79 » » » So(lines, ShouldResemble, []string{ |
88 `original error: bad thing`, | 80 `original error: bad thing`, |
89 ``, | 81 ``, |
90 `GOROUTINE LINE`, | 82 `GOROUTINE LINE`, |
91 » » » » `#? github.com/luci/luci-go/common/errors/annota
te_test.go:47 - errors.TestAnnotation.func1()`, | 83 » » » » `#? github.com/luci/luci-go/common/errors/annota
te_test.go:43 - errors.TestAnnotation.func1()`, |
92 ` annotation #0:`, | 84 ` annotation #0:`, |
93 » » » » ` reason: "outer frame outer"`, | 85 » » » » ` reason: outer frame outer`, |
94 » » » » ` "first" = "outer"`, | |
95 ` annotation #1:`, | 86 ` annotation #1:`, |
96 » » » » ` reason: "20 some error: \"stringy\""`, | 87 » » » » ` reason: 20 some error: "stringy"`, |
97 » » » » ` "extra" = 8.200`, | 88 » » » » ` internal reason: extra(8.200)`, |
98 » » » » ` "first" = 0x00000014`, | |
99 » » » » ` "second" = "stringy"`, | |
100 ``, | 89 ``, |
101 `... skipped SOME frames in pkg "github.com/smar
tystreets/goconvey/convey"...`, | 90 `... skipped SOME frames in pkg "github.com/smar
tystreets/goconvey/convey"...`, |
102 `... skipped SOME frames in pkg "github.com/jtol
ds/gls"...`, | 91 `... skipped SOME frames in pkg "github.com/jtol
ds/gls"...`, |
103 `... skipped SOME frames in pkg "github.com/smar
tystreets/goconvey/convey"...`, | 92 `... skipped SOME frames in pkg "github.com/smar
tystreets/goconvey/convey"...`, |
104 ``, | 93 ``, |
105 » » » » `#? github.com/luci/luci-go/common/errors/annota
te_test.go:110 - errors.TestAnnotation()`, | 94 » » » » `#? github.com/luci/luci-go/common/errors/annota
te_test.go:99 - errors.TestAnnotation()`, |
106 `#? testing/testing.go:XXX - testing.tRunner()`, | 95 `#? testing/testing.go:XXX - testing.tRunner()`, |
107 `... skipped SOME frames in pkg "runtime"...`, | 96 `... skipped SOME frames in pkg "runtime"...`, |
108 }) | 97 }) |
109 }) | 98 }) |
110 }) | 99 }) |
111 } | 100 } |
112 | |
113 func TestDataFormat(t *testing.T) { | |
114 t.Parallel() | |
115 | |
116 testCases := []struct { | |
117 format string | |
118 expected string | |
119 }{ | |
120 {"", ""}, | |
121 {"no replacements", `no replacements`}, | |
122 {"%(foo)s", `bar`}, | |
123 {"%%(foo)s", `%(foo)s`}, | |
124 {"%(foo)s|%(foo)s|%(foo)s", `bar|bar|bar`}, | |
125 {"|%(foo)s|%(foo)s|%(foo)s|", `|bar|bar|bar|`}, | |
126 {"%(missing)s", `MISSING(key="missing")`}, | |
127 {"replacing %(foo)q", `replacing "bar"`}, | |
128 {"replacing (%(foo)q)", `replacing ("bar")`}, | |
129 } | |
130 | |
131 Convey(`A testing Data object`, t, func() { | |
132 data := Data{ | |
133 "foo": {"bar", ""}, | |
134 } | |
135 | |
136 for _, testCase := range testCases { | |
137 Convey(fmt.Sprintf(`Formatting %q yields: %q`, testCase.
format, testCase.expected), func() { | |
138 So(data.Format(testCase.format), ShouldEqual, te
stCase.expected) | |
139 }) | |
140 } | |
141 }) | |
142 } | |
OLD | NEW |