github.com/influx6/npkg@v0.8.8/ntext/nmapsort/sort_test.go (about) 1 // Copyright 2018 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 5 package nmapsort 6 7 import ( 8 "fmt" 9 "math" 10 "reflect" 11 "strings" 12 "testing" 13 ) 14 15 var compareTests = [][]reflect.Value{ 16 ct(reflect.TypeOf(int(0)), -1, 0, 1), 17 ct(reflect.TypeOf(int8(0)), -1, 0, 1), 18 ct(reflect.TypeOf(int16(0)), -1, 0, 1), 19 ct(reflect.TypeOf(int32(0)), -1, 0, 1), 20 ct(reflect.TypeOf(int64(0)), -1, 0, 1), 21 ct(reflect.TypeOf(uint(0)), 0, 1, 5), 22 ct(reflect.TypeOf(uint8(0)), 0, 1, 5), 23 ct(reflect.TypeOf(uint16(0)), 0, 1, 5), 24 ct(reflect.TypeOf(uint32(0)), 0, 1, 5), 25 ct(reflect.TypeOf(uint64(0)), 0, 1, 5), 26 ct(reflect.TypeOf(uintptr(0)), 0, 1, 5), 27 ct(reflect.TypeOf(string("")), "", "a", "ab"), 28 ct(reflect.TypeOf(float32(0)), math.NaN(), math.Inf(-1), -1e10, 0, 1e10, math.Inf(1)), 29 ct(reflect.TypeOf(float64(0)), math.NaN(), math.Inf(-1), -1e10, 0, 1e10, math.Inf(1)), 30 ct(reflect.TypeOf(complex64(0+1i)), -1-1i, -1+0i, -1+1i, 0-1i, 0+0i, 0+1i, 1-1i, 1+0i, 1+1i), 31 ct(reflect.TypeOf(complex128(0+1i)), -1-1i, -1+0i, -1+1i, 0-1i, 0+0i, 0+1i, 1-1i, 1+0i, 1+1i), 32 ct(reflect.TypeOf(false), false, true), 33 ct(reflect.TypeOf(&ints[0]), &ints[0], &ints[1], &ints[2]), 34 ct(reflect.TypeOf(chans[0]), chans[0], chans[1], chans[2]), 35 ct(reflect.TypeOf(toy{}), toy{0, 1}, toy{0, 2}, toy{1, -1}, toy{1, 1}), 36 ct(reflect.TypeOf([2]int{}), [2]int{1, 1}, [2]int{1, 2}, [2]int{2, 0}), 37 ct(reflect.TypeOf(interface{}(interface{}(0))), iFace, 1, 2, 3), 38 } 39 40 var iFace interface{} 41 42 func ct(typ reflect.Type, args ...interface{}) []reflect.Value { 43 value := make([]reflect.Value, len(args)) 44 for i, v := range args { 45 x := reflect.ValueOf(v) 46 if !x.IsValid() { // Make it a typed nil. 47 x = reflect.Zero(typ) 48 } else { 49 x = x.Convert(typ) 50 } 51 value[i] = x 52 } 53 return value 54 } 55 56 func TestCompare(t *testing.T) { 57 for _, test := range compareTests { 58 for i, v0 := range test { 59 for j, v1 := range test { 60 c := Compare(v0, v1) 61 var expect int 62 switch { 63 case i == j: 64 expect = 0 65 // NaNs are tricky. 66 if typ := v0.Type(); (typ.Kind() == reflect.Float32 || typ.Kind() == reflect.Float64) && math.IsNaN(v0.Float()) { 67 expect = -1 68 } 69 case i < j: 70 expect = -1 71 case i > j: 72 expect = 1 73 } 74 if c != expect { 75 t.Errorf("%s: compare(%v,%v)=%d; expect %d", v0.Type(), v0, v1, c, expect) 76 } 77 } 78 } 79 } 80 } 81 82 type sortTest struct { 83 data interface{} // Always a map. 84 print string // Printed result using our custom printer. 85 } 86 87 var sortTests = []sortTest{ 88 { 89 map[int]string{7: "bar", -3: "foo"}, 90 "-3:foo 7:bar", 91 }, 92 { 93 map[uint8]string{7: "bar", 3: "foo"}, 94 "3:foo 7:bar", 95 }, 96 { 97 map[string]string{"7": "bar", "3": "foo"}, 98 "3:foo 7:bar", 99 }, 100 { 101 map[float64]string{7: "bar", -3: "foo", math.NaN(): "nan", math.Inf(0): "inf"}, 102 "NaN:nan -3:foo 7:bar +Inf:inf", 103 }, 104 { 105 map[complex128]string{7 + 2i: "bar2", 7 + 1i: "bar", -3: "foo", complex(math.NaN(), 0i): "nan", complex(math.Inf(0), 0i): "inf"}, 106 "(NaN+0i):nan (-3+0i):foo (7+1i):bar (7+2i):bar2 (+Inf+0i):inf", 107 }, 108 { 109 map[bool]string{true: "true", false: "false"}, 110 "false:false true:true", 111 }, 112 { 113 chanMap(), 114 "CHAN0:0 CHAN1:1 CHAN2:2", 115 }, 116 { 117 pointerMap(), 118 "PTR0:0 PTR1:1 PTR2:2", 119 }, 120 { 121 map[toy]string{{7, 2}: "72", {7, 1}: "71", {3, 4}: "34"}, 122 "{3 4}:34 {7 1}:71 {7 2}:72", 123 }, 124 { 125 map[[2]int]string{{7, 2}: "72", {7, 1}: "71", {3, 4}: "34"}, 126 "[3 4]:34 [7 1]:71 [7 2]:72", 127 }, 128 } 129 130 func sprint(data interface{}) string { 131 om := Sort(reflect.ValueOf(data)) 132 if om == nil { 133 return "nil" 134 } 135 b := new(strings.Builder) 136 for i, key := range om.Key { 137 if i > 0 { 138 b.WriteRune(' ') 139 } 140 b.WriteString(sprintKey(key)) 141 b.WriteRune(':') 142 b.WriteString(fmt.Sprint(om.Value[i])) 143 } 144 return b.String() 145 } 146 147 // sprintKey formats a reflect.Value but gives reproducible values for some 148 // problematic types such as pointers. Note that it only does special handling 149 // for the troublesome types used in the test cases; it is not a general 150 // printer. 151 func sprintKey(key reflect.Value) string { 152 switch str := key.Type().String(); str { 153 case "*int": 154 ptr := key.Interface().(*int) 155 for i := range ints { 156 if ptr == &ints[i] { 157 return fmt.Sprintf("PTR%d", i) 158 } 159 } 160 return "PTR???" 161 case "chan int": 162 c := key.Interface().(chan int) 163 for i := range chans { 164 if c == chans[i] { 165 return fmt.Sprintf("CHAN%d", i) 166 } 167 } 168 return "CHAN???" 169 default: 170 return fmt.Sprint(key) 171 } 172 } 173 174 var ( 175 ints [3]int 176 chans = [3]chan int{make(chan int), make(chan int), make(chan int)} 177 ) 178 179 func pointerMap() map[*int]string { 180 m := make(map[*int]string) 181 for i := 2; i >= 0; i-- { 182 m[&ints[i]] = fmt.Sprint(i) 183 } 184 return m 185 } 186 187 func chanMap() map[chan int]string { 188 m := make(map[chan int]string) 189 for i := 2; i >= 0; i-- { 190 m[chans[i]] = fmt.Sprint(i) 191 } 192 return m 193 } 194 195 type toy struct { 196 A int // Exported. 197 b int // Unexported. 198 } 199 200 func TestOrder(t *testing.T) { 201 for _, test := range sortTests { 202 got := sprint(test.data) 203 if got != test.print { 204 t.Errorf("%s: got %q, want %q", reflect.TypeOf(test.data), got, test.print) 205 } 206 } 207 } 208 209 func TestInterface(t *testing.T) { 210 // A map containing multiple concrete types should be sorted by type, 211 // then value. However, the relative ordering of types is unspecified, 212 // so test this by checking the presence of sorted subgroups. 213 m := map[interface{}]string{ 214 [2]int{1, 0}: "", 215 [2]int{0, 1}: "", 216 true: "", 217 false: "", 218 3.1: "", 219 2.1: "", 220 1.1: "", 221 math.NaN(): "", 222 3: "", 223 2: "", 224 1: "", 225 "c": "", 226 "b": "", 227 "a": "", 228 struct{ x, y int }{1, 0}: "", 229 struct{ x, y int }{0, 1}: "", 230 } 231 got := sprint(m) 232 typeGroups := []string{ 233 "NaN: 1.1: 2.1: 3.1:", // float64 234 "false: true:", // bool 235 "1: 2: 3:", // int 236 "a: b: c:", // string 237 "[0 1]: [1 0]:", // [2]int 238 "{0 1}: {1 0}:", // struct{ x int; y int } 239 } 240 for _, g := range typeGroups { 241 if !strings.Contains(got, g) { 242 t.Errorf("sorted map should contain %q", g) 243 } 244 } 245 }