route/vendor/github.com/mitchellh/hashstructure/hashstructure_test.go

566 lines
9.9 KiB
Go

package hashstructure
import (
"fmt"
"testing"
"time"
)
func TestHash_identity(t *testing.T) {
cases := []interface{}{
nil,
"foo",
42,
true,
false,
[]string{"foo", "bar"},
[]interface{}{1, nil, "foo"},
map[string]string{"foo": "bar"},
map[interface{}]string{"foo": "bar"},
map[interface{}]interface{}{"foo": "bar", "bar": 0},
struct {
Foo string
Bar []interface{}
}{
Foo: "foo",
Bar: []interface{}{nil, nil, nil},
},
&struct {
Foo string
Bar []interface{}
}{
Foo: "foo",
Bar: []interface{}{nil, nil, nil},
},
}
for _, tc := range cases {
// We run the test 100 times to try to tease out variability
// in the runtime in terms of ordering.
valuelist := make([]uint64, 100)
for i, _ := range valuelist {
v, err := Hash(tc, nil)
if err != nil {
t.Fatalf("Error: %s\n\n%#v", err, tc)
}
valuelist[i] = v
}
// Zero is always wrong
if valuelist[0] == 0 {
t.Fatalf("zero hash: %#v", tc)
}
// Make sure all the values match
t.Logf("%#v: %d", tc, valuelist[0])
for i := 1; i < len(valuelist); i++ {
if valuelist[i] != valuelist[0] {
t.Fatalf("non-matching: %d, %d\n\n%#v", i, 0, tc)
}
}
}
}
func TestHash_equal(t *testing.T) {
type testFoo struct{ Name string }
type testBar struct{ Name string }
cases := []struct {
One, Two interface{}
Match bool
}{
{
map[string]string{"foo": "bar"},
map[interface{}]string{"foo": "bar"},
true,
},
{
map[string]interface{}{"1": "1"},
map[string]interface{}{"1": "1", "2": "2"},
false,
},
{
struct{ Fname, Lname string }{"foo", "bar"},
struct{ Fname, Lname string }{"bar", "foo"},
false,
},
{
struct{ Lname, Fname string }{"foo", "bar"},
struct{ Fname, Lname string }{"foo", "bar"},
false,
},
{
struct{ Lname, Fname string }{"foo", "bar"},
struct{ Fname, Lname string }{"bar", "foo"},
true,
},
{
testFoo{"foo"},
testBar{"foo"},
false,
},
{
struct {
Foo string
unexported string
}{
Foo: "bar",
unexported: "baz",
},
struct {
Foo string
unexported string
}{
Foo: "bar",
unexported: "bang",
},
true,
},
{
struct {
testFoo
Foo string
}{
Foo: "bar",
testFoo: testFoo{Name: "baz"},
},
struct {
testFoo
Foo string
}{
Foo: "bar",
},
true,
},
{
struct {
Foo string
}{
Foo: "bar",
},
struct {
testFoo
Foo string
}{
Foo: "bar",
},
true,
},
}
for i, tc := range cases {
t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
t.Logf("Hashing: %#v", tc.One)
one, err := Hash(tc.One, nil)
t.Logf("Result: %d", one)
if err != nil {
t.Fatalf("Failed to hash %#v: %s", tc.One, err)
}
t.Logf("Hashing: %#v", tc.Two)
two, err := Hash(tc.Two, nil)
t.Logf("Result: %d", two)
if err != nil {
t.Fatalf("Failed to hash %#v: %s", tc.Two, err)
}
// Zero is always wrong
if one == 0 {
t.Fatalf("zero hash: %#v", tc.One)
}
// Compare
if (one == two) != tc.Match {
t.Fatalf("bad, expected: %#v\n\n%#v\n\n%#v", tc.Match, tc.One, tc.Two)
}
})
}
}
func TestHash_equalIgnore(t *testing.T) {
type Test1 struct {
Name string
UUID string `hash:"ignore"`
}
type Test2 struct {
Name string
UUID string `hash:"-"`
}
type TestTime struct {
Name string
Time time.Time `hash:"string"`
}
type TestTime2 struct {
Name string
Time time.Time
}
now := time.Now()
cases := []struct {
One, Two interface{}
Match bool
}{
{
Test1{Name: "foo", UUID: "foo"},
Test1{Name: "foo", UUID: "bar"},
true,
},
{
Test1{Name: "foo", UUID: "foo"},
Test1{Name: "foo", UUID: "foo"},
true,
},
{
Test2{Name: "foo", UUID: "foo"},
Test2{Name: "foo", UUID: "bar"},
true,
},
{
Test2{Name: "foo", UUID: "foo"},
Test2{Name: "foo", UUID: "foo"},
true,
},
{
TestTime{Name: "foo", Time: now},
TestTime{Name: "foo", Time: time.Time{}},
false,
},
{
TestTime{Name: "foo", Time: now},
TestTime{Name: "foo", Time: now},
true,
},
{
TestTime2{Name: "foo", Time: now},
TestTime2{Name: "foo", Time: time.Time{}},
true,
},
}
for _, tc := range cases {
one, err := Hash(tc.One, nil)
if err != nil {
t.Fatalf("Failed to hash %#v: %s", tc.One, err)
}
two, err := Hash(tc.Two, nil)
if err != nil {
t.Fatalf("Failed to hash %#v: %s", tc.Two, err)
}
// Zero is always wrong
if one == 0 {
t.Fatalf("zero hash: %#v", tc.One)
}
// Compare
if (one == two) != tc.Match {
t.Fatalf("bad, expected: %#v\n\n%#v\n\n%#v", tc.Match, tc.One, tc.Two)
}
}
}
func TestHash_stringTagError(t *testing.T) {
type Test1 struct {
Name string
BrokenField string `hash:"string"`
}
type Test2 struct {
Name string
BustedField int `hash:"string"`
}
type Test3 struct {
Name string
Time time.Time `hash:"string"`
}
cases := []struct {
Test interface{}
Field string
}{
{
Test1{Name: "foo", BrokenField: "bar"},
"BrokenField",
},
{
Test2{Name: "foo", BustedField: 23},
"BustedField",
},
{
Test3{Name: "foo", Time: time.Now()},
"",
},
}
for _, tc := range cases {
_, err := Hash(tc.Test, nil)
if err != nil {
if ens, ok := err.(*ErrNotStringer); ok {
if ens.Field != tc.Field {
t.Fatalf("did not get expected field %#v: got %s wanted %s", tc.Test, ens.Field, tc.Field)
}
} else {
t.Fatalf("unknown error %#v: got %s", tc, err)
}
}
}
}
func TestHash_equalNil(t *testing.T) {
type Test struct {
Str *string
Int *int
Map map[string]string
Slice []string
}
cases := []struct {
One, Two interface{}
ZeroNil bool
Match bool
}{
{
Test{
Str: nil,
Int: nil,
Map: nil,
Slice: nil,
},
Test{
Str: new(string),
Int: new(int),
Map: make(map[string]string),
Slice: make([]string, 0),
},
true,
true,
},
{
Test{
Str: nil,
Int: nil,
Map: nil,
Slice: nil,
},
Test{
Str: new(string),
Int: new(int),
Map: make(map[string]string),
Slice: make([]string, 0),
},
false,
false,
},
{
nil,
0,
true,
true,
},
{
nil,
0,
false,
true,
},
}
for _, tc := range cases {
one, err := Hash(tc.One, &HashOptions{ZeroNil: tc.ZeroNil})
if err != nil {
t.Fatalf("Failed to hash %#v: %s", tc.One, err)
}
two, err := Hash(tc.Two, &HashOptions{ZeroNil: tc.ZeroNil})
if err != nil {
t.Fatalf("Failed to hash %#v: %s", tc.Two, err)
}
// Zero is always wrong
if one == 0 {
t.Fatalf("zero hash: %#v", tc.One)
}
// Compare
if (one == two) != tc.Match {
t.Fatalf("bad, expected: %#v\n\n%#v\n\n%#v", tc.Match, tc.One, tc.Two)
}
}
}
func TestHash_equalSet(t *testing.T) {
type Test struct {
Name string
Friends []string `hash:"set"`
}
cases := []struct {
One, Two interface{}
Match bool
}{
{
Test{Name: "foo", Friends: []string{"foo", "bar"}},
Test{Name: "foo", Friends: []string{"bar", "foo"}},
true,
},
{
Test{Name: "foo", Friends: []string{"foo", "bar"}},
Test{Name: "foo", Friends: []string{"foo", "bar"}},
true,
},
}
for _, tc := range cases {
one, err := Hash(tc.One, nil)
if err != nil {
t.Fatalf("Failed to hash %#v: %s", tc.One, err)
}
two, err := Hash(tc.Two, nil)
if err != nil {
t.Fatalf("Failed to hash %#v: %s", tc.Two, err)
}
// Zero is always wrong
if one == 0 {
t.Fatalf("zero hash: %#v", tc.One)
}
// Compare
if (one == two) != tc.Match {
t.Fatalf("bad, expected: %#v\n\n%#v\n\n%#v", tc.Match, tc.One, tc.Two)
}
}
}
func TestHash_includable(t *testing.T) {
cases := []struct {
One, Two interface{}
Match bool
}{
{
testIncludable{Value: "foo"},
testIncludable{Value: "foo"},
true,
},
{
testIncludable{Value: "foo", Ignore: "bar"},
testIncludable{Value: "foo"},
true,
},
{
testIncludable{Value: "foo", Ignore: "bar"},
testIncludable{Value: "bar"},
false,
},
}
for _, tc := range cases {
one, err := Hash(tc.One, nil)
if err != nil {
t.Fatalf("Failed to hash %#v: %s", tc.One, err)
}
two, err := Hash(tc.Two, nil)
if err != nil {
t.Fatalf("Failed to hash %#v: %s", tc.Two, err)
}
// Zero is always wrong
if one == 0 {
t.Fatalf("zero hash: %#v", tc.One)
}
// Compare
if (one == two) != tc.Match {
t.Fatalf("bad, expected: %#v\n\n%#v\n\n%#v", tc.Match, tc.One, tc.Two)
}
}
}
func TestHash_includableMap(t *testing.T) {
cases := []struct {
One, Two interface{}
Match bool
}{
{
testIncludableMap{Map: map[string]string{"foo": "bar"}},
testIncludableMap{Map: map[string]string{"foo": "bar"}},
true,
},
{
testIncludableMap{Map: map[string]string{"foo": "bar", "ignore": "true"}},
testIncludableMap{Map: map[string]string{"foo": "bar"}},
true,
},
{
testIncludableMap{Map: map[string]string{"foo": "bar", "ignore": "true"}},
testIncludableMap{Map: map[string]string{"bar": "baz"}},
false,
},
}
for _, tc := range cases {
one, err := Hash(tc.One, nil)
if err != nil {
t.Fatalf("Failed to hash %#v: %s", tc.One, err)
}
two, err := Hash(tc.Two, nil)
if err != nil {
t.Fatalf("Failed to hash %#v: %s", tc.Two, err)
}
// Zero is always wrong
if one == 0 {
t.Fatalf("zero hash: %#v", tc.One)
}
// Compare
if (one == two) != tc.Match {
t.Fatalf("bad, expected: %#v\n\n%#v\n\n%#v", tc.Match, tc.One, tc.Two)
}
}
}
type testIncludable struct {
Value string
Ignore string
}
func (t testIncludable) HashInclude(field string, v interface{}) (bool, error) {
return field != "Ignore", nil
}
type testIncludableMap struct {
Map map[string]string
}
func (t testIncludableMap) HashIncludeMap(field string, k, v interface{}) (bool, error) {
if field != "Map" {
return true, nil
}
if s, ok := k.(string); ok && s == "ignore" {
return false, nil
}
return true, nil
}