godoc: sort package listing

The directory slice gets created first from newDirTree and then
gets appended again after reading from the dirchs channel. Hence
we need to sort the slice after all the entries are added.

Fixes golang/go#24601

Change-Id: I9282e8643a4448b2c1c84495b7642610f6c56d50
Reviewed-on: https://go-review.googlesource.com/103955
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
Reviewed-by: Kevin Burke <kev@inburke.com>
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
diff --git a/godoc/dirtrees.go b/godoc/dirtrees.go
index fed6ee1..6f51e2f 100644
--- a/godoc/dirtrees.go
+++ b/godoc/dirtrees.go
@@ -14,6 +14,7 @@
 	"os"
 	pathpkg "path"
 	"runtime"
+	"sort"
 	"strings"
 )
 
@@ -173,6 +174,12 @@
 		}
 	}
 
+	// We need to sort the dirs slice because
+	// it is appended again after reading from dirchs.
+	sort.Slice(dirs, func(i, j int) bool {
+		return dirs[i].Name < dirs[j].Name
+	})
+
 	// if there are no package files and no subdirectories
 	// containing package files, ignore the directory
 	if !hasPkgFiles && len(dirs) == 0 {
diff --git a/godoc/dirtrees_test.go b/godoc/dirtrees_test.go
new file mode 100644
index 0000000..c8e8827
--- /dev/null
+++ b/godoc/dirtrees_test.go
@@ -0,0 +1,40 @@
+// 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 godoc
+
+import (
+	"runtime"
+	"sort"
+	"testing"
+
+	"golang.org/x/tools/godoc/vfs"
+	"golang.org/x/tools/godoc/vfs/gatefs"
+)
+
+func TestNewDirTree(t *testing.T) {
+	fsGate := make(chan bool, 20)
+	rootfs := gatefs.New(vfs.OS(runtime.GOROOT()), fsGate)
+	fs := vfs.NameSpace{}
+	fs.Bind("/", rootfs, "/", vfs.BindReplace)
+
+	c := NewCorpus(fs)
+	// 3 levels deep is enough for testing
+	dir := c.newDirectory("/", 3)
+
+	processDir(t, dir)
+}
+
+func processDir(t *testing.T, dir *Directory) {
+	var list []string
+	for _, d := range dir.Dirs {
+		list = append(list, d.Name)
+		// recursively process the lower level
+		processDir(t, d)
+	}
+
+	if sort.StringsAreSorted(list) == false {
+		t.Errorf("list: %v is not sorted\n", list)
+	}
+}