diff --git a/cmd/gdt/cmd/run.go b/cmd/gdt/cmd/run.go index f71c809..8acfea0 100644 --- a/cmd/gdt/cmd/run.go +++ b/cmd/gdt/cmd/run.go @@ -1,15 +1,20 @@ package cmd import ( + "encoding/json" + "encoding/xml" "fmt" "os" + "path/filepath" "strings" "time" + "github.com/gdt-dev/core/api" gdtcontext "github.com/gdt-dev/core/context" "github.com/gdt-dev/core/run" "github.com/gdt-dev/core/scenario" "github.com/gdt-dev/core/suite" + "github.com/gdt-dev/core/testunit" "github.com/spf13/cobra" "github.com/gdt-dev/gdt/cmd/gdt/pkg/cli" @@ -24,11 +29,24 @@ The command will run gdt test scenarios or test suites pointed to by . should be a path to a YAML file or a directory containing YAML files. -Returns 0 on if all subject test scenarios complete without failure, 1 -otherwise. +Returns 0 if all subject test scenarios complete without failure, 1 otherwise. ` ) +var ( + runOutputFormatHuman = "human" + runOutputFormatJSON = "json" + runOutputFormatXUnit = "xunit" + defaultRunOutputFormat = runOutputFormatHuman + supportedOutputFormats = []string{ + runOutputFormatHuman, + runOutputFormatJSON, + runOutputFormatXUnit, + } + optRunOutputFormat = defaultRunOutputFormat + usageRunOutputFormat = `output format ("human","json","xunit")` +) + var RunCmd = &cobra.Command{ Use: runUsage, Short: "run test scenario/suites.", @@ -45,6 +63,11 @@ func init() { false, optQuietUsage, ) + RunCmd.Flags().StringVarP( + &optRunOutputFormat, + "output-format", "o", + defaultRunOutputFormat, usageRunOutputFormat, + ) } func doRun(cmd *cobra.Command, args []string) error { @@ -55,7 +78,7 @@ func doRun(cmd *cobra.Command, args []string) error { cli.CommonOptions.Verbose = true } ctx := gdtcontext.New(gdtcontext.WithDebugPrefix(debugPrefix)) - run := run.New() + runs := []*run.Run{} for _, path := range args { fi, err := os.Stat(path) if err != nil { @@ -64,6 +87,7 @@ func doRun(cmd *cobra.Command, args []string) error { } return err } + run := run.New() if fi.IsDir() { cli.Df("loading suite from directory %q ...", path) su, err := suite.FromDir(path) @@ -97,34 +121,49 @@ func doRun(cmd *cobra.Command, args []string) error { return err } } + // We do this here so that we get more immediate output results + // when using human output format + if optRunOutputFormat == runOutputFormatHuman { + printRun(run) + } + runs = append(runs, run) + } + if optRunOutputFormat != runOutputFormatHuman { + return printResults(runs) } + return nil +} +// printRun outputs the human-readable results of a test scenario run. +func printRun( + run *run.Run, +) { if !optQuiet { paths := run.ScenarioPaths() for _, path := range paths { + shortPath := filepath.Base(path) if cli.CommonOptions.Verbose { - fmt.Printf("=== RUN: %s\n", path) + fmt.Printf("=== RUN: %s\n", shortPath) } var scenElapsed time.Duration results := run.ScenarioResults(path) scenOK := true + for _, res := range results { scenElapsed += res.Elapsed() scenOK = scenOK && res.OK() printTestUnitResult(res) } - if !optQuiet { - if scenOK { - if cli.CommonOptions.Verbose { - fmt.Printf("PASS (%s)\n", scenElapsed) - } else { - fmt.Printf("ok\t%s\t%s\n", path, scenElapsed) - } + if scenOK { + if cli.CommonOptions.Verbose { + fmt.Printf("PASS (%s)\n", scenElapsed) } else { - fmt.Printf("FAIL\t%s\t%s\n", path, scenElapsed) + fmt.Printf("ok\t%s\t%s\n", path, scenElapsed) } + } else { + fmt.Printf("FAIL\t%s\t%s\n", path, scenElapsed) } } } @@ -138,10 +177,9 @@ func doRun(cmd *cobra.Command, args []string) error { fmt.Println("PASS") } } - return nil } -func printTestUnitResult(r run.TestUnitResult) { +func printTestUnitResult(r testunit.Result) { if r.Skipped() { if cli.CommonOptions.Verbose { fmt.Printf("--- SKIP: %s (%s)\n", r.Name(), r.Elapsed()) @@ -172,6 +210,38 @@ func printTestUnitResult(r run.TestUnitResult) { } } +// printResults outputs the test results of all test scenarios for JSON or +// XUnit output. +func printResults( + runs []*run.Run, +) error { + suites := []api.XUnitTestSuite{} + for _, r := range runs { + suites = append(suites, r.XUnit()...) + } + res := api.XUnitResults{ + TestSuites: suites, + } + var err error + var out string + var b []byte + if optRunOutputFormat == runOutputFormatJSON { + b, err = json.MarshalIndent(res, "", " ") + if err != nil { + return err + } + out = string(b) + } else { + b, err = xml.MarshalIndent(res, "", " ") + if err != nil { + return err + } + out = xml.Header + string(b) + } + fmt.Println(out) + return nil +} + func indent(subject string, level int) string { indentStr := strings.Repeat(" ", level*4) b := strings.Builder{} diff --git a/cmd/gdt/go.mod b/cmd/gdt/go.mod index 4071b44..b6c284f 100644 --- a/cmd/gdt/go.mod +++ b/cmd/gdt/go.mod @@ -3,7 +3,7 @@ module github.com/gdt-dev/gdt/cmd/gdt go 1.24.3 require ( - github.com/gdt-dev/core v1.12.0 + github.com/gdt-dev/core v1.12.1 github.com/gdt-dev/http v1.11.0 github.com/gdt-dev/kube v1.11.0 github.com/samber/lo v1.51.0 diff --git a/cmd/gdt/go.sum b/cmd/gdt/go.sum index 28aed87..af8f332 100644 --- a/cmd/gdt/go.sum +++ b/cmd/gdt/go.sum @@ -21,8 +21,8 @@ github.com/evanphx/json-patch/v5 v5.9.11 h1:/8HVnzMq13/3x9TPvjG08wUGqBTmZBsCWzjT github.com/evanphx/json-patch/v5 v5.9.11/go.mod h1:3j+LviiESTElxA4p3EMKAB9HXj3/XEtnUf6OZxqIQTM= github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM= github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= -github.com/gdt-dev/core v1.12.0 h1:/Apz/cwEWhkT3jmexIDQmnVbbzzC6iwmgunfpPMyMx0= -github.com/gdt-dev/core v1.12.0/go.mod h1:hGXsI+PXavvLkm0BQIYKZ1O3yRw86m/VD2xFiNwFR/Y= +github.com/gdt-dev/core v1.12.1 h1:nW6/+aY42ZpQZaWbKwDQyYt10XXwZj0scV5ca3NMAnY= +github.com/gdt-dev/core v1.12.1/go.mod h1:hGXsI+PXavvLkm0BQIYKZ1O3yRw86m/VD2xFiNwFR/Y= github.com/gdt-dev/http v1.11.0 h1:xEkdLLXbI3wwd61RDcjE2lQdsQ+R0y7ROlbjbBDAe/o= github.com/gdt-dev/http v1.11.0/go.mod h1:SHsSUi6+OybfQCY7kSaLc9LaKovXokVjVKaAzVkZvFE= github.com/gdt-dev/kube v1.11.0 h1:o6/Dq5ho6LJP1FPxili1jvYuRl+D6HNhzCyXlfmQOpg= diff --git a/go.mod b/go.mod index d0d8ac0..d3012ec 100644 --- a/go.mod +++ b/go.mod @@ -2,7 +2,7 @@ module github.com/gdt-dev/gdt go 1.24.3 -require github.com/gdt-dev/core v1.12.0 +require github.com/gdt-dev/core v1.12.1 require ( github.com/Masterminds/semver/v3 v3.4.0 // indirect diff --git a/go.sum b/go.sum index 24a42d0..60dadfc 100644 --- a/go.sum +++ b/go.sum @@ -4,8 +4,8 @@ github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEe github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/gdt-dev/core v1.12.0 h1:/Apz/cwEWhkT3jmexIDQmnVbbzzC6iwmgunfpPMyMx0= -github.com/gdt-dev/core v1.12.0/go.mod h1:hGXsI+PXavvLkm0BQIYKZ1O3yRw86m/VD2xFiNwFR/Y= +github.com/gdt-dev/core v1.12.1 h1:nW6/+aY42ZpQZaWbKwDQyYt10XXwZj0scV5ca3NMAnY= +github.com/gdt-dev/core v1.12.1/go.mod h1:hGXsI+PXavvLkm0BQIYKZ1O3yRw86m/VD2xFiNwFR/Y= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=