|  | // Copyright 2018 The Go Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style | 
|  | // license that can be found in the LICENSE file. | 
|  |  | 
|  | package fmtsort_test | 
|  |  | 
|  | import ( | 
|  | "fmt" | 
|  | "internal/fmtsort" | 
|  | "math" | 
|  | "reflect" | 
|  | "strings" | 
|  | "testing" | 
|  | ) | 
|  |  | 
|  | var compareTests = [][]reflect.Value{ | 
|  | ct(reflect.TypeOf(int(0)), -1, 0, 1), | 
|  | ct(reflect.TypeOf(int8(0)), -1, 0, 1), | 
|  | ct(reflect.TypeOf(int16(0)), -1, 0, 1), | 
|  | ct(reflect.TypeOf(int32(0)), -1, 0, 1), | 
|  | ct(reflect.TypeOf(int64(0)), -1, 0, 1), | 
|  | ct(reflect.TypeOf(uint(0)), 0, 1, 5), | 
|  | ct(reflect.TypeOf(uint8(0)), 0, 1, 5), | 
|  | ct(reflect.TypeOf(uint16(0)), 0, 1, 5), | 
|  | ct(reflect.TypeOf(uint32(0)), 0, 1, 5), | 
|  | ct(reflect.TypeOf(uint64(0)), 0, 1, 5), | 
|  | ct(reflect.TypeOf(uintptr(0)), 0, 1, 5), | 
|  | ct(reflect.TypeOf(string("")), "", "a", "ab"), | 
|  | ct(reflect.TypeOf(float32(0)), math.NaN(), math.Inf(-1), -1e10, 0, 1e10, math.Inf(1)), | 
|  | ct(reflect.TypeOf(float64(0)), math.NaN(), math.Inf(-1), -1e10, 0, 1e10, math.Inf(1)), | 
|  | ct(reflect.TypeOf(complex64(0+1i)), -1-1i, -1+0i, -1+1i, 0-1i, 0+0i, 0+1i, 1-1i, 1+0i, 1+1i), | 
|  | ct(reflect.TypeOf(complex128(0+1i)), -1-1i, -1+0i, -1+1i, 0-1i, 0+0i, 0+1i, 1-1i, 1+0i, 1+1i), | 
|  | ct(reflect.TypeOf(false), false, true), | 
|  | ct(reflect.TypeOf(&ints[0]), &ints[0], &ints[1], &ints[2]), | 
|  | ct(reflect.TypeOf(chans[0]), chans[0], chans[1], chans[2]), | 
|  | ct(reflect.TypeOf(toy{}), toy{0, 1}, toy{0, 2}, toy{1, -1}, toy{1, 1}), | 
|  | ct(reflect.TypeOf([2]int{}), [2]int{1, 1}, [2]int{1, 2}, [2]int{2, 0}), | 
|  | ct(reflect.TypeOf(interface{}(interface{}(0))), iFace, 1, 2, 3), | 
|  | } | 
|  |  | 
|  | var iFace interface{} | 
|  |  | 
|  | func ct(typ reflect.Type, args ...interface{}) []reflect.Value { | 
|  | value := make([]reflect.Value, len(args)) | 
|  | for i, v := range args { | 
|  | x := reflect.ValueOf(v) | 
|  | if !x.IsValid() { // Make it a typed nil. | 
|  | x = reflect.Zero(typ) | 
|  | } else { | 
|  | x = x.Convert(typ) | 
|  | } | 
|  | value[i] = x | 
|  | } | 
|  | return value | 
|  | } | 
|  |  | 
|  | func TestCompare(t *testing.T) { | 
|  | for _, test := range compareTests { | 
|  | for i, v0 := range test { | 
|  | for j, v1 := range test { | 
|  | c := fmtsort.Compare(v0, v1) | 
|  | var expect int | 
|  | switch { | 
|  | case i == j: | 
|  | expect = 0 | 
|  | // NaNs are tricky. | 
|  | if typ := v0.Type(); (typ.Kind() == reflect.Float32 || typ.Kind() == reflect.Float64) && math.IsNaN(v0.Float()) { | 
|  | expect = -1 | 
|  | } | 
|  | case i < j: | 
|  | expect = -1 | 
|  | case i > j: | 
|  | expect = 1 | 
|  | } | 
|  | if c != expect { | 
|  | t.Errorf("%s: compare(%v,%v)=%d; expect %d", v0.Type(), v0, v1, c, expect) | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | type sortTest struct { | 
|  | data  interface{} // Always a map. | 
|  | print string      // Printed result using our custom printer. | 
|  | } | 
|  |  | 
|  | var sortTests = []sortTest{ | 
|  | { | 
|  | map[int]string{7: "bar", -3: "foo"}, | 
|  | "-3:foo 7:bar", | 
|  | }, | 
|  | { | 
|  | map[uint8]string{7: "bar", 3: "foo"}, | 
|  | "3:foo 7:bar", | 
|  | }, | 
|  | { | 
|  | map[string]string{"7": "bar", "3": "foo"}, | 
|  | "3:foo 7:bar", | 
|  | }, | 
|  | { | 
|  | map[float64]string{7: "bar", -3: "foo", math.NaN(): "nan", math.Inf(0): "inf"}, | 
|  | "NaN:nan -3:foo 7:bar +Inf:inf", | 
|  | }, | 
|  | { | 
|  | map[complex128]string{7 + 2i: "bar2", 7 + 1i: "bar", -3: "foo", complex(math.NaN(), 0i): "nan", complex(math.Inf(0), 0i): "inf"}, | 
|  | "(NaN+0i):nan (-3+0i):foo (7+1i):bar (7+2i):bar2 (+Inf+0i):inf", | 
|  | }, | 
|  | { | 
|  | map[bool]string{true: "true", false: "false"}, | 
|  | "false:false true:true", | 
|  | }, | 
|  | { | 
|  | chanMap(), | 
|  | "CHAN0:0 CHAN1:1 CHAN2:2", | 
|  | }, | 
|  | { | 
|  | pointerMap(), | 
|  | "PTR0:0 PTR1:1 PTR2:2", | 
|  | }, | 
|  | { | 
|  | map[toy]string{toy{7, 2}: "72", toy{7, 1}: "71", toy{3, 4}: "34"}, | 
|  | "{3 4}:34 {7 1}:71 {7 2}:72", | 
|  | }, | 
|  | { | 
|  | map[[2]int]string{{7, 2}: "72", {7, 1}: "71", {3, 4}: "34"}, | 
|  | "[3 4]:34 [7 1]:71 [7 2]:72", | 
|  | }, | 
|  | } | 
|  |  | 
|  | func sprint(data interface{}) string { | 
|  | om := fmtsort.Sort(reflect.ValueOf(data)) | 
|  | if om == nil { | 
|  | return "nil" | 
|  | } | 
|  | b := new(strings.Builder) | 
|  | for i, key := range om.Key { | 
|  | if i > 0 { | 
|  | b.WriteRune(' ') | 
|  | } | 
|  | b.WriteString(sprintKey(key)) | 
|  | b.WriteRune(':') | 
|  | b.WriteString(fmt.Sprint(om.Value[i])) | 
|  | } | 
|  | return b.String() | 
|  | } | 
|  |  | 
|  | // sprintKey formats a reflect.Value but gives reproducible values for some | 
|  | // problematic types such as pointers. Note that it only does special handling | 
|  | // for the troublesome types used in the test cases; it is not a general | 
|  | // printer. | 
|  | func sprintKey(key reflect.Value) string { | 
|  | switch str := key.Type().String(); str { | 
|  | case "*int": | 
|  | ptr := key.Interface().(*int) | 
|  | for i := range ints { | 
|  | if ptr == &ints[i] { | 
|  | return fmt.Sprintf("PTR%d", i) | 
|  | } | 
|  | } | 
|  | return "PTR???" | 
|  | case "chan int": | 
|  | c := key.Interface().(chan int) | 
|  | for i := range chans { | 
|  | if c == chans[i] { | 
|  | return fmt.Sprintf("CHAN%d", i) | 
|  | } | 
|  | } | 
|  | return "CHAN???" | 
|  | default: | 
|  | return fmt.Sprint(key) | 
|  | } | 
|  | } | 
|  |  | 
|  | var ( | 
|  | ints  [3]int | 
|  | chans = [3]chan int{make(chan int), make(chan int), make(chan int)} | 
|  | ) | 
|  |  | 
|  | func pointerMap() map[*int]string { | 
|  | m := make(map[*int]string) | 
|  | for i := 2; i >= 0; i-- { | 
|  | m[&ints[i]] = fmt.Sprint(i) | 
|  | } | 
|  | return m | 
|  | } | 
|  |  | 
|  | func chanMap() map[chan int]string { | 
|  | m := make(map[chan int]string) | 
|  | for i := 2; i >= 0; i-- { | 
|  | m[chans[i]] = fmt.Sprint(i) | 
|  | } | 
|  | return m | 
|  | } | 
|  |  | 
|  | type toy struct { | 
|  | A int // Exported. | 
|  | b int // Unexported. | 
|  | } | 
|  |  | 
|  | func TestOrder(t *testing.T) { | 
|  | for _, test := range sortTests { | 
|  | got := sprint(test.data) | 
|  | if got != test.print { | 
|  | t.Errorf("%s: got %q, want %q", reflect.TypeOf(test.data), got, test.print) | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | func TestInterface(t *testing.T) { | 
|  | // A map containing multiple concrete types should be sorted by type, | 
|  | // then value. However, the relative ordering of types is unspecified, | 
|  | // so test this by checking the presence of sorted subgroups. | 
|  | m := map[interface{}]string{ | 
|  | [2]int{1, 0}:             "", | 
|  | [2]int{0, 1}:             "", | 
|  | true:                     "", | 
|  | false:                    "", | 
|  | 3.1:                      "", | 
|  | 2.1:                      "", | 
|  | 1.1:                      "", | 
|  | math.NaN():               "", | 
|  | 3:                        "", | 
|  | 2:                        "", | 
|  | 1:                        "", | 
|  | "c":                      "", | 
|  | "b":                      "", | 
|  | "a":                      "", | 
|  | struct{ x, y int }{1, 0}: "", | 
|  | struct{ x, y int }{0, 1}: "", | 
|  | } | 
|  | got := sprint(m) | 
|  | typeGroups := []string{ | 
|  | "NaN: 1.1: 2.1: 3.1:", // float64 | 
|  | "false: true:",        // bool | 
|  | "1: 2: 3:",            // int | 
|  | "a: b: c:",            // string | 
|  | "[0 1]: [1 0]:",       // [2]int | 
|  | "{0 1}: {1 0}:",       // struct{ x int; y int } | 
|  | } | 
|  | for _, g := range typeGroups { | 
|  | if !strings.Contains(got, g) { | 
|  | t.Errorf("sorted map should contain %q", g) | 
|  | } | 
|  | } | 
|  | } |