1// Copyright 2017, The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package value_test
6
7import (
8	"math"
9	"reflect"
10	"testing"
11
12	"github.com/google/go-cmp/cmp"
13	"github.com/google/go-cmp/cmp/internal/value"
14)
15
16func TestSortKeys(t *testing.T) {
17	type (
18		MyString string
19		MyArray  [2]int
20		MyStruct struct {
21			A MyString
22			B MyArray
23			C chan float64
24		}
25		EmptyStruct struct{}
26	)
27
28	opts := []cmp.Option{
29		cmp.Comparer(func(x, y float64) bool {
30			if math.IsNaN(x) && math.IsNaN(y) {
31				return true
32			}
33			return x == y
34		}),
35		cmp.Comparer(func(x, y complex128) bool {
36			rx, ix, ry, iy := real(x), imag(x), real(y), imag(y)
37			if math.IsNaN(rx) && math.IsNaN(ry) {
38				rx, ry = 0, 0
39			}
40			if math.IsNaN(ix) && math.IsNaN(iy) {
41				ix, iy = 0, 0
42			}
43			return rx == ry && ix == iy
44		}),
45		cmp.Comparer(func(x, y chan bool) bool { return true }),
46		cmp.Comparer(func(x, y chan int) bool { return true }),
47		cmp.Comparer(func(x, y chan float64) bool { return true }),
48		cmp.Comparer(func(x, y chan interface{}) bool { return true }),
49		cmp.Comparer(func(x, y *int) bool { return true }),
50	}
51
52	tests := []struct {
53		in   map[interface{}]bool // Set of keys to sort
54		want []interface{}
55	}{{
56		in:   map[interface{}]bool{1: true, 2: true, 3: true},
57		want: []interface{}{1, 2, 3},
58	}, {
59		in: map[interface{}]bool{
60			nil:                    true,
61			true:                   true,
62			false:                  true,
63			-5:                     true,
64			-55:                    true,
65			-555:                   true,
66			uint(1):                true,
67			uint(11):               true,
68			uint(111):              true,
69			"abc":                  true,
70			"abcd":                 true,
71			"abcde":                true,
72			"foo":                  true,
73			"bar":                  true,
74			MyString("abc"):        true,
75			MyString("abcd"):       true,
76			MyString("abcde"):      true,
77			new(int):               true,
78			new(int):               true,
79			make(chan bool):        true,
80			make(chan bool):        true,
81			make(chan int):         true,
82			make(chan interface{}): true,
83			math.Inf(+1):           true,
84			math.Inf(-1):           true,
85			1.2345:                 true,
86			12.345:                 true,
87			123.45:                 true,
88			1234.5:                 true,
89			0 + 0i:                 true,
90			1 + 0i:                 true,
91			2 + 0i:                 true,
92			0 + 1i:                 true,
93			0 + 2i:                 true,
94			0 + 3i:                 true,
95			[2]int{2, 3}:           true,
96			[2]int{4, 0}:           true,
97			[2]int{2, 4}:           true,
98			MyArray([2]int{2, 4}):  true,
99			EmptyStruct{}:          true,
100			MyStruct{
101				"bravo", [2]int{2, 3}, make(chan float64),
102			}: true,
103			MyStruct{
104				"alpha", [2]int{3, 3}, make(chan float64),
105			}: true,
106		},
107		want: []interface{}{
108			nil, false, true,
109			-555, -55, -5, uint(1), uint(11), uint(111),
110			math.Inf(-1), 1.2345, 12.345, 123.45, 1234.5, math.Inf(+1),
111			(0 + 0i), (0 + 1i), (0 + 2i), (0 + 3i), (1 + 0i), (2 + 0i),
112			[2]int{2, 3}, [2]int{2, 4}, [2]int{4, 0}, MyArray([2]int{2, 4}),
113			make(chan bool), make(chan bool), make(chan int), make(chan interface{}),
114			new(int), new(int),
115			"abc", "abcd", "abcde", "bar", "foo",
116			MyString("abc"), MyString("abcd"), MyString("abcde"),
117			EmptyStruct{},
118			MyStruct{"alpha", [2]int{3, 3}, make(chan float64)},
119			MyStruct{"bravo", [2]int{2, 3}, make(chan float64)},
120		},
121	}, {
122		// NaN values cannot be properly deduplicated.
123		// This is okay since map entries with NaN in the keys cannot be
124		// retrieved anyways.
125		in: map[interface{}]bool{
126			math.NaN():                      true,
127			math.NaN():                      true,
128			complex(0, math.NaN()):          true,
129			complex(0, math.NaN()):          true,
130			complex(math.NaN(), 0):          true,
131			complex(math.NaN(), 0):          true,
132			complex(math.NaN(), math.NaN()): true,
133		},
134		want: []interface{}{
135			math.NaN(),
136			complex(math.NaN(), math.NaN()),
137			complex(math.NaN(), 0),
138			complex(0, math.NaN()),
139		},
140	}}
141
142	for i, tt := range tests {
143		// Intentionally pass the map via an unexported field to detect panics.
144		// Unfortunately, we cannot actually test the keys without using unsafe.
145		v := reflect.ValueOf(struct{ x map[interface{}]bool }{tt.in}).Field(0)
146		value.SortKeys(append(v.MapKeys(), v.MapKeys()...))
147
148		// Try again, with keys that have read-write access in reflect.
149		v = reflect.ValueOf(tt.in)
150		keys := append(v.MapKeys(), v.MapKeys()...)
151		var got []interface{}
152		for _, k := range value.SortKeys(keys) {
153			got = append(got, k.Interface())
154		}
155		if d := cmp.Diff(got, tt.want, opts...); d != "" {
156			t.Errorf("test %d, Sort() mismatch (-got +want):\n%s", i, d)
157		}
158	}
159}
160