mirror of
https://github.com/jstemmer/go-junit-report.git
synced 2025-04-05 05:00:15 -05:00
Move common go-junit-report code into separate internal package
There was some code duplication between the go-junit-report binary, its tests and the testdata/generate-golden script. This has been moved into an internal package. The go-junit-report binary can now just focus on flag parsing and validation, and it should be less likely that the binary, tests and golden report generator behave differently.
This commit is contained in:
parent
d84b066208
commit
3260a9d2e0
98
internal/gojunitreport/go-junit-report.go
Normal file
98
internal/gojunitreport/go-junit-report.go
Normal file
@ -0,0 +1,98 @@
|
||||
package gojunitreport
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/jstemmer/go-junit-report/v2/gtr"
|
||||
"github.com/jstemmer/go-junit-report/v2/junit"
|
||||
"github.com/jstemmer/go-junit-report/v2/parser/gotest"
|
||||
)
|
||||
|
||||
type parser interface {
|
||||
Parse(r io.Reader) (gtr.Report, error)
|
||||
Events() []gotest.Event
|
||||
}
|
||||
|
||||
// Config contains the go-junit-report command configuration.
|
||||
type Config struct {
|
||||
Parser string
|
||||
Hostname string
|
||||
PackageName string
|
||||
SkipXMLHeader bool
|
||||
Properties map[string]string
|
||||
TimestampFunc func() time.Time
|
||||
|
||||
// For debugging
|
||||
PrintEvents bool
|
||||
}
|
||||
|
||||
// Run runs the go-junit-report command and returns the generated report.
|
||||
func (c Config) Run(input io.Reader, output io.Writer) (*gtr.Report, error) {
|
||||
var p parser
|
||||
switch c.Parser {
|
||||
case "gotest":
|
||||
p = gotest.NewParser(
|
||||
gotest.PackageName(c.PackageName),
|
||||
gotest.TimestampFunc(c.TimestampFunc),
|
||||
)
|
||||
case "gojson":
|
||||
p = gotest.NewJSONParser(
|
||||
gotest.PackageName(c.PackageName),
|
||||
gotest.TimestampFunc(c.TimestampFunc),
|
||||
)
|
||||
default:
|
||||
return nil, fmt.Errorf("invalid parser: %s", c.Parser)
|
||||
}
|
||||
|
||||
report, err := p.Parse(input)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error parsing input: %w", err)
|
||||
}
|
||||
|
||||
if c.PrintEvents {
|
||||
enc := json.NewEncoder(os.Stderr)
|
||||
for _, event := range p.Events() {
|
||||
if err := enc.Encode(event); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for i := range report.Packages {
|
||||
for k, v := range c.Properties {
|
||||
report.Packages[i].SetProperty(k, v)
|
||||
}
|
||||
}
|
||||
|
||||
if err = c.writeXML(output, report); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &report, nil
|
||||
}
|
||||
|
||||
func (c Config) writeXML(w io.Writer, report gtr.Report) error {
|
||||
testsuites := junit.CreateFromReport(report, c.Hostname)
|
||||
|
||||
if !c.SkipXMLHeader {
|
||||
_, err := fmt.Fprintf(w, xml.Header)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
enc := xml.NewEncoder(w)
|
||||
enc.Indent("", "\t")
|
||||
if err := enc.Encode(testsuites); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := enc.Flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
_, err := fmt.Fprintf(w, "\n")
|
||||
return err
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package main
|
||||
package gojunitreport
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@ -12,25 +12,17 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/jstemmer/go-junit-report/v2/junit"
|
||||
"github.com/jstemmer/go-junit-report/v2/parser/gotest"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
)
|
||||
|
||||
const testDataDir = "testdata/"
|
||||
const testDataDir = "../../testdata/"
|
||||
|
||||
var matchTest = flag.String("match", "", "only test testdata matching this pattern")
|
||||
|
||||
type TestConfig struct {
|
||||
noXMLHeader bool
|
||||
packageName string
|
||||
}
|
||||
|
||||
var testConfigs = map[int]TestConfig{
|
||||
5: {noXMLHeader: true},
|
||||
6: {noXMLHeader: true},
|
||||
7: {packageName: "test/package"},
|
||||
var testConfigs = map[int]Config{
|
||||
5: {SkipXMLHeader: true},
|
||||
6: {SkipXMLHeader: true},
|
||||
7: {PackageName: "test/package"},
|
||||
}
|
||||
|
||||
func TestRun(t *testing.T) {
|
||||
@ -58,7 +50,7 @@ func TestRun(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func testRun(inputFile, reportFile string, config TestConfig, t *testing.T) {
|
||||
func testRun(inputFile, reportFile string, config Config, t *testing.T) {
|
||||
input, err := os.Open(inputFile)
|
||||
if err != nil {
|
||||
t.Fatalf("error opening input file: %v", err)
|
||||
@ -72,33 +64,19 @@ func testRun(inputFile, reportFile string, config TestConfig, t *testing.T) {
|
||||
t.Fatalf("error loading report file: %v", err)
|
||||
}
|
||||
|
||||
options := []gotest.Option{
|
||||
gotest.PackageName(config.packageName),
|
||||
gotest.TimestampFunc(func() time.Time {
|
||||
return time.Date(2022, 1, 1, 0, 0, 0, 0, time.UTC)
|
||||
}),
|
||||
}
|
||||
|
||||
var parser Parser
|
||||
config.Parser = "gotest"
|
||||
if strings.HasSuffix(inputFile, ".gojson.txt") {
|
||||
parser = gotest.NewJSONParser(options...)
|
||||
} else {
|
||||
parser = gotest.NewParser(options...)
|
||||
config.Parser = "gojson"
|
||||
}
|
||||
|
||||
report, err := parser.Parse(input)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
config.Hostname = "hostname"
|
||||
config.Properties = map[string]string{"go.version": "1.0"}
|
||||
config.TimestampFunc = func() time.Time {
|
||||
return time.Date(2022, 1, 1, 0, 0, 0, 0, time.UTC)
|
||||
}
|
||||
|
||||
for i := range report.Packages {
|
||||
report.Packages[i].SetProperty("go.version", "1.0")
|
||||
}
|
||||
testsuites := junit.CreateFromReport(report, "hostname")
|
||||
|
||||
var output bytes.Buffer
|
||||
if err := writeXML(&output, testsuites, config.noXMLHeader); err != nil {
|
||||
t.Fatalf("error writing XML: %v", err)
|
||||
if _, err := config.Run(input, &output); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(output.String(), string(wantReport)); diff != "" {
|
||||
@ -106,16 +84,16 @@ func testRun(inputFile, reportFile string, config TestConfig, t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func testFileConfig(filename string) (conf TestConfig, reportFile string, err error) {
|
||||
func testFileConfig(filename string) (config Config, reportFile string, err error) {
|
||||
var prefix string
|
||||
if idx := strings.IndexByte(filename, '-'); idx < 0 {
|
||||
return conf, "", fmt.Errorf("testdata file does not contain a dash (-); expected name `{id}-{name}.txt` got `%s`", filename)
|
||||
return config, "", fmt.Errorf("testdata file does not contain a dash (-); expected name `{id}-{name}.txt` got `%s`", filename)
|
||||
} else {
|
||||
prefix = filename[:idx]
|
||||
}
|
||||
id, err := strconv.Atoi(prefix)
|
||||
if err != nil {
|
||||
return conf, "", fmt.Errorf("testdata file did not start with a valid number: %w", err)
|
||||
return config, "", fmt.Errorf("testdata file did not start with a valid number: %w", err)
|
||||
}
|
||||
return testConfigs[id], fmt.Sprintf("%s-report.xml", prefix), nil
|
||||
}
|
@ -1,17 +1,13 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"encoding/xml"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/jstemmer/go-junit-report/v2/gtr"
|
||||
"github.com/jstemmer/go-junit-report/v2/junit"
|
||||
"github.com/jstemmer/go-junit-report/v2/parser/gotest"
|
||||
"github.com/jstemmer/go-junit-report/v2/internal/gojunitreport"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -29,7 +25,7 @@ var (
|
||||
output = flag.String("out", "", "write XML report to `file`")
|
||||
iocopy = flag.Bool("iocopy", false, "copy input to stdout; can only be used in conjunction with -out")
|
||||
properties = make(keyValueFlag)
|
||||
inputParser = flag.String("parser", "gotest", "set input parser: gotest, gojson")
|
||||
parser = flag.String("parser", "gotest", "set input parser: gotest, gojson")
|
||||
|
||||
// debug flags
|
||||
printEvents = flag.Bool("debug.print-events", false, "print events generated by the go test parser")
|
||||
@ -39,8 +35,9 @@ var (
|
||||
)
|
||||
|
||||
func main() {
|
||||
flag.Var(&properties, "prop", "add property to generated report; properties should be specified as `key=value`")
|
||||
flag.Var(&properties, "p", "add `key=value` property to generated report; repeat this flag to add multiple properties.")
|
||||
flag.Parse()
|
||||
|
||||
if *iocopy && *output == "" {
|
||||
exitf("you must specify an output file with -out when using -iocopy")
|
||||
}
|
||||
@ -51,7 +48,7 @@ func main() {
|
||||
}
|
||||
|
||||
if *goVersionFlag != "" {
|
||||
fmt.Fprintf(os.Stderr, "the -go-version flag is deprecated and will be removed in the future.\n")
|
||||
fmt.Fprintf(os.Stderr, "the -go-version flag is deprecated and will be removed in the future, use the -p flag instead.\n")
|
||||
properties["go.version"] = *goVersionFlag
|
||||
}
|
||||
|
||||
@ -62,7 +59,6 @@ func main() {
|
||||
exitf("")
|
||||
}
|
||||
|
||||
// Read input
|
||||
var in io.Reader = os.Stdin
|
||||
if *input != "" {
|
||||
f, err := os.Open(*input)
|
||||
@ -73,44 +69,6 @@ func main() {
|
||||
in = f
|
||||
}
|
||||
|
||||
if *iocopy {
|
||||
in = io.TeeReader(in, os.Stdout)
|
||||
}
|
||||
|
||||
var parser Parser
|
||||
switch *inputParser {
|
||||
case "gotest":
|
||||
parser = gotest.NewParser(gotest.PackageName(*packageName))
|
||||
case "gojson":
|
||||
parser = gotest.NewJSONParser(gotest.PackageName(*packageName))
|
||||
default:
|
||||
fmt.Fprintf(os.Stderr, "invalid parser: %s\n", *inputParser)
|
||||
flag.Usage()
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
report, err := parser.Parse(in)
|
||||
if err != nil {
|
||||
exitf("error parsing input: %s\n", err)
|
||||
}
|
||||
|
||||
if *printEvents {
|
||||
enc := json.NewEncoder(os.Stderr)
|
||||
for _, event := range parser.Events() {
|
||||
if err := enc.Encode(event); err != nil {
|
||||
exitf("error printing events: %v\n", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
for i := range report.Packages {
|
||||
for k, v := range properties {
|
||||
report.Packages[i].SetProperty(k, v)
|
||||
}
|
||||
}
|
||||
|
||||
hostname, _ := os.Hostname() // ignore error
|
||||
testsuites := junit.CreateFromReport(report, hostname)
|
||||
|
||||
var out io.Writer = os.Stdout
|
||||
if *output != "" {
|
||||
f, err := os.Create(*output)
|
||||
@ -121,8 +79,23 @@ func main() {
|
||||
out = f
|
||||
}
|
||||
|
||||
if err := writeXML(out, testsuites, *noXMLHeader); err != nil {
|
||||
exitf("error writing XML: %v", err)
|
||||
if *iocopy {
|
||||
in = io.TeeReader(in, os.Stdout)
|
||||
}
|
||||
|
||||
hostname, _ := os.Hostname() // ignore error
|
||||
|
||||
config := gojunitreport.Config{
|
||||
Parser: *parser,
|
||||
Hostname: hostname,
|
||||
PackageName: *packageName,
|
||||
SkipXMLHeader: *noXMLHeader,
|
||||
Properties: properties,
|
||||
PrintEvents: *printEvents,
|
||||
}
|
||||
report, err := config.Run(in, out)
|
||||
if err != nil {
|
||||
exitf("error: %v\n", err)
|
||||
}
|
||||
|
||||
if *setExitCode && !report.IsSuccessful() {
|
||||
@ -130,25 +103,6 @@ func main() {
|
||||
}
|
||||
}
|
||||
|
||||
func writeXML(w io.Writer, testsuites junit.Testsuites, skipHeader bool) error {
|
||||
if !skipHeader {
|
||||
_, err := fmt.Fprintf(w, xml.Header)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
enc := xml.NewEncoder(w)
|
||||
enc.Indent("", "\t")
|
||||
if err := enc.Encode(testsuites); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := enc.Flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
_, err := fmt.Fprintf(w, "\n")
|
||||
return err
|
||||
}
|
||||
|
||||
func exitf(msg string, args ...interface{}) {
|
||||
if msg != "" {
|
||||
fmt.Fprintf(os.Stderr, msg+"\n", args...)
|
||||
@ -178,8 +132,3 @@ func (f *keyValueFlag) Set(value string) error {
|
||||
(*f)[k] = v
|
||||
return nil
|
||||
}
|
||||
|
||||
type Parser interface {
|
||||
Parse(r io.Reader) (gtr.Report, error)
|
||||
Events() []gotest.Event
|
||||
}
|
@ -1,9 +1,8 @@
|
||||
//go:generate go run generate-golden.go -w
|
||||
//go:generate go run generate-golden-reports.go -w
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
@ -12,22 +11,15 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/jstemmer/go-junit-report/v2/gtr"
|
||||
"github.com/jstemmer/go-junit-report/v2/junit"
|
||||
"github.com/jstemmer/go-junit-report/v2/parser/gotest"
|
||||
"github.com/jstemmer/go-junit-report/v2/internal/gojunitreport"
|
||||
)
|
||||
|
||||
var verbose bool
|
||||
|
||||
type Settings struct {
|
||||
skipXMLHeader bool
|
||||
packageName string
|
||||
}
|
||||
|
||||
var fileSettings = map[string]Settings{
|
||||
"005-no_xml_header.txt": {skipXMLHeader: true},
|
||||
"006-mixed.txt": {skipXMLHeader: true},
|
||||
"007-compiled_test.txt": {packageName: "test/package"},
|
||||
var configs = map[string]gojunitreport.Config{
|
||||
"005-no_xml_header.txt": {SkipXMLHeader: true},
|
||||
"006-mixed.txt": {SkipXMLHeader: true},
|
||||
"007-compiled_test.txt": {PackageName: "test/package"},
|
||||
}
|
||||
|
||||
func main() {
|
||||
@ -102,53 +94,18 @@ func createReportFromInput(inputFile, outputFile string, write bool) error {
|
||||
out = f
|
||||
}
|
||||
|
||||
settings := fileSettings[inputFile]
|
||||
|
||||
options := []gotest.Option{
|
||||
gotest.PackageName(settings.packageName),
|
||||
gotest.TimestampFunc(func() time.Time {
|
||||
return time.Date(2022, 1, 1, 0, 0, 0, 0, time.UTC)
|
||||
}),
|
||||
}
|
||||
|
||||
var parser Parser
|
||||
config := configs[inputFile]
|
||||
config.Parser = "gotest"
|
||||
if strings.HasSuffix(inputFile, ".gojson.txt") {
|
||||
parser = gotest.NewJSONParser(options...)
|
||||
} else {
|
||||
parser = gotest.NewParser(options...)
|
||||
config.Parser = "gojson"
|
||||
}
|
||||
|
||||
report, err := parser.Parse(in)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return writeReport(report, out, settings)
|
||||
config.Hostname = "hostname"
|
||||
config.TimestampFunc = func() time.Time {
|
||||
return time.Date(2022, 1, 1, 0, 0, 0, 0, time.UTC)
|
||||
}
|
||||
config.Properties = map[string]string{"go.version": "1.0"}
|
||||
|
||||
type Parser interface {
|
||||
Parse(r io.Reader) (gtr.Report, error)
|
||||
}
|
||||
|
||||
func writeReport(report gtr.Report, out io.Writer, settings Settings) error {
|
||||
for i := range report.Packages {
|
||||
report.Packages[i].SetProperty("go.version", "1.0")
|
||||
}
|
||||
testsuites := junit.CreateFromReport(report, "hostname")
|
||||
|
||||
if !settings.skipXMLHeader {
|
||||
if _, err := fmt.Fprintf(out, xml.Header); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
enc := xml.NewEncoder(out)
|
||||
enc.Indent("", "\t")
|
||||
if err := enc.Encode(testsuites); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := enc.Flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
_, err := fmt.Fprintf(out, "\n")
|
||||
_, err = config.Run(in, out)
|
||||
return err
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user