integration_test.go: use custom parallel test code

The t.Parallel method does not interact well with the -failfast flag.
See golang/go#30522.

Instead, start off tests ourselves in a goroutine.
Doing it this way allows the -failfast flag to take effect sooner.

One nice benefit of this change is that the test output is cleaner:

Before:
<<<
=== RUN   Test
=== RUN   Test/Go1.9.7
=== RUN   Test/Go1.9.7/TestNormal
=== PAUSE Test/Go1.9.7/TestNormal
=== RUN   Test/Go1.9.7/TestPureGo
=== PAUSE Test/Go1.9.7/TestPureGo
=== RUN   Test/Go1.9.7/TestReflect
=== PAUSE Test/Go1.9.7/TestReflect
=== CONT  Test/Go1.9.7/TestNormal
=== CONT  Test/Go1.9.7/TestReflect
=== CONT  Test/Go1.9.7/TestPureGo
=== RUN   Test/Go1.10.8
=== RUN   Test/Go1.10.8/TestNormal
=== PAUSE Test/Go1.10.8/TestNormal
=== RUN   Test/Go1.10.8/TestPureGo
=== PAUSE Test/Go1.10.8/TestPureGo
=== RUN   Test/Go1.10.8/TestReflect
=== PAUSE Test/Go1.10.8/TestReflect
=== CONT  Test/Go1.10.8/TestNormal
=== CONT  Test/Go1.10.8/TestReflect
=== CONT  Test/Go1.10.8/TestPureGo
=== RUN   Test/Go1.11.6
=== RUN   Test/Go1.11.6/TestNormal
=== PAUSE Test/Go1.11.6/TestNormal
=== RUN   Test/Go1.11.6/TestPureGo
=== PAUSE Test/Go1.11.6/TestPureGo
=== RUN   Test/Go1.11.6/TestReflect
=== PAUSE Test/Go1.11.6/TestReflect
=== CONT  Test/Go1.11.6/TestNormal
=== CONT  Test/Go1.11.6/TestReflect
=== CONT  Test/Go1.11.6/TestPureGo
=== RUN   Test/Go1.12.1
=== RUN   Test/Go1.12.1/TestNormal
=== PAUSE Test/Go1.12.1/TestNormal
=== RUN   Test/Go1.12.1/TestPureGo
=== PAUSE Test/Go1.12.1/TestPureGo
=== RUN   Test/Go1.12.1/TestReflect
=== PAUSE Test/Go1.12.1/TestReflect
=== RUN   Test/Go1.12.1/TestProto1Legacy
=== PAUSE Test/Go1.12.1/TestProto1Legacy
=== RUN   Test/Go1.12.1/TestProtocGenGo
=== PAUSE Test/Go1.12.1/TestProtocGenGo
=== RUN   Test/Go1.12.1/TestProtocGenGoGRPC
=== PAUSE Test/Go1.12.1/TestProtocGenGoGRPC
=== CONT  Test/Go1.12.1/TestNormal
=== CONT  Test/Go1.12.1/TestProto1Legacy
=== CONT  Test/Go1.12.1/TestProtocGenGoGRPC
=== CONT  Test/Go1.12.1/TestProtocGenGo
=== CONT  Test/Go1.12.1/TestReflect
=== CONT  Test/Go1.12.1/TestPureGo
=== RUN   Test/ConformanceTests
=== RUN   Test/GeneratedGoFiles
=== RUN   Test/FormattedGoFiles
=== RUN   Test/CommittedGitChanges
=== RUN   Test/TrackedGitFiles
--- PASS: Test (509.59s)
    --- PASS: Test/Go1.9.7 (0.00s)
        --- PASS: Test/Go1.9.7/TestReflect (100.81s)
        --- PASS: Test/Go1.9.7/TestNormal (100.88s)
        --- PASS: Test/Go1.9.7/TestPureGo (56.93s)
    --- PASS: Test/Go1.10.8 (0.00s)
        --- PASS: Test/Go1.10.8/TestNormal (64.85s)
        --- PASS: Test/Go1.10.8/TestReflect (65.20s)
        --- PASS: Test/Go1.10.8/TestPureGo (36.14s)
    --- PASS: Test/Go1.11.6 (0.00s)
        --- PASS: Test/Go1.11.6/TestReflect (59.69s)
        --- PASS: Test/Go1.11.6/TestNormal (60.25s)
        --- PASS: Test/Go1.11.6/TestPureGo (34.17s)
    --- PASS: Test/Go1.12.1 (0.00s)
        --- PASS: Test/Go1.12.1/TestProto1Legacy (59.41s)
        --- PASS: Test/Go1.12.1/TestNormal (59.87s)
        --- PASS: Test/Go1.12.1/TestProtocGenGo (3.55s)
        --- PASS: Test/Go1.12.1/TestProtocGenGoGRPC (4.12s)
        --- PASS: Test/Go1.12.1/TestReflect (60.10s)
        --- PASS: Test/Go1.12.1/TestPureGo (60.66s)
    --- PASS: Test/ConformanceTests (0.78s)
    --- PASS: Test/GeneratedGoFiles (8.99s)
    --- PASS: Test/FormattedGoFiles (2.64s)
    --- PASS: Test/CommittedGitChanges (0.10s)
    --- PASS: Test/TrackedGitFiles (0.00s)
PASS
>>>

