...

Source file src/kubevirt.io/client-go/reporter/reporter.go

Documentation: kubevirt.io/client-go/reporter

     1  /*
     2   * This file is part of the KubeVirt project
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *     http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   *
    16   * Copyright 2022 Red Hat, Inc.
    17   *
    18   */
    19  
    20  package reporter
    21  
    22  import (
    23  	"encoding/xml"
    24  	"fmt"
    25  	"math"
    26  	"os"
    27  	"path/filepath"
    28  	"strings"
    29  
    30  	"github.com/onsi/ginkgo/v2/config"
    31  	"github.com/onsi/ginkgo/v2/types"
    32  )
    33  
    34  type JUnitTestCase struct {
    35  	Name           string               `xml:"name,attr"`
    36  	ClassName      string               `xml:"classname,attr"`
    37  	PassedMessage  *JUnitPassedMessage  `xml:"passed,omitempty"`
    38  	FailureMessage *JUnitFailureMessage `xml:"failure,omitempty"`
    39  	Skipped        *JUnitSkipped        `xml:"skipped,omitempty"`
    40  	Time           float64              `xml:"time,attr"`
    41  	SystemOut      string               `xml:"system-out,omitempty"`
    42  }
    43  
    44  type JUnitPassedMessage struct {
    45  	Message string `xml:",chardata"`
    46  }
    47  
    48  type JUnitFailureMessage struct {
    49  	Type    string `xml:"type,attr"`
    50  	Message string `xml:",chardata"`
    51  }
    52  
    53  type JUnitSkipped struct {
    54  	XMLName xml.Name `xml:"skipped"`
    55  }
    56  
    57  type JUnitTestSuite struct {
    58  	XMLName   xml.Name        `xml:"testsuite"`
    59  	TestCases []JUnitTestCase `xml:"testcase"`
    60  	Name      string          `xml:"name,attr"`
    61  	Tests     int             `xml:"tests,attr"`
    62  	Failures  int             `xml:"failures,attr"`
    63  	Errors    int             `xml:"errors,attr"`
    64  	Time      float64         `xml:"time,attr"`
    65  }
    66  type V1JUnitReporter struct {
    67  	suite          JUnitTestSuite
    68  	filename       string
    69  	testSuiteName  string
    70  	ReporterConfig config.DefaultReporterConfigType
    71  }
    72  
    73  // NewV1JUnitReporter creates a new V1 JUnit XML reporter.  The XML will be stored in the passed in filename.
    74  func NewV1JUnitReporter(filename string) *V1JUnitReporter {
    75  	return &V1JUnitReporter{
    76  		filename: filename,
    77  	}
    78  }
    79  
    80  func (reporter *V1JUnitReporter) AfterSuiteDidRun(setupSummary *types.SetupSummary) {
    81  	reporter.handleSetupSummary("AfterSuite", setupSummary)
    82  }
    83  
    84  func (reporter *V1JUnitReporter) BeforeSuiteDidRun(setupSummary *types.SetupSummary) {
    85  	reporter.handleSetupSummary("BeforeSuite", setupSummary)
    86  }
    87  
    88  func (reporter *V1JUnitReporter) handleSetupSummary(name string, setupSummary *types.SetupSummary) {
    89  	if setupSummary.State != types.SpecStatePassed {
    90  		testCase := JUnitTestCase{
    91  			Name:      name,
    92  			ClassName: reporter.testSuiteName,
    93  		}
    94  
    95  		testCase.FailureMessage = &JUnitFailureMessage{
    96  			Type:    reporter.failureTypeForState(setupSummary.State),
    97  			Message: failureMessage(setupSummary.Failure),
    98  		}
    99  		testCase.SystemOut = setupSummary.CapturedOutput
   100  		testCase.Time = setupSummary.RunTime.Seconds()
   101  		reporter.suite.TestCases = append(reporter.suite.TestCases, testCase)
   102  	}
   103  }
   104  
   105  func failureMessage(failure types.SpecFailure) string {
   106  	return fmt.Sprintf("%s\n%s\n%s", failure.ComponentCodeLocation.String(), failure.Message, failure.Location.String())
   107  }
   108  
   109  func (reporter *V1JUnitReporter) SpecDidComplete(specSummary *types.SpecSummary) {
   110  	testCase := JUnitTestCase{
   111  		Name:      strings.Join(specSummary.ComponentTexts, " "),
   112  		ClassName: reporter.testSuiteName,
   113  	}
   114  	if reporter.ReporterConfig.ReportPassed && specSummary.State == types.SpecStatePassed {
   115  		testCase.PassedMessage = &JUnitPassedMessage{
   116  			Message: specSummary.CapturedOutput,
   117  		}
   118  	}
   119  	if specSummary.State == types.SpecStateFailed || specSummary.State == types.SpecStateInterrupted || specSummary.State == types.SpecStatePanicked {
   120  		testCase.FailureMessage = &JUnitFailureMessage{
   121  			Type:    reporter.failureTypeForState(specSummary.State),
   122  			Message: failureMessage(specSummary.Failure),
   123  		}
   124  		if specSummary.State == types.SpecStatePanicked {
   125  			testCase.FailureMessage.Message += fmt.Sprintf("\n\nPanic: %s\n\nFull stack:\n%s",
   126  				specSummary.Failure.ForwardedPanic,
   127  				specSummary.Failure.Location.FullStackTrace)
   128  		}
   129  		testCase.SystemOut = specSummary.CapturedOutput
   130  	}
   131  	if specSummary.State == types.SpecStateSkipped || specSummary.State == types.SpecStatePending {
   132  		testCase.Skipped = &JUnitSkipped{}
   133  	}
   134  	testCase.Time = specSummary.RunTime.Seconds()
   135  	reporter.suite.TestCases = append(reporter.suite.TestCases, testCase)
   136  }
   137  
   138  func (reporter *V1JUnitReporter) SpecWillRun(specSummary *types.SpecSummary) {
   139  }
   140  
   141  func (reporter *V1JUnitReporter) SuiteDidEnd(summary *types.SuiteSummary) {
   142  	reporter.suite.Tests = summary.NumberOfSpecsThatWillBeRun
   143  	reporter.suite.Time = math.Trunc(summary.RunTime.Seconds()*1000) / 1000
   144  	reporter.suite.Failures = summary.NumberOfFailedSpecs
   145  	reporter.suite.Errors = 0
   146  	if reporter.ReporterConfig.ReportFile != "" {
   147  		reporter.filename = reporter.ReporterConfig.ReportFile
   148  		fmt.Printf("\nJUnit path was configured: %s\n", reporter.filename)
   149  	}
   150  	filePath, _ := filepath.Abs(reporter.filename)
   151  	dirPath := filepath.Dir(filePath)
   152  	err := os.MkdirAll(dirPath, os.ModePerm)
   153  	if err != nil {
   154  		fmt.Printf("\nFailed to create JUnit directory: %s\n\t%s", filePath, err.Error())
   155  	}
   156  	file, err := os.Create(filePath)
   157  	if err != nil {
   158  		fmt.Fprintf(os.Stderr, "Failed to create JUnit report file: %s\n\t%s", filePath, err.Error())
   159  	}
   160  	defer file.Close()
   161  	file.WriteString(xml.Header)
   162  	encoder := xml.NewEncoder(file)
   163  	encoder.Indent("  ", "    ")
   164  	err = encoder.Encode(reporter.suite)
   165  	if err == nil {
   166  		fmt.Fprintf(os.Stdout, "\nJUnit report was created: %s\n", filePath)
   167  	} else {
   168  		fmt.Fprintf(os.Stderr, "\nFailed to generate JUnit report data:\n\t%s", err.Error())
   169  	}
   170  }
   171  
   172  func (reporter *V1JUnitReporter) SuiteWillBegin(ginkgoConfig config.GinkgoConfigType, summary *types.SuiteSummary) {
   173  	reporter.suite = JUnitTestSuite{
   174  		Name:      summary.SuiteDescription,
   175  		TestCases: []JUnitTestCase{},
   176  	}
   177  	reporter.testSuiteName = summary.SuiteDescription
   178  	reporter.ReporterConfig = config.DefaultReporterConfigType{}
   179  }
   180  
   181  func (reporter *V1JUnitReporter) failureTypeForState(state types.SpecState) string {
   182  	switch state {
   183  	case types.SpecStateFailed:
   184  		return "Failure"
   185  	case types.SpecStateInterrupted:
   186  		return "Interrupted"
   187  	case types.SpecStatePanicked:
   188  		return "Panic"
   189  	default:
   190  		return ""
   191  	}
   192  }
   193  

View as plain text