Skip to content

Commit

Permalink
Add file attributes for JUnit Test Report
Browse files Browse the repository at this point in the history
  • Loading branch information
Kuniwak committed Apr 17, 2024
1 parent 9f28a2b commit 5f89d5f
Show file tree
Hide file tree
Showing 5 changed files with 188 additions and 72 deletions.
5 changes: 1 addition & 4 deletions tool/gh-action/runner/runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import (
"github.com/DeNA/unity-meta-check/unity/checker"
"github.com/DeNA/unity-meta-check/util/logging"
"io"
"time"
)

type Runner func(opts *Options) (bool, error)
Expand All @@ -25,8 +24,6 @@ func NewRunner(
logger logging.Logger,
) Runner {
return func(opts *Options) (bool, error) {
startTime := time.Now()

logger.Debug(fmt.Sprintf("check: %#v", opts.CheckerOpts))
resultNotFiltered, err := check(opts.RootDirAbs, opts.CheckerOpts)
if err != nil {
Expand Down Expand Up @@ -57,7 +54,7 @@ func NewRunner(

if opts.EnableJUnit {
logger.Debug(fmt.Sprintf("write junit report: %q", opts.JUnitOutPath))
if err := writeJunitXML(resultFiltered, startTime, opts.JUnitOutPath); err != nil {
if err := writeJunitXML(resultFiltered, opts.JUnitOutPath); err != nil {
return false, err
}
} else {
Expand Down
6 changes: 1 addition & 5 deletions tool/unity-meta-check-junit/cmd/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,10 @@ import (
"github.com/DeNA/unity-meta-check/util/cli"
"github.com/DeNA/unity-meta-check/version"
"io"
"time"
)

func NewMain() cli.Command {
return func(args []string, procInout cli.ProcessInout, env cli.Env) cli.ExitStatus {
startTime := time.Now()

opts, err := options.BuildOptions(args, procInout)
if err != nil {
if err != flag.ErrHelp {
Expand All @@ -32,7 +29,7 @@ func NewMain() cli.Command {
parse := report.NewParser()
result := parse(io.TeeReader(procInout.Stdin, procInout.Stdout))

if err := junit.WriteToFile(result, startTime, opts.OutPath); err != nil {
if err := junit.WriteToFile(result, opts.OutPath); err != nil {
_, _ = fmt.Fprintln(procInout.Stderr, err.Error())
return cli.ExitAbnormal
}
Expand All @@ -43,4 +40,3 @@ func NewMain() cli.Command {
return cli.ExitNormal
}
}

161 changes: 119 additions & 42 deletions tool/unity-meta-check-junit/junit/junit.go
Original file line number Diff line number Diff line change
@@ -1,21 +1,62 @@
package junit

import (
"encoding/xml"
"fmt"
"github.com/DeNA/unity-meta-check/unity"
"github.com/DeNA/unity-meta-check/unity/checker"
"github.com/DeNA/unity-meta-check/util/typedpath"
"github.com/jstemmer/go-junit-report/formatter"
"github.com/jstemmer/go-junit-report/parser"
"io"
"os"
"runtime"
"time"
)

type WriteToFileFunc func(result *checker.CheckResult, startTime time.Time, outPath typedpath.RawPath) error
type TestSuites struct {
XMLName xml.Name `xml:"testsuites"`
TestSuites []TestSuite `xml:"testsuite"`
}

type TestSuite struct {
XMLName xml.Name `xml:"testsuite"`
Tests int `xml:"tests,attr"`
Failures int `xml:"failures,attr"`
Time string `xml:"time,attr"`
Name string `xml:"name,attr"`
File *string `xml:"file,attr,omitempty"`
Properties Properties `xml:"properties"`
TestCases []TestCase `xml:"testcase"`
}

type TestCase struct {
XMLName xml.Name `xml:"testcase"`
ClassName string `xml:"classname,attr"`
Name string `xml:"name,attr"`
Time string `xml:"time,attr"`
File *string `xml:"file,attr,omitempty"`
Failure *Failure `xml:"failure,omitempty"`
}

type Failure struct {
XMLName xml.Name `xml:"failure"`
Message string `xml:"message,attr"`
Type string `xml:"type,attr"`
Contents string `xml:",chardata"`
}

func WriteToFile(result *checker.CheckResult, startTime time.Time, outPath typedpath.RawPath) error {
type Properties struct {
XMLName xml.Name `xml:"properties"`
Properties []Property `xml:"property"`
}

type Property struct {
XMLName xml.Name `xml:"property"`
Name string `xml:"name,attr"`
Value string `xml:"value,attr"`
}

type WriteToFileFunc func(result *checker.CheckResult, outPath typedpath.RawPath) error

func WriteToFile(result *checker.CheckResult, outPath typedpath.RawPath) error {
if err := os.MkdirAll(string(outPath.Dir()), 0755); err != nil {
return err
}
Expand All @@ -24,72 +65,108 @@ func WriteToFile(result *checker.CheckResult, startTime time.Time, outPath typed
if err != nil {
return err
}
defer func(){ _ = file.Close() }()
defer func() { _ = file.Close() }()

endTime := time.Now()
return Write(result, endTime.Sub(startTime), file)
return Write(result, file)
}

func Write(result *checker.CheckResult, duration time.Duration, writer io.Writer) error {
func Write(result *checker.CheckResult, writer io.Writer) error {
maxLen := result.Len()
var packages []parser.Package
if maxLen == 0 {
packages = []parser.Package{
props := &Properties{
Properties: []Property{
{
Name: "unity-meta-check",
Tests: []*parser.Test{
{
Name: "OK",
Result: parser.PASS,
Output: []string{"No missing or dangling .meta exist. Perfect!"},
Duration: duration,
Name: "go.version",
Value: runtime.Version(),
},
},
}
var testSuites *TestSuites
if maxLen == 0 {
testSuites = &TestSuites{
TestSuites: []TestSuite{
{
Name: "unity-meta-check",
Tests: 1,
Failures: 0,
Time: "0.000",
Properties: *props,
TestCases: []TestCase{
{
ClassName: "unity-meta-check",
Name: "OK",
Time: "0.000",
},
},
},
},
}
} else {
durationAvg := time.Duration(int(duration) / maxLen)
packages = make([]parser.Package, maxLen)
suites := make([]TestSuite, maxLen)
i := 0
for _, missingMeta := range result.MissingMeta {
packages[i] = parser.Package{
Name: string(unity.TrimMetaFromSlash(missingMeta)),
Tests: []*parser.Test{
file := string(unity.TrimMetaFromSlash(missingMeta))
suites[i] = TestSuite{
Name: file,
Tests: 1,
Time: "0.000",
Failures: 1,
File: &file,
Properties: *props,
TestCases: []TestCase{
{
Name: "meta",
Result: parser.FAIL,
Output: []string{
fmt.Sprintf("File or directory exists: %s", unity.TrimMetaFromSlash(missingMeta)),
fmt.Sprintf("But .meta is missing: %s", missingMeta),
ClassName: "missing",
Name: "meta",
Time: "0.000",
File: &file,
Failure: &Failure{
Message: "Failed",
Contents: fmt.Sprintf("File or directory exists: %s\nBut .meta is missing: %s", file, missingMeta),
},
Duration: durationAvg,
},
},
}
i++
}
for _, danglingMeta := range result.DanglingMeta {
packages[i] = parser.Package{
Name: string(unity.TrimMetaFromSlash(danglingMeta)),
Tests: []*parser.Test{
file := string(unity.TrimMetaFromSlash(danglingMeta))
suites[i] = TestSuite{
Name: file,
Tests: 1,
Time: "0.000",
Failures: 1,
File: &file,
Properties: *props,
TestCases: []TestCase{
{
Name: "meta",
Result: parser.FAIL,
Output: []string{
fmt.Sprintf("File or directory does not exist: %s", unity.TrimMetaFromSlash(danglingMeta)),
fmt.Sprintf("But .meta is present: %s", danglingMeta),
ClassName: "dangling",
Name: "meta",
Time: "0.000",
File: &file,
Failure: &Failure{
Message: "Failed",
Contents: fmt.Sprintf("File or directory does not exist: %s\nBut .meta is present: %s", file, danglingMeta),
},
Duration: durationAvg,
},
},
}
i++
}
testSuites = &TestSuites{
TestSuites: suites,
}
}

junitReport := &parser.Report{Packages: packages}

if err := formatter.JUnitReportXML(junitReport, false, runtime.Version(), writer); err != nil {
bs, err := xml.MarshalIndent(testSuites, "", "\t")
if err != nil {
return err
}
if _, err := io.WriteString(writer, xml.Header); err != nil {
return err
}
if _, err := writer.Write(bs); err != nil {
return err
}
if _, err := writer.Write([]byte{'\n'}); err != nil {
return err
}
return nil
Expand Down
11 changes: 4 additions & 7 deletions tool/unity-meta-check-junit/junit/junit_stub.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,31 +4,28 @@ import (
"errors"
"github.com/DeNA/unity-meta-check/unity/checker"
"github.com/DeNA/unity-meta-check/util/typedpath"
"time"
)

func StubWriteToFileFunc(err error) WriteToFileFunc {
return func(_ *checker.CheckResult, _ time.Time, _ typedpath.RawPath) error {
return func(_ *checker.CheckResult, _ typedpath.RawPath) error {
return err
}
}

type WriteToFileCallArgs struct {
CheckResult *checker.CheckResult
StartTime time.Time
OutPath typedpath.RawPath
}

func SpyWriteToFileFunc(inherited WriteToFileFunc, callArgs *[]WriteToFileCallArgs) WriteToFileFunc {
if inherited == nil {
inherited = StubWriteToFileFunc(errors.New("SPY_WRITE_TO_FILE_FUNC"))
}
return func(result *checker.CheckResult, startTime time.Time, outPath typedpath.RawPath) error {
return func(result *checker.CheckResult, outPath typedpath.RawPath) error {
*callArgs = append(*callArgs, WriteToFileCallArgs{
CheckResult: result,
StartTime: startTime,
OutPath: outPath,
})
return inherited(result, startTime, outPath)
return inherited(result, outPath)
}
}
}
77 changes: 63 additions & 14 deletions tool/unity-meta-check-junit/junit/junit_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,78 @@ package junit

import (
"bytes"
"fmt"
"github.com/DeNA/unity-meta-check/unity/checker"
"github.com/DeNA/unity-meta-check/util/typedpath"
"github.com/google/go-cmp/cmp"
"runtime"
"testing"
)

func TestWrite(t *testing.T) {
result := checker.NewCheckResult(
[]typedpath.SlashPath{
typedpath.NewSlashPathUnsafe("path/to/missing.meta"),
cases := map[string]struct {
Result *checker.CheckResult
Expected string
}{
"empty (boundary)": {
Result: checker.NewCheckResult([]typedpath.SlashPath{}, []typedpath.SlashPath{}),
Expected: fmt.Sprintf(`<?xml version="1.0" encoding="UTF-8"?>
<testsuites>
<testsuite tests="1" failures="0" time="0.000" name="unity-meta-check">
<properties>
<property name="go.version" value="%s"></property>
</properties>
<testcase classname="unity-meta-check" name="OK" time="0.000"></testcase>
</testsuite>
</testsuites>
`, runtime.Version()),
},
[]typedpath.SlashPath{
typedpath.NewSlashPathUnsafe("path/to/dangling.meta"),
"both missing and dangling (easy to test)": {
Result: checker.NewCheckResult(
[]typedpath.SlashPath{
typedpath.NewSlashPathUnsafe("path/to/missing.meta"),
},
[]typedpath.SlashPath{
typedpath.NewSlashPathUnsafe("path/to/dangling.meta"),
},
),
Expected: fmt.Sprintf(`<?xml version="1.0" encoding="UTF-8"?>
<testsuites>
<testsuite tests="1" failures="1" time="0.000" name="path/to/missing" file="path/to/missing">
<properties>
<property name="go.version" value="%s"></property>
</properties>
<testcase classname="missing" name="meta" time="0.000" file="path/to/missing">
<failure message="Failed" type="">File or directory exists: path/to/missing&#xA;But .meta is missing: path/to/missing.meta</failure>
</testcase>
</testsuite>
<testsuite tests="1" failures="1" time="0.000" name="path/to/dangling" file="path/to/dangling">
<properties>
<property name="go.version" value="%s"></property>
</properties>
<testcase classname="dangling" name="meta" time="0.000" file="path/to/dangling">
<failure message="Failed" type="">File or directory does not exist: path/to/dangling&#xA;But .meta is present: path/to/dangling.meta</failure>
</testcase>
</testsuite>
</testsuites>
`, runtime.Version(), runtime.Version()),
},
)

buf := &bytes.Buffer{}
if err := Write(result, 0, buf); err != nil {
t.Errorf("want nil, got %#v", err)
return
}

if len(buf.String()) == 0 {
t.Error("want not empty string, got empty string")
return
for name, c := range cases {
t.Run(name, func(t *testing.T) {

buf := &bytes.Buffer{}
if err := Write(c.Result, buf); err != nil {
t.Errorf("want nil, got %#v", err)
return
}

actual := buf.String()
if actual != c.Expected {
t.Error(cmp.Diff(c.Expected, actual))
return
}
})
}
}

0 comments on commit 5f89d5f

Please sign in to comment.