godoc/vfs: fix union logic in NameSpace.ReadDir
ReadDir now returns files from directories in all matching mount
points if no Go files are present in any of them. The behavior now
matches the documentation.
Fixes golang/go#34571
Change-Id: I3a0c8d49a5906ec33ebe9e3efea9d2b9d267506c
Reviewed-on: https://go-review.googlesource.com/c/tools/+/197801
Run-TryBot: Jay Conrod <jayconrod@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org>
diff --git a/godoc/vfs/namespace_test.go b/godoc/vfs/namespace_test.go
new file mode 100644
index 0000000..edf3bc7
--- /dev/null
+++ b/godoc/vfs/namespace_test.go
@@ -0,0 +1,137 @@
+// Copyright 2016 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 vfs_test
+
+import (
+ "fmt"
+ "strings"
+ "testing"
+ "time"
+
+ "golang.org/x/tools/godoc/vfs"
+ "golang.org/x/tools/godoc/vfs/mapfs"
+)
+
+func TestNewNameSpace(t *testing.T) {
+
+ // We will mount this filesystem under /fs1
+ mount := mapfs.New(map[string]string{"fs1file": "abcdefgh"})
+
+ // Existing process. This should give error on Stat("/")
+ t1 := vfs.NameSpace{}
+ t1.Bind("/fs1", mount, "/", vfs.BindReplace)
+
+ // using NewNameSpace. This should work fine.
+ t2 := vfs.NewNameSpace()
+ t2.Bind("/fs1", mount, "/", vfs.BindReplace)
+
+ testcases := map[string][]bool{
+ "/": {false, true},
+ "/fs1": {true, true},
+ "/fs1/fs1file": {true, true},
+ }
+
+ fss := []vfs.FileSystem{t1, t2}
+
+ for j, fs := range fss {
+ for k, v := range testcases {
+ _, err := fs.Stat(k)
+ result := err == nil
+ if result != v[j] {
+ t.Errorf("fs: %d, testcase: %s, want: %v, got: %v, err: %s", j, k, v[j], result, err)
+ }
+ }
+ }
+
+ fi, err := t2.Stat("/")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if fi.Name() != "/" {
+ t.Errorf("t2.Name() : want:%s got:%s", "/", fi.Name())
+ }
+
+ if !fi.ModTime().IsZero() {
+ t.Errorf("t2.ModTime() : want:%v got:%v", time.Time{}, fi.ModTime())
+ }
+}
+
+func TestReadDirUnion(t *testing.T) {
+ for _, tc := range []struct {
+ desc string
+ ns vfs.NameSpace
+ path, want string
+ }{
+ {
+ desc: "no_go_files",
+ ns: func() vfs.NameSpace {
+ rootFs := mapfs.New(map[string]string{
+ "doc/a.txt": "1",
+ "doc/b.txt": "1",
+ "doc/dir1/d1.txt": "",
+ })
+ docFs := mapfs.New(map[string]string{
+ "doc/a.txt": "22",
+ "doc/dir2/d2.txt": "",
+ })
+ ns := vfs.NameSpace{}
+ ns.Bind("/", rootFs, "/", vfs.BindReplace)
+ ns.Bind("/doc", docFs, "/doc", vfs.BindBefore)
+ return ns
+ }(),
+ path: "/doc",
+ want: "a.txt:2,b.txt:1,dir1:0,dir2:0",
+ }, {
+ desc: "have_go_files",
+ ns: func() vfs.NameSpace {
+ a := mapfs.New(map[string]string{
+ "src/x/a.txt": "",
+ "src/x/suba/sub.txt": "",
+ })
+ b := mapfs.New(map[string]string{
+ "src/x/b.go": "package b",
+ "src/x/subb/sub.txt": "",
+ })
+ c := mapfs.New(map[string]string{
+ "src/x/c.txt": "",
+ "src/x/subc/sub.txt": "",
+ })
+ ns := vfs.NameSpace{}
+ ns.Bind("/", a, "/", vfs.BindReplace)
+ ns.Bind("/", b, "/", vfs.BindAfter)
+ ns.Bind("/", c, "/", vfs.BindAfter)
+ return ns
+ }(),
+ path: "/src/x",
+ want: "b.go:9,suba:0,subb:0,subc:0",
+ }, {
+ desc: "empty_mount",
+ ns: func() vfs.NameSpace {
+ ns := vfs.NameSpace{}
+ ns.Bind("/empty", mapfs.New(nil), "/empty", vfs.BindReplace)
+ return ns
+ }(),
+ path: "/",
+ want: "empty:0",
+ },
+ } {
+ t.Run(tc.desc, func(t *testing.T) {
+ fis, err := tc.ns.ReadDir(tc.path)
+ if err != nil {
+ t.Fatal(err)
+ }
+ buf := &strings.Builder{}
+ sep := ""
+ for _, fi := range fis {
+ fmt.Fprintf(buf, "%s%s:%d", sep, fi.Name(), fi.Size())
+ sep = ","
+ }
+ if got := buf.String(); got != tc.want {
+ t.Errorf("got %q; want %q", got, tc.want)
+ }
+ })
+ }
+}