[gndoc] Fix gndoc unstable output
This change adds an additional sort step when gndoc iterates over
a map. It should fix the gndoc output instability issue.
Bug: TC-445
Tests: Local
Change-Id: Ie4a06c135a1c340873386cfc30e606d66252235e
diff --git a/gndoc/argmap.go b/gndoc/argmap.go
index da3bd20..24888a6 100644
--- a/gndoc/argmap.go
+++ b/gndoc/argmap.go
@@ -26,68 +26,74 @@
}
}
-// AddArg creates args from GNArgs and adds them to the maps
+// AddArgs creates args from GNArgs and adds them to the maps
func (a *ArgMap) AddArgs(gnArgs <-chan Arg) {
for gnArg := range gnArgs {
a.AddArg(gnArg)
}
}
+// AddArg adds Arg to the maps
func (a *ArgMap) AddArg(gnArg Arg) {
a.args[gnArg.Key] = append(a.args[gnArg.Key], gnArg)
a.argLookup[gnArg.Name] = append(a.argLookup[gnArg.Name], gnArg.Key)
}
-// sortedArgs returns the list of args in the appropriate order to print.
-func (a *ArgMap) sortedArgs() (map[string]map[string][]Arg, []string) {
+// EmitMarkdown emits Markdown text for the map of arguments.
+func (a *ArgMap) EmitMarkdown(out io.Writer) {
+ type mappedArgs struct {
+ m map[string][]Arg
+ k []string
+ }
+ sortedArgs := struct {
+ m map[string]*mappedArgs
+ k []string
+ }{
+ m: make(map[string]*mappedArgs),
+ k: make([]string, 0),
+ }
+
numKeys := len(a.args)
- args := make(map[string]map[string][]Arg)
- // For each arg, we need to push it into the appropiate list.
for _, gnArgs := range a.args {
for _, gnArg := range gnArgs {
// Lookup the keys associated with this arg & sort & stringify.
keys, _ := a.argLookup[gnArg.Name]
if len(keys) == numKeys {
- // Incoming keys will always have an `=`, and so this is an okay value.
+ // Incoming keys will always have an `=`, and so this is an
+ // okay value.
keys = []string{"all"}
}
+ sort.Strings(keys)
key := strings.Join(keys, ", ")
- _, ok := args[key]
- if !ok {
- args[key] = make(map[string][]Arg)
+ if _, ok := sortedArgs.m[key]; !ok {
+ sortedArgs.m[key] = &mappedArgs{
+ m: make(map[string][]Arg),
+ k: make([]string, 0)}
}
- args[key][gnArg.Name] = append(args[key][gnArg.Name], gnArg)
+ sortedArgs.m[key].m[gnArg.Name] = append(sortedArgs.m[key].m[gnArg.Name], gnArg)
}
}
-
- // Get the keys in alphabetical order
- keys := make([]string, 0, numKeys)
- for k := range args {
- // Sort by name first, then by key
- for argName := range args[k] {
- sort.Slice(args[k][argName], func(i, j int) bool { return args[k][argName][i].Key < args[k][argName][j].Key })
+ for k := range sortedArgs.m {
+ for argName := range sortedArgs.m[k].m {
+ sort.Slice(sortedArgs.m[k].m[argName], func(i, j int) bool {
+ return sortedArgs.m[k].m[argName][i].Key < sortedArgs.m[k].m[argName][j].Key
+ })
+ sortedArgs.m[k].k = append(sortedArgs.m[k].k, argName)
}
-
- keys = append(keys, k)
+ sort.Strings(sortedArgs.m[k].k)
+ sortedArgs.k = append(sortedArgs.k, k)
}
- sort.Strings(keys)
-
- return args, keys
-}
-
-// EmitMarkdown emits Markdown text for the map of arguments.
-func (a *ArgMap) EmitMarkdown(out io.Writer) {
+ sort.Strings(sortedArgs.k)
// Emit a header.
fmt.Fprintf(out, "# %s\n\n", pageTitle)
- gnArgsMap, keys := a.sortedArgs()
- for _, name := range keys {
+ for _, name := range sortedArgs.k {
if name == "all" {
fmt.Fprintf(out, "## All builds\n\n")
} else {
fmt.Fprintf(out, "## `%s`\n\n", name)
}
-
- for _, gnArgs := range gnArgsMap[name] {
+ for _, argsKey := range sortedArgs.m[name].k {
+ gnArgs := sortedArgs.m[name].m[argsKey]
writeArgs(gnArgs, out, a.sources)
}
}
diff --git a/gndoc/argmap_test.go b/gndoc/argmap_test.go
new file mode 100644
index 0000000..005c97b
--- /dev/null
+++ b/gndoc/argmap_test.go
@@ -0,0 +1,136 @@
+// Copyright 2019 The Fuchsia 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 gndoc
+
+import (
+ "bytes"
+ "math/rand"
+ "testing"
+ "time"
+)
+
+func genArgMapInRandomOrder() *ArgMap {
+ testArgMap := NewArgMap(Sources())
+ gnArgs := []Arg{
+ defaultx64Arg,
+ defaultarm64Arg,
+ defaultarm64ArgWithCurrent,
+ defaultx64ArgWithCurrent,
+ x64Arg,
+ arm64Arg,
+ twoKeyarm64TestArg,
+ twoKeyx64TestArg,
+ twoKeyarm64OtherArg,
+ twoKeyx64OtherArg,
+ newLineValueArg,
+ }
+ // Shuffle the gnArgs.
+ r := rand.New(rand.NewSource(time.Now().Unix()))
+ shuffledGnArgs := make([]Arg, 0)
+ for _, j := range r.Perm(len(gnArgs)) {
+ shuffledGnArgs = append(shuffledGnArgs, gnArgs[j])
+ }
+ for _, arg := range shuffledGnArgs {
+ testArgMap.AddArg(arg)
+ }
+ return testArgMap
+}
+
+func TestArgMapEmitMarkdown(t *testing.T) {
+ expectedOutput := `# GN Build Arguments
+
+## ` + "`target_cpu = arm64, package='other/package/default'`" + `
+
+### arm64Other
+Description of arm64 arg.
+
+**Current value for ` + "`target_cpu = arm64, package='other/package/default'`:** `arg`" + `
+
+**Overridden from the default:** ` + "`value`" + `
+
+## ` + "`target_cpu = arm64, target_cpu = arm64, package='test/package/default'`" + `
+
+### arm64
+Description of arm64 arg.
+
+**Current value for ` + "`target_cpu = arm64`:** `arg`" + `
+
+**Overridden from the default:** ` + "`value`" + `
+
+**Current value for ` + "`target_cpu = arm64, package='test/package/default'`:** `arg`" + `
+
+**Overridden from the default:** ` + "`value`" + `
+
+## ` + "`target_cpu = arm64, target_cpu = x64`" + `
+
+### default
+Description of default arg.
+
+**Current value (from the default):** ` + "`false`" + `
+
+From //test/BUILD.gn:2
+
+### default_current
+Description of default_current arg.
+
+**Current value for ` + "`target_cpu = arm64`:** `[1, 2]`" + `
+
+From [//build/BUILD.gn:24](http://fuchsia.com/build/BUILD.gn#24)
+
+**Overridden from the default:** ` + "`[3, 4]`" + `
+
+From [//base/BUILD.gn:4](http://fuchsia.com/base/BUILD.gn#4)
+
+**Current value for ` + "`target_cpu = x64`:** `3`" + `
+
+From [//build/BUILD.gn:24](http://fuchsia.com/build/BUILD.gn#24)
+
+**Overridden from the default:** ` + "`4`" + `
+
+From [//base/BUILD.gn:2](http://fuchsia.com/base/BUILD.gn#2)
+
+## ` + "`target_cpu = x64, package='other/package/default'`" + `
+
+### NewLine
+Description of newline arg.
+
+**Current value (from the default):**
+` + "```" + `
+{
+ base = "//build/toolchain/fuchsia:x64"
+}
+` + "```" + `
+
+### x64Other
+Description of x64 arg.
+
+**Current value for ` + "`target_cpu = x64, package='other/package/default'`:** `arg`" + `
+
+**Overridden from the default:** ` + "`value`" + `
+
+## ` + "`target_cpu = x64, target_cpu = x64, package='test/package/default'`" + `
+
+### x64
+Description of x64 arg that references [//build/path.py](http://fuchsia.com/build/path.py), //sources, and [//base](http://fuchsia.com/base).
+
+**Current value for ` + "`target_cpu = x64`:** `1`" + `
+
+**Overridden from the default:** ` + "`2`" + `
+
+**Current value for ` + "`target_cpu = x64, package='test/package/default'`:** `arg`" + `
+
+**Overridden from the default:** ` + "`value`" + `
+
+`
+ for i := 0; i < 10; i++ {
+ testArgMap := genArgMapInRandomOrder()
+ var testOutput bytes.Buffer
+ testArgMap.EmitMarkdown(&testOutput)
+ if expectedOutput != testOutput.String() {
+ t.Errorf("expecting output:\n%s\n, got:\n%s\n", expectedOutput, testOutput.String())
+ break
+ }
+ }
+}