cmd/go/internal/vgo: vendor testdata from parent directories of copied packages

We should keep all the testdata directories in parent directories
of copied packages. And we should also avoid re-copying directories
already copied.

If the package being vendored is a/b/c, try to copy a/b/c/testdata,
a/b/testdata and a/testdata to vendor directory, up to the module
root.

Fixes golang/go#23997

Change-Id: If3a2ab9e4bcce80acc978caf569a4aa33219fd62
Reviewed-on: https://go-review.googlesource.com/116575
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Russ Cox <rsc@golang.org>
diff --git a/vendor/cmd/go/internal/vgo/vendor.go b/vendor/cmd/go/internal/vgo/vendor.go
index f7000b2..cbdf084 100644
--- a/vendor/cmd/go/internal/vgo/vendor.go
+++ b/vendor/cmd/go/internal/vgo/vendor.go
@@ -32,6 +32,7 @@
 }
 
 var vendorV = CmdVendor.Flag.Bool("v", false, "")
+var copiedDir map[string]bool
 
 func init() {
 	CmdVendor.Run = runVendor // break init cycle
@@ -62,6 +63,7 @@
 	}
 
 	var buf bytes.Buffer
+	copiedDir = make(map[string]bool)
 	for _, m := range buildList[1:] {
 		if pkgs := modpkgs[m]; len(pkgs) > 0 {
 			repl := ""
@@ -104,7 +106,36 @@
 	if src == "" {
 		fmt.Fprintf(os.Stderr, "internal error: no pkg for %s -> %s\n", pkg, realPath)
 	}
+
 	copyDir(dst, src, false)
+	if mod, ok := pkgmod[pkg]; ok {
+		copyTestdata(mod.Path, pkg, dst, src)
+	}
+}
+
+// Copy the testdata directories in parent directories.
+// If the package being vendored is a/b/c,
+// try to copy a/b/c/testdata, a/b/testdata and a/testdata to vendor directory,
+// up to the module root.
+func copyTestdata(modPath, pkg, dst, src string) {
+	testdata := func(dir string) string {
+		return filepath.Join(dir, "testdata")
+	}
+	for {
+		if copiedDir[dst] {
+			break
+		}
+		copiedDir[dst] = true
+		if info, err := os.Stat(testdata(src)); err == nil && info.IsDir() {
+			copyDir(testdata(dst), testdata(src), true)
+		}
+		if modPath == pkg {
+			break
+		}
+		pkg = filepath.Dir(pkg)
+		dst = filepath.Dir(dst)
+		src = filepath.Dir(src)
+	}
 }
 
 func copyDir(dst, src string, recursive bool) {
diff --git a/vendor/cmd/go/testdata/vendormod/a/foo/bar/b/main.go b/vendor/cmd/go/testdata/vendormod/a/foo/bar/b/main.go
new file mode 100644
index 0000000..e0836a8
--- /dev/null
+++ b/vendor/cmd/go/testdata/vendormod/a/foo/bar/b/main.go
@@ -0,0 +1 @@
+package b
diff --git a/vendor/cmd/go/testdata/vendormod/a/foo/bar/b/main_test.go b/vendor/cmd/go/testdata/vendormod/a/foo/bar/b/main_test.go
new file mode 100644
index 0000000..d3ef177
--- /dev/null
+++ b/vendor/cmd/go/testdata/vendormod/a/foo/bar/b/main_test.go
@@ -0,0 +1,12 @@
+package b
+
+import (
+	"os"
+	"testing"
+)
+
+func TestDir(t *testing.T) {
+	if _, err := os.Stat("../testdata/1"); err != nil {
+		t.Fatalf("testdata: %v", err)
+	}
+}
diff --git a/vendor/cmd/go/testdata/vendormod/a/foo/bar/c/main.go b/vendor/cmd/go/testdata/vendormod/a/foo/bar/c/main.go
new file mode 100644
index 0000000..7f96c22
--- /dev/null
+++ b/vendor/cmd/go/testdata/vendormod/a/foo/bar/c/main.go
@@ -0,0 +1 @@
+package c
diff --git a/vendor/cmd/go/testdata/vendormod/a/foo/bar/c/main_test.go b/vendor/cmd/go/testdata/vendormod/a/foo/bar/c/main_test.go
new file mode 100644
index 0000000..460a4e7
--- /dev/null
+++ b/vendor/cmd/go/testdata/vendormod/a/foo/bar/c/main_test.go
@@ -0,0 +1,15 @@
+package c
+
+import (
+	"os"
+	"testing"
+)
+
+func TestDir(t *testing.T) {
+	if _, err := os.Stat("../../../testdata/1"); err != nil {
+		t.Fatalf("testdata: %v", err)
+	}
+	if _, err := os.Stat("./testdata/1"); err != nil {
+		t.Fatalf("testdata: %v", err)
+	}
+}
diff --git a/vendor/cmd/go/testdata/vendormod/a/foo/bar/c/testdata/1 b/vendor/cmd/go/testdata/vendormod/a/foo/bar/c/testdata/1
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/vendor/cmd/go/testdata/vendormod/a/foo/bar/c/testdata/1
diff --git a/vendor/cmd/go/testdata/vendormod/a/foo/bar/testdata/1 b/vendor/cmd/go/testdata/vendormod/a/foo/bar/testdata/1
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/vendor/cmd/go/testdata/vendormod/a/foo/bar/testdata/1
diff --git a/vendor/cmd/go/testdata/vendormod/a/go.mod b/vendor/cmd/go/testdata/vendormod/a/go.mod
new file mode 100644
index 0000000..4c8c5cf
--- /dev/null
+++ b/vendor/cmd/go/testdata/vendormod/a/go.mod
@@ -0,0 +1 @@
+module a
\ No newline at end of file
diff --git a/vendor/cmd/go/testdata/vendormod/a/main.go b/vendor/cmd/go/testdata/vendormod/a/main.go
new file mode 100644
index 0000000..2a93cde
--- /dev/null
+++ b/vendor/cmd/go/testdata/vendormod/a/main.go
@@ -0,0 +1 @@
+package a
diff --git a/vendor/cmd/go/testdata/vendormod/a/main_test.go b/vendor/cmd/go/testdata/vendormod/a/main_test.go
new file mode 100644
index 0000000..30b0a1a
--- /dev/null
+++ b/vendor/cmd/go/testdata/vendormod/a/main_test.go
@@ -0,0 +1,12 @@
+package a
+
+import (
+	"os"
+	"testing"
+)
+
+func TestDir(t *testing.T) {
+	if _, err := os.Stat("./testdata/1"); err != nil {
+		t.Fatalf("testdata: %v", err)
+	}
+}
diff --git a/vendor/cmd/go/testdata/vendormod/a/testdata/1 b/vendor/cmd/go/testdata/vendormod/a/testdata/1
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/vendor/cmd/go/testdata/vendormod/a/testdata/1
diff --git a/vendor/cmd/go/testdata/vendormod/go.mod b/vendor/cmd/go/testdata/vendormod/go.mod
index 6f71634..801f286 100644
--- a/vendor/cmd/go/testdata/vendormod/go.mod
+++ b/vendor/cmd/go/testdata/vendormod/go.mod
@@ -1,5 +1,7 @@
 module m
 
+replace a v1.0.0 => ./a
+
 replace x v1.0.0 => ./x
 
 replace y v1.0.0 => ./y
@@ -9,6 +11,7 @@
 replace w v1.0.0 => ./w
 
 require (
+	a v1.0.0
 	w v1.0.0
 	x v1.0.0
 	y v1.0.0
diff --git a/vendor/cmd/go/testdata/vendormod/testdata1.go b/vendor/cmd/go/testdata/vendormod/testdata1.go
new file mode 100644
index 0000000..fc029e0
--- /dev/null
+++ b/vendor/cmd/go/testdata/vendormod/testdata1.go
@@ -0,0 +1,3 @@
+package m
+
+import _ "a"
diff --git a/vendor/cmd/go/testdata/vendormod/testdata2.go b/vendor/cmd/go/testdata/vendormod/testdata2.go
new file mode 100644
index 0000000..61660bf
--- /dev/null
+++ b/vendor/cmd/go/testdata/vendormod/testdata2.go
@@ -0,0 +1,4 @@
+package m
+
+import _ "a/foo/bar/b"
+import _ "a/foo/bar/c"
diff --git a/vendor/cmd/go/vgo_test.go b/vendor/cmd/go/vgo_test.go
index 0f304d9..de99473 100644
--- a/vendor/cmd/go/vgo_test.go
+++ b/vendor/cmd/go/vgo_test.go
@@ -273,6 +273,9 @@
 		tg.run("-vgo", "build")
 		tg.run("-vgo", "build", "-getmode=vendor")
 	}
+	//test testdata copy
+	tg.cd(filepath.Join(wd, "testdata/vendormod/vendor"))
+	tg.run("-vgo", "test", "./...")
 }
 
 func TestFillGoMod(t *testing.T) {