diff --git a/assert/assertions.go b/assert/assertions.go index 6905dbf..4be88a3 100644 --- a/assert/assertions.go +++ b/assert/assertions.go @@ -766,42 +766,40 @@ func InDeltaSlice(t TestingT, expected, actual interface{}, delta float64, msgAn return true } -// min(|expected|, |actual|) * epsilon -func calcEpsilonDelta(expected, actual interface{}, epsilon float64) float64 { +func calcRelativeError(expected, actual interface{}) (float64, error) { af, aok := toFloat(expected) + if !aok { + return 0, fmt.Errorf("expected value %q cannot be converted to float", expected) + } + if af == 0 { + return 0, fmt.Errorf("expected value must have a value other than zero to calculate the relative error") + } bf, bok := toFloat(actual) - - if !aok || !bok { - // invalid input - return 0 + if !bok { + return 0, fmt.Errorf("expected value %q cannot be converted to float", actual) } - if af < 0 { - af = -af - } - if bf < 0 { - bf = -bf - } - var delta float64 - if af < bf { - delta = af * epsilon - } else { - delta = bf * epsilon - } - return delta + return math.Abs(af-bf) / math.Abs(af), nil } // InEpsilon asserts that expected and actual have a relative error less than epsilon // // Returns whether the assertion was successful (true) or not (false). func InEpsilon(t TestingT, expected, actual interface{}, epsilon float64, msgAndArgs ...interface{}) bool { - delta := calcEpsilonDelta(expected, actual, epsilon) + actualEpsilon, err := calcRelativeError(expected, actual) + if err != nil { + return Fail(t, err.Error(), msgAndArgs...) + } + if actualEpsilon > epsilon { + return Fail(t, fmt.Sprintf("Relative error is too high: %#v (expected)\n"+ + " < %#v (actual)", actualEpsilon, epsilon), msgAndArgs...) + } - return InDelta(t, expected, actual, delta, msgAndArgs...) + return true } -// InEpsilonSlice is the same as InEpsilon, except it compares two slices. -func InEpsilonSlice(t TestingT, expected, actual interface{}, delta float64, msgAndArgs ...interface{}) bool { +// InEpsilonSlice is the same as InEpsilon, except it compares each value from two slices. +func InEpsilonSlice(t TestingT, expected, actual interface{}, epsilon float64, msgAndArgs ...interface{}) bool { if expected == nil || actual == nil || reflect.TypeOf(actual).Kind() != reflect.Slice || reflect.TypeOf(expected).Kind() != reflect.Slice { @@ -812,7 +810,7 @@ func InEpsilonSlice(t TestingT, expected, actual interface{}, delta float64, msg expectedSlice := reflect.ValueOf(expected) for i := 0; i < actualSlice.Len(); i++ { - result := InEpsilon(t, actualSlice.Index(i).Interface(), expectedSlice.Index(i).Interface(), delta) + result := InEpsilon(t, actualSlice.Index(i).Interface(), expectedSlice.Index(i).Interface(), epsilon) if !result { return result } diff --git a/assert/assertions_test.go b/assert/assertions_test.go index dcbcd5c..15a04b8 100644 --- a/assert/assertions_test.go +++ b/assert/assertions_test.go @@ -830,10 +830,11 @@ func TestInEpsilon(t *testing.T) { {-2.2, -2.1, 0.1}, {uint64(100), uint8(101), 0.01}, {0.1, -0.1, 2}, + {0.1, 0, 2}, } for _, tc := range cases { - True(t, InEpsilon(mockT, tc.a, tc.b, tc.epsilon, "Expected %V and %V to have a relative difference of %v", tc.a, tc.b, tc.epsilon)) + True(t, InEpsilon(t, tc.a, tc.b, tc.epsilon, "Expected %V and %V to have a relative difference of %v", tc.a, tc.b, tc.epsilon), "test: %q", tc) } cases = []struct { @@ -847,6 +848,7 @@ func TestInEpsilon(t *testing.T) { {2.1, -2.2, 1}, {2.1, "bla-bla", 0}, {0.1, -0.1, 1.99}, + {0, 0.1, 2}, // expected must be different to zero } for _, tc := range cases {