func (o *JUnitReportOptions) Run() error { var builder builder.TestSuitesBuilder switch o.BuilderType { case flatBuilderType: builder = flat.NewTestSuitesBuilder() case nestedBuilderType: builder = nested.NewTestSuitesBuilder(o.RootSuiteNames) } var testParser parser.TestOutputParser switch o.ParserType { case goTestParserType: testParser = gotest.NewParser(builder, o.Stream) case osCmdParserType: testParser = oscmd.NewParser(builder, o.Stream) } testSuites, err := testParser.Parse(bufio.NewScanner(o.Input)) if err != nil { return err } _, err = io.WriteString(o.Output, xml.Header) if err != nil { return fmt.Errorf("error writing XML header to file: %v", err) } encoder := xml.NewEncoder(o.Output) encoder.Indent("", "\t") // no prefix, indent with tabs if err := encoder.Encode(testSuites); err != nil { return fmt.Errorf("error encoding test suites to XML: %v", err) } _, err = io.WriteString(o.Output, "\n") if err != nil { return fmt.Errorf("error writing last newline to file: %v", err) } return nil }
// TestNestedParse tests that parsing the `go test` output in the test directory with a nested builder works as expected func TestNestedParse(t *testing.T) { var testCases = []struct { name string testFile string rootSuiteNames []string expectedSuites *api.TestSuites }{ { name: "basic", testFile: "1.txt", expectedSuites: &api.TestSuites{ Suites: []*api.TestSuite{ { Name: "package", NumTests: 2, Duration: 0.16, Children: []*api.TestSuite{ { Name: "package/name", NumTests: 2, Duration: 0.16, TestCases: []*api.TestCase{ { Name: "TestOne", Duration: 0.06, }, { Name: "TestTwo", Duration: 0.1, }, }, }, }, }, }, }, }, { name: "basic with restricted root", testFile: "1.txt", rootSuiteNames: []string{"package/name"}, expectedSuites: &api.TestSuites{ Suites: []*api.TestSuite{ { Name: "package/name", NumTests: 2, Duration: 0.16, TestCases: []*api.TestCase{ { Name: "TestOne", Duration: 0.06, }, { Name: "TestTwo", Duration: 0.1, }, }, }, }, }, }, { name: "failure", testFile: "2.txt", expectedSuites: &api.TestSuites{ Suites: []*api.TestSuite{ { Name: "package", NumTests: 2, NumFailed: 1, Duration: 0.15, Children: []*api.TestSuite{ { Name: "package/name", NumTests: 2, NumFailed: 1, Duration: 0.15, TestCases: []*api.TestCase{ { Name: "TestOne", Duration: 0.02, FailureOutput: &api.FailureOutput{ Output: `=== RUN TestOne --- FAIL: TestOne (0.02 seconds) file_test.go:11: Error message file_test.go:11: Longer error message.`, }, }, { Name: "TestTwo", Duration: 0.13, }, }, }, }, }, }, }, }, { name: "skip", testFile: "3.txt", expectedSuites: &api.TestSuites{ Suites: []*api.TestSuite{ { Name: "package", NumTests: 2, NumSkipped: 1, Duration: 0.15, Children: []*api.TestSuite{ { Name: "package/name", NumTests: 2, NumSkipped: 1, Duration: 0.15, TestCases: []*api.TestCase{ { Name: "TestOne", Duration: 0.02, SkipMessage: &api.SkipMessage{ Message: `=== RUN TestOne --- SKIP: TestOne (0.02 seconds) file_test.go:11: Skip message`, }, }, { Name: "TestTwo", Duration: 0.13, }, }, }, }, }, }, }, }, { name: "go 1.4", testFile: "4.txt", expectedSuites: &api.TestSuites{ Suites: []*api.TestSuite{ { Name: "package", NumTests: 2, Duration: 0.16, Children: []*api.TestSuite{ { Name: "package/name", NumTests: 2, Duration: 0.16, TestCases: []*api.TestCase{ { Name: "TestOne", Duration: 0.06, }, { Name: "TestTwo", Duration: 0.1, }, }, }, }, }, }, }, }, { name: "multiple suites", testFile: "5.txt", expectedSuites: &api.TestSuites{ Suites: []*api.TestSuite{ { Name: "package", NumTests: 4, NumFailed: 1, Duration: 0.31, Children: []*api.TestSuite{ { Name: "package/name1", NumTests: 2, Duration: 0.16, TestCases: []*api.TestCase{ { Name: "TestOne", Duration: 0.06, }, { Name: "TestTwo", Duration: 0.1, }, }, }, { Name: "package/name2", NumTests: 2, Duration: 0.15, NumFailed: 1, TestCases: []*api.TestCase{ { Name: "TestOne", Duration: 0.02, FailureOutput: &api.FailureOutput{ Output: `=== RUN TestOne --- FAIL: TestOne (0.02 seconds) file_test.go:11: Error message file_test.go:11: Longer error message.`, }, }, { Name: "TestTwo", Duration: 0.13, }, }, }, }, }, }, }, }, { name: "coverage statement", testFile: "6.txt", expectedSuites: &api.TestSuites{ Suites: []*api.TestSuite{ { Name: "package", NumTests: 2, Duration: 0.16, Children: []*api.TestSuite{ { Name: "package/name", NumTests: 2, Duration: 0.16, Properties: []*api.TestSuiteProperty{ { Name: "coverage.statements.pct", Value: "13.37", }, }, TestCases: []*api.TestCase{ { Name: "TestOne", Duration: 0.06, }, { Name: "TestTwo", Duration: 0.1, }, }, }, }, }, }, }, }, { name: "coverage statement in package result", testFile: "7.txt", expectedSuites: &api.TestSuites{ Suites: []*api.TestSuite{ { Name: "package", NumTests: 2, Duration: 0.16, Children: []*api.TestSuite{ { Name: "package/name", NumTests: 2, Duration: 0.16, Properties: []*api.TestSuiteProperty{ { Name: "coverage.statements.pct", Value: "10.0", }, }, TestCases: []*api.TestCase{ { Name: "TestOne", Duration: 0.06, }, { Name: "TestTwo", Duration: 0.1, }, }, }, }, }, }, }, }, { name: "go 1.5", testFile: "8.txt", expectedSuites: &api.TestSuites{ Suites: []*api.TestSuite{ { Name: "package", NumTests: 2, Duration: 0.05, Children: []*api.TestSuite{ { Name: "package/name", NumTests: 2, Duration: 0.05, TestCases: []*api.TestCase{ { Name: "TestOne", Duration: 0.02, }, { Name: "TestTwo", Duration: 0.03, }, }, }, }, }, }, }, }, { name: "nested ", testFile: "9.txt", expectedSuites: &api.TestSuites{ Suites: []*api.TestSuite{ { Name: "package", NumTests: 6, NumFailed: 1, NumSkipped: 1, Duration: 0.4, Children: []*api.TestSuite{ { Name: "package/name", NumTests: 4, NumFailed: 1, NumSkipped: 1, Duration: 0.1, TestCases: []*api.TestCase{ { Name: "TestOne", Duration: 0.02, }, { Name: "TestTwo", Duration: 0.03, }, }, Children: []*api.TestSuite{ { Name: "package/name/nested", NumTests: 2, NumFailed: 1, NumSkipped: 1, Duration: 0.05, TestCases: []*api.TestCase{ { Name: "TestOne", Duration: 0.02, FailureOutput: &api.FailureOutput{ Output: `=== RUN TestOne --- FAIL: TestOne (0.02 seconds) file_test.go:11: Error message file_test.go:11: Longer error message.`, }, }, { Name: "TestTwo", Duration: 0.03, SkipMessage: &api.SkipMessage{ Message: `=== RUN TestTwo --- SKIP: TestTwo (0.03 seconds) file_test.go:11: Skip message PASS`, // we include this line greedily even though it does not belong to the test }, }, }, }, }, }, { Name: "package/other", NumTests: 2, Duration: 0.3, Children: []*api.TestSuite{ { Name: "package/other/nested", NumTests: 2, Duration: 0.3, TestCases: []*api.TestCase{ { Name: "TestOne", Duration: 0.1, }, { Name: "TestTwo", Duration: 0.2, }, }, }, }, }, }, }, }, }, }, { name: "test case timing doesn't add to test suite timing", testFile: "10.txt", expectedSuites: &api.TestSuites{ Suites: []*api.TestSuite{ { Name: "package", NumTests: 2, Duration: 2.16, Children: []*api.TestSuite{ { Name: "package/name", NumTests: 2, Duration: 2.16, TestCases: []*api.TestCase{ { Name: "TestOne", Duration: 0.06, }, { Name: "TestTwo", Duration: 0.1, }, }, }, }, }, }, }, }, { name: "coverage statement in package result and inline", testFile: "11.txt", expectedSuites: &api.TestSuites{ Suites: []*api.TestSuite{ { Name: "package", NumTests: 2, Duration: 0.16, Children: []*api.TestSuite{ { Name: "package/name", NumTests: 2, Duration: 0.16, Properties: []*api.TestSuiteProperty{ { Name: "coverage.statements.pct", Value: "10.0", }, }, TestCases: []*api.TestCase{ { Name: "TestOne", Duration: 0.06, }, { Name: "TestTwo", Duration: 0.1, }, }, }, }, }, }, }, }, } for _, testCase := range testCases { parser := NewParser(nested.NewTestSuitesBuilder(testCase.rootSuiteNames)) testFile := "./../../../test/gotest/testdata/" + testCase.testFile reader, err := os.Open(testFile) if err != nil { t.Errorf("%s: unexpected error opening file %q: %v", testCase.name, testFile, err) continue } testSuites, err := parser.Parse(bufio.NewScanner(reader)) if err != nil { t.Errorf("%s: unexpected error parsing file: %v", testCase.name, err) continue } if !reflect.DeepEqual(testSuites, testCase.expectedSuites) { t.Errorf("%s: did not produce the correct test suites from file:\n\texpected:\n\t%v,\n\tgot\n\t%v", testCase.name, testCase.expectedSuites, testSuites) } } }
// TestNestedParse tests that parsing the `go test` output in the test directory with a nested builder works as expected func TestNestedParse(t *testing.T) { var testCases = []struct { name string testFile string rootSuiteNames []string expectedSuites *api.TestSuites }{ { name: "basic", testFile: "1.txt", expectedSuites: &api.TestSuites{ Suites: []*api.TestSuite{ { Name: "package", NumTests: 2, Duration: 11.245, Children: []*api.TestSuite{ { Name: "package/name", NumTests: 2, Duration: 11.245, TestCases: []*api.TestCase{ { Name: `package/name/file.sh:23: executing 'some command' expecting success`, Duration: 0.123, }, { Name: `package/name/file.sh:24: executing 'some other command' expecting success`, Duration: 11.123, }, }, }, }, }, }, }, }, { name: "basic with restricted root", testFile: "1.txt", rootSuiteNames: []string{"package/name"}, expectedSuites: &api.TestSuites{ Suites: []*api.TestSuite{ { Name: "package/name", NumTests: 2, Duration: 11.245, TestCases: []*api.TestCase{ { Name: `package/name/file.sh:23: executing 'some command' expecting success`, Duration: 0.123, }, { Name: `package/name/file.sh:24: executing 'some other command' expecting success`, Duration: 11.123, }, }, }, }, }, }, { name: "failure", testFile: "2.txt", expectedSuites: &api.TestSuites{ Suites: []*api.TestSuite{ { Name: "package", NumTests: 2, NumFailed: 1, Duration: 11.245, Children: []*api.TestSuite{ { Name: "package/name", NumTests: 2, NumFailed: 1, Duration: 11.245, TestCases: []*api.TestCase{ { Name: `package/name/file.sh:23: executing 'some command' expecting success`, Duration: 0.123, FailureOutput: &api.FailureOutput{ Output: `=== BEGIN TEST CASE === package/name/file.sh:23: executing 'some command' expecting success FAILURE after 0.1234s: package/name/file.sh:23: executing 'some command' expecting success: the command returned the wrong error code There was no output from the command. There was no error output from the command. === END TEST CASE ===`, Message: "the command returned the wrong error code", }, }, { Name: `package/name/file.sh:24: executing 'some other command' expecting success`, Duration: 11.123, }, }, }, }, }, }, }, }, { name: "multiple suites", testFile: "3.txt", expectedSuites: &api.TestSuites{ Suites: []*api.TestSuite{ { Name: "package", NumTests: 4, Duration: 22.49, NumFailed: 1, Children: []*api.TestSuite{ { Name: "package/name", NumTests: 2, Duration: 11.245, TestCases: []*api.TestCase{ { Name: `package/name/file.sh:23: executing 'some command' expecting success`, Duration: 0.123, }, { Name: `package/name/file.sh:24: executing 'some other command' expecting success`, Duration: 11.123, }, }, }, { Name: "package/name2", NumTests: 2, NumFailed: 1, Duration: 11.245, TestCases: []*api.TestCase{ { Name: `package/name/file.sh:23: executing 'some command' expecting success`, Duration: 0.123, FailureOutput: &api.FailureOutput{ Output: `=== BEGIN TEST CASE === package/name/file.sh:23: executing 'some command' expecting success FAILURE after 0.1234s: package/name/file.sh:23: executing 'some command' expecting success: the command returned the wrong error code There was no output from the command. There was no error output from the command. === END TEST CASE ===`, Message: "the command returned the wrong error code", }, }, { Name: `package/name/file.sh:24: executing 'some other command' expecting success`, Duration: 11.123, }, }, }, }, }, }, }, }, { name: "nested", testFile: "4.txt", expectedSuites: &api.TestSuites{ Suites: []*api.TestSuite{ { Name: "package", NumTests: 6, Duration: 33.735, NumFailed: 1, Children: []*api.TestSuite{ { Name: "package/name", NumTests: 4, Duration: 22.49, NumFailed: 1, TestCases: []*api.TestCase{ { Name: `package/name/file.sh:23: executing 'some command' expecting success`, Duration: 0.123, }, { Name: `package/name/file.sh:24: executing 'some other command' expecting success`, Duration: 11.123, }, }, Children: []*api.TestSuite{ { Name: "package/name/nested", NumTests: 2, NumFailed: 1, Duration: 11.245, TestCases: []*api.TestCase{ { Name: `package/name/file.sh:23: executing 'some command' expecting success`, Duration: 0.123, FailureOutput: &api.FailureOutput{ Output: `=== BEGIN TEST CASE === package/name/file.sh:23: executing 'some command' expecting success FAILURE after 0.1234s: package/name/file.sh:23: executing 'some command' expecting success: the command returned the wrong error code There was no output from the command. There was no error output from the command. === END TEST CASE ===`, Message: "the command returned the wrong error code", }, }, { Name: `package/name/file.sh:24: executing 'some other command' expecting success`, Duration: 11.123, }, }, }, }, }, { Name: "package/other", NumTests: 2, Duration: 11.245, Children: []*api.TestSuite{ { Name: "package/other/nested", NumTests: 2, Duration: 11.245, TestCases: []*api.TestCase{ { Name: `package/name/file.sh:23: executing 'some command' expecting success`, Duration: 0.123, }, { Name: `package/name/file.sh:24: executing 'some other command' expecting success`, Duration: 11.123, }, }, }, }, }, }, }, }, }, }, } for _, testCase := range testCases { parser := NewParser(nested.NewTestSuitesBuilder(testCase.rootSuiteNames), false) testFile := "./../../../test/oscmd/testdata/" + testCase.testFile reader, err := os.Open(testFile) if err != nil { t.Errorf("%s: unexpected error opening file %q: %v", testCase.name, testFile, err) continue } testSuites, err := parser.Parse(bufio.NewScanner(reader)) if err != nil { t.Errorf("%s: unexpected error parsing file: %v", testCase.name, err) continue } if !reflect.DeepEqual(testSuites, testCase.expectedSuites) { t.Errorf("%s: did not produce the correct test suites from file:\n\texpected:\n\t%v,\n\tgot\n\t%v", testCase.name, testCase.expectedSuites, testSuites) } } }