13 Commits
Author SHA1 Message Date
erik 3ce73b8db9 fixed problem with testcase test 2024-11-22 22:45:58 +01:00
erik fe733f96d6 simplification and elapsed time now available everywhere. 2024-11-22 22:42:02 +01:00
erik 7cf5827f22 added time 2024-11-22 22:03:23 +01:00
erik 09fd2cb2a4 simple testcase names 2024-11-22 21:50:28 +01:00
erik 74383ddcc0 added a prefix option to separate test results 2024-11-22 21:34:52 +01:00
erik e20795e7b9 no more duplication of test suites.
Parsing the test tree and deciding in the marsheller whether it is a
testcase (leaf) or a suite (non-leaf)
2024-11-22 20:59:23 +01:00
erik 36563f55fb avoid duplicate testsuites for everyt line of output 2024-11-20 23:42:06 +01:00
erik bfbf24a6c4 reintroducing nesting. 2024-11-20 23:35:05 +01:00
erik e0207bab2a now using classname in testcases to refer to the testsuite 2024-11-20 23:25:27 +01:00
erik 37f4359dcb now forcing the orot name of the output to be correct 2024-11-20 22:32:49 +01:00
erik befec17e06 testing autoupdate 2024-11-20 22:19:24 +01:00
erik 220b73dc49 now writing each TestSuite to a separate file 2024-11-20 22:15:44 +01:00
erik d97ffd7fea root element rename to testsuite 2024-11-20 21:54:52 +01:00
2 changed files with 144 additions and 107 deletions
+83 -27
View File
@@ -5,11 +5,51 @@ import (
"encoding/json" "encoding/json"
"encoding/xml" "encoding/xml"
"fmt" "fmt"
"log"
"os" "os"
"path/filepath"
"strings"
) )
func main() { func (t *Test) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
if len(t.Tests) == 0 {
start.Name = xml.Name{Local: "testcase"}
classname := ""
if t.parent != nil {
classname = t.parent.Name
}
var skipped *Result
if t.Skipped > 0 {
skipped = &Result{
Message: "skipped",
}
}
var failed *Result
if t.Failures > 0 {
failed = &Result{
Message: "failed",
}
}
log.Printf("TIME %f", t.Time)
parts := strings.Split(t.Name, "/")
tc := Testcase{
Name: parts[len(parts)-1],
// parent class name
Classname: classname,
Time: t.Time,
Skipped: skipped,
Error: nil,
Failure: failed,
SystemOut: t.SystemOut,
}
return e.EncodeElement(tc, start)
}
type Alias Test
start.Name = xml.Name{Local: "testsuite"}
return e.EncodeElement(Alias(*t), start)
}
func main() {
/* /*
testsuites := Testsuites{ testsuites := Testsuites{
Time: 1.23, Time: 1.23,
@@ -63,21 +103,22 @@ func main() {
testsuites := Testsuites{} testsuites := Testsuites{}
if len(os.Args) != 2 { if len(os.Args) != 2 && len(os.Args) != 3 {
fmt.Fprintf(os.Stderr, "Usage: go2junit <gotest.json>\n") fmt.Fprintf(os.Stderr, "Usage: go2junit <outputdir> [<prefix>]\n")
os.Exit(1) os.Exit(1)
} }
filename := os.Args[1] path := os.Args[1]
var file *os.File path = filepath.Clean(path)
if filename != "-" { err := os.MkdirAll(path, 0755)
file, err := os.Open(filename) if err != nil {
if err != nil { panic(err)
panic(err)
}
defer file.Close()
} else {
file = os.Stdin
} }
prefix := ""
if len(os.Args) == 3 {
prefix = os.Args[2]
}
var file = os.Stdin
scanner := bufio.NewScanner(file) scanner := bufio.NewScanner(file)
lineno := 0 lineno := 0
@@ -94,33 +135,48 @@ func main() {
//.fmt.Printf("Parsed %d:\n%v\n\n", lineno, item) //.fmt.Printf("Parsed %d:\n%v\n\n", lineno, item)
pkg := prefix + item.Package
switch item.Action { switch item.Action {
case "start": case "start":
testsuites.Suite(item.Time, item.Package) testsuites.Start(item.Time, pkg)
case "run": case "run":
testsuites.Test(item.Time, item.Package, item.Test) fmt.Println()
testsuites.Test(item.Time, pkg, item.Test)
case "output": case "output":
testsuites.Output(item.Time, item.Package, item.Test, item.Output) testsuites.Output(item.Time, pkg, item.Test, item.Output)
fmt.Fprintf(os.Stderr, "%s", item.Output) fmt.Printf("%s", item.Output)
case "pause": case "pause":
testsuites.Output(item.Time, item.Package, item.Test, "PAUSED") testsuites.Output(item.Time, pkg, item.Test, "PAUSED")
case "cont": case "cont":
testsuites.Output(item.Time, item.Package, item.Test, "CONTINUED") testsuites.Output(item.Time, pkg, item.Test, "CONTINUED")
case "pass": case "pass":
testsuites.Pass(item.Time, item.Package, item.Test, item.Elapsed) testsuites.Pass(item.Time, pkg, item.Test)
case "bench": case "bench":
testsuites.Bench(item.Time, item.Package, item.Test, item.Output, item.Elapsed) testsuites.Bench(item.Time, pkg, item.Test, item.Output)
case "fail": case "fail":
testsuites.Fail(item.Time, item.Package, item.Test, item.Elapsed) testsuites.Fail(item.Time, pkg, item.Test)
case "skip": case "skip":
testsuites.Skip(item.Time, item.Package, item.Test) testsuites.Skip(item.Time, pkg, item.Test)
} }
} }
testsuites.Complete() testsuites.Complete()
xml, err := xml.MarshalIndent(testsuites, "", " ") for _, suite := range testsuites.Suites {
if err != nil { xml, err := xml.MarshalIndent(suite, "", " ")
panic(err) if err != nil {
panic(err)
}
fname := path + "/" + suite.Name + ".xml"
dir := filepath.Dir(fname)
err = os.MkdirAll(dir, 0755)
if err != nil {
panic(err)
}
fmt.Fprintf(os.Stderr, "Writing %s\n", fname)
err = os.WriteFile(fname, xml, 0644)
if err != nil {
panic(err)
}
} }
fmt.Printf("%s", xml)
} }
+61 -80
View File
@@ -2,7 +2,6 @@ package main
import ( import (
"encoding/xml" "encoding/xml"
"log"
"strings" "strings"
"time" "time"
) )
@@ -18,26 +17,30 @@ type Testsuites struct {
Time float64 `xml:"time,attr"` Time float64 `xml:"time,attr"`
Suites []*Testsuite `xml:"testsuite,omitempty"` Suites []*Test `xml:"testsuite,omitempty"`
} }
type Testsuite struct { type Test struct {
XMLName xml.Name `xml:"testsuite"`
// required attributes // required attributes
Name string `xml:"name,attr"` Name string `xml:"name,attr"`
Tests int `xml:"tests,attr"` TestCount int `xml:"tests,attr"`
Failures int `xml:"failures,attr"` Failures int `xml:"failures,attr"`
Errors int `xml:"errors,attr"` Errors int `xml:"errors,attr"`
// optional attributes // optional attributes
Disabled int `xml:"disabled,attr,omitempty"` Disabled int `xml:"disabled,attr,omitempty"`
Package string `xml:"package,attr,omitempty"` Package string `xml:"package,attr,omitempty"`
Skipped int `xml:"skipped,attr,omitempty"` Skipped int `xml:"skipped,attr,omitempty"`
Time string `xml:"time,attr"` Time float64 `xml:"time,attr"`
Timestamp time.Time `xml:"timestamp,attr,omitempty"` Timestamp time.Time `xml:"timestamp,attr,omitempty"`
Testsuites []*Testsuite `xml:"testsuite,omitempty"` Tests []*Test `xml:"testsuite,omitempty"`
Testcases []*Testcase `xml:"testcase,omitempty"` SystemOut string `xml:"system-out,omitempty"`
SystemOut string `xml:"system-out,omitempty"`
parent *Test
t0 time.Time
} }
type Result struct { type Result struct {
@@ -58,137 +61,115 @@ type Testcase struct {
SystemOut string `xml:"system-out,omitempty"` SystemOut string `xml:"system-out,omitempty"`
} }
func (testsuites *Testsuites) getSuite(t time.Time, pkg string) *Testsuite { // Looks for a test in root
func (testsuites *Testsuites) getRootSuite(t time.Time, pkg string, create bool) *Test {
for _, suite := range testsuites.Suites { for _, suite := range testsuites.Suites {
if suite.Name == pkg { if suite.Name == pkg {
return suite return suite
} }
} }
suite := Testsuite{ if !create {
return nil
}
suite := Test{
Name: pkg, Name: pkg,
Skipped: 0, Skipped: 0,
Timestamp: t, Timestamp: t,
t0: t,
} }
log.Printf("Adding suite %s", pkg)
testsuites.Suites = append(testsuites.Suites, &suite) testsuites.Suites = append(testsuites.Suites, &suite)
return &suite return &suite
} }
func (suite *Testsuite) getSuite(t time.Time, name string) *Testsuite { func (suite *Test) getSuite(t time.Time, name string) *Test {
for _, s := range suite.Testsuites { for _, s := range suite.Tests {
if s.Name == name { if s.Name == suite.Name+"/"+name {
return s return s
} }
} }
s := Testsuite{ s := Test{
Name: name, Name: suite.Name + "/" + name,
Timestamp: t, Timestamp: t,
t0: t,
} }
suite.Testsuites = append(suite.Testsuites, &s) suite.Tests = append(suite.Tests, &s)
return &s return &s
} }
func (suite *Testsuite) getTest(t time.Time, testname string) *Testcase { func (suite *Test) getTest(t time.Time, testname string) *Test {
if testname == "" {
return suite
}
suitename := suite.Name
path := strings.Split(testname, "/") path := strings.Split(testname, "/")
for i := 0; i < len(path)-1; i++ { for i := 0; i < len(path); i++ {
suite = suite.getSuite(t, path[i]) suite = suite.getSuite(t, path[i])
suitename = suitename + "/" + path[i]
} }
for _, test := range suite.Testcases { return suite
if test.Name == path[len(path)-1] {
return test
}
}
if path[len(path)-1] == "" {
panic("Empty testcase")
}
test := Testcase{
Name: path[len(path)-1],
}
suite.Testcases = append(suite.Testcases, &test)
return &test
} }
func (testsuites *Testsuites) getTest(t time.Time, pkg string, testname string) *Testcase { func (testsuites *Testsuites) getTest(t time.Time, pkg string, testname string) *Test {
suite := testsuites.getSuite(t, pkg) suite := testsuites.getRootSuite(t, pkg, true)
test := suite.getTest(t, testname) test := suite.getTest(t, testname)
return test return test
} }
func (testsuites *Testsuites) Suite(t time.Time, pkg string) { func (testsuites *Testsuites) Start(t time.Time, pkg string) {
testsuites.getSuite(t, pkg) testsuites.getRootSuite(t, pkg, true)
} }
func (testsuites *Testsuites) Test(t time.Time, pkg string, test string) { func (testsuites *Testsuites) Test(t time.Time, pkg string, test string) {
// This can be a test suite as well
testsuites.getTest(t, pkg, test) testsuites.getTest(t, pkg, test)
} }
func (testsuites *Testsuites) Output(t time.Time, pkg string, test string, output string) { func (testsuites *Testsuites) Output(t time.Time, pkg string, test string, output string) {
if test == "" {
ts := testsuites.getSuite(t, pkg)
ts.SystemOut = ts.SystemOut + output
return
}
tc := testsuites.getTest(t, pkg, test) tc := testsuites.getTest(t, pkg, test)
tc.SystemOut = tc.SystemOut + output tc.SystemOut = tc.SystemOut + output
} }
func (testsuites *Testsuites) Pass(t time.Time, pkg string, test string, elapsed float64) { func (testsuites *Testsuites) Pass(t time.Time, pkg string, test string) {
if test == "" { tc := testsuites.getTest(t, pkg, test)
return tc.Time = float64(t.Sub(tc.t0).Nanoseconds()) / 1000_000_000.0
}
testsuites.getTest(t, pkg, test)
} }
func (testsuites *Testsuites) Bench(t time.Time, pkg string, test string, output string, elapsed float64) { func (testsuites *Testsuites) Bench(t time.Time, pkg string, test string, output string) {
tc := testsuites.getTest(t, pkg, test) tc := testsuites.getTest(t, pkg, test)
tc.SystemOut = tc.SystemOut + output + "\n" tc.SystemOut = tc.SystemOut + output + "\n"
} }
func (testsuites *Testsuites) Fail(t time.Time, pkg string, test string, elapsed float64) { func (testsuites *Testsuites) Fail(t time.Time, pkg string, test string) {
if test == "" {
return
}
tc := testsuites.getTest(t, pkg, test) tc := testsuites.getTest(t, pkg, test)
tc.Time = elapsed tc.Time = float64(t.Sub(tc.t0).Nanoseconds()) / 1000_000_000.0
tc.Failure = &Result{ tc.Failures = 1
Message: "failed",
}
} }
func (testsuites *Testsuites) Skip(t time.Time, pkg string, test string) { func (testsuites *Testsuites) Skip(t time.Time, pkg string, test string) {
if test == "" {
return
}
tc := testsuites.getTest(t, pkg, test) tc := testsuites.getTest(t, pkg, test)
tc.Failure = &Result{ tc.Time = float64(t.Sub(tc.t0).Nanoseconds()) / 1000_000_000.0
Message: "skipped", tc.Skipped = 1
}
} }
func (suite *Testsuite) Complete() { func (suite *Test) Complete() {
suite.Tests = 0 suite.TestCount = 0
suite.Failures = 0 suite.Failures = 0
suite.Errors = 0 suite.Errors = 0
suite.Disabled = 0 suite.Disabled = 0
suite.Skipped = 0 suite.Skipped = 0
for _, ts := range suite.Testsuites {
if len(suite.Tests) == 0 {
suite.TestCount = 1
}
for _, ts := range suite.Tests {
ts.Complete() ts.Complete()
suite.Tests += ts.Tests suite.TestCount += ts.TestCount
suite.Failures += ts.Failures suite.Failures += ts.Failures
suite.Errors += ts.Errors suite.Errors += ts.Errors
suite.Disabled += ts.Disabled suite.Disabled += ts.Disabled
suite.Skipped += ts.Skipped suite.Skipped += ts.Skipped
} ts.parent = suite
for _, tc := range suite.Testcases {
suite.Tests += 1
if tc.Failure != nil {
switch tc.Failure.Message {
case "skipped":
suite.Skipped++
default:
suite.Failures++
}
}
} }
} }
@@ -200,7 +181,7 @@ func (testsuites *Testsuites) Complete() {
testsuites.Skipped = 0 testsuites.Skipped = 0
for _, ts := range testsuites.Suites { for _, ts := range testsuites.Suites {
ts.Complete() ts.Complete()
testsuites.Tests += ts.Tests testsuites.Tests += ts.TestCount
testsuites.Failures += ts.Failures testsuites.Failures += ts.Failures
testsuites.Errors += ts.Errors testsuites.Errors += ts.Errors
testsuites.Disabled += ts.Disabled testsuites.Disabled += ts.Disabled