1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133
|
package cty
import (
"reflect"
"sort"
"testing"
"github.com/google/go-cmp/cmp"
)
func TestSetOperations(t *testing.T) {
// This test is for the mechanisms that allow a calling application to
// implement set operations using the underlying set.Set type. This is
// not expected to be a common case but is useful, for example, for
// implementing the set-related functions in function/stdlib .
s1 := SetVal([]Value{
StringVal("a"),
StringVal("b"),
StringVal("c"),
})
s2 := SetVal([]Value{
StringVal("c"),
StringVal("d"),
StringVal("e"),
})
s1r := s1.AsValueSet()
s2r := s2.AsValueSet()
s3r := s1r.Union(s2r)
s3 := SetValFromValueSet(s3r)
if got, want := s3.LengthInt(), 5; got != want {
t.Errorf("wrong length %d; want %d", got, want)
}
for _, wantStr := range []string{"a", "b", "c", "d", "e"} {
if got, want := s3.HasElement(StringVal(wantStr)), True; got != want {
t.Errorf("missing element %q", wantStr)
}
}
}
func TestSetOfCapsuleType(t *testing.T) {
type capsuleTypeForSetTests struct {
name string
}
encapsulatedNames := func(vals []Value) []string {
if len(vals) == 0 {
return nil
}
ret := make([]string, len(vals))
for i, v := range vals {
ret[i] = v.EncapsulatedValue().(*capsuleTypeForSetTests).name
}
sort.Strings(ret)
return ret
}
typeWithHash := CapsuleWithOps("with hash function", reflect.TypeOf(capsuleTypeForSetTests{}), &CapsuleOps{
RawEquals: func(a, b interface{}) bool {
return a.(*capsuleTypeForSetTests).name == b.(*capsuleTypeForSetTests).name
},
HashKey: func(v interface{}) string {
return v.(*capsuleTypeForSetTests).name
},
})
typeWithoutHash := CapsuleWithOps("without hash function", reflect.TypeOf(capsuleTypeForSetTests{}), &CapsuleOps{
RawEquals: func(a, b interface{}) bool {
return a.(*capsuleTypeForSetTests).name == b.(*capsuleTypeForSetTests).name
},
})
typeWithoutEquals := Capsule("without hash function", reflect.TypeOf(capsuleTypeForSetTests{}))
t.Run("with hash", func(t *testing.T) {
// When we provide a hashing function the set implementation can
// optimize its internal storage by spreading values over multiple
// smaller buckets.
v := SetVal([]Value{
CapsuleVal(typeWithHash, &capsuleTypeForSetTests{"a"}),
CapsuleVal(typeWithHash, &capsuleTypeForSetTests{"b"}),
CapsuleVal(typeWithHash, &capsuleTypeForSetTests{"a"}),
CapsuleVal(typeWithHash, &capsuleTypeForSetTests{"c"}),
})
got := encapsulatedNames(v.AsValueSlice())
want := []string{"a", "b", "c"}
if diff := cmp.Diff(want, got); diff != "" {
t.Errorf("wrong element names\n%s", diff)
}
})
t.Run("without hash", func(t *testing.T) {
// When we don't provide a hashing function the outward behavior
// should still be identical but the internal storage won't be
// so efficient, due to everything living in one big bucket and
// so we have to scan over all values to test if a particular
// element is present.
v := SetVal([]Value{
CapsuleVal(typeWithoutHash, &capsuleTypeForSetTests{"a"}),
CapsuleVal(typeWithoutHash, &capsuleTypeForSetTests{"b"}),
CapsuleVal(typeWithoutHash, &capsuleTypeForSetTests{"a"}),
CapsuleVal(typeWithoutHash, &capsuleTypeForSetTests{"c"}),
})
got := encapsulatedNames(v.AsValueSlice())
want := []string{"a", "b", "c"}
if diff := cmp.Diff(want, got); diff != "" {
t.Errorf("wrong element names\n%s", diff)
}
})
t.Run("without equals", func(t *testing.T) {
// When we don't even have an equals function we can still store
// values in the set but we will use the default capsule type
// behavior of comparing by pointer equality. That means that
// the name field doesn't coalesce anymore, but two instances
// of this same d should.
d := &capsuleTypeForSetTests{"d"}
v := SetVal([]Value{
CapsuleVal(typeWithoutEquals, &capsuleTypeForSetTests{"a"}),
CapsuleVal(typeWithoutEquals, &capsuleTypeForSetTests{"b"}),
CapsuleVal(typeWithoutEquals, d),
CapsuleVal(typeWithoutEquals, &capsuleTypeForSetTests{"a"}),
CapsuleVal(typeWithoutEquals, &capsuleTypeForSetTests{"c"}),
CapsuleVal(typeWithoutEquals, d),
})
got := encapsulatedNames(v.AsValueSlice())
want := []string{"a", "a", "b", "c", "d"}
if diff := cmp.Diff(want, got); diff != "" {
t.Errorf("wrong element names\n%s", diff)
}
})
}
|