Skip to content

Commit

Permalink
cmd/tool/slowest: Add a skeleton for a new command
Browse files Browse the repository at this point in the history
  • Loading branch information
dnephin committed May 15, 2020
1 parent ab93a78 commit 2cc4019
Show file tree
Hide file tree
Showing 3 changed files with 148 additions and 20 deletions.
20 changes: 20 additions & 0 deletions cmd/tool/cmd.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package tool

import (
"fmt"

"gotest.tools/gotestsum/cmd/tool/slowest"
)

func Run(name string, args []string) error {
if len(args) == 0 {
// TOOD: print help
return fmt.Errorf("invalid command: %v", name)
}
switch args[0] {
case "slowest":
return slowest.Run(name+" "+args[0], args[1:])
}
// TOOD: print help
return fmt.Errorf("invalid command: %v", name)
}
92 changes: 92 additions & 0 deletions cmd/tool/slowest/slowest.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package slowest

import (
"bytes"
"fmt"
"os"
"sort"
"time"

"github.com/spf13/pflag"
"gotest.tools/gotestsum/testjson"
)

func Run(name string, args []string) error {
flags, opts := setupFlags(name)
if err := flags.Parse(args); err != nil {
return err
}
return run(opts)
}

func setupFlags(name string) (*pflag.FlagSet, *options) {
opts := &options{}
flags := pflag.NewFlagSet(name, pflag.ContinueOnError)
flags.SetInterspersed(false)
flags.Usage = func() {
fmt.Fprintf(os.Stderr, `Usage:
%s [flags]
Flags:
`, name)
flags.PrintDefaults()
}
flags.DurationVar(&opts.threshold, "threshold", 100*time.Millisecond,
"tests faster than this threshold will be omitted from the output")
return flags, opts
}

type options struct {
threshold time.Duration
}

func run(opts *options) error {
exec, err := testjson.ScanTestOutput(testjson.ScanConfig{
Stdout: os.Stdin,
Stderr: bytes.NewReader(nil),
Handler: eventHandler{},
})
if err != nil {
return err
}
for _, tc := range slowTestCases(exec, opts.threshold) {
// TODO: allow elapsed time unit to be configurable
fmt.Printf("%s %s %d\n", tc.Package, tc.Test, tc.Elapsed.Milliseconds())
}

return nil
}

// slowTestCases returns a slice of all tests with an elapsed time greater than
// threshold. The slice is sorted by Elapsed time in descending order (slowest
// test first).
// TODO: may be shared with testjson Summary
func slowTestCases(exec *testjson.Execution, threshold time.Duration) []testjson.TestCase {
if threshold == 0 {
return nil
}
pkgs := exec.Packages()
tests := make([]testjson.TestCase, 0, len(pkgs))
for _, pkg := range pkgs {
tests = append(tests, exec.Package(pkg).TestCases()...)
}
// TODO: use median test runtime
sort.Slice(tests, func(i, j int) bool {
return tests[i].Elapsed > tests[j].Elapsed
})
end := sort.Search(len(tests), func(i int) bool {
return tests[i].Elapsed < threshold
})
return tests[:end]
}

type eventHandler struct{}

func (h eventHandler) Err(text string) error {
_, err := fmt.Fprintln(os.Stdout, text)
return err
}

func (h eventHandler) Event(_ testjson.TestEvent, _ *testjson.Execution) error {
return nil
}
56 changes: 36 additions & 20 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,41 +11,57 @@ import (
"github.com/fatih/color"
"github.com/pkg/errors"
"github.com/spf13/pflag"
"gotest.tools/gotestsum/cmd/tool"
"gotest.tools/gotestsum/log"
"gotest.tools/gotestsum/testjson"
)

var version = "master"

func main() {
name := os.Args[0]
flags, opts := setupFlags(name)
switch err := flags.Parse(os.Args[1:]); {
case err == pflag.ErrHelp:
os.Exit(0)
case err != nil:
err := route(os.Args)
if err == pflag.ErrHelp || err == nil {
return
}
switch err.(type) {
// TODO: on parse error print usage
//log.Error(err.Error())
//flags.Usage()
//os.Exit(1)
case *exec.ExitError:
// go test should already report the error to stderr, exit with
// the same status code
os.Exit(ExitCodeWithDefault(err))
default:
log.Errorf(err.Error())
flags.Usage()
os.Exit(1)
os.Exit(3)
}
}

func route(args []string) error {
if len(args) == 1 {
return runMain(args[0], args[1:])
}
switch args[1] {
case "tool":
return tool.Run(args[0]+" "+args[1], args[2:])
}
return runMain(args[0], args[1:])
}

func runMain(name string, args []string) error {
flags, opts := setupFlags(name)
if err := flags.Parse(args); err != nil {
return err
}
opts.args = flags.Args()
setupLogging(opts)

if opts.version {
fmt.Fprintf(os.Stdout, "gotestsum version %s\n", version)
os.Exit(0)
}

switch err := run(opts).(type) {
case nil:
case *exec.ExitError:
// go test should already report the error to stderr so just exit with
// the same status code
os.Exit(ExitCodeWithDefault(err))
default:
log.Errorf(err.Error())
os.Exit(3)
return nil
}
return run(opts)
}

func setupFlags(name string) (*pflag.FlagSet, *options) {
Expand Down

0 comments on commit 2cc4019

Please sign in to comment.