go/e2e_test_utils/junitxml/junitxml.go (95 lines of code) (raw):
// Copyright 2019 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package junitxml provides helpers around creating junit XML data.
package junitxml
import (
"bytes"
"encoding/xml"
"fmt"
"regexp"
"time"
"github.com/google/uuid"
)
// NewTestSuite creates a new TestSuite.
func NewTestSuite(name string) *TestSuite {
return &TestSuite{
Name: name,
start: time.Now(),
}
}
// TestSuite is a junitxml TestSuite.
type TestSuite struct {
XMLName xml.Name `xml:"testsuite"`
Name string `xml:"name,attr"`
Tests int `xml:"tests,attr"`
Failures int `xml:"failures,attr"`
Errors int `xml:"errors,attr"`
Disabled int `xml:"disabled,attr"`
Skipped int `xml:"skipped,attr"`
Time float64 `xml:"time,attr"`
TestCase []*TestCase `xml:"testcase"`
start time.Time
}
// Finish marks a TestSuite as finished and sends it in the provided channel.
func (s *TestSuite) Finish(tests chan *TestSuite) {
s.Time = time.Since(s.start).Seconds()
s.Tests = len(s.TestCase)
for _, tc := range s.TestCase {
if tc.Failure != nil {
s.Failures++
}
if tc.Skipped != nil {
s.Skipped++
}
}
tests <- s
}
// NewTestCase creates a new TestCase.
func NewTestCase(classname, name string) *TestCase {
return &TestCase{
Classname: classname,
Name: fmt.Sprintf("[%s] %s", classname, name),
ID: uuid.New().String(),
start: time.Now(),
}
}
// TestCase is a junitxml TestCase.
type TestCase struct {
Classname string `xml:"classname,attr"`
ID string `xml:"id,attr"`
Name string `xml:"name,attr"`
Time float64 `xml:"time,attr"`
Skipped *junitSkipped `xml:"skipped,omitempty"`
Failure *junitFailure `xml:"failure,omitempty"`
SystemOut string `xml:"system-out,omitempty"`
start time.Time
buf bytes.Buffer
}
type junitSkipped struct {
Message string `xml:"message,attr"`
}
type junitFailure struct {
FailMessage string `xml:",chardata"`
FailType string `xml:"type,attr"`
}
// Logf logs to the TestCase SystemOut.
func (c *TestCase) Logf(msg string, args ...interface{}) {
c.buf.WriteString(fmt.Sprintf("%s: %s\n", time.Now().Format(time.RFC3339), fmt.Sprintf(msg, args...)))
}
// WriteFailure marks a TestCase as failed with the provided message.
func (c *TestCase) WriteFailure(msg string, args ...interface{}) {
msg = fmt.Sprintf(msg, args...)
c.Logf(msg)
c.Failure = &junitFailure{
FailMessage: msg,
FailType: "Failure",
}
}
// WriteSkipped marks a TestCase as skipped with the provided message.
func (c *TestCase) WriteSkipped(msg string, args ...interface{}) {
msg = fmt.Sprintf(msg, args...)
c.Skipped = &junitSkipped{
Message: msg,
}
}
// Finish marks a TestCase as finished and sends it in the provided channel.
func (c *TestCase) Finish(tests chan *TestCase) {
c.Time = time.Since(c.start).Seconds()
c.SystemOut = c.buf.String()
tests <- c
}
// FilterTestCase markes a TestCase as skipped if the name matches does not
// match the regex.
func (c *TestCase) FilterTestCase(regex *regexp.Regexp) bool {
if regex != nil && !regex.MatchString(c.Name) {
c.WriteSkipped("Test does not match filter: %q", regex.String())
return true
}
return false
}