After:
<<<
=== RUN   Test
=== RUN   Test/Go1.9.7/PureGo
=== RUN   Test/Go1.9.7/Normal
=== RUN   Test/Go1.9.7/Reflect
=== RUN   Test/Go1.10.8/Normal
=== RUN   Test/Go1.10.8/PureGo
=== RUN   Test/Go1.10.8/Reflect
=== RUN   Test/Go1.11.6/Normal
=== RUN   Test/Go1.11.6/PureGo
=== RUN   Test/Go1.11.6/Reflect
=== RUN   Test/Go1.12.1/Normal
=== RUN   Test/Go1.12.1/PureGo
=== RUN   Test/Go1.12.1/Reflect
=== RUN   Test/Go1.12.1/Proto1Legacy
=== RUN   Test/Go1.12.1/ProtocGenGo
=== RUN   Test/Go1.12.1/ProtocGenGoGRPC
=== RUN   Test/ConformanceTests
=== RUN   Test/GeneratedGoFiles
=== RUN   Test/FormattedGoFiles
=== RUN   Test/CommittedGitChanges
--- PASS: Test (182.87s)
    --- PASS: Test/Go1.9.7/PureGo (72.17s)
    --- PASS: Test/Go1.9.7/Normal (72.59s)
    --- PASS: Test/Go1.10.8/Normal (8.03s)
    --- PASS: Test/Go1.10.8/PureGo (8.03s)
    --- PASS: Test/Go1.10.8/Reflect (8.34s)
    --- PASS: Test/Go1.11.6/Normal (10.70s)
    --- PASS: Test/Go1.11.6/PureGo (8.21s)
    --- PASS: Test/Go1.11.6/Reflect (8.77s)
    --- PASS: Test/Go1.12.1/Normal (7.41s)
    --- PASS: Test/Go1.12.1/PureGo (6.52s)
    --- PASS: Test/Go1.12.1/Reflect (6.04s)
    --- PASS: Test/Go1.12.1/Proto1Legacy (6.96s)
    --- PASS: Test/Go1.12.1/ProtocGenGo (1.15s)
    --- PASS: Test/Go1.12.1/ProtocGenGoGRPC (2.43s)
    --- PASS: Test/Go1.9.7/Reflect (88.47s)
    --- PASS: Test/ConformanceTests (1.81s)
    --- PASS: Test/GeneratedGoFiles (16.28s)
    --- PASS: Test/FormattedGoFiles (3.17s)
    --- PASS: Test/CommittedGitChanges (0.17s)
PASS
>>>

Change-Id: I777ffe3f4d5f6407c87e3866cbaa890017204cba
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/185877
Reviewed-by: Herbie Ong <herbie@google.com>
diff --git a/integration_test.go b/integration_test.go
index bc3944d..433de7c 100644
--- a/integration_test.go
+++ b/integration_test.go
@@ -22,6 +22,7 @@
 	"regexp"
 	"runtime"
 	"strings"
+	"sync"
 	"testing"
 	"time"
 )
@@ -55,26 +56,35 @@
 		t.SkipNow()
 	}
 
-	for _, v := range golangVersions {
-		t.Run("Go"+v, func(t *testing.T) {
-			runGo := func(label, workDir string, args ...string) {
-				args[0] += v
-				t.Run(label, func(t *testing.T) {
-					t.Parallel()
+	var wg sync.WaitGroup
+	sema := make(chan bool, (runtime.NumCPU()+1)/2)
+	for i := range golangVersions {
+		goVersion := golangVersions[i]
+		goLabel := "Go" + goVersion
+		runGo := func(label, workDir string, args ...string) {
+			wg.Add(1)
+			sema <- true
+			go func() {
+				defer wg.Done()
+				defer func() { <-sema }()
+				t.Run(goLabel+"/"+label, func(t *testing.T) {
+					args[0] += goVersion
 					mustRunCommand(t, workDir, args...)
 				})
-			}
-			workDir := filepath.Join(goPath, "src", modulePath)
-			runGo("TestNormal", workDir, "go", "test", "-race", "./...")
-			runGo("TestPureGo", workDir, "go", "test", "-race", "-tags", "purego", "./...")
-			runGo("TestReflect", workDir, "go", "test", "-race", "-tags", "protoreflect", "./...")
-			if v == golangLatest {
-				runGo("TestProto1Legacy", workDir, "go", "test", "-race", "-tags", "proto1_legacy", "./...")
-				runGo("TestProtocGenGo", "cmd/protoc-gen-go/testdata", "go", "test")
-				runGo("TestProtocGenGoGRPC", "cmd/protoc-gen-go-grpc/testdata", "go", "test")
-			}
-		})
+			}()
+		}
+
+		workDir := filepath.Join(goPath, "src", modulePath)
+		runGo("Normal", workDir, "go", "test", "-race", "./...")
+		runGo("PureGo", workDir, "go", "test", "-race", "-tags", "purego", "./...")
+		runGo("Reflect", workDir, "go", "test", "-race", "-tags", "protoreflect", "./...")
+		if goVersion == golangLatest {
+			runGo("Proto1Legacy", workDir, "go", "test", "-race", "-tags", "proto1_legacy", "./...")
+			runGo("ProtocGenGo", "cmd/protoc-gen-go/testdata", "go", "test")
+			runGo("ProtocGenGoGRPC", "cmd/protoc-gen-go-grpc/testdata", "go", "test")
+		}
 	}
+	wg.Wait()
 
 	t.Run("ConformanceTests", func(t *testing.T) {
 		driverPath := filepath.Join("internal", "cmd", "conformance")