config/hcl2shim: ValuesSDKEquivalent float64 comparison of numbers

The SDK uses only the native int and float64 types internally for values
that are specified as being "number" in schema, so for SDK purposes only
a float64 level of precision is significant.

To avoid any weirdness introduced as we shim and un-shim numbers, we'll
reduce floating point numbers to float64 precision before comparing them
to try to mimic the result the SDK itself would've gotten from comparing
its own float64 versions of these values using the Go "==" operator.
This commit is contained in:
Martin Atkins 2019-01-22 18:18:32 -08:00
parent 46a4628782
commit 995042666a
2 changed files with 59 additions and 0 deletions

View File

@ -60,6 +60,8 @@ func ValuesSDKEquivalent(a, b cty.Value) bool {
return valuesSDKEquivalentMappings(a, b)
case aTy.IsObjectType() && bTy.IsObjectType():
return valuesSDKEquivalentMappings(a, b)
case aTy == cty.Number && bTy == cty.Number:
return valuesSDKEquivalentNumbers(a, b)
default:
// We've now covered all the interesting cases, so anything that falls
// down here cannot be equivalent.
@ -175,3 +177,38 @@ func valuesSDKEquivalentMappings(a, b cty.Value) bool {
}
return true
}
// valuesSDKEquivalentNumbers decides equivalence for two number values based
// on the fact that the SDK uses int and float64 representations while
// cty (and thus Terraform Core) uses big.Float, and so we expect to lose
// precision in the round-trip.
//
// This does _not_ attempt to allow for an epsilon difference that may be
// caused by accumulated innacuracy in a float calculation, under the
// expectation that providers generally do not actually do compuations on
// floats and instead just pass string representations of them on verbatim
// to remote APIs. A remote API _itself_ may introduce inaccuracy, but that's
// a problem for the provider itself to deal with, based on its knowledge of
// the remote system, e.g. using DiffSuppressFunc.
func valuesSDKEquivalentNumbers(a, b cty.Value) bool {
if a.RawEquals(b) {
return true // easy
}
af := a.AsBigFloat()
bf := b.AsBigFloat()
if af.IsInt() != bf.IsInt() {
return false
}
if af.IsInt() && bf.IsInt() {
return false // a.RawEquals(b) test above is good enough for integers
}
// The SDK supports only int and float64, so if it's not an integer
// we know that only a float64-level of precision can possibly be
// significant.
af64, _ := af.Float64()
bf64, _ := bf.Float64()
return af64 == bf64
}

View File

@ -2,12 +2,19 @@ package hcl2shim
import (
"fmt"
"math/big"
"testing"
"github.com/zclconf/go-cty/cty"
)
func TestValuesSDKEquivalent(t *testing.T) {
piBig, _, err := big.ParseFloat("3.14159265358979323846264338327950288419716939937510582097494459", 10, 512, big.ToZero)
if err != nil {
t.Fatal(err)
}
pi64, _ := piBig.Float64()
tests := []struct {
A, B cty.Value
Want bool
@ -55,6 +62,21 @@ func TestValuesSDKEquivalent(t *testing.T) {
cty.Zero,
true,
},
{
cty.NumberVal(piBig),
cty.Zero,
false,
},
{
cty.NumberFloatVal(pi64),
cty.Zero,
false,
},
{
cty.NumberFloatVal(pi64),
cty.NumberVal(piBig),
true,
},
// Bools
{