Skip to content

Commit

Permalink
render/junit: add new output
Browse files Browse the repository at this point in the history
This produces JUnit XML compatible output when the 'junit' output format is specified.

Fixes #459

Signed-off-by: Sebastian Hoß <seb@xn--ho-hia.de>
  • Loading branch information
sebhoss authored and zegl committed Jul 12, 2024
1 parent fb19eb5 commit 4177485
Show file tree
Hide file tree
Showing 5 changed files with 226 additions and 0 deletions.
3 changes: 3 additions & 0 deletions cmd/kube-score/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"github.com/zegl/kube-score/renderer/ci"
"github.com/zegl/kube-score/renderer/human"
"github.com/zegl/kube-score/renderer/json_v2"
"github.com/zegl/kube-score/renderer/junit"
"github.com/zegl/kube-score/renderer/sarif"
"github.com/zegl/kube-score/score"
"github.com/zegl/kube-score/score/checks"
Expand Down Expand Up @@ -255,6 +256,8 @@ Use "-" as filename to read from STDIN.`, execName(binName))
r = ci.CI(scoreCard)
case *outputFormat == "sarif":
r = sarif.Output(scoreCard)
case *outputFormat == "junit":
r = junit.JUnit(scoreCard)
default:
return fmt.Errorf("error: Unknown --output-format or --output-version")
}
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ require (
github.com/eidolon/wordwrap v0.0.0-20161011182207-e0f54129b8bb
github.com/fatih/color v1.17.0
github.com/google/go-cmp v0.6.0
github.com/jstemmer/go-junit-report/v2 v2.1.0
github.com/mattn/go-isatty v0.0.20
github.com/spf13/pflag v1.0.5
github.com/stretchr/testify v1.9.0
Expand Down
3 changes: 3 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
Expand All @@ -17,6 +18,8 @@ github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/jstemmer/go-junit-report/v2 v2.1.0 h1:X3+hPYlSczH9IMIpSC9CQSZA0L+BipYafciZUWHEmsc=
github.com/jstemmer/go-junit-report/v2 v2.1.0/go.mod h1:mgHVr7VUo5Tn8OLVr1cKnLuEy0M92wdRntM99h7RkgQ=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
Expand Down
87 changes: 87 additions & 0 deletions renderer/junit/junit.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package junit

import (
"bytes"
"github.com/jstemmer/go-junit-report/v2/junit"
"github.com/zegl/kube-score/scorecard"
"io"
)

// JUnit XML output
func JUnit(scoreCard *scorecard.Scorecard) io.Reader {
testSuites := junit.Testsuites{
Name: "kube-score",
}

for _, scoredObject := range *scoreCard {
testsuite := junit.Testsuite{
Name: scoredObject.HumanFriendlyRef(),
}

for _, testScore := range scoredObject.Checks {
if len(testScore.Comments) == 0 {
if testScore.Skipped {
testsuite.AddTestcase(junit.Testcase{
Name: testScore.Check.Name,
Classname: scoredObject.HumanFriendlyRef(),
Skipped: &junit.Result{},
})
} else {
if testScore.Grade == scorecard.GradeAlmostOK || testScore.Grade == scorecard.GradeAllOK {
testsuite.AddTestcase(junit.Testcase{
Name: testScore.Check.Name,
Classname: scoredObject.HumanFriendlyRef(),
})
} else {
testsuite.AddTestcase(junit.Testcase{
Name: testScore.Check.Name,
Classname: scoredObject.HumanFriendlyRef(),
Failure: &junit.Result{},
})
}
}
} else {
for _, comment := range testScore.Comments {
message := comment.Summary
if comment.Path != "" {
message = "(" + comment.Path + ") " + comment.Summary
}

if testScore.Skipped {
testsuite.AddTestcase(junit.Testcase{
Name: testScore.Check.Name,
Classname: scoredObject.HumanFriendlyRef(),
Skipped: &junit.Result{
Message: message,
},
})
} else {
if testScore.Grade == scorecard.GradeAlmostOK || testScore.Grade == scorecard.GradeAllOK {
testsuite.AddTestcase(junit.Testcase{
Name: testScore.Check.Name,
Classname: scoredObject.HumanFriendlyRef(),
})
} else {
testsuite.AddTestcase(junit.Testcase{
Name: testScore.Check.Name,
Classname: scoredObject.HumanFriendlyRef(),
Failure: &junit.Result{
Message: message,
},
})
}

}
}
}
}
testSuites.AddSuite(testsuite)
}

buffer := &bytes.Buffer{}
err := testSuites.WriteXML(buffer)
if err != nil {
panic(err)
}
return buffer
}
132 changes: 132 additions & 0 deletions renderer/junit/junit_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
package junit

import (
"io"
"testing"

"github.com/stretchr/testify/assert"
"github.com/zegl/kube-score/domain"
"github.com/zegl/kube-score/scorecard"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

func getTestCard() *scorecard.Scorecard {
checks := []scorecard.TestScore{
{
Check: domain.Check{
Name: "test-warning-two-comments",
},
Grade: scorecard.GradeWarning,
Comments: []scorecard.TestScoreComment{
{
Path: "a",
Summary: "summary",
Description: "description",
},
{
// No path
Summary: "summary",
Description: "description",
},
},
},
{
Check: domain.Check{
Name: "test-ok-comment",
},
Grade: scorecard.GradeAllOK,
Comments: []scorecard.TestScoreComment{
{
Path: "a",
Summary: "summary",
Description: "description",
},
},
},
{
Check: domain.Check{
Name: "test-skipped-comment",
},
Skipped: true,
Comments: []scorecard.TestScoreComment{
{
Path: "a",
Summary: "skipped sum",
Description: "skipped description",
},
},
},
{
Check: domain.Check{
Name: "test-skipped-no-comment",
},
Skipped: true,
},
}

return &scorecard.Scorecard{
"a": &scorecard.ScoredObject{
TypeMeta: v1.TypeMeta{
Kind: "Testing",
APIVersion: "v1",
},
ObjectMeta: v1.ObjectMeta{
Name: "foo",
Namespace: "foofoo",
},
Checks: checks,
},

// No namespace
"b": &scorecard.ScoredObject{
TypeMeta: v1.TypeMeta{
Kind: "Testing",
APIVersion: "v1",
},
ObjectMeta: v1.ObjectMeta{
Name: "bar-no-namespace",
},
Checks: checks,
},
}
}

func TestJUnitOutput(t *testing.T) {
t.Parallel()
r := JUnit(getTestCard())
all, err := io.ReadAll(r)
assert.Nil(t, err)
assert.Equal(t, `<testsuites name="kube-score" tests="10" failures="4" skipped="4">
<testsuite name="foo/foofoo v1/Testing" tests="5" failures="2" errors="0" id="0" skipped="2" time="">
<testcase name="test-warning-two-comments" classname="foo/foofoo v1/Testing">
<failure message="(a) summary"></failure>
</testcase>
<testcase name="test-warning-two-comments" classname="foo/foofoo v1/Testing">
<failure message="summary"></failure>
</testcase>
<testcase name="test-ok-comment" classname="foo/foofoo v1/Testing"></testcase>
<testcase name="test-skipped-comment" classname="foo/foofoo v1/Testing">
<skipped message="(a) skipped sum"></skipped>
</testcase>
<testcase name="test-skipped-no-comment" classname="foo/foofoo v1/Testing">
<skipped message=""></skipped>
</testcase>
</testsuite>
<testsuite name="bar-no-namespace v1/Testing" tests="5" failures="2" errors="0" id="0" skipped="2" time="">
<testcase name="test-warning-two-comments" classname="bar-no-namespace v1/Testing">
<failure message="(a) summary"></failure>
</testcase>
<testcase name="test-warning-two-comments" classname="bar-no-namespace v1/Testing">
<failure message="summary"></failure>
</testcase>
<testcase name="test-ok-comment" classname="bar-no-namespace v1/Testing"></testcase>
<testcase name="test-skipped-comment" classname="bar-no-namespace v1/Testing">
<skipped message="(a) skipped sum"></skipped>
</testcase>
<testcase name="test-skipped-no-comment" classname="bar-no-namespace v1/Testing">
<skipped message=""></skipped>
</testcase>
</testsuite>
</testsuites>
`, string(all))
}

0 comments on commit 4177485

Please # to comment.