From eab174d6f5ba6d02fb5ccdbf12a393f58a78d81c Mon Sep 17 00:00:00 2001 From: mtojek Date: Wed, 21 Jul 2021 10:46:17 +0200 Subject: [PATCH 01/16] WIP --- cmd/testrunner.go | 17 +++++++++++++++++ internal/cobraext/const.go | 3 +++ internal/testrunner/coverage_output.go | 10 ++++++++++ 3 files changed, 30 insertions(+) create mode 100644 internal/testrunner/coverage_output.go diff --git a/cmd/testrunner.go b/cmd/testrunner.go index f1d04c9491..b0fbca5837 100644 --- a/cmd/testrunner.go +++ b/cmd/testrunner.go @@ -67,6 +67,7 @@ func setupTestCommand() *cobraext.Command { cmd.PersistentFlags().BoolP(cobraext.GenerateTestResultFlagName, "g", false, cobraext.GenerateTestResultFlagDescription) cmd.PersistentFlags().StringP(cobraext.ReportFormatFlagName, "", string(formats.ReportFormatHuman), cobraext.ReportFormatFlagDescription) cmd.PersistentFlags().StringP(cobraext.ReportOutputFlagName, "", string(outputs.ReportOutputSTDOUT), cobraext.ReportOutputFlagDescription) + cmd.PersistentFlags().BoolP(cobraext.TestCoverageFlagName, "", false, cobraext.TestCoverageFlagDescription) cmd.PersistentFlags().DurationP(cobraext.DeferCleanupFlagName, "", 0, cobraext.DeferCleanupFlagDescription) cmd.PersistentFlags().String(cobraext.VariantFlagName, "", cobraext.VariantFlagDescription) @@ -116,6 +117,11 @@ func testTypeCommandActionFactory(runner testrunner.TestRunner) cobraext.Command return cobraext.FlagParsingError(err, cobraext.ReportOutputFlagName) } + testCoverage, err := cmd.Flags().GetBool(cobraext.TestCoverageFlagName) + if err != nil { + return cobraext.FlagParsingError(err, cobraext.TestCoverageFlagName) + } + packageRootPath, found, err := packages.FindPackageRoot() if !found { return errors.New("package root not found") @@ -143,6 +149,10 @@ func testTypeCommandActionFactory(runner testrunner.TestRunner) cobraext.Command if err != nil { return cobraext.FlagParsingError(err, cobraext.DataStreamsFlagName) } + + if testCoverage { + return cobraext.FlagParsingError(errors.New("test coverage can be calculated only if all data streams are selected"), cobraext.DataStreamsFlagName) + } } if runner.TestFolderRequired() { @@ -217,6 +227,13 @@ func testTypeCommandActionFactory(runner testrunner.TestRunner) cobraext.Command return errors.Wrap(err, "error writing test report") } + if testCoverage { + err := testrunner.WriteCoverage(m.Name, results) + if err != nil { + return errors.Wrap(err, "error writing test coverage") + } + } + // Check if there is any error or failure reported for _, r := range results { if r.ErrorMsg != "" || r.FailureMsg != "" { diff --git a/internal/cobraext/const.go b/internal/cobraext/const.go index c016fd1fab..ff8be88443 100644 --- a/internal/cobraext/const.go +++ b/internal/cobraext/const.go @@ -72,6 +72,9 @@ const ( StackDumpOutputFlagName = "output" StackDumpOutputFlagDescription = "output location for the stack dump" + TestCoverageFlagName = "test-coverage" + TestCoverageFlagDescription = "generate Cobertura test coverage reports" + VariantFlagName = "variant" VariantFlagDescription = "service variant" diff --git a/internal/testrunner/coverage_output.go b/internal/testrunner/coverage_output.go new file mode 100644 index 0000000000..0496ff8f81 --- /dev/null +++ b/internal/testrunner/coverage_output.go @@ -0,0 +1,10 @@ +// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +// or more contributor license agreements. Licensed under the Elastic License; +// you may not use this file except in compliance with the Elastic License. + +package testrunner + +// WriteCoverage function calculate test coverage for the given package. +func WriteCoverage(pkg string, results []TestResult) error { + return nil +} From 6e4ddb379d5e54bdeaa2dacb12204996612f77fa Mon Sep 17 00:00:00 2001 From: mtojek Date: Wed, 21 Jul 2021 11:13:20 +0200 Subject: [PATCH 02/16] WIP --- cmd/testrunner.go | 2 +- internal/testrunner/coverage_output.go | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/cmd/testrunner.go b/cmd/testrunner.go index b0fbca5837..cd7773f06b 100644 --- a/cmd/testrunner.go +++ b/cmd/testrunner.go @@ -228,7 +228,7 @@ func testTypeCommandActionFactory(runner testrunner.TestRunner) cobraext.Command } if testCoverage { - err := testrunner.WriteCoverage(m.Name, results) + err := testrunner.WriteCoverage(m.Name, runner.Type(), results) if err != nil { return errors.Wrap(err, "error writing test coverage") } diff --git a/internal/testrunner/coverage_output.go b/internal/testrunner/coverage_output.go index 0496ff8f81..d6940ba38d 100644 --- a/internal/testrunner/coverage_output.go +++ b/internal/testrunner/coverage_output.go @@ -4,7 +4,10 @@ package testrunner -// WriteCoverage function calculate test coverage for the given package. -func WriteCoverage(pkg string, results []TestResult) error { +// WriteCoverage function calculates test coverage for the given package. +// It requires to execute tests for all data streams (same test type), so the coverage can be calculated properly. +// The function includes following test types in the coverage report - pipeline and system. +func WriteCoverage(pkg string, testType TestType, results []TestResult) error { + return nil } From 44722e99713278fd7ee592ec822b269293601dba Mon Sep 17 00:00:00 2001 From: mtojek Date: Wed, 21 Jul 2021 13:20:28 +0200 Subject: [PATCH 03/16] No transformation yet --- cmd/testrunner.go | 2 +- internal/testrunner/coverage_output.go | 146 ++++++++++++++++++++++++- 2 files changed, 146 insertions(+), 2 deletions(-) diff --git a/cmd/testrunner.go b/cmd/testrunner.go index cd7773f06b..06a3f7cf8c 100644 --- a/cmd/testrunner.go +++ b/cmd/testrunner.go @@ -228,7 +228,7 @@ func testTypeCommandActionFactory(runner testrunner.TestRunner) cobraext.Command } if testCoverage { - err := testrunner.WriteCoverage(m.Name, runner.Type(), results) + err := testrunner.WriteCoverage(packageRootPath, m.Name, runner.Type(), results) if err != nil { return errors.Wrap(err, "error writing test coverage") } diff --git a/internal/testrunner/coverage_output.go b/internal/testrunner/coverage_output.go index d6940ba38d..927396111e 100644 --- a/internal/testrunner/coverage_output.go +++ b/internal/testrunner/coverage_output.go @@ -4,10 +4,154 @@ package testrunner +import ( + "bytes" + "encoding/xml" + "fmt" + "io/ioutil" + "os" + "path/filepath" + "time" + + "github.com/pkg/errors" + + "github.com/elastic/elastic-package/internal/builder" +) + +type testCoverageDetails struct { + packageName string + dataStreams map[string][]string // : +} + +func newTestCoverageDetails(packageName string) *testCoverageDetails { + return &testCoverageDetails{packageName: packageName, dataStreams: map[string][]string{}} +} + +func (tcd *testCoverageDetails) withUncoveredDataStreams(dataStreams []string) *testCoverageDetails { + for _, wt := range dataStreams { + tcd.dataStreams[wt] = []string{} + } + return tcd +} + +func (tcd *testCoverageDetails) withTestResults(results []TestResult) *testCoverageDetails { + for _, result := range results { + if _, ok := tcd.dataStreams[result.DataStream]; !ok { + tcd.dataStreams[result.DataStream] = []string{} + } + tcd.dataStreams[result.DataStream] = append(tcd.dataStreams[result.DataStream], result.DataStream) + } + return tcd +} + +type coberturaReport struct { +} + +func (r *coberturaReport) bytes() ([]byte, error) { + out, err := xml.MarshalIndent(&r, "", " ") + if err != nil { + return nil, errors.Wrap(err, "unable to format test results as xUnit") + } + + var buffer bytes.Buffer + buffer.WriteString(xml.Header) + buffer.WriteString("\n") + buffer.Write(out) + return buffer.Bytes(), nil +} + // WriteCoverage function calculates test coverage for the given package. // It requires to execute tests for all data streams (same test type), so the coverage can be calculated properly. // The function includes following test types in the coverage report - pipeline and system. -func WriteCoverage(pkg string, testType TestType, results []TestResult) error { +func WriteCoverage(packageRootPath, packageName string, testType TestType, results []TestResult) error { + details, err := collectTestCoverageDetails(packageRootPath, packageName, testType, results) + if err != nil { + return errors.Wrap(err, "can't collect test coverage details") + } + + report := transformToCoberturaReport(details) + + err = writeCoverageReportFile(report, packageRootPath) + if err != nil { + return errors.Wrap(err, "can't write test coverage report file") + } return nil } + +func collectTestCoverageDetails(packageRootPath, packageName string, testType TestType, results []TestResult) (*testCoverageDetails, error) { + withoutTests, err := findDataStreamsWithoutTests(packageRootPath, testType) + if err != nil { + return nil, errors.Wrap(err, "can't find data streams without tests") + } + + details := newTestCoverageDetails(packageName). + withUncoveredDataStreams(withoutTests). + withTestResults(results) + return details, nil +} + +func findDataStreamsWithoutTests(packageRootPath string, testType TestType) ([]string, error) { + dataStreamDir := filepath.Join(packageRootPath, "data_stream") + dataStreams, err := ioutil.ReadDir(dataStreamDir) + if err != nil { + return nil, errors.Wrap(err, "can't list data streams directory") + } + + var noTests []string + for _, dataStream := range dataStreams { + if !dataStream.IsDir() { + continue + } + + dataStreamTestPath := filepath.Join(packageRootPath, "data_stream", dataStream.Name(), "_dev", "test", string(testType)) + _, err := os.Stat(dataStreamTestPath) + if errors.Is(err, os.ErrNotExist) { + noTests = append(noTests, dataStream.Name()) + } + if err != nil { + return nil, errors.Wrapf(err, "can't stat path: %s", dataStreamTestPath) + } + } + return noTests, nil +} + +func transformToCoberturaReport(details *testCoverageDetails) *coberturaReport { + panic("TODO") +} + +func writeCoverageReportFile(report *coberturaReport, packageName string) error { + dest, err := testCoverageReportsDir() + if err != nil { + return errors.Wrap(err, "could not determine test coverage reports folder") + } + + // Create test coverage reports folder if it doesn't exist + _, err = os.Stat(dest) + if err != nil && os.IsNotExist(err) { + if err := os.MkdirAll(dest, 0755); err != nil { + return errors.Wrap(err, "could not create test coverage reports folder") + } + } + + fileName := fmt.Sprintf("coverage-%s-%d-report.xml", packageName, time.Now().UnixNano()) + filePath := filepath.Join(dest, fileName) + + b, err := report.bytes() + if err != nil { + return errors.Wrap(err, "can't marshal test coverage report") + } + + if err := ioutil.WriteFile(filePath, b, 0644); err != nil { + return errors.Wrap(err, "could not write test coverage report file") + } + return nil +} + +func testCoverageReportsDir() (string, error) { + buildDir, _, err := builder.FindBuildDirectory() + if err != nil { + return "", errors.Wrap(err, "locating build directory failed") + } + return filepath.Join(buildDir, "test-coverage"), nil +} From 72b8382eba52eb094860d596b136597b8bb68d8c Mon Sep 17 00:00:00 2001 From: mtojek Date: Wed, 21 Jul 2021 13:23:00 +0200 Subject: [PATCH 04/16] Update Jenkinsfile --- .ci/Jenkinsfile | 1 + 1 file changed, 1 insertion(+) diff --git a/.ci/Jenkinsfile b/.ci/Jenkinsfile index f856e4d0ce..c5bf92df46 100644 --- a/.ci/Jenkinsfile +++ b/.ci/Jenkinsfile @@ -64,6 +64,7 @@ pipeline { junit(allowEmptyResults: false, keepLongStdio: true, testResults: "build/test-results/*.xml") + coverageReport('build/test-coverage') } } } From a5f04611751dfaad8cdfd7970fba6d5d00d15c35 Mon Sep 17 00:00:00 2001 From: mtojek Date: Wed, 21 Jul 2021 13:27:42 +0200 Subject: [PATCH 05/16] Coverage DTD --- internal/testrunner/coverage_output.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/internal/testrunner/coverage_output.go b/internal/testrunner/coverage_output.go index 927396111e..8305b6e4e9 100644 --- a/internal/testrunner/coverage_output.go +++ b/internal/testrunner/coverage_output.go @@ -18,6 +18,8 @@ import ( "github.com/elastic/elastic-package/internal/builder" ) +const coverageDtd = `` + type testCoverageDetails struct { packageName string dataStreams map[string][]string // : @@ -56,6 +58,8 @@ func (r *coberturaReport) bytes() ([]byte, error) { var buffer bytes.Buffer buffer.WriteString(xml.Header) buffer.WriteString("\n") + buffer.WriteString(coverageDtd) + buffer.WriteString("\n") buffer.Write(out) return buffer.Bytes(), nil } From 8b9a62442ea033adb8231a73f8323b60c9c4e26b Mon Sep 17 00:00:00 2001 From: mtojek Date: Wed, 21 Jul 2021 13:28:09 +0200 Subject: [PATCH 06/16] Typo --- internal/testrunner/coverage_output.go | 1 - 1 file changed, 1 deletion(-) diff --git a/internal/testrunner/coverage_output.go b/internal/testrunner/coverage_output.go index 8305b6e4e9..f1e3ec2bb3 100644 --- a/internal/testrunner/coverage_output.go +++ b/internal/testrunner/coverage_output.go @@ -79,7 +79,6 @@ func WriteCoverage(packageRootPath, packageName string, testType TestType, resul if err != nil { return errors.Wrap(err, "can't write test coverage report file") } - return nil } From 62094bd76a1f6e1c84c3ca8ec138d1a12dde7f8c Mon Sep 17 00:00:00 2001 From: mtojek Date: Wed, 21 Jul 2021 13:52:31 +0200 Subject: [PATCH 07/16] Write attrs for Cobertura report --- internal/testrunner/coverage_output.go | 53 +++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 2 deletions(-) diff --git a/internal/testrunner/coverage_output.go b/internal/testrunner/coverage_output.go index f1e3ec2bb3..9c1166d120 100644 --- a/internal/testrunner/coverage_output.go +++ b/internal/testrunner/coverage_output.go @@ -11,7 +11,6 @@ import ( "io/ioutil" "os" "path/filepath" - "time" "github.com/pkg/errors" @@ -47,8 +46,58 @@ func (tcd *testCoverageDetails) withTestResults(results []TestResult) *testCover } type coberturaReport struct { + XMLName xml.Name `xml:"coverage"` + LineRate float32 `xml:"line-rate,attr"` + BranchRate float32 `xml:"branch-rate,attr"` + Version string `xml:"version,attr"` + Timestamp int64 `xml:"timestamp,attr"` + LinesCovered int64 `xml:"lines-covered,attr"` + LinesValid int64 `xml:"lines-valid,attr"` + BranchesCovered int64 `xml:"branches-covered,attr"` + BranchesValid int64 `xml:"branches-valid,attr"` + Complexity float32 `xml:"complexity,attr"` + Sources []*source `xml:"sources>source"` + Packages []*aPackage `xml:"packages>package"` } +type source struct { + Path string `xml:",chardata"` +} + +type aPackage struct { + Name string `xml:"name,attr"` + LineRate float32 `xml:"line-rate,attr"` + BranchRate float32 `xml:"branch-rate,attr"` + Complexity float32 `xml:"complexity,attr"` + Classes []*class `xml:"classes>class"` +} + +type class struct { + Name string `xml:"name,attr"` + Filename string `xml:"filename,attr"` + LineRate float32 `xml:"line-rate,attr"` + BranchRate float32 `xml:"branch-rate,attr"` + Complexity float32 `xml:"complexity,attr"` + Methods []*method `xml:"methods>method"` + Lines lines `xml:"lines>line"` +} + +type method struct { + Name string `xml:"name,attr"` + Signature string `xml:"signature,attr"` + LineRate float32 `xml:"line-rate,attr"` + BranchRate float32 `xml:"branch-rate,attr"` + Complexity float32 `xml:"complexity,attr"` + Lines lines `xml:"lines>line"` +} + +type line struct { + Number int `xml:"number,attr"` + Hits int64 `xml:"hits,attr"` +} + +type lines []*line + func (r *coberturaReport) bytes() ([]byte, error) { out, err := xml.MarshalIndent(&r, "", " ") if err != nil { @@ -137,7 +186,7 @@ func writeCoverageReportFile(report *coberturaReport, packageName string) error } } - fileName := fmt.Sprintf("coverage-%s-%d-report.xml", packageName, time.Now().UnixNano()) + fileName := fmt.Sprintf("coverage-%s-%d-report.xml", packageName, report.Timestamp) filePath := filepath.Join(dest, fileName) b, err := report.bytes() From dae9c20599cf20fc7e82b76eb5d77a015bfe24f1 Mon Sep 17 00:00:00 2001 From: mtojek Date: Wed, 21 Jul 2021 13:54:05 +0200 Subject: [PATCH 08/16] Enable coverage for CI scripts --- scripts/test-check-packages.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/test-check-packages.sh b/scripts/test-check-packages.sh index 8146622c71..7c86ed8e2d 100755 --- a/scripts/test-check-packages.sh +++ b/scripts/test-check-packages.sh @@ -59,7 +59,7 @@ for d in test/packages/*/; do elastic-package install -v # defer-cleanup is set to a short period to verify that the option is available - elastic-package test -v --report-format xUnit --report-output file --defer-cleanup 1s + elastic-package test -v --report-format xUnit --report-output file --defer-cleanup 1s --test-coverage ) cd - done From dba0aeec35e250d66a5308eeecfe8790db52f51a Mon Sep 17 00:00:00 2001 From: mtojek Date: Wed, 21 Jul 2021 14:19:00 +0200 Subject: [PATCH 09/16] Implementation full --- internal/testrunner/coverage_output.go | 126 ++++++++++++++++--------- 1 file changed, 81 insertions(+), 45 deletions(-) diff --git a/internal/testrunner/coverage_output.go b/internal/testrunner/coverage_output.go index 9c1166d120..b0f7649146 100644 --- a/internal/testrunner/coverage_output.go +++ b/internal/testrunner/coverage_output.go @@ -11,6 +11,7 @@ import ( "io/ioutil" "os" "path/filepath" + "time" "github.com/pkg/errors" @@ -21,11 +22,12 @@ const coverageDtd = ` : } -func newTestCoverageDetails(packageName string) *testCoverageDetails { - return &testCoverageDetails{packageName: packageName, dataStreams: map[string][]string{}} +func newTestCoverageDetails(packageName string, testType TestType) *testCoverageDetails { + return &testCoverageDetails{packageName: packageName, testType: testType, dataStreams: map[string][]string{}} } func (tcd *testCoverageDetails) withUncoveredDataStreams(dataStreams []string) *testCoverageDetails { @@ -45,61 +47,61 @@ func (tcd *testCoverageDetails) withTestResults(results []TestResult) *testCover return tcd } -type coberturaReport struct { - XMLName xml.Name `xml:"coverage"` - LineRate float32 `xml:"line-rate,attr"` - BranchRate float32 `xml:"branch-rate,attr"` - Version string `xml:"version,attr"` - Timestamp int64 `xml:"timestamp,attr"` - LinesCovered int64 `xml:"lines-covered,attr"` - LinesValid int64 `xml:"lines-valid,attr"` - BranchesCovered int64 `xml:"branches-covered,attr"` - BranchesValid int64 `xml:"branches-valid,attr"` - Complexity float32 `xml:"complexity,attr"` - Sources []*source `xml:"sources>source"` - Packages []*aPackage `xml:"packages>package"` +type coberturaCoverage struct { + XMLName xml.Name `xml:"coverage"` + LineRate float32 `xml:"line-rate,attr"` + BranchRate float32 `xml:"branch-rate,attr"` + Version string `xml:"version,attr"` + Timestamp int64 `xml:"timestamp,attr"` + LinesCovered int64 `xml:"lines-covered,attr"` + LinesValid int64 `xml:"lines-valid,attr"` + BranchesCovered int64 `xml:"branches-covered,attr"` + BranchesValid int64 `xml:"branches-valid,attr"` + Complexity float32 `xml:"complexity,attr"` + Sources []*coberturaSource `xml:"sources>source"` + Packages []*coberturaPackage `xml:"packages>package"` } -type source struct { +type coberturaSource struct { Path string `xml:",chardata"` } -type aPackage struct { - Name string `xml:"name,attr"` - LineRate float32 `xml:"line-rate,attr"` - BranchRate float32 `xml:"branch-rate,attr"` - Complexity float32 `xml:"complexity,attr"` - Classes []*class `xml:"classes>class"` +type coberturaPackage struct { + Name string `xml:"name,attr"` + LineRate float32 `xml:"line-rate,attr"` + BranchRate float32 `xml:"branch-rate,attr"` + Complexity float32 `xml:"complexity,attr"` + Classes []*coberturaClass `xml:"classes>class"` } -type class struct { - Name string `xml:"name,attr"` - Filename string `xml:"filename,attr"` - LineRate float32 `xml:"line-rate,attr"` - BranchRate float32 `xml:"branch-rate,attr"` - Complexity float32 `xml:"complexity,attr"` - Methods []*method `xml:"methods>method"` - Lines lines `xml:"lines>line"` +type coberturaClass struct { + Name string `xml:"name,attr"` + Filename string `xml:"filename,attr"` + LineRate float32 `xml:"line-rate,attr"` + BranchRate float32 `xml:"branch-rate,attr"` + Complexity float32 `xml:"complexity,attr"` + Methods []*coberturaMethod `xml:"methods>method"` + Lines coberturaLines `xml:"lines>line"` } -type method struct { - Name string `xml:"name,attr"` - Signature string `xml:"signature,attr"` - LineRate float32 `xml:"line-rate,attr"` - BranchRate float32 `xml:"branch-rate,attr"` - Complexity float32 `xml:"complexity,attr"` - Lines lines `xml:"lines>line"` +type coberturaMethod struct { + Name string `xml:"name,attr"` + Signature string `xml:"signature,attr"` + LineRate float32 `xml:"line-rate,attr"` + BranchRate float32 `xml:"branch-rate,attr"` + Complexity float32 `xml:"complexity,attr"` + Lines coberturaLines `xml:"lines>line"` } -type line struct { +type coberturaLine struct { Number int `xml:"number,attr"` Hits int64 `xml:"hits,attr"` } -type lines []*line +type coberturaLines []*coberturaLine -func (r *coberturaReport) bytes() ([]byte, error) { - out, err := xml.MarshalIndent(&r, "", " ") +func (c *coberturaCoverage) bytes() ([]byte, error) { + out, err := xml.MarshalIndent(&c, "", " ") if err != nil { return nil, errors.Wrap(err, "unable to format test results as xUnit") } @@ -137,7 +139,7 @@ func collectTestCoverageDetails(packageRootPath, packageName string, testType Te return nil, errors.Wrap(err, "can't find data streams without tests") } - details := newTestCoverageDetails(packageName). + details := newTestCoverageDetails(packageName, testType). withUncoveredDataStreams(withoutTests). withTestResults(results) return details, nil @@ -168,11 +170,45 @@ func findDataStreamsWithoutTests(packageRootPath string, testType TestType) ([]s return noTests, nil } -func transformToCoberturaReport(details *testCoverageDetails) *coberturaReport { - panic("TODO") +func transformToCoberturaReport(details *testCoverageDetails) *coberturaCoverage { + var classes []*coberturaClass + for dataStream, testCases := range details.dataStreams { + var methods []*coberturaMethod + + if len(testCases) == 0 { + methods = append(methods, &coberturaMethod{ + Name: "no-test", + Lines: []*coberturaLine{{Number: 1, Hits: 0}}, + }) + } else { + for i, tc := range testCases { + methods = append(methods, &coberturaMethod{ + Name: tc, + Lines: []*coberturaLine{{Number: i + 1, Hits: 1}}, + }) + } + } + + aClass := &coberturaClass{ + Name: string(details.testType), + Filename: details.packageName + "/" + dataStream, + Methods: methods, + } + classes = append(classes, aClass) + } + + return &coberturaCoverage{ + Timestamp: time.Now().UnixNano(), + Packages: []*coberturaPackage{ + { + Name: details.packageName, + Classes: classes, + }, + }, + } } -func writeCoverageReportFile(report *coberturaReport, packageName string) error { +func writeCoverageReportFile(report *coberturaCoverage, packageName string) error { dest, err := testCoverageReportsDir() if err != nil { return errors.Wrap(err, "could not determine test coverage reports folder") From 73c30e9d176a27a2cdf73a9055b603ce20e1dd60 Mon Sep 17 00:00:00 2001 From: mtojek Date: Wed, 21 Jul 2021 14:39:27 +0200 Subject: [PATCH 10/16] Few bugfixes --- cmd/testrunner.go | 2 +- internal/testrunner/coverage_output.go | 9 ++++++--- internal/testrunner/reporters/outputs/file.go | 5 ++++- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/cmd/testrunner.go b/cmd/testrunner.go index 06a3f7cf8c..8dc13e5232 100644 --- a/cmd/testrunner.go +++ b/cmd/testrunner.go @@ -150,7 +150,7 @@ func testTypeCommandActionFactory(runner testrunner.TestRunner) cobraext.Command return cobraext.FlagParsingError(err, cobraext.DataStreamsFlagName) } - if testCoverage { + if len(dataStreams) > 0 { return cobraext.FlagParsingError(errors.New("test coverage can be calculated only if all data streams are selected"), cobraext.DataStreamsFlagName) } } diff --git a/internal/testrunner/coverage_output.go b/internal/testrunner/coverage_output.go index b0f7649146..e18830c97f 100644 --- a/internal/testrunner/coverage_output.go +++ b/internal/testrunner/coverage_output.go @@ -81,7 +81,6 @@ type coberturaClass struct { BranchRate float32 `xml:"branch-rate,attr"` Complexity float32 `xml:"complexity,attr"` Methods []*coberturaMethod `xml:"methods>method"` - Lines coberturaLines `xml:"lines>line"` } type coberturaMethod struct { @@ -126,7 +125,7 @@ func WriteCoverage(packageRootPath, packageName string, testType TestType, resul report := transformToCoberturaReport(details) - err = writeCoverageReportFile(report, packageRootPath) + err = writeCoverageReportFile(report, packageName) if err != nil { return errors.Wrap(err, "can't write test coverage report file") } @@ -162,6 +161,7 @@ func findDataStreamsWithoutTests(packageRootPath string, testType TestType) ([]s _, err := os.Stat(dataStreamTestPath) if errors.Is(err, os.ErrNotExist) { noTests = append(noTests, dataStream.Name()) + continue } if err != nil { return nil, errors.Wrapf(err, "can't stat path: %s", dataStreamTestPath) @@ -237,7 +237,10 @@ func writeCoverageReportFile(report *coberturaCoverage, packageName string) erro } func testCoverageReportsDir() (string, error) { - buildDir, _, err := builder.FindBuildDirectory() + buildDir, found, err := builder.FindBuildDirectory() + if !found { + return "", errors.New("package must be built first") + } if err != nil { return "", errors.Wrap(err, "locating build directory failed") } diff --git a/internal/testrunner/reporters/outputs/file.go b/internal/testrunner/reporters/outputs/file.go index cef2b469f8..4f138e0282 100644 --- a/internal/testrunner/reporters/outputs/file.go +++ b/internal/testrunner/reporters/outputs/file.go @@ -58,7 +58,10 @@ func reportToFile(pkg, report string, format testrunner.TestReportFormat) error // testReportsDir returns the location of the directory to store test reports. func testReportsDir() (string, error) { - buildDir, _, err := builder.FindBuildDirectory() + buildDir, found, err := builder.FindBuildDirectory() + if !found { + return "", errors.New("package must be built first") + } if err != nil { return "", errors.Wrap(err, "locating build directory failed") } From d3ddcd470add00f1f5b5b0515cd7fbce0dd82c6f Mon Sep 17 00:00:00 2001 From: mtojek Date: Wed, 21 Jul 2021 16:16:49 +0200 Subject: [PATCH 11/16] Fix: hell in builder --- internal/builder/packages.go | 30 +++++++++++++++---- internal/testrunner/coverage_output.go | 5 +--- internal/testrunner/reporters/outputs/file.go | 5 +--- 3 files changed, 26 insertions(+), 14 deletions(-) diff --git a/internal/builder/packages.go b/internal/builder/packages.go index e0990f6a0f..28c7461b8c 100644 --- a/internal/builder/packages.go +++ b/internal/builder/packages.go @@ -15,8 +15,22 @@ import ( "github.com/pkg/errors" ) -// FindBuildDirectory locates the target build directory. -func FindBuildDirectory() (string, bool, error) { +// MustFindBuildDirectory function locates the target build directory. If the directory doesn't exist, it will create it. +func MustFindBuildDirectory() (string, error) { + buildDir, found, err := findBuildDirectory() + if err != nil { + return "", errors.Wrap(err, "locating build directory failed") + } + if !found { + buildDir, err = createBuildDirectory() + if err != nil { + return "", errors.Wrap(err, "creating new build directory failed") + } + } + return buildDir, nil +} + +func findBuildDirectory() (string, bool, error) { workDir, err := os.Getwd() if err != nil { return "", false, errors.Wrap(err, "locating working directory failed") @@ -46,7 +60,7 @@ func MustFindBuildPackagesDirectory(packageRoot string) (string, error) { return "", errors.Wrap(err, "locating build directory failed") } if !found { - buildDir, err = createBuildPackagesDirectory() + buildDir, err = createBuildDirectory("integrations") // TODO add support for other package types if err != nil { return "", errors.Wrap(err, "creating new build directory failed") } @@ -61,7 +75,7 @@ func MustFindBuildPackagesDirectory(packageRoot string) (string, error) { // FindBuildPackagesDirectory function locates the target build directory for packages. func FindBuildPackagesDirectory() (string, bool, error) { - buildDir, found, err := FindBuildDirectory() + buildDir, found, err := findBuildDirectory() if err != nil { return "", false, err } @@ -118,7 +132,7 @@ func BuildPackage(packageRoot string) (string, error) { return destinationDir, nil } -func createBuildPackagesDirectory() (string, error) { +func createBuildDirectory(dirs ...string) (string, error) { workDir, err := os.Getwd() if err != nil { return "", errors.Wrap(err, "locating working directory failed") @@ -129,7 +143,11 @@ func createBuildPackagesDirectory() (string, error) { path := filepath.Join(dir, ".git") fileInfo, err := os.Stat(path) if err == nil && fileInfo.IsDir() { - buildDir := filepath.Join(dir, "build", "integrations") // TODO add support for other package types + p := []string{dir, "build"} + if len(dirs) > 0 { + p = append(p, dirs...) + } + buildDir := filepath.Join(p...) err = os.MkdirAll(buildDir, 0755) if err != nil { return "", errors.Wrapf(err, "mkdir failed (path: %s)", buildDir) diff --git a/internal/testrunner/coverage_output.go b/internal/testrunner/coverage_output.go index e18830c97f..10272f7c71 100644 --- a/internal/testrunner/coverage_output.go +++ b/internal/testrunner/coverage_output.go @@ -237,10 +237,7 @@ func writeCoverageReportFile(report *coberturaCoverage, packageName string) erro } func testCoverageReportsDir() (string, error) { - buildDir, found, err := builder.FindBuildDirectory() - if !found { - return "", errors.New("package must be built first") - } + buildDir, err := builder.MustFindBuildDirectory() if err != nil { return "", errors.Wrap(err, "locating build directory failed") } diff --git a/internal/testrunner/reporters/outputs/file.go b/internal/testrunner/reporters/outputs/file.go index 4f138e0282..f5afd1adec 100644 --- a/internal/testrunner/reporters/outputs/file.go +++ b/internal/testrunner/reporters/outputs/file.go @@ -58,10 +58,7 @@ func reportToFile(pkg, report string, format testrunner.TestReportFormat) error // testReportsDir returns the location of the directory to store test reports. func testReportsDir() (string, error) { - buildDir, found, err := builder.FindBuildDirectory() - if !found { - return "", errors.New("package must be built first") - } + buildDir, err := builder.MustFindBuildDirectory() if err != nil { return "", errors.Wrap(err, "locating build directory failed") } From 7a1c04ce92ad44f1d838d26f786b04e1d135600e Mon Sep 17 00:00:00 2001 From: mtojek Date: Wed, 21 Jul 2021 16:26:56 +0200 Subject: [PATCH 12/16] Fix: wrong method name --- internal/testrunner/coverage_output.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/testrunner/coverage_output.go b/internal/testrunner/coverage_output.go index 10272f7c71..c318ad3000 100644 --- a/internal/testrunner/coverage_output.go +++ b/internal/testrunner/coverage_output.go @@ -42,7 +42,7 @@ func (tcd *testCoverageDetails) withTestResults(results []TestResult) *testCover if _, ok := tcd.dataStreams[result.DataStream]; !ok { tcd.dataStreams[result.DataStream] = []string{} } - tcd.dataStreams[result.DataStream] = append(tcd.dataStreams[result.DataStream], result.DataStream) + tcd.dataStreams[result.DataStream] = append(tcd.dataStreams[result.DataStream], result.Name) } return tcd } From 82b3762c200bcb0a2eee1db1e82f103cf70f1fff Mon Sep 17 00:00:00 2001 From: mtojek Date: Wed, 21 Jul 2021 16:57:54 +0200 Subject: [PATCH 13/16] Supports all test types --- internal/testrunner/coverage_output.go | 1 - 1 file changed, 1 deletion(-) diff --git a/internal/testrunner/coverage_output.go b/internal/testrunner/coverage_output.go index c318ad3000..9bf61dbb5a 100644 --- a/internal/testrunner/coverage_output.go +++ b/internal/testrunner/coverage_output.go @@ -116,7 +116,6 @@ func (c *coberturaCoverage) bytes() ([]byte, error) { // WriteCoverage function calculates test coverage for the given package. // It requires to execute tests for all data streams (same test type), so the coverage can be calculated properly. -// The function includes following test types in the coverage report - pipeline and system. func WriteCoverage(packageRootPath, packageName string, testType TestType, results []TestResult) error { details, err := collectTestCoverageDetails(packageRootPath, packageName, testType, results) if err != nil { From 4a35447f68e5c6406b4723a8b144227480dc5b98 Mon Sep 17 00:00:00 2001 From: mtojek Date: Wed, 21 Jul 2021 17:10:29 +0200 Subject: [PATCH 14/16] Fix: package context --- internal/testrunner/coverage_output.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/internal/testrunner/coverage_output.go b/internal/testrunner/coverage_output.go index 9bf61dbb5a..1bc093de46 100644 --- a/internal/testrunner/coverage_output.go +++ b/internal/testrunner/coverage_output.go @@ -188,6 +188,10 @@ func transformToCoberturaReport(details *testCoverageDetails) *coberturaCoverage } } + if dataStream == "" { + dataStream = "-" // workaround for Cobertura to properly analyze tests running in the package context (not data stream) + } + aClass := &coberturaClass{ Name: string(details.testType), Filename: details.packageName + "/" + dataStream, From a0000599734c8475152f269f6554452b39e34e5a Mon Sep 17 00:00:00 2001 From: mtojek Date: Thu, 22 Jul 2021 13:43:33 +0200 Subject: [PATCH 15/16] Rename Must --- internal/builder/packages.go | 10 +++++----- internal/docs/readme.go | 2 +- internal/testrunner/coverage_output.go | 2 +- internal/testrunner/reporters/outputs/file.go | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/internal/builder/packages.go b/internal/builder/packages.go index 28c7461b8c..fdc6ac919a 100644 --- a/internal/builder/packages.go +++ b/internal/builder/packages.go @@ -15,8 +15,8 @@ import ( "github.com/pkg/errors" ) -// MustFindBuildDirectory function locates the target build directory. If the directory doesn't exist, it will create it. -func MustFindBuildDirectory() (string, error) { +// BuildDirectory function locates the target build directory. If the directory doesn't exist, it will create it. +func BuildDirectory() (string, error) { buildDir, found, err := findBuildDirectory() if err != nil { return "", errors.Wrap(err, "locating build directory failed") @@ -52,9 +52,9 @@ func findBuildDirectory() (string, bool, error) { return "", false, nil } -// MustFindBuildPackagesDirectory function locates the target build directory for packages. +// BuildPackagesDirectory function locates the target build directory for packages. // If the directories path doesn't exist, it will create it. -func MustFindBuildPackagesDirectory(packageRoot string) (string, error) { +func BuildPackagesDirectory(packageRoot string) (string, error) { buildDir, found, err := FindBuildPackagesDirectory() if err != nil { return "", errors.Wrap(err, "locating build directory failed") @@ -100,7 +100,7 @@ func FindBuildPackagesDirectory() (string, bool, error) { // BuildPackage function builds the package. func BuildPackage(packageRoot string) (string, error) { - destinationDir, err := MustFindBuildPackagesDirectory(packageRoot) + destinationDir, err := BuildPackagesDirectory(packageRoot) if err != nil { return "", errors.Wrap(err, "locating build directory for package failed") } diff --git a/internal/docs/readme.go b/internal/docs/readme.go index 0f2a7b1d60..6b00c1ead3 100644 --- a/internal/docs/readme.go +++ b/internal/docs/readme.go @@ -128,7 +128,7 @@ func updateReadme(fileName, packageRoot string) (string, error) { return "", errors.Wrapf(err, "writing %s file failed", fileName) } - packageBuildRoot, err := builder.MustFindBuildPackagesDirectory(packageRoot) + packageBuildRoot, err := builder.BuildPackagesDirectory(packageRoot) if err != nil { return "", errors.Wrap(err, "package build root not found") } diff --git a/internal/testrunner/coverage_output.go b/internal/testrunner/coverage_output.go index 1bc093de46..f6e4e55456 100644 --- a/internal/testrunner/coverage_output.go +++ b/internal/testrunner/coverage_output.go @@ -240,7 +240,7 @@ func writeCoverageReportFile(report *coberturaCoverage, packageName string) erro } func testCoverageReportsDir() (string, error) { - buildDir, err := builder.MustFindBuildDirectory() + buildDir, err := builder.BuildDirectory() if err != nil { return "", errors.Wrap(err, "locating build directory failed") } diff --git a/internal/testrunner/reporters/outputs/file.go b/internal/testrunner/reporters/outputs/file.go index f5afd1adec..64b5d1dfe0 100644 --- a/internal/testrunner/reporters/outputs/file.go +++ b/internal/testrunner/reporters/outputs/file.go @@ -58,7 +58,7 @@ func reportToFile(pkg, report string, format testrunner.TestReportFormat) error // testReportsDir returns the location of the directory to store test reports. func testReportsDir() (string, error) { - buildDir, err := builder.MustFindBuildDirectory() + buildDir, err := builder.BuildDirectory() if err != nil { return "", errors.Wrap(err, "locating build directory failed") } From 9b52a654a43b9e19607ac189346f70e197044d72 Mon Sep 17 00:00:00 2001 From: mtojek Date: Thu, 22 Jul 2021 13:44:27 +0200 Subject: [PATCH 16/16] Rename --- internal/testrunner/{coverage_output.go => coverageoutput.go} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename internal/testrunner/{coverage_output.go => coverageoutput.go} (100%) diff --git a/internal/testrunner/coverage_output.go b/internal/testrunner/coverageoutput.go similarity index 100% rename from internal/testrunner/coverage_output.go rename to internal/testrunner/coverageoutput.go