From 9d40fadb1c3245a318b155ee3e19a4de139401dc Mon Sep 17 00:00:00 2001 From: lukechampine Date: Thu, 28 Feb 2019 19:03:18 +0000 Subject: [PATCH] fmtsort: sort interfaces deterministically Previously, the result of sorting a map[interface{}] containing multiple concrete types was non-deterministic. To ensure consistent results, sort first by type name, then by concrete value. Fixes #30398 Change-Id: I10fd4b6a74eefbc87136853af6b2e689bc76ae9d GitHub-Last-Rev: 1b07f0c275716e1b2834f74f9c67f897bae82882 GitHub-Pull-Request: golang/go#30406 Reviewed-on: https://go-review.googlesource.com/c/163745 Reviewed-by: Rob Pike Reviewed-by: Keith Randall Run-TryBot: Keith Randall TryBot-Result: Gobot Gobot --- src/internal/fmtsort/sort.go | 2 +- src/internal/fmtsort/sort_test.go | 42 ++++++++++++++++++++++++++++--- 2 files changed, 39 insertions(+), 5 deletions(-) diff --git a/src/internal/fmtsort/sort.go b/src/internal/fmtsort/sort.go index c959cbee1f..70a305a3a1 100644 --- a/src/internal/fmtsort/sort.go +++ b/src/internal/fmtsort/sort.go @@ -167,7 +167,7 @@ func compare(aVal, bVal reflect.Value) int { if c, ok := nilCompare(aVal, bVal); ok { return c } - c := compare(reflect.ValueOf(aType), reflect.ValueOf(bType)) + c := compare(reflect.ValueOf(aVal.Elem().Type()), reflect.ValueOf(bVal.Elem().Type())) if c != 0 { return c } diff --git a/src/internal/fmtsort/sort_test.go b/src/internal/fmtsort/sort_test.go index 6b10c775b0..e060d4bf51 100644 --- a/src/internal/fmtsort/sort_test.go +++ b/src/internal/fmtsort/sort_test.go @@ -126,10 +126,6 @@ var sortTests = []sortTest{ map[[2]int]string{{7, 2}: "72", {7, 1}: "71", {3, 4}: "34"}, "[3 4]:34 [7 1]:71 [7 2]:72", }, - { - map[interface{}]string{7: "7", 4: "4", 3: "3", nil: "nil"}, - ":nil 3:3 4:4 7:7", - }, } func sprint(data interface{}) string { @@ -210,3 +206,41 @@ func TestOrder(t *testing.T) { } } } + +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) + } + } +} -- 2.48.1