Skip to content

Commit 74cac7a

Browse files
authored
Merge pull request #10 from ninedraft/dev
Custom error factory support
2 parents 694d046 + 1a8fc28 commit 74cac7a

File tree

3 files changed

+83
-6
lines changed

3 files changed

+83
-6
lines changed

go.mod

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module github.com/storozhukBM/verifier
2+
3+
go 1.12

verifier.go

+20-5
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,19 @@ import (
1818
func New() *Verify {
1919
v := &Verify{
2020
creationStack: captureCreationStack(),
21+
errFactory: fmt.Errorf,
2122
}
2223
runtime.SetFinalizer(v, printWarningOnUncheckedVerification)
2324
return v
2425
}
2526

27+
// WithErrFactory sets error construction function (default: fmt.Errorf).
28+
// Use it to set custom error type of error, returned by Verify.GetError().
29+
func (v *Verify) WithErrFactory(factory func(string, ...interface{}) error) *Verify {
30+
v.errFactory = factory
31+
return v
32+
}
33+
2634
// Offensive creates verification instance (not-recommended).
2735
// It tracks verification state and stops application process when founds unchecked verification.
2836
// If you forget to check internal error, using `GetError` or `PanicOnError` methods,
@@ -45,6 +53,7 @@ func Offensive() *Verify {
4553
type Verify struct {
4654
creationStack []uintptr
4755
err error
56+
errFactory func(string, ...interface{}) error
4857
checked bool
4958
}
5059

@@ -72,29 +81,28 @@ func (v *Verify) WithError(positiveCondition bool, err error) *Verify {
7281
// That verifies condition passed as first argument.
7382
// If `positiveCondition == true`, verification will proceed for other checks.
7483
// If `positiveCondition == false`, internal state will be filled with error,
75-
// using message argument as format in fmt.Errorf(message, args...).
84+
// using message argument as format in error factory func(message, args...) (default: fmt.Errorf).
7685
// After the first failed verification all others won't count and predicates won't be evaluated.
7786
func (v *Verify) That(positiveCondition bool, message string, args ...interface{}) *Verify {
7887
vObj := v
7988
if v == nil {
8089
vObj = &Verify{}
8190
}
82-
8391
vObj.checked = false
8492
if vObj.err != nil {
8593
return vObj
8694
}
8795
if positiveCondition {
8896
return vObj
8997
}
90-
vObj.err = fmt.Errorf(message, args...)
98+
vObj.err = vObj.errorf(message, args...)
9199
return vObj
92100
}
93101

94102
// That evaluates predicate passed as first argument.
95103
// If `predicate() == true`, verification will proceed for other checks.
96104
// If `predicate() == false`, internal state will be filled with error,
97-
// using message argument as format in fmt.Errorf(message, args...).
105+
// using message argument as format in error factory func(message, args...) (default: fmt.Errorf).
98106
// After the first failed verification all others won't count and predicates won't be evaluated.
99107
func (v *Verify) Predicate(predicate func() bool, message string, args ...interface{}) *Verify {
100108
vObj := v
@@ -108,7 +116,7 @@ func (v *Verify) Predicate(predicate func() bool, message string, args ...interf
108116
if predicate() {
109117
return vObj
110118
}
111-
vObj.err = fmt.Errorf(message, args...)
119+
vObj.err = vObj.errorf(message, args...)
112120
return vObj
113121
}
114122

@@ -144,6 +152,13 @@ func (v *Verify) String() string {
144152
return "verification failure: " + v.err.Error()
145153
}
146154

155+
func (v *Verify) errorf(message string, args ...interface{}) error {
156+
if v.errFactory == nil {
157+
v.errFactory = fmt.Errorf
158+
}
159+
return v.errFactory(message, args...)
160+
}
161+
147162
func (v *Verify) printCreationStack(writer io.Writer) {
148163
frames := runtime.CallersFrames(v.creationStack)
149164
for {

verifier_test.go

+60-1
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,18 @@ package verifier_test
33
import (
44
"bytes"
55
"errors"
6-
"github.com/storozhukBM/verifier"
6+
"fmt"
77
"math/rand"
88
"os"
9+
"os/exec"
10+
"reflect"
911
"runtime"
1012
"strings"
1113
"sync"
1214
"testing"
1315
"time"
16+
17+
"github.com/storozhukBM/verifier"
1418
)
1519

1620
func TestVerifier_positive_conditions(t *testing.T) {
@@ -203,6 +207,61 @@ func TestVerifier_negative_nil_panic(t *testing.T) {
203207
verify.PanicOnError()
204208
}
205209

210+
type TestError struct {
211+
message string
212+
}
213+
214+
func NewTestError(msg string, args ...interface{}) error {
215+
return TestError{
216+
message: fmt.Sprintf(msg, args...),
217+
}
218+
}
219+
220+
func (err TestError) Error() string {
221+
return err.message
222+
}
223+
224+
func TestVerifies_WithErrorFactory(test *testing.T) {
225+
226+
var tf = func(name string, ver *verifier.Verify, err error) {
227+
test.Run(name, func(test *testing.T) {
228+
var got = ver.That(false, "test error message").GetError()
229+
if reflect.TypeOf(err) != reflect.TypeOf(got) {
230+
test.Fatalf("got error of invalid type %T", got)
231+
}
232+
})
233+
}
234+
235+
tf("empty Verifier", &verifier.Verify{}, fmt.Errorf(""))
236+
tf("verifier created with New factory", verifier.New(), fmt.Errorf(""))
237+
tf("verifier with TestError factory", verifier.New().WithErrFactory(NewTestError), TestError{})
238+
}
239+
240+
// Testing Offensive verifier, which crashes programm if GCed unchecked.
241+
// Idea: https://talks.golang.org/2014/testing.slide
242+
func TestOffensive(test *testing.T) {
243+
if os.Getenv("BE_CRASHER") == "1" {
244+
var verifiers = make([]*verifier.Verify, 10000)
245+
for i := range verifiers {
246+
verifiers[i] = verifier.
247+
Offensive().
248+
That(false, "test error message %d", i)
249+
}
250+
for i := range verifiers {
251+
verifiers[i] = nil
252+
}
253+
runtime.GC()
254+
return
255+
}
256+
cmd := exec.Command(os.Args[0], "-test.run=TestCrasher", "-race")
257+
cmd.Env = append(os.Environ(), "BE_CRASHER=1")
258+
err := cmd.Run()
259+
if e, ok := err.(*exec.ExitError); ok && !e.Success() {
260+
return
261+
}
262+
test.Fatalf("process ran with err %v, want exit status 1", err)
263+
}
264+
206265
type safeBuffer struct {
207266
b bytes.Buffer
208267
m sync.Mutex

0 commit comments

Comments
 (0)