Deduplicate identical typecheck errors between platforms.

pull/8/head
Ryan Hitchman 2018-03-28 19:26:52 -07:00
parent da440386d6
commit 137268d332
2 changed files with 107 additions and 2 deletions

View File

@ -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(&currentWork, int64(n<<8|i))
if results != nil {
results <- analyzerResult{p, dir, a.dumpAndResetErrors()}
}
}
if a.failed {
statuses[i] = 1

View File

@ -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)
}
}
}