apidiff: add subtests

The Changes test used to read in a single file, testdata/tests.go,
with all the test cases. Now support multiple testdata/*.go files,
each running in its own subtest. This will help to isolate the
tests.

Make a single split as a demonstration.

Change-Id: Ibccdca813f099e196be5e8381c2b3b905220ef56
Reviewed-on: https://go-review.googlesource.com/c/exp/+/512616
Reviewed-by: Ian Lance Taylor <iant@google.com>
Run-TryBot: Jonathan Amsterdam <jba@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
diff --git a/apidiff/apidiff_test.go b/apidiff/apidiff_test.go
index b439f4e..56a1fab 100644
--- a/apidiff/apidiff_test.go
+++ b/apidiff/apidiff_test.go
@@ -80,38 +80,47 @@
 }
 
 func TestChanges(t *testing.T) {
-	dir := filepath.Join(t.TempDir(), "go")
-	wanti, wantc := splitIntoPackages(t, dir)
-	sort.Strings(wanti)
-	sort.Strings(wantc)
-
-	oldpkg, err := loadPackage(t, "apidiff/old", dir)
+	testfiles, err := filepath.Glob(filepath.Join("testdata", "*.go"))
 	if err != nil {
 		t.Fatal(err)
 	}
-	newpkg, err := loadPackage(t, "apidiff/new", dir)
-	if err != nil {
-		t.Fatal(err)
-	}
+	for _, testfile := range testfiles {
+		name := strings.TrimSuffix(filepath.Base(testfile), ".go")
+		t.Run(name, func(t *testing.T) {
+			dir := filepath.Join(t.TempDir(), "go")
+			wanti, wantc := splitIntoPackages(t, testfile, dir)
+			sort.Strings(wanti)
+			sort.Strings(wantc)
 
-	report := Changes(oldpkg.Types, newpkg.Types)
+			oldpkg, err := loadPackage(t, "apidiff/old", dir)
+			if err != nil {
+				t.Fatal(err)
+			}
+			newpkg, err := loadPackage(t, "apidiff/new", dir)
+			if err != nil {
+				t.Fatal(err)
+			}
 
-	got := report.messages(false)
-	if diff := cmp.Diff(wanti, got); diff != "" {
-		t.Errorf("incompatibles: mismatch (-want, +got)\n%s", diff)
-	}
-	got = report.messages(true)
-	if diff := cmp.Diff(wantc, got); diff != "" {
-		t.Errorf("compatibles: mismatch (-want, +got)\n%s", diff)
+			report := Changes(oldpkg.Types, newpkg.Types)
+
+			got := report.messages(false)
+			if diff := cmp.Diff(wanti, got); diff != "" {
+				t.Errorf("incompatibles: mismatch (-want, +got)\n%s", diff)
+			}
+			got = report.messages(true)
+			if diff := cmp.Diff(wantc, got); diff != "" {
+				t.Errorf("compatibles: mismatch (-want, +got)\n%s", diff)
+			}
+		})
 	}
 }
 
-func splitIntoPackages(t *testing.T, dir string) (incompatibles, compatibles []string) {
+func splitIntoPackages(t *testing.T, file, dir string) (incompatibles, compatibles []string) {
 	// Read the input file line by line.
 	// Write a line into the old or new package,
 	// dependent on comments.
 	// Also collect expected messages.
-	f, err := os.Open("testdata/tests.go")
+	f, err := os.Open(file)
 	if err != nil {
 		t.Fatal(err)
 	}
diff --git a/apidiff/testdata/README.md b/apidiff/testdata/README.md
new file mode 100644
index 0000000..8029a13
--- /dev/null
+++ b/apidiff/testdata/README.md
@@ -0,0 +1,12 @@
+The .go files in this directory are split into two packages, old and new.
+They are syntactically valid Go so that gofmt can process them.
+
+```
+If a comment begins with:  Then:
+old                        write subsequent lines to the "old" package
+new                        write subsequent lines to the "new" package
+both                       write subsequent lines to both packages
+c                          expect a compatible error with the following text
+i                          expect an incompatible error with the following text
+
+```
diff --git a/apidiff/testdata/basics.go b/apidiff/testdata/basics.go
new file mode 100644
index 0000000..94c0940
--- /dev/null
+++ b/apidiff/testdata/basics.go
@@ -0,0 +1,110 @@
+package p
+
+// Same type in both: OK.
+// both
+type A int
+
+// Changing the type is an incompatible change.
+// old
+type B int
+
+// new
+// i B: changed from int to string
+type B string
+
+// Adding a new type, whether alias or not, is a compatible change.
+// new
+// c AA: added
+type AA = A
+
+// c B1: added
+type B1 bool
+
+// Change of type for an unexported name doesn't matter...
+// old
+type t int
+
+// new
+type t string // OK: t isn't part of the API
+
+// ...unless it is exposed.
+// both
+var V2 u
+
+// old
+type u string
+
+// new
+// i u: changed from string to int
+type u int
+
+// An exposed, unexported type can be renamed.
+// both
+type u2 int
+
+// old
+type u1 int
+
+var V5 u1
+
+// new
+var V5 u2 // OK: V5 has changed type, but old u1 corresopnds to new u2
+
+// Splitting a single type into two is an incompatible change.
+// both
+type u3 int
+
+// old
+type (
+	Split1 = u1
+	Split2 = u1
+)
+
+// new
+type (
+	Split1 = u2 // OK, since old u1 corresponds to new u2
+
+	// This tries to make u1 correspond to u3
+	// i Split2: changed from u1 to u3
+	Split2 = u3
+)
+
+// Merging two types into one is OK.
+// old
+type (
+	GoodMerge1 = u2
+	GoodMerge2 = u3
+)
+
+// new
+type (
+	GoodMerge1 = u3
+	GoodMerge2 = u3
+)
+
+// Merging isn't OK here because a method is lost.
+// both
+type u4 int
+
+func (u4) M() {}
+
+// old
+type (
+	BadMerge1 = u3
+	BadMerge2 = u4
+)
+
+// new
+type (
+	BadMerge1 = u3
+	// i u4.M: removed
+	// What's really happening here is that old u4 corresponds to new u3,
+	// and new u3's method set is not a superset of old u4's.
+	BadMerge2 = u3
+)
+
+// old
+type Rem int
+
+// new
+// i Rem: removed
diff --git a/apidiff/testdata/tests.go b/apidiff/testdata/tests.go
index edee045..655239a 100644
--- a/apidiff/testdata/tests.go
+++ b/apidiff/testdata/tests.go
@@ -1,127 +1,20 @@
-// This file is split into two packages, old and new.
-// It is syntactically valid Go so that gofmt can process it.
-//
-//	If a comment begins with:  Then:
-//	old                        write subsequent lines to the "old" package
-//	new                        write subsequent lines to the "new" package
-//	both                       write subsequent lines to both packages
-//	c                          expect a compatible error with the following text
-//	i                          expect an incompatible error with the following text
-package ignore
+package p
 
 // both
 import "io"
 
-//////////////// Basics
-
-// Same type in both: OK.
-// both
-type A int
-
-// Changing the type is an incompatible change.
-// old
-type B int
-
-// new
-// i B: changed from int to string
-type B string
-
-// Adding a new type, whether alias or not, is a compatible change.
-// new
-// c AA: added
-type AA = A
-
-// c B1: added
-type B1 bool
-
-// Change of type for an unexported name doesn't matter...
-// old
-type t int
-
-// new
-type t string // OK: t isn't part of the API
-
-// ...unless it is exposed.
-// both
-var V2 u
-
-// old
-type u string
-
-// new
-// i u: changed from string to int
-type u int
-
-// An exposed, unexported type can be renamed.
-// both
-type u2 int
-
 // old
 type u1 int
 
-var V5 u1
-
-// new
-var V5 u2 // OK: V5 has changed type, but old u1 corresopnds to new u2
-
-// Splitting a single type into two is an incompatible change.
 // both
-type u3 int
+type u2 int
 
-// old
-type (
-	Split1 = u1
-	Split2 = u1
-)
-
-// new
-type (
-	Split1 = u2 // OK, since old u1 corresponds to new u2
-
-	// This tries to make u1 correspond to u3
-	// i Split2: changed from u1 to u3
-	Split2 = u3
-)
-
-// Merging two types into one is OK.
-// old
-type (
-	GoodMerge1 = u2
-	GoodMerge2 = u3
-)
-
-// new
-type (
-	GoodMerge1 = u3
-	GoodMerge2 = u3
-)
-
-// Merging isn't OK here because a method is lost.
 // both
-type u4 int
-
-func (u4) M() {}
-
-// old
-type (
-	BadMerge1 = u3
-	BadMerge2 = u4
-)
+type A int
 
 // new
-type (
-	BadMerge1 = u3
-	// i u4.M: removed
-	// What's really happening here is that old u4 corresponds to new u3,
-	// and new u3's method set is not a superset of old u4's.
-	BadMerge2 = u3
-)
-
-// old
-type Rem int
-
-// new
-// i Rem: removed
+// c AA: added
+type AA = A
 
 //////////////// Constants