diff --git a/test/typecheck/main.go b/test/typecheck/main.go
index 411a945d477..2df32f41c83 100644
--- a/test/typecheck/main.go
+++ b/test/typecheck/main.go
@@ -24,6 +24,7 @@ import (
"go/build"
"go/parser"
"go/token"
+ "io"
"log"
"os"
"path/filepath"
@@ -60,6 +61,8 @@ var (
"linux/arm64", "linux/ppc64le",
"linux/s390x", "darwin/386",
}
+ darwinPlatString = "darwin/386,darwin/amd64"
+ windowsPlatString = "windows/386,windows/amd64"
)
type analyzer struct {
@@ -69,6 +72,7 @@ type analyzer struct {
failed bool
platform string
donePaths map[string]interface{}
+ errors []string
}
func newAnalyzer(platform string) *analyzer {
@@ -120,11 +124,19 @@ func (a *analyzer) handleError(err error) {
return
}
}
- // TODO(rmmh): dedup errors across platforms?
- fmt.Fprintf(os.Stderr, "%sERROR(%s) %s\n", logPrefix, a.platform, err)
+ a.errors = append(a.errors, err.Error())
+ if *serial {
+ fmt.Fprintf(os.Stderr, "%sERROR(%s) %s\n", logPrefix, a.platform, err)
+ }
a.failed = true
}
+func (a *analyzer) dumpAndResetErrors() []string {
+ es := a.errors
+ a.errors = nil
+ return es
+}
+
// collect extracts test metadata from a file.
func (a *analyzer) collect(dir string) {
if _, ok := a.donePaths[dir]; ok {
@@ -262,6 +274,51 @@ func (c *collector) handlePath(path string, info os.FileInfo, err error) error {
return nil
}
+type analyzerResult struct {
+ platform string
+ dir string
+ errors []string
+}
+
+func dedupeErrors(out io.Writer, results chan analyzerResult, nDirs, nPlatforms int) {
+ pkgRes := make(map[string][]analyzerResult)
+ for done := 0; done < nDirs; {
+ res := <-results
+ pkgRes[res.dir] = append(pkgRes[res.dir], res)
+ if len(pkgRes[res.dir]) != nPlatforms {
+ continue // expect more results for dir
+ }
+ done++
+ // Collect list of platforms for each error
+ errPlats := map[string][]string{}
+ for _, res := range pkgRes[res.dir] {
+ for _, err := range res.errors {
+ errPlats[err] = append(errPlats[err], res.platform)
+ }
+ }
+ // Print each error (in the same order!) once.
+ for _, res := range pkgRes[res.dir] {
+ for _, err := range res.errors {
+ if errPlats[err] == nil {
+ continue // already printed
+ }
+ sort.Strings(errPlats[err])
+ plats := strings.Join(errPlats[err], ",")
+ if len(errPlats[err]) == len(crossPlatforms) {
+ plats = "all"
+ } else if plats == darwinPlatString {
+ plats = "darwin"
+ } else if plats == windowsPlatString {
+ plats = "windows"
+ }
+ fmt.Fprintf(out, "%sERROR(%s) %s\n", logPrefix, plats, err)
+ delete(errPlats, err)
+ }
+ }
+ delete(pkgRes, res.dir)
+ }
+}
+
func main() {
flag.Parse()
args := flag.Args()
@@ -296,6 +353,15 @@ func main() {
var processedDirs int64
var currentWork int64 // (dir_index << 8) | platform_index
statuses := make([]int, len(ps))
+ var results chan analyzerResult
+ if !*serial {
+ results = make(chan analyzerResult)
+ wg.Add(1)
+ go func() {
+ dedupeErrors(os.Stderr, results, len(c.dirs), len(ps))
+ wg.Done()
+ }()
+ }
for i, p := range ps {
wg.Add(1)
fn := func(i int, p string) {
@@ -305,6 +371,9 @@ func main() {
a.collect(dir)
atomic.AddInt64(&processedDirs, 1)
atomic.StoreInt64(¤tWork, int64(n<<8|i))
+ if results != nil {
+ results <- analyzerResult{p, dir, a.dumpAndResetErrors()}
+ }
}
if a.failed {
statuses[i] = 1
diff --git a/test/typecheck/main_test.go b/test/typecheck/main_test.go
index 803963f2a80..9b1fb830d56 100644
--- a/test/typecheck/main_test.go
+++ b/test/typecheck/main_test.go
@@ -17,6 +17,7 @@ limitations under the License.
package main
import (
+ "bytes"
"errors"
"fmt"
"go/ast"
@@ -155,3 +156,38 @@ func TestHandlePath(t *testing.T) {
t.Error("should skip vendor")
}
}
+
+func TestDedupeErrors(t *testing.T) {
+ testcases := []struct {
+ nPlatforms int
+ results []analyzerResult
+ expected string
+ }{
+ {1, []analyzerResult{}, ""},
+ {1, []analyzerResult{{"linux/arm", "test", nil}}, ""},
+ {1, []analyzerResult{
+ {"linux/arm", "test", []string{"a"}}},
+ "ERROR(linux/arm) a\n"},
+ {3, []analyzerResult{
+ {"linux/arm", "test", []string{"a"}},
+ {"windows/386", "test", []string{"b"}},
+ {"windows/amd64", "test", []string{"b", "c"}}},
+ "ERROR(linux/arm) a\n" +
+ "ERROR(windows) b\n" +
+ "ERROR(windows/amd64) c\n"},
+ }
+ for _, tc := range testcases {
+ out := &bytes.Buffer{}
+ results := make(chan analyzerResult, len(tc.results))
+ for _, res := range tc.results {
+ results <- res
+ }
+ close(results)
+ dedupeErrors(out, results, len(tc.results)/tc.nPlatforms, tc.nPlatforms)
+ outString := out.String()
+ if outString != tc.expected {
+ t.Errorf("dedupeErrors(%v) = '%s', expected '%s'",
+ tc.results, outString, tc.expected)
+ }
+ }
+}
diff --git a/third_party/forked/shell2junit/sh2ju.sh b/third_party/forked/shell2junit/sh2ju.sh
index e461475366a..64da996d631 100755
--- a/third_party/forked/shell2junit/sh2ju.sh
+++ b/third_party/forked/shell2junit/sh2ju.sh
@@ -136,21 +136,13 @@ function juLog() {
# write the junit xml report
## failure tag
[[ ${err} = 0 ]] && failure="" || failure="
-
-
-
+
"
## testcase tag
content="${content}
${failure}
-
-
-
+
"
## testsuite block