Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Merge pull request #37 from paybyphone/add-teamcity-reporter

Add TeamCity reporter
  • Loading branch information...
commit 14aa8c214deb77a37409236c71ce8f6c2bad568f 2 parents 8ecc0b5 + ea448bb
@onsi authored
Showing with 207 additions and 0 deletions.
  1. +72 −0 reporters/teamcity_reporter.go
  2. +135 −0 reporters/teamcity_reporter_test.go
View
72 reporters/teamcity_reporter.go
@@ -0,0 +1,72 @@
+/*
+
+TeamCity Reporter for Ginkgo
+
+Makes use of TeamCity's support for Service Messages
+http://confluence.jetbrains.com/display/TCD7/Build+Script+Interaction+with+TeamCity#BuildScriptInteractionwithTeamCity-ReportingTests
+*/
+
+package reporters
+
+import (
+ "fmt"
+ "github.com/onsi/ginkgo/config"
+ "github.com/onsi/ginkgo/types"
+ "io"
+ "strings"
+)
+
+const (
+ messageId = "##teamcity"
+)
+
+type TeamCityReporter struct {
+ writer io.Writer
+ testSuiteName string
+}
+
+func NewTeamCityReporter(writer io.Writer) *TeamCityReporter {
+ return &TeamCityReporter{
+ writer: writer,
+ }
+}
+
+func (reporter *TeamCityReporter) SpecSuiteWillBegin(config config.GinkgoConfigType, summary *types.SuiteSummary) {
+ reporter.testSuiteName = escape(summary.SuiteDescription)
+ fmt.Fprintf(reporter.writer, "%s[testSuiteStarted name='%s']", messageId, reporter.testSuiteName)
+}
+
+func (reporter *TeamCityReporter) ExampleWillRun(exampleSummary *types.ExampleSummary) {
+ testName := escape(strings.Join(exampleSummary.ComponentTexts[1:], " "))
+ fmt.Fprintf(reporter.writer, "%s[testStarted name='%s']", messageId, testName)
+}
+
+func (reporter *TeamCityReporter) ExampleDidComplete(exampleSummary *types.ExampleSummary) {
+ testName := escape(strings.Join(exampleSummary.ComponentTexts[1:], " "))
+
+ if exampleSummary.State == types.ExampleStateFailed || exampleSummary.State == types.ExampleStateTimedOut || exampleSummary.State == types.ExampleStatePanicked {
+ message := escape(exampleSummary.Failure.ComponentCodeLocation.String())
+ details := escape(exampleSummary.Failure.Message)
+ fmt.Fprintf(reporter.writer, "%s[testFailed name='%s' message='%s' details='%s']", messageId, testName, message, details)
+ }
+ if exampleSummary.State == types.ExampleStateSkipped || exampleSummary.State == types.ExampleStatePending {
+ fmt.Fprintf(reporter.writer, "%s[testIgnored name='%s']", messageId, testName)
+ }
+
+ durationInMilliseconds := exampleSummary.RunTime.Seconds() * 1000
+ fmt.Fprintf(reporter.writer, "%s[testFinished name='%s' duration='%v']", messageId, testName, durationInMilliseconds)
+}
+
+func (reporter *TeamCityReporter) SpecSuiteDidEnd(summary *types.SuiteSummary) {
+ fmt.Fprintf(reporter.writer, "%s[testSuiteFinished name='%s']", messageId, reporter.testSuiteName)
+}
+
+func escape(output string) string {
+ output = strings.Replace(output, "|", "||", -1)
+ output = strings.Replace(output, "'", "|'", -1)
+ output = strings.Replace(output, "\n", "|n", -1)
+ output = strings.Replace(output, "\r", "|r", -1)
+ output = strings.Replace(output, "[", "|[", -1)
+ output = strings.Replace(output, "]", "|]", -1)
+ return output
+}
View
135 reporters/teamcity_reporter_test.go
@@ -0,0 +1,135 @@
+package reporters_test
+
+import (
+ "bytes"
+ "fmt"
+ . "github.com/onsi/ginkgo"
+ "github.com/onsi/ginkgo/config"
+ "github.com/onsi/ginkgo/reporters"
+ "github.com/onsi/ginkgo/types"
+ . "github.com/onsi/gomega"
+ "time"
+)
+
+var _ = Describe("TeamCity Reporter", func() {
+ var (
+ buffer bytes.Buffer
+ reporter Reporter
+ )
+
+ BeforeEach(func() {
+ buffer.Truncate(0)
+ reporter = reporters.NewTeamCityReporter(&buffer)
+ reporter.SpecSuiteWillBegin(config.GinkgoConfigType{}, &types.SuiteSummary{
+ SuiteDescription: "Foo's test suite",
+ NumberOfExamplesThatWillBeRun: 1,
+ })
+ })
+
+ Describe("a passing test", func() {
+ BeforeEach(func() {
+ example := &types.ExampleSummary{
+ ComponentTexts: []string{"[Top Level]", "A", "B", "C"},
+ State: types.ExampleStatePassed,
+ RunTime: 5 * time.Second,
+ }
+ reporter.ExampleWillRun(example)
+ reporter.ExampleDidComplete(example)
+
+ reporter.SpecSuiteDidEnd(&types.SuiteSummary{
+ NumberOfExamplesThatWillBeRun: 1,
+ NumberOfFailedExamples: 0,
+ RunTime: 10 * time.Second,
+ })
+ })
+
+ It("should record the test as passing", func() {
+ actual := buffer.String()
+ expected :=
+ "##teamcity[testSuiteStarted name='Foo|'s test suite']" +
+ "##teamcity[testStarted name='A B C']" +
+ "##teamcity[testFinished name='A B C' duration='5000']" +
+ "##teamcity[testSuiteFinished name='Foo|'s test suite']"
+ Ω(actual).Should(Equal(expected))
+ })
+ })
+
+ exampleStateCases := []struct {
+ state types.ExampleState
+ message string
+ }{
+ {types.ExampleStateFailed, "Failure"},
+ {types.ExampleStateTimedOut, "Timeout"},
+ {types.ExampleStatePanicked, "Panic"},
+ }
+
+ for _, exampleStateCase := range exampleStateCases {
+ exampleStateCase := exampleStateCase
+ Describe("a failing test", func() {
+ var example *types.ExampleSummary
+ BeforeEach(func() {
+ example = &types.ExampleSummary{
+ ComponentTexts: []string{"[Top Level]", "A", "B", "C"},
+ State: exampleStateCase.state,
+ RunTime: 5 * time.Second,
+ Failure: types.ExampleFailure{
+ ComponentCodeLocation: types.GenerateCodeLocation(0),
+ Message: "I failed",
+ },
+ }
+ reporter.ExampleWillRun(example)
+ reporter.ExampleDidComplete(example)
+
+ reporter.SpecSuiteDidEnd(&types.SuiteSummary{
+ NumberOfExamplesThatWillBeRun: 1,
+ NumberOfFailedExamples: 1,
+ RunTime: 10 * time.Second,
+ })
+ })
+
+ It("should record test as failing", func() {
+ actual := buffer.String()
+ expected :=
+ fmt.Sprintf("##teamcity[testSuiteStarted name='Foo|'s test suite']"+
+ "##teamcity[testStarted name='A B C']"+
+ "##teamcity[testFailed name='A B C' message='%s' details='I failed']"+
+ "##teamcity[testFinished name='A B C' duration='5000']"+
+ "##teamcity[testSuiteFinished name='Foo|'s test suite']", example.Failure.ComponentCodeLocation.String())
+ Ω(actual).Should(Equal(expected))
+ })
+ })
+ }
+
+ for _, exampleStateCase := range []types.ExampleState{types.ExampleStatePending, types.ExampleStateSkipped} {
+ exampleStateCase := exampleStateCase
+ Describe("a skipped test", func() {
+ var example *types.ExampleSummary
+ BeforeEach(func() {
+ example = &types.ExampleSummary{
+ ComponentTexts: []string{"[Top Level]", "A", "B", "C"},
+ State: exampleStateCase,
+ RunTime: 5 * time.Second,
+ }
+ reporter.ExampleWillRun(example)
+ reporter.ExampleDidComplete(example)
+
+ reporter.SpecSuiteDidEnd(&types.SuiteSummary{
+ NumberOfExamplesThatWillBeRun: 1,
+ NumberOfFailedExamples: 0,
+ RunTime: 10 * time.Second,
+ })
+ })
+
+ It("should record test as ignored", func() {
+ actual := buffer.String()
+ expected :=
+ "##teamcity[testSuiteStarted name='Foo|'s test suite']" +
+ "##teamcity[testStarted name='A B C']" +
+ "##teamcity[testIgnored name='A B C']" +
+ "##teamcity[testFinished name='A B C' duration='5000']" +
+ "##teamcity[testSuiteFinished name='Foo|'s test suite']"
+ Ω(actual).Should(Equal(expected))
+ })
+ })
+ }
+})
Please sign in to comment.
Something went wrong with that request. Please try again.