/* 'actual' should be an `*errors.Error`; 'expected' should be an `*errors.ErrorClass`; we'll check that the error is under the umbrella of the error class. */ func ShouldBeErrorClass(actual interface{}, expected ...interface{}) string { err, ok := actual.(error) if !ok { return fmt.Sprintf("You must provide an `error` as the first argument to this assertion; got `%T`", actual) } var class *errors.ErrorClass switch len(expected) { case 0: return "You must provide a spacemonkey `ErrorClass` as the expectation parameter to this assertion." case 1: cls, ok := expected[0].(*errors.ErrorClass) if !ok { return "You must provide a spacemonkey `ErrorClass` as the expectation parameter to this assertion." } class = cls default: return "You must provide one parameter as an expectation to this assertion." } // checking if this is nil is surprisingly complicated due to https://golang.org/doc/faq#nil_error if reflect.ValueOf(err).IsNil() { return fmt.Sprintf("Expected error to be of class %q but it was nil!", class.String()) } spaceClass := errors.GetClass(err) if spaceClass.Is(class) { return "" } return fmt.Sprintf("Expected error to be of class %q but it had %q instead! (Full message: %s)", class.String(), spaceClass.String(), err.Error()) }
// GetErrorBody will return the user-visible error message given an error. // The message will be determined by errors.GetMessage() unless the error class // has an error body overridden by OverrideErrorBody. func GetErrorBody(err error) string { rv := errors.GetData(err, errorBody) message, ok := rv.(string) if !ok { return errors.GetMessage(err) } class := errors.GetClass(err) if class == nil { return message } return fmt.Sprintf("%s: %s", class.String(), message) }
// Finish records a successful task completion. You must pass a pointer to // the named error return value (or nil if there isn't one) and the result // of recover() out of the method that was deferred for this to work right. // Finish will re-panic any recovered panics (provided it wasn't a nil panic) // after bookkeeping. func (c *TaskCtx) Finish(err_ref *error, rec interface{}) { duration_nanoseconds := int64(c.ElapsedTime()) var error_name string var err error if err_ref != nil { err = *err_ref } if rec != nil { var ok bool err, ok = rec.(error) if !ok || err == nil { err = errors.PanicError.New("%v", rec) } } if err != nil { error_name = errors.GetClass(err).String() max_len := Config.MaxErrorLength if len(error_name) > max_len { error_name = error_name[:max_len] } error_name = SanitizeName(error_name) } // we keep granularity on the order microseconds, which should keep // sum_squared useful duration_microseconds := int64(duration_nanoseconds / microsecondInNanoseconds) c.monitor.mtx.Lock() c.monitor.current -= 1 c.monitor.total_completed += 1 delete(c.monitor.running, c) if err != nil { c.monitor.errors[error_name] += 1 if rec != nil { c.monitor.panics += 1 } c.monitor.error_timing.Add(duration_microseconds) } else { c.monitor.success_timing.Add(duration_microseconds) c.monitor.success += 1 } c.monitor.mtx.Unlock() c.monitor.total_timing.Add(duration_microseconds) // doh, we didn't actually want to stop the panic codepath. // we have to repanic. Oh and great, panics can be nil. Welp! if rec != nil { panic(rec) } }
func (f *FuncStats) end(err error, panicked bool, duration time.Duration) { atomic.AddInt64(&f.current, -1) f.parentsAndMutex.Lock() if panicked { f.panics -= 1 f.failureTimes.Insert(duration) f.parentsAndMutex.Unlock() return } if err == nil { f.successTimes.Insert(duration) f.parentsAndMutex.Unlock() return } f.failureTimes.Insert(duration) f.errors[errors.GetClass(err).String()] += 1 f.parentsAndMutex.Unlock() }
/* 'actual' should be a `func()`; 'expected' should be an `*errors.ErrorClass`; we'll run the function, and check that it panics, and that the error is under the umbrella of the error class. */ func ShouldPanicWith(actual interface{}, expected ...interface{}) string { fn, ok := actual.(func()) if !ok { return fmt.Sprintf("You must provide a `func()` as the first argument to this assertion; got `%T`", actual) } var errClass *errors.ErrorClass switch len(expected) { case 0: return "You must provide a spacemonkey `ErrorClass` as the expectation parameter to this assertion." case 1: cls, ok := expected[0].(*errors.ErrorClass) if !ok { return "You must provide a spacemonkey `ErrorClass` as the expectation parameter to this assertion." } errClass = cls default: return "You must provide one parameter as an expectation to this assertion." } var caught error try.Do( fn, ).CatchAll(func(err error) { caught = err }).Done() if caught == nil { return fmt.Sprintf("Expected error to be of class %q but no error was raised!", errClass.String()) } spaceClass := errors.GetClass(caught) if spaceClass.Is(errClass) { return "" } return fmt.Sprintf("Expected error to be of class %q but it had %q instead! (Full message: %s)", errClass.String(), spaceClass.String(), caught.Error()) }