blob: b18b9a89bc2aa8ffdae248c6cfad2fd12c01c8d3 [file]
package main
/*
This program runs all unit tests in the repository.
*/
import (
"flag"
"fmt"
"os"
"os/exec"
"path"
"path/filepath"
"runtime"
"strings"
"sync"
"github.com/skia-dev/glog"
"go.skia.org/infra/go/common"
"go.skia.org/infra/go/timer"
)
const (
// This gets filled out and printed when a test fails.
TEST_FAILURE = `
============================= TEST FAILURE =============================
Test: %s
Command: %s
Error:
%s
Full output:
------------------------------------------------------------------------
%s
------------------------------------------------------------------------
`
)
// flags
var (
short = flag.Bool("short", false, "Whether or not to run the short version of the tests.")
)
var (
// Error message shown when a required executable is not installed.
ERR_NEED_INSTALL = "%s failed to run! Is it installed? Error: %v"
// Directories in which to run "go test"
GO_DIRS = []string{
".",
"alertserver",
"ct",
"datahopper",
"golden",
"perf",
"status",
}
// Directories with these names are skipped when searching for tests.
NO_CRAWL_DIR_NAMES = []string{
".git",
"bower_components",
"third_party",
"node_modules",
}
// Directories with these paths, relative to the checkout root, are
// skipped when searching for tests.
NO_CRAWL_REL_PATHS = []string{
"common",
}
)
// cmdTest returns a test which runs a command and fails if the command fails.
func cmdTest(cmd []string, cwd, name string) *test {
return &test{
Name: name,
Cmd: strings.Join(cmd, " "),
run: func() (error, string) {
command := exec.Command(cmd[0], cmd[1:]...)
if cwd != "" {
command.Dir = cwd
}
output, err := command.Output()
if err != nil {
if _, err2 := exec.LookPath(cmd[0]); err2 != nil {
return fmt.Errorf(ERR_NEED_INSTALL, cmd[0], err), string(output)
}
}
return err, string(output)
},
}
}
// goTest returns a test which runs `go test` in the given cwd.
func goTest(cwd string) *test {
cmd := []string{"go", "test", "-v", "./go/...", "-parallel", "1"}
if *short {
cmd = append(cmd, "-test.short")
}
return cmdTest(cmd, cwd, fmt.Sprintf("go tests in %s", cwd))
}
// pythonTest returns a test which runs the given Python script and fails if
// the script fails.
func pythonTest(testPath string) *test {
return cmdTest([]string{"python", testPath}, ".", path.Base(testPath))
}
// test is a struct which represents a single test to run.
type test struct {
Name string
Cmd string
run func() (error, string)
}
// Run executes the function for the given test and returns an error if it fails.
func (t test) Run() error {
defer timer.New(t.Name).Stop()
err, output := t.run()
if err != nil {
return fmt.Errorf(TEST_FAILURE, t.Name, t.Cmd, err, output)
}
return nil
}
// Find and run tests.
func main() {
defer common.LogPanic()
common.Init()
defer timer.New("Finished").Stop()
_, filename, _, _ := runtime.Caller(0)
rootDir := path.Dir(filename)
// If we are running full tests make sure we have the latest
// pdfium_test installed.
if !*short {
glog.Info("Installing pdfium_test if necessary.")
pdfiumInstall := path.Join(rootDir, "pdfium", "install_pdfium.sh")
if err := exec.Command(pdfiumInstall).Run(); err != nil {
glog.Fatalf("Failed to install pdfium_test: %v", err)
}
glog.Info("Latest pdfium_test installed successfully.")
}
// Gather all of the tests to run.
glog.Info("Searching for tests.")
tests := []*test{}
// Search for Python tests in the repo.
if err := filepath.Walk(rootDir, func(p string, info os.FileInfo, err error) error {
basename := path.Base(p)
// Skip some directories.
if info.IsDir() {
for _, skip := range NO_CRAWL_DIR_NAMES {
if basename == skip {
return filepath.SkipDir
}
}
for _, skip := range NO_CRAWL_REL_PATHS {
if p == path.Join(rootDir, skip) {
return filepath.SkipDir
}
}
}
if strings.HasSuffix(basename, "_test.py") {
tests = append(tests, pythonTest(p))
}
return nil
}); err != nil {
glog.Fatal(err)
}
// Go tests.
for _, go_dir := range GO_DIRS {
tests = append(tests, goTest(go_dir))
}
// Other tests.
tests = append(tests, cmdTest([]string{"go", "vet", "./..."}, ".", "go vet"))
tests = append(tests, cmdTest([]string{"errcheck", "go.skia.org/infra/..."}, ".", "errcheck"))
goimportsCmd := []string{"goimports", "-l", "."}
tests = append(tests, &test{
Name: "goimports",
Cmd: strings.Join(goimportsCmd, " "),
run: func() (error, string) {
command := exec.Command(goimportsCmd[0], goimportsCmd[1:]...)
output, err := command.Output()
outStr := strings.Trim(string(output), "\n")
if err != nil {
if _, err2 := exec.LookPath(goimportsCmd[0]); err2 != nil {
return fmt.Errorf(ERR_NEED_INSTALL, goimportsCmd[0], err), outStr
}
return err, outStr
}
diffFiles := strings.Split(outStr, "\n")
if len(diffFiles) > 0 && !(len(diffFiles) == 1 && diffFiles[0] == "") {
return fmt.Errorf("goimports found diffs in the following files:\n - %s", strings.Join(diffFiles, ",\n - ")), outStr
}
return nil, ""
},
})
// Run the tests.
glog.Infof("Found %d tests.", len(tests))
var mutex sync.Mutex
errors := map[string]error{}
var wg sync.WaitGroup
for _, t := range tests {
wg.Add(1)
go func(t *test) {
defer wg.Done()
if err := t.Run(); err != nil {
mutex.Lock()
errors[t.Name] = err
mutex.Unlock()
}
}(t)
}
wg.Wait()
if len(errors) > 0 {
for _, e := range errors {
glog.Error(e)
}
os.Exit(1)
}
glog.Info("All tests succeeded.")
}