From eb3964d133db68c4dc87fb884b5a54cdeebb474a Mon Sep 17 00:00:00 2001 From: Simon Baeumer Date: Tue, 5 Jan 2021 16:52:54 +0100 Subject: [PATCH 1/2] add outputs --- cmd/commander/commander.go | 8 +++++- pkg/app/app.go | 2 ++ pkg/app/test_command.go | 12 ++++++--- pkg/output/cli.go | 26 +++++++++---------- pkg/output/cli_test.go | 20 +++++++------- pkg/output/output.go | 28 ++++++++++++++++++++ pkg/output/output_test.go | 13 ++++++++++ pkg/output/tap.go | 53 ++++++++++++++++++++++++++++++++++++++ pkg/runtime/runtime.go | 2 +- 9 files changed, 136 insertions(+), 28 deletions(-) create mode 100644 pkg/output/output.go create mode 100644 pkg/output/output_test.go create mode 100644 pkg/output/tap.go diff --git a/cmd/commander/commander.go b/cmd/commander/commander.go index 1cc12b45..1d84c821 100644 --- a/cmd/commander/commander.go +++ b/cmd/commander/commander.go @@ -2,6 +2,7 @@ package main import ( "fmt" + "github.com/commander-cli/commander/pkg/output" "io/ioutil" "log" "os" @@ -75,8 +76,8 @@ commander test commander.yaml --filter="^filter1$" Flags: []cli.Flag{ cli.BoolFlag{ Name: "no-color", - EnvVar: "COMMANDER_NO_COLOR", Usage: "Activate or deactivate colored output", + EnvVar: "COMMANDER_NO_COLOR", }, cli.BoolFlag{ Name: "verbose", @@ -95,6 +96,11 @@ commander test commander.yaml --filter="^filter1$" Name: "filter", Usage: `Filter tests by a given regex pattern. Tests are filtered by its title.`, }, + cli.StringFlag{ + Name: "format", + Usage: `Use a different test output format. Available are: cli, tap`, + Value: output.CLI, + }, }, Action: func(c *cli.Context) error { return app.TestCommand(c.Args().First(), app.NewTestContextFromCli(c)) diff --git a/pkg/app/app.go b/pkg/app/app.go index 8ec97bbc..eb9191dd 100644 --- a/pkg/app/app.go +++ b/pkg/app/app.go @@ -19,6 +19,7 @@ type TestCommandContext struct { Workdir string Concurrent int Filters []string + Format string } //NewTestContextFromCli is a constructor which creates the context @@ -30,5 +31,6 @@ func NewTestContextFromCli(c *cli.Context) TestCommandContext { Workdir: c.String("workdir"), Concurrent: c.Int("concurrent"), Filters: c.StringSlice("filter"), + Format: c.String("format"), } } diff --git a/pkg/app/test_command.go b/pkg/app/test_command.go index a4b04364..42797abb 100644 --- a/pkg/app/test_command.go +++ b/pkg/app/test_command.go @@ -15,7 +15,7 @@ import ( "github.com/commander-cli/commander/pkg/suite" ) -var out output.OutputWriter +var out output.Output // TestCommand executes the test argument // testPath is the path to the test suite config, it can be a dir or file @@ -33,14 +33,16 @@ func TestCommand(testPath string, ctx TestCommandContext) error { log.SetOutput(os.Stdout) } - out = output.NewCliOutput(!ctx.NoColor) + out, err := output.NewOutput(ctx.Format, !ctx.NoColor) + if err != nil { + return err + } if testPath == "" { testPath = CommanderFile } var result runtime.Result - var err error switch { case ctx.Dir: fmt.Println("Starting test against directory: " + testPath + "...") @@ -64,7 +66,9 @@ func TestCommand(testPath string, ctx TestCommandContext) error { return fmt.Errorf(err.Error()) } - if !out.PrintSummary(result) && !ctx.Verbose { + + out.PrintSummary(result) + if result.Failed != 0 && !ctx.Verbose { return fmt.Errorf("Test suite failed, use --verbose for more detailed output") } diff --git a/pkg/output/cli.go b/pkg/output/cli.go index 36add127..0c1fe599 100644 --- a/pkg/output/cli.go +++ b/pkg/output/cli.go @@ -11,15 +11,17 @@ import ( "github.com/logrusorgru/aurora" ) -// OutputWriter represents the output -type OutputWriter struct { +var _ Output = (*CLIOutputWriter)(nil) + +// CLIOutputWriter represents the output +type CLIOutputWriter struct { out io.Writer au aurora.Aurora template cliTemplate } -// NewCliOutput creates a new OutputWriter with a stdout writer -func NewCliOutput(color bool) OutputWriter { +// NewCliOutput creates a new C̄LIOutputWriter with a stdout writer +func NewCliOutput(color bool) Output { au := aurora.NewAurora(color) if run.GOOS == "windows" { au = aurora.NewAurora(false) @@ -27,7 +29,7 @@ func NewCliOutput(color bool) OutputWriter { t := newCliTemplate() - return OutputWriter{ + return CLIOutputWriter{ out: os.Stdout, au: au, template: t, @@ -48,7 +50,7 @@ type TestResult struct { } // GetEventHandler create a new runtime.EventHandler -func (w *OutputWriter) GetEventHandler() *runtime.EventHandler { +func (w CLIOutputWriter) GetEventHandler() *runtime.EventHandler { handler := runtime.EventHandler{} handler.TestFinished = func(testResult runtime.TestResult) { tr := convertTestResult(testResult) @@ -64,7 +66,7 @@ func (w *OutputWriter) GetEventHandler() *runtime.EventHandler { } // PrintSummary prints summary -func (w *OutputWriter) PrintSummary(result runtime.Result) bool { +func (w CLIOutputWriter) PrintSummary(result runtime.Result) { if result.Failed > 0 { w.printFailures(result.TestResults) } @@ -77,12 +79,10 @@ func (w *OutputWriter) PrintSummary(result runtime.Result) bool { } else { w.fprintf(w.au.Green(summary)) } - - return result.Failed == 0 } // printResult prints the simple output form of a TestReault -func (w *OutputWriter) printResult(r TestResult) { +func (w CLIOutputWriter) printResult(r TestResult) { if !r.Success { w.fprintf(w.au.Red(w.template.testResult(r))) return @@ -90,11 +90,11 @@ func (w *OutputWriter) printResult(r TestResult) { w.fprintf(w.template.testResult(r)) } -func (w *OutputWriter) printSkip(r TestResult) { +func (w CLIOutputWriter) printSkip(r TestResult) { w.fprintf(fmt.Sprintf("- [%s] %s, was skipped", r.Node, r.Title)) } -func (w *OutputWriter) printFailures(results []runtime.TestResult) { +func (w CLIOutputWriter) printFailures(results []runtime.TestResult) { w.fprintf("") w.fprintf(w.au.Bold("Results")) w.fprintf(w.au.Bold("")) @@ -118,7 +118,7 @@ func (w *OutputWriter) printFailures(results []runtime.TestResult) { } } -func (w *OutputWriter) fprintf(a ...interface{}) { +func (w CLIOutputWriter) fprintf(a ...interface{}) { if _, err := fmt.Fprintln(w.out, a...); err != nil { log.Fatal(err) } diff --git a/pkg/output/cli_test.go b/pkg/output/cli_test.go index 239b2a97..654d5d5a 100644 --- a/pkg/output/cli_test.go +++ b/pkg/output/cli_test.go @@ -11,7 +11,7 @@ import ( func Test_NewCliOutput(t *testing.T) { got := NewCliOutput(true) - assert.IsType(t, OutputWriter{}, got) + assert.IsType(t, CLIOutputWriter{}, got) } func Test_GetEventHandler(t *testing.T) { @@ -22,8 +22,9 @@ func Test_GetEventHandler(t *testing.T) { func Test_EventHandlerTestFinished(t *testing.T) { var buf bytes.Buffer - writer := NewCliOutput(true) - writer.out = &buf + writer := CLIOutputWriter{ + out: &buf, + } eh := writer.GetEventHandler() testResults := createFakeTestResults() @@ -39,8 +40,9 @@ func Test_EventHandlerTestFinished(t *testing.T) { func Test_EventHandlerTestSkipped(t *testing.T) { var buf bytes.Buffer - writer := NewCliOutput(true) - writer.out = &buf + writer := CLIOutputWriter{ + out: &buf, + } eh := writer.GetEventHandler() testResults := createFakeTestResults() @@ -62,11 +64,11 @@ func Test_PrintSummary(t *testing.T) { } var buf bytes.Buffer - writer := NewCliOutput(true) - writer.out = &buf + writer := CLIOutputWriter{ + out: &buf, + } - outResult := writer.PrintSummary(r) - assert.False(t, outResult) + writer.PrintSummary(r) output := buf.String() assert.Contains(t, output, "✗ [192.168.0.1] 'Failed test', on property 'Stdout'") diff --git a/pkg/output/output.go b/pkg/output/output.go new file mode 100644 index 00000000..24142fc1 --- /dev/null +++ b/pkg/output/output.go @@ -0,0 +1,28 @@ +package output + +import ( + "errors" + "fmt" + "github.com/commander-cli/commander/pkg/runtime" +) + +const ( + TAP = "tap" + CLI = "cli" +) + +type Output interface { + GetEventHandler() *runtime.EventHandler + PrintSummary(result runtime.Result) +} + +// NewOutput creates a new output +func NewOutput(format string, color bool) (Output, error) { + switch format { + case TAP: + return NewTAPOutputWriter(), nil + case CLI: + return NewCliOutput(color), nil + } + return nil, errors.New(fmt.Sprintf("Invalid format type %s", format)) +} diff --git a/pkg/output/output_test.go b/pkg/output/output_test.go new file mode 100644 index 00000000..884cfdda --- /dev/null +++ b/pkg/output/output_test.go @@ -0,0 +1,13 @@ +package output + +import ( + "github.com/stretchr/testify/assert" + "testing" +) + +func TestNewOutput(t *testing.T) { + var o Output + o, err := NewOutput(CLI, false) + assert.NoError(t, err) + assert.Implements(t, (*Output)(nil), o) +} diff --git a/pkg/output/tap.go b/pkg/output/tap.go new file mode 100644 index 00000000..929e2d4b --- /dev/null +++ b/pkg/output/tap.go @@ -0,0 +1,53 @@ +package output + +import ( + "fmt" + "github.com/commander-cli/commander/pkg/runtime" + "io" + "log" + "os" +) + +var _ Output = (*CLIOutputWriter)(nil) + +// TAPOutputWriter writes TAP results +type TAPOutputWriter struct { + out io.Writer +} + +// NewTAPOutputWriter represents the output, defaults to stdout +func NewTAPOutputWriter() Output { + return TAPOutputWriter{ + out: os.Stdout, + } +} + +func (w TAPOutputWriter) GetEventHandler() *runtime.EventHandler { + return nil +} + +func (w TAPOutputWriter) PrintSummary(result runtime.Result) { + counter := 0 + for _, r := range result.TestResults { + if r.Skipped { + // skipped tests are not specified in the TAP specification + continue + } + + counter++ + if r.FailedProperty != "" { + w.fprintf("%d ok - %s", counter+1, r.TestCase.Title) + } + if r.FailedProperty != "" { + w.fprintf("%d not ok - %s", counter+1, r.TestCase.Title) + } + } + + w.fprintf("1..%d", counter) +} + +func (w TAPOutputWriter) fprintf(msg string , a ...interface{}) { + if _, err := fmt.Fprintln(w.out, fmt.Sprintf(msg, a...)); err != nil { + log.Fatal(err) + } +} diff --git a/pkg/runtime/runtime.go b/pkg/runtime/runtime.go index 5e933211..67613c6b 100644 --- a/pkg/runtime/runtime.go +++ b/pkg/runtime/runtime.go @@ -123,7 +123,7 @@ type TestResult struct { Skipped bool } -// Result respresents the aggregation of all TestResults/summary of a runtime +// Result represents the aggregation of all TestResults/summary of a runtime type Result struct { TestResults []TestResult Duration time.Duration From 72790bb33b31f27703e44e845d6cfc908918b7aa Mon Sep 17 00:00:00 2001 From: Simon Baeumer Date: Tue, 5 Jan 2021 17:00:00 +0100 Subject: [PATCH 2/2] add output --- pkg/app/test_command.go | 4 ++-- pkg/output/tap.go | 5 ++--- pkg/runtime/runtime.go | 8 ++++++++ 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/pkg/app/test_command.go b/pkg/app/test_command.go index 42797abb..15cbdfbd 100644 --- a/pkg/app/test_command.go +++ b/pkg/app/test_command.go @@ -33,7 +33,8 @@ func TestCommand(testPath string, ctx TestCommandContext) error { log.SetOutput(os.Stdout) } - out, err := output.NewOutput(ctx.Format, !ctx.NoColor) + var err error + out, err = output.NewOutput(ctx.Format, !ctx.NoColor) if err != nil { return err } @@ -66,7 +67,6 @@ func TestCommand(testPath string, ctx TestCommandContext) error { return fmt.Errorf(err.Error()) } - out.PrintSummary(result) if result.Failed != 0 && !ctx.Verbose { return fmt.Errorf("Test suite failed, use --verbose for more detailed output") diff --git a/pkg/output/tap.go b/pkg/output/tap.go index 929e2d4b..7f22be54 100644 --- a/pkg/output/tap.go +++ b/pkg/output/tap.go @@ -23,7 +23,7 @@ func NewTAPOutputWriter() Output { } func (w TAPOutputWriter) GetEventHandler() *runtime.EventHandler { - return nil + return runtime.NewEmptyEventHandler() } func (w TAPOutputWriter) PrintSummary(result runtime.Result) { @@ -37,8 +37,7 @@ func (w TAPOutputWriter) PrintSummary(result runtime.Result) { counter++ if r.FailedProperty != "" { w.fprintf("%d ok - %s", counter+1, r.TestCase.Title) - } - if r.FailedProperty != "" { + } else { w.fprintf("%d not ok - %s", counter+1, r.TestCase.Title) } } diff --git a/pkg/runtime/runtime.go b/pkg/runtime/runtime.go index 67613c6b..0601a76e 100644 --- a/pkg/runtime/runtime.go +++ b/pkg/runtime/runtime.go @@ -166,3 +166,11 @@ func (r *Runtime) Start(tests []TestCase) Result { return result } + +// NewEmptyEventHandler returns an event handler with empty implementations +func NewEmptyEventHandler() *EventHandler { + return &EventHandler{ + TestFinished: func(result TestResult) {}, + TestSkipped: func(result TestResult) {}, + } +}