From 3b602c6e81234a68503fe716be46491ef65fbe41 Mon Sep 17 00:00:00 2001 From: bhenderson Date: Thu, 10 Jul 2014 08:44:52 -0700 Subject: [PATCH 1/6] Add InDelta() for comparing floats. --- assert/assertions.go | 58 ++++++++++++++++++++++++++++++++++++++- assert/assertions_test.go | 34 +++++++++++++++++++++++ 2 files changed, 91 insertions(+), 1 deletion(-) diff --git a/assert/assertions.go b/assert/assertions.go index 72247d0..820f12c 100644 --- a/assert/assertions.go +++ b/assert/assertions.go @@ -475,7 +475,63 @@ func WithinDuration(t TestingT, expected, actual time.Time, delta time.Duration, dt := expected.Sub(actual) if dt < -delta || dt > delta { - return Fail(t, fmt.Sprintf("Max difference between %v and %v allowed is %v, but difference was %v", expected, actual, dt, delta), msgAndArgs...) + return Fail(t, fmt.Sprintf("Max difference between %v and %v allowed is %v, but difference was %v", expected, actual, delta, dt), msgAndArgs...) + } + + return true +} + +func toFloat(x interface{}) (float64, bool) { + var xf float64 + xok := true + + switch xn := x.(type) { + case uint8: + xf = float64(xn) + case uint16: + xf = float64(xn) + case uint32: + xf = float64(xn) + case uint64: + xf = float64(xn) + case int: + xf = float64(xn) + case int8: + xf = float64(xn) + case int16: + xf = float64(xn) + case int32: + xf = float64(xn) + case int64: + xf = float64(xn) + case float32: + xf = float64(xn) + case float64: + xf = float64(xn) + default: + xok = false + } + + return xf, xok +} + +// InDelta asserts that the two numerals are within delta of each other. +// +// assert.InDelta(t, math.Pi, (22 / 7.0), 0.01) +// +// Returns whether the assertion was successful (true) or not (false). +func InDelta(t TestingT, expected, actual interface{}, delta float64, msgAndArgs ...interface{}) bool { + + af, aok := toFloat(expected) + bf, bok := toFloat(actual) + + if !aok || !bok { + return Fail(t, fmt.Sprintf("Parameters must be numerical"), msgAndArgs...) + } + + dt := af - bf + if dt < -delta || dt > delta { + return Fail(t, fmt.Sprintf("Max difference between %v and %v allowed is %v, but difference was %v", expected, actual, delta, dt), msgAndArgs...) } return true diff --git a/assert/assertions_test.go b/assert/assertions_test.go index d006169..ecb74e3 100644 --- a/assert/assertions_test.go +++ b/assert/assertions_test.go @@ -418,3 +418,37 @@ func TestWithinDuration(t *testing.T) { False(t, WithinDuration(mockT, a, b, -11*time.Second), "A 10s difference is not within a 9s time difference") False(t, WithinDuration(mockT, b, a, -11*time.Second), "A 10s difference is not within a 9s time difference") } + +func TestInDelta(t *testing.T) { + mockT := new(testing.T) + + True(t, InDelta(mockT, 1.001, 1, 0.01), "|1.001 - 1| <= 0.01") + True(t, InDelta(mockT, 1, 1.001, 0.01), "|1 - 1.001| <= 0.01") + True(t, InDelta(mockT, 1, 2, 1), "|1 - 2| <= 1") + False(t, InDelta(mockT, 1, 2, 0.5), "Expected |1 - 2| <= 0.5 to fail") + False(t, InDelta(mockT, 2, 1, 0.5), "Expected |2 - 1| <= 0.5 to fail") + False(t, InDelta(mockT, "", nil, 1), "Expected non numerals to fail") + + cases := []struct { + a, b interface{} + delta float64 + }{ + {uint8(2), uint8(1), 1}, + {uint16(2), uint16(1), 1}, + {uint32(2), uint32(1), 1}, + {uint64(2), uint64(1), 1}, + + {int(2), int(1), 1}, + {int8(2), int8(1), 1}, + {int16(2), int16(1), 1}, + {int32(2), int32(1), 1}, + {int64(2), int64(1), 1}, + + {float32(2), float32(1), 1}, + {float64(2), float64(1), 1}, + } + + for _, tc := range cases { + True(t, InDelta(mockT, tc.a, tc.b, tc.delta), "Expected |%V - %V| <= %v", tc.a, tc.b, tc.delta) + } +} From 60a27ebea665f98006328b8b54fc3bcab76f5e8b Mon Sep 17 00:00:00 2001 From: bhenderson Date: Thu, 10 Jul 2014 08:58:49 -0700 Subject: [PATCH 2/6] Add InEpsilon(). --- assert/assertions.go | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/assert/assertions.go b/assert/assertions.go index 820f12c..6cd92e9 100644 --- a/assert/assertions.go +++ b/assert/assertions.go @@ -537,6 +537,39 @@ func InDelta(t TestingT, expected, actual interface{}, delta float64, msgAndArgs return true } +// 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 { + af, aok := toFloat(expected) + bf, bok := toFloat(actual) + + if !aok || !bok { + return Fail(t, fmt.Sprintf("Parameters must be numerical"), msgAndArgs...) + } + + if af < 0 { + af = -af + } + if bf < 0 { + bf = -bf + } + var delta float64 + if af < bf { + delta = af * epsilon + } else { + delta = bf * epsilon + } + + // delta = min(|expected|, |actual|) * epsilon + dt := af - bf + if dt < -delta || dt > delta { + return Fail(t, fmt.Sprintf("Max difference between %v and %v allowed is %v, but difference was %v", expected, actual, delta, dt), msgAndArgs...) + } + + return true +} + /* Errors */ From 256f07baef79ba356ee17f9ed2d6979dd496a3c6 Mon Sep 17 00:00:00 2001 From: bhenderson Date: Thu, 10 Jul 2014 09:12:30 -0700 Subject: [PATCH 3/6] Refactor InEpsilon() to use InDelta(). --- assert/assertions.go | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/assert/assertions.go b/assert/assertions.go index 6cd92e9..cc05752 100644 --- a/assert/assertions.go +++ b/assert/assertions.go @@ -537,15 +537,14 @@ func InDelta(t TestingT, expected, actual interface{}, delta float64, msgAndArgs return true } -// 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 { +// min(|expected|, |actual|) * epsilon +func calcEpsilonDelta(expected, actual interface{}, epsilon float64) float64 { af, aok := toFloat(expected) bf, bok := toFloat(actual) if !aok || !bok { - return Fail(t, fmt.Sprintf("Parameters must be numerical"), msgAndArgs...) + // invalid input + return 0 } if af < 0 { @@ -560,14 +559,16 @@ func InEpsilon(t TestingT, expected, actual interface{}, epsilon float64, msgAnd } else { delta = bf * epsilon } + return delta +} - // delta = min(|expected|, |actual|) * epsilon - dt := af - bf - if dt < -delta || dt > delta { - return Fail(t, fmt.Sprintf("Max difference between %v and %v allowed is %v, but difference was %v", expected, actual, delta, dt), msgAndArgs...) - } +// 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) - return true + return InDelta(t, expected, actual, delta, msgAndArgs...) } /* From d8398675f90a9d8c93784ecf08cb4edc78bcc99d Mon Sep 17 00:00:00 2001 From: bhenderson Date: Thu, 10 Jul 2014 09:26:13 -0700 Subject: [PATCH 4/6] Add InDelta() and InEpsilon() to docs and remove doc duplication. --- assert/doc.go | 52 ++++----------------------------------------------- 1 file changed, 4 insertions(+), 48 deletions(-) diff --git a/assert/doc.go b/assert/doc.go index 678ac8a..0f3c087 100644 --- a/assert/doc.go +++ b/assert/doc.go @@ -44,54 +44,6 @@ // allowing custom error messages to be appended to the message the assertion method outputs. // // Here is an overview of the assert functions: -// -// assert.Equal(t, expected, actual [, message [, format-args]) -// -// assert.NotEqual(t, notExpected, actual [, message [, format-args]]) -// -// assert.True(t, actualBool [, message [, format-args]]) -// -// assert.False(t, actualBool [, message [, format-args]]) -// -// assert.Nil(t, actualObject [, message [, format-args]]) -// -// assert.NotNil(t, actualObject [, message [, format-args]]) -// -// assert.Empty(t, actualObject [, message [, format-args]]) -// -// assert.NotEmpty(t, actualObject [, message [, format-args]]) -// -// assert.Error(t, errorObject [, message [, format-args]]) -// -// assert.NoError(t, errorObject [, message [, format-args]]) -// -// assert.EqualError(t, theError, errString [, message [, format-args]]) -// -// assert.Implements(t, (*MyInterface)(nil), new(MyObject) [,message [, format-args]]) -// -// assert.IsType(t, expectedObject, actualObject [, message [, format-args]]) -// -// assert.Contains(t, string, substring [, message [, format-args]]) -// -// assert.NotContains(t, string, substring [, message [, format-args]]) -// -// assert.Panics(t, func(){ -// -// // call code that should panic -// -// } [, message [, format-args]]) -// -// assert.NotPanics(t, func(){ -// -// // call code that should not panic -// -// } [, message [, format-args]]) -// -// assert.WithinDuration(t, timeA, timeB, deltaTime, [, message [, format-args]]) -// -// assert package contains Assertions object. it has assertion methods. -// -// Here is an overview of the assert functions: // assert.Equal(expected, actual [, message [, format-args]) // // assert.NotEqual(notExpected, actual [, message [, format-args]]) @@ -135,4 +87,8 @@ // } [, message [, format-args]]) // // assert.WithinDuration(timeA, timeB, deltaTime, [, message [, format-args]]) +// +// assert.InDelta(numA, numB, delta, [, message [, format-args]]) +// +// assert.InEpsilon(numA, numB, epsilon, [, message [, format-args]]) package assert From d0803a1a3a9839ff32e03d64870d73d917eb963c Mon Sep 17 00:00:00 2001 From: bhenderson Date: Thu, 10 Jul 2014 09:27:48 -0700 Subject: [PATCH 5/6] Revert "Add InDelta() and InEpsilon() to docs and remove doc duplication." This reverts commit d8398675f90a9d8c93784ecf08cb4edc78bcc99d. --- assert/doc.go | 52 +++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 48 insertions(+), 4 deletions(-) diff --git a/assert/doc.go b/assert/doc.go index 0f3c087..678ac8a 100644 --- a/assert/doc.go +++ b/assert/doc.go @@ -44,6 +44,54 @@ // allowing custom error messages to be appended to the message the assertion method outputs. // // Here is an overview of the assert functions: +// +// assert.Equal(t, expected, actual [, message [, format-args]) +// +// assert.NotEqual(t, notExpected, actual [, message [, format-args]]) +// +// assert.True(t, actualBool [, message [, format-args]]) +// +// assert.False(t, actualBool [, message [, format-args]]) +// +// assert.Nil(t, actualObject [, message [, format-args]]) +// +// assert.NotNil(t, actualObject [, message [, format-args]]) +// +// assert.Empty(t, actualObject [, message [, format-args]]) +// +// assert.NotEmpty(t, actualObject [, message [, format-args]]) +// +// assert.Error(t, errorObject [, message [, format-args]]) +// +// assert.NoError(t, errorObject [, message [, format-args]]) +// +// assert.EqualError(t, theError, errString [, message [, format-args]]) +// +// assert.Implements(t, (*MyInterface)(nil), new(MyObject) [,message [, format-args]]) +// +// assert.IsType(t, expectedObject, actualObject [, message [, format-args]]) +// +// assert.Contains(t, string, substring [, message [, format-args]]) +// +// assert.NotContains(t, string, substring [, message [, format-args]]) +// +// assert.Panics(t, func(){ +// +// // call code that should panic +// +// } [, message [, format-args]]) +// +// assert.NotPanics(t, func(){ +// +// // call code that should not panic +// +// } [, message [, format-args]]) +// +// assert.WithinDuration(t, timeA, timeB, deltaTime, [, message [, format-args]]) +// +// assert package contains Assertions object. it has assertion methods. +// +// Here is an overview of the assert functions: // assert.Equal(expected, actual [, message [, format-args]) // // assert.NotEqual(notExpected, actual [, message [, format-args]]) @@ -87,8 +135,4 @@ // } [, message [, format-args]]) // // assert.WithinDuration(timeA, timeB, deltaTime, [, message [, format-args]]) -// -// assert.InDelta(numA, numB, delta, [, message [, format-args]]) -// -// assert.InEpsilon(numA, numB, epsilon, [, message [, format-args]]) package assert From 0241e55b78fc89454efe21eda0433e5da0454e68 Mon Sep 17 00:00:00 2001 From: bhenderson Date: Thu, 10 Jul 2014 09:31:52 -0700 Subject: [PATCH 6/6] Add to forward_assertions and fix docs. --- assert/doc.go | 8 ++++++++ assert/forward_assertions.go | 16 ++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/assert/doc.go b/assert/doc.go index 678ac8a..a660769 100644 --- a/assert/doc.go +++ b/assert/doc.go @@ -89,6 +89,10 @@ // // assert.WithinDuration(t, timeA, timeB, deltaTime, [, message [, format-args]]) // +// assert.InDelta(t, numA, numB, delta, [, message [, format-args]]) +// +// assert.InEpsilon(t, numA, numB, epsilon, [, message [, format-args]]) +// // assert package contains Assertions object. it has assertion methods. // // Here is an overview of the assert functions: @@ -135,4 +139,8 @@ // } [, message [, format-args]]) // // assert.WithinDuration(timeA, timeB, deltaTime, [, message [, format-args]]) +// +// assert.InDelta(numA, numB, delta, [, message [, format-args]]) +// +// assert.InEpsilon(numA, numB, epsilon, [, message [, format-args]]) package assert diff --git a/assert/forward_assertions.go b/assert/forward_assertions.go index 7172074..0b77aaf 100644 --- a/assert/forward_assertions.go +++ b/assert/forward_assertions.go @@ -168,6 +168,22 @@ func (a *Assertions) WithinDuration(expected, actual time.Time, delta time.Durat return WithinDuration(a.t, expected, actual, delta, msgAndArgs...) } +// InDelta asserts that the two numerals are within delta of each other. +// +// assert.InDelta(t, math.Pi, (22 / 7.0), 0.01) +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) InDelta(t TestingT, expected, actual interface{}, delta float64, msgAndArgs ...interface{}) bool { + return InDelta(a.t, expected, actual, delta, msgAndArgs...) +} + +// InEpsilon asserts that expected and actual have a relative error less than epsilon +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) InEpsilon(t TestingT, expected, actual interface{}, epsilon float64, msgAndArgs ...interface{}) bool { + return InEpsilon(a.t, expected, actual, epsilon, msgAndArgs...) +} + // NoError asserts that a function returned no error (i.e. `nil`). // // actualObj, err := SomeFunction()