Skip to content

Commit fec5be7

Browse files
committedDec 30, 2015
change mind on panic recovery
1 parent 0a7c54f commit fec5be7

File tree

4 files changed

+9
-90
lines changed

4 files changed

+9
-90
lines changed
 

‎README.md

+7-2
Original file line numberDiff line numberDiff line change
@@ -46,15 +46,20 @@ Write a `Use` callback around the code's original behavior, and a `Try` around t
4646
* Randomizes the order in which `Use` and `Try` callbacks are run,
4747
* Measures the durations of all behaviors,
4848
* Compares the result of `Try` to the result of `Use`,
49-
* Swallows (but records) any errors or panics in the `Try` callback, and
49+
* Swallows (but records) any errors in the `Try` callback, and
5050
* Publishes all this information.
5151

5252
The `Use` callback is called the **control**. The `Try` callback is called the **candidate**.
5353

54-
NOTE: mention helpers like scientist.Bool()
54+
TODO: mention helpers like scientist.Bool()
5555

5656
If you don't declare any `Try` callbacks, none of the Scientist machinery is invoked and the control value is always returned.
5757

58+
Experiments do not attempt to recover from any runtime panics, and are not
59+
goroutine safe. Any `*scientist.Experiment` objects should be Run and discarded
60+
immediately after being initialized. Ideally, your application should already
61+
handle any runtime panics somehow.
62+
5863
## Making science useful
5964

6065
The examples above will run, but they're not really *doing* anything. The `Try` callbacks run every time and none of the results get published. Replace the default experiment implementation to control execution and reporting:

‎experiment.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ func (e *Experiment) RunBehavior(name string) (interface{}, error) {
106106
return nil, behaviorNotFound(e, name)
107107
}
108108

109-
return runBehavior(e, name, behavior)
109+
return behavior()
110110
}
111111

112112
func (e *Experiment) resultErr(name string, err error) ResultError {

‎experiment_test.go

-67
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package scientist
22

33
import (
4-
"errors"
54
"fmt"
65
"testing"
76
)
@@ -282,69 +281,3 @@ func TestExperimentRunIfError(t *testing.T) {
282281
t.Errorf("result errors never reported!")
283282
}
284283
}
285-
286-
type testStringer struct {
287-
output string
288-
}
289-
290-
func (s testStringer) String() string {
291-
return s.output
292-
}
293-
294-
func TestRecoverCandidatePanic(t *testing.T) {
295-
e := New("recover")
296-
e.Use(func() (interface{}, error) {
297-
return 1, nil
298-
})
299-
300-
e.Try(func() (interface{}, error) {
301-
panic("candidate")
302-
})
303-
304-
e.Behavior("two", func() (interface{}, error) {
305-
panic(errors.New("candidate"))
306-
})
307-
308-
e.Behavior("three", func() (interface{}, error) {
309-
panic(testStringer{"candidate"})
310-
})
311-
312-
e.ReportErrors(func(errors ...ResultError) {
313-
for _, e := range errors {
314-
t.Errorf("unexpected experiment error: %v", e)
315-
}
316-
})
317-
318-
published := false
319-
e.Publish(func(r Result) error {
320-
published = true
321-
for _, c := range r.Candidates {
322-
if c.Value != nil {
323-
t.Errorf("Unexpected candidate %q value: %v", c.Name, c.Value)
324-
}
325-
326-
if c.Err == nil {
327-
t.Errorf("expected candidate %q panic err!", c.Name)
328-
}
329-
330-
if msg := c.Err.Error(); msg != "candidate" {
331-
t.Errorf("bad candidate %q error: %v", c.Name, msg)
332-
}
333-
}
334-
335-
return nil
336-
})
337-
338-
v, err := e.Run()
339-
if v != 1 {
340-
t.Errorf("Unexpected control value: %d", v)
341-
}
342-
343-
if err != nil {
344-
t.Errorf("Unexpected control error: %v", err)
345-
}
346-
347-
if !published {
348-
t.Errorf("results never published")
349-
}
350-
}

‎scientist.go

+1-20
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package scientist
22

33
import (
4-
"errors"
54
"fmt"
65
"time"
76
)
@@ -160,7 +159,7 @@ func observe(e *Experiment, name string, b behaviorFunc) *Observation {
160159
o.Runtime = time.Since(o.Started)
161160
o.Err = behaviorNotFound(e, name)
162161
} else {
163-
v, err := runBehavior(e, name, b)
162+
v, err := b()
164163
o.Runtime = time.Since(o.Started)
165164
o.Value = v
166165
o.Err = err
@@ -169,24 +168,6 @@ func observe(e *Experiment, name string, b behaviorFunc) *Observation {
169168
return o
170169
}
171170

172-
func runBehavior(e *Experiment, name string, b behaviorFunc) (value interface{}, err error) {
173-
defer func() {
174-
if er := recover(); er != nil {
175-
value = nil
176-
switch t := er.(type) {
177-
case string:
178-
err = errors.New(t)
179-
case error:
180-
err = t
181-
default:
182-
err = fmt.Errorf("%v", t)
183-
}
184-
}
185-
}()
186-
value, err = b()
187-
return
188-
}
189-
190171
type ResultError struct {
191172
Operation string
192173
Experiment string

0 commit comments

Comments
 (0)