From 05454150228890f52539c074418de9c6a923e28a Mon Sep 17 00:00:00 2001 From: Noam Manos Date: Tue, 25 Jun 2019 16:16:27 +0300 Subject: [PATCH] Option to print output into report, when tests have passed A new CLI option: -ginkgo.reportPassed It will print output for each passed test in the generated report, including JUnit, Teamcity, and Default reporters. For example, in JUnit (XML), the test output will be added under: ` ` The default behavior (without this option), prints test output only if the test case (spec) has failed. [Fixes #583] --- config/config.go | 6 +++++ internal/specrunner/spec_runner.go | 2 +- internal/specrunner/spec_runner_test.go | 2 ++ reporters/default_reporter.go | 9 ++++--- reporters/default_reporter_test.go | 32 +++++++++++++++++++++---- reporters/junit_reporter.go | 20 ++++++++++++---- reporters/junit_reporter_test.go | 9 +++++-- reporters/teamcity_reporter.go | 9 +++++-- reporters/teamcity_reporter_test.go | 7 +++++- 9 files changed, 79 insertions(+), 17 deletions(-) diff --git a/config/config.go b/config/config.go index dab2a2470..a733a32c3 100644 --- a/config/config.go +++ b/config/config.go @@ -52,6 +52,7 @@ type DefaultReporterConfigType struct { Succinct bool Verbose bool FullTrace bool + ReportPassed bool } var DefaultReporterConfig = DefaultReporterConfigType{} @@ -98,6 +99,7 @@ func Flags(flagSet *flag.FlagSet, prefix string, includeParallelFlags bool) { flagSet.BoolVar(&(DefaultReporterConfig.Verbose), prefix+"v", false, "If set, default reporter print out all specs as they begin.") flagSet.BoolVar(&(DefaultReporterConfig.Succinct), prefix+"succinct", false, "If set, default reporter prints out a very succinct report") flagSet.BoolVar(&(DefaultReporterConfig.FullTrace), prefix+"trace", false, "If set, default reporter prints out the full stack trace when a failure occurs") + flagSet.BoolVar(&(DefaultReporterConfig.ReportPassed), prefix+"reportPassed", false, "If set, default reporter prints out captured output of passed tests.") } func BuildFlagArgs(prefix string, ginkgo GinkgoConfigType, reporter DefaultReporterConfigType) []string { @@ -196,5 +198,9 @@ func BuildFlagArgs(prefix string, ginkgo GinkgoConfigType, reporter DefaultRepor result = append(result, fmt.Sprintf("--%strace", prefix)) } + if reporter.ReportPassed { + result = append(result, fmt.Sprintf("--%sreportPassed", prefix)) + } + return result } diff --git a/internal/specrunner/spec_runner.go b/internal/specrunner/spec_runner.go index 2c683cb8b..c9a0a60d8 100644 --- a/internal/specrunner/spec_runner.go +++ b/internal/specrunner/spec_runner.go @@ -300,7 +300,7 @@ func (runner *SpecRunner) reportSpecWillRun(summary *types.SpecSummary) { } func (runner *SpecRunner) reportSpecDidComplete(summary *types.SpecSummary, failed bool) { - if failed && len(summary.CapturedOutput) == 0 { + if len(summary.CapturedOutput) == 0 { summary.CapturedOutput = string(runner.writer.Bytes()) } for i := len(runner.reporters) - 1; i >= 1; i-- { diff --git a/internal/specrunner/spec_runner_test.go b/internal/specrunner/spec_runner_test.go index a41437922..772f569d8 100644 --- a/internal/specrunner/spec_runner_test.go +++ b/internal/specrunner/spec_runner_test.go @@ -720,6 +720,7 @@ var _ = Describe("Spec Runner", func() { "R1.WillRun", "R2.WillRun", "A", + "BYTES", "R2.DidComplete", "R1.DidComplete", "TRUNCATE", @@ -734,6 +735,7 @@ var _ = Describe("Spec Runner", func() { "R1.WillRun", "R2.WillRun", "C", + "BYTES", "R2.DidComplete", "R1.DidComplete", })) diff --git a/reporters/default_reporter.go b/reporters/default_reporter.go index ac58dd5f7..8f9066df1 100644 --- a/reporters/default_reporter.go +++ b/reporters/default_reporter.go @@ -14,9 +14,9 @@ import ( ) type DefaultReporter struct { - config config.DefaultReporterConfigType - stenographer stenographer.Stenographer - specSummaries []*types.SpecSummary + config config.DefaultReporterConfigType + stenographer stenographer.Stenographer + specSummaries []*types.SpecSummary } func NewDefaultReporter(config config.DefaultReporterConfigType, stenographer stenographer.Stenographer) *DefaultReporter { @@ -62,6 +62,9 @@ func (reporter *DefaultReporter) SpecDidComplete(specSummary *types.SpecSummary) reporter.stenographer.AnnounceSuccesfulSlowSpec(specSummary, reporter.config.Succinct) } else { reporter.stenographer.AnnounceSuccesfulSpec(specSummary) + if reporter.config.ReportPassed { + reporter.stenographer.AnnounceCapturedOutput(specSummary.CapturedOutput) + } } case types.SpecStatePending: reporter.stenographer.AnnouncePendingSpec(specSummary, reporter.config.NoisyPendings && !reporter.config.Succinct) diff --git a/reporters/default_reporter_test.go b/reporters/default_reporter_test.go index 2dcf276d3..5d8e1d7bc 100644 --- a/reporters/default_reporter_test.go +++ b/reporters/default_reporter_test.go @@ -237,10 +237,22 @@ var _ = Describe("DefaultReporter", func() { }) }) - Context("Otherwise", func() { - It("should announce the succesful spec", func() { + Context("When the spec is successful", func() { + It("should announce the successful spec", func() { Ω(stenographer.Calls()[0]).Should(Equal(call("AnnounceSuccesfulSpec", spec))) }) + + Context("When ReportPassed flag is set", func() { + BeforeEach(func() { + reporterConfig.ReportPassed = true + reporter = reporters.NewDefaultReporter(reporterConfig, stenographer) + spec.CapturedOutput = "test scenario" + }) + + It("should announce the captured output", func() { + Ω(stenographer.Calls()[1]).Should(Equal(call("AnnounceCapturedOutput", spec.CapturedOutput))) + }) + }) }) }) @@ -361,10 +373,22 @@ var _ = Describe("DefaultReporter", func() { }) }) - Context("Otherwise", func() { - It("should announce the succesful spec", func() { + Context("When the spec is successful", func() { + It("should announce the successful spec", func() { Ω(stenographer.Calls()[0]).Should(Equal(call("AnnounceSuccesfulSpec", spec))) }) + + Context("When ReportPassed flag is set", func() { + BeforeEach(func() { + reporterConfig.ReportPassed = true + reporter = reporters.NewDefaultReporter(reporterConfig, stenographer) + spec.CapturedOutput = "test scenario" + }) + + It("should announce the captured output", func() { + Ω(stenographer.Calls()[1]).Should(Equal(call("AnnounceCapturedOutput", spec.CapturedOutput))) + }) + }) }) }) diff --git a/reporters/junit_reporter.go b/reporters/junit_reporter.go index 2c9f3c792..5a452e655 100644 --- a/reporters/junit_reporter.go +++ b/reporters/junit_reporter.go @@ -32,12 +32,17 @@ type JUnitTestSuite struct { type JUnitTestCase struct { Name string `xml:"name,attr"` ClassName string `xml:"classname,attr"` + PassedMessage *JUnitPassedMessage `xml:"passed,omitempty"` FailureMessage *JUnitFailureMessage `xml:"failure,omitempty"` Skipped *JUnitSkipped `xml:"skipped,omitempty"` Time float64 `xml:"time,attr"` SystemOut string `xml:"system-out,omitempty"` } +type JUnitPassedMessage struct { + Message string `xml:",chardata"` +} + type JUnitFailureMessage struct { Type string `xml:"type,attr"` Message string `xml:",chardata"` @@ -48,9 +53,10 @@ type JUnitSkipped struct { } type JUnitReporter struct { - suite JUnitTestSuite - filename string - testSuiteName string + suite JUnitTestSuite + filename string + testSuiteName string + ReporterConfig config.DefaultReporterConfigType } //NewJUnitReporter creates a new JUnit XML reporter. The XML will be stored in the passed in filename. @@ -60,12 +66,13 @@ func NewJUnitReporter(filename string) *JUnitReporter { } } -func (reporter *JUnitReporter) SpecSuiteWillBegin(config config.GinkgoConfigType, summary *types.SuiteSummary) { +func (reporter *JUnitReporter) SpecSuiteWillBegin(ginkgoConfig config.GinkgoConfigType, summary *types.SuiteSummary) { reporter.suite = JUnitTestSuite{ Name: summary.SuiteDescription, TestCases: []JUnitTestCase{}, } reporter.testSuiteName = summary.SuiteDescription + reporter.ReporterConfig = config.DefaultReporterConfig } func (reporter *JUnitReporter) SpecWillRun(specSummary *types.SpecSummary) { @@ -105,6 +112,11 @@ func (reporter *JUnitReporter) SpecDidComplete(specSummary *types.SpecSummary) { Name: strings.Join(specSummary.ComponentTexts[1:], " "), ClassName: reporter.testSuiteName, } + if reporter.ReporterConfig.ReportPassed && specSummary.State == types.SpecStatePassed { + testCase.PassedMessage = &JUnitPassedMessage{ + Message: specSummary.CapturedOutput, + } + } if specSummary.State == types.SpecStateFailed || specSummary.State == types.SpecStateTimedOut || specSummary.State == types.SpecStatePanicked { testCase.FailureMessage = &JUnitFailureMessage{ Type: reporter.failureTypeForState(specSummary.State), diff --git a/reporters/junit_reporter_test.go b/reporters/junit_reporter_test.go index 9b75dc006..e3238fd6b 100644 --- a/reporters/junit_reporter_test.go +++ b/reporters/junit_reporter_test.go @@ -16,8 +16,8 @@ import ( var _ = Describe("JUnit Reporter", func() { var ( - outputFile string - reporter Reporter + outputFile string + reporter *reporters.JUnitReporter ) testSuiteTime := 12456999 * time.Microsecond reportedSuiteTime := 12.456 @@ -61,8 +61,12 @@ var _ = Describe("JUnit Reporter", func() { } reporter.AfterSuiteDidRun(afterSuite) + // Set the ReportPassed config flag, in order to show captured output when tests have passed. + reporter.ReporterConfig.ReportPassed = true + spec := &types.SpecSummary{ ComponentTexts: []string{"[Top Level]", "A", "B", "C"}, + CapturedOutput: "Test scenario...", State: types.SpecStatePassed, RunTime: 5 * time.Second, } @@ -89,6 +93,7 @@ var _ = Describe("JUnit Reporter", func() { Ω(output.TestCases[0].FailureMessage).Should(BeNil()) Ω(output.TestCases[0].Skipped).Should(BeNil()) Ω(output.TestCases[0].Time).Should(Equal(5.0)) + Ω(output.TestCases[0].PassedMessage.Message).Should(ContainSubstring("Test scenario")) }) }) diff --git a/reporters/teamcity_reporter.go b/reporters/teamcity_reporter.go index 36ee2a600..c8e27b2a7 100644 --- a/reporters/teamcity_reporter.go +++ b/reporters/teamcity_reporter.go @@ -22,8 +22,9 @@ const ( ) type TeamCityReporter struct { - writer io.Writer - testSuiteName string + writer io.Writer + testSuiteName string + ReporterConfig config.DefaultReporterConfigType } func NewTeamCityReporter(writer io.Writer) *TeamCityReporter { @@ -65,6 +66,10 @@ func (reporter *TeamCityReporter) SpecWillRun(specSummary *types.SpecSummary) { func (reporter *TeamCityReporter) SpecDidComplete(specSummary *types.SpecSummary) { testName := escape(strings.Join(specSummary.ComponentTexts[1:], " ")) + if reporter.ReporterConfig.ReportPassed && specSummary.State == types.SpecStatePassed { + details := escape(specSummary.CapturedOutput) + fmt.Fprintf(reporter.writer, "%s[testPassed name='%s' details='%s']", messageId, testName, details) + } if specSummary.State == types.SpecStateFailed || specSummary.State == types.SpecStateTimedOut || specSummary.State == types.SpecStatePanicked { message := escape(specSummary.Failure.ComponentCodeLocation.String()) details := escape(specSummary.Failure.Message) diff --git a/reporters/teamcity_reporter_test.go b/reporters/teamcity_reporter_test.go index b45d5db01..53b4d29d3 100644 --- a/reporters/teamcity_reporter_test.go +++ b/reporters/teamcity_reporter_test.go @@ -16,7 +16,7 @@ import ( var _ = Describe("TeamCity Reporter", func() { var ( buffer bytes.Buffer - reporter Reporter + reporter *reporters.TeamCityReporter ) BeforeEach(func() { @@ -40,8 +40,12 @@ var _ = Describe("TeamCity Reporter", func() { } reporter.AfterSuiteDidRun(afterSuite) + // Set the ReportPassed config flag, in order to show captured output when tests have passed. + reporter.ReporterConfig.ReportPassed = true + spec := &types.SpecSummary{ ComponentTexts: []string{"[Top Level]", "A", "B", "C"}, + CapturedOutput: "Test scenario...", State: types.SpecStatePassed, RunTime: 5 * time.Second, } @@ -60,6 +64,7 @@ var _ = Describe("TeamCity Reporter", func() { expected := "##teamcity[testSuiteStarted name='Foo|'s test suite']" + "##teamcity[testStarted name='A B C']" + + "##teamcity[testPassed name='A B C' details='Test scenario...']" + "##teamcity[testFinished name='A B C' duration='5000']" + "##teamcity[testSuiteFinished name='Foo|'s test suite']" Ω(actual).Should(Equal(expected))