Merge pull request #129035 from pohly/prune-junit-xml-failure-fix

prune-junit-xml: join stdout when merging tests
This commit is contained in:
Kubernetes Prow Robot 2024-12-12 05:29:06 +00:00 committed by GitHub
commit 3e4e1364db
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 55 additions and 4 deletions

View File

@ -135,6 +135,8 @@ func pruneTESTS(suites *junitxml.JUnitTestSuites) {
updatedTestcase.Classname = match[1]
updatedTestcase.Name = match[2]
updatedTestcase.Time = suite.Time
updatedSystemOut := ""
updatedSystemErr := ""
for _, testcase := range suite.TestCases {
// The top level testcase element in a JUnit xml file does not have the / character.
if testcase.Failure != nil {
@ -142,10 +144,14 @@ func pruneTESTS(suites *junitxml.JUnitTestSuites) {
updatedTestcaseFailure.Message = joinTexts(updatedTestcaseFailure.Message, testcase.Failure.Message)
updatedTestcaseFailure.Contents = joinTexts(updatedTestcaseFailure.Contents, testcase.Failure.Contents)
updatedTestcaseFailure.Type = joinTexts(updatedTestcaseFailure.Type, testcase.Failure.Type)
updatedSystemOut = joinTexts(updatedSystemOut, testcase.SystemOut)
updatedSystemErr = joinTexts(updatedSystemErr, testcase.SystemErr)
}
}
if failflag {
updatedTestcase.Failure = &updatedTestcaseFailure
updatedTestcase.SystemOut = updatedSystemOut
updatedTestcase.SystemErr = updatedSystemErr
}
suite.TestCases = append(updatedTestcases, updatedTestcase)
updatedTestsuites = append(updatedTestsuites, suite)
@ -153,8 +159,15 @@ func pruneTESTS(suites *junitxml.JUnitTestSuites) {
suites.Suites = updatedTestsuites
}
// joinTexts returns "<a>; <b>" if both are non-empty,
// joinTexts returns "<a><empty line><b>" if both are non-empty,
// otherwise just the non-empty string, if there is one.
//
// If <b> is contained completely in <a>, <a> gets returned because repeating
// exactly the same string again doesn't add any information. Typically
// this occurs when joining the failure message because that is the fixed
// string "Failed" for all tests, regardless of what the test logged.
// The test log output is typically different because it cointains "=== RUN
// <test name>" and thus doesn't get dropped.
func joinTexts(a, b string) string {
if a == "" {
return b
@ -162,7 +175,14 @@ func joinTexts(a, b string) string {
if b == "" {
return a
}
return a + "; " + b
if strings.Contains(a, b) {
return a
}
sep := "\n"
if !strings.HasSuffix(a, "\n") {
sep = "\n\n"
}
return a + sep + b
}
func fetchXML(xmlReader io.Reader) (*junitxml.JUnitTestSuites, error) {

View File

@ -100,7 +100,10 @@ func TestPruneTESTS(t *testing.T) {
<properties>
<property name="go.version" value="go1.18 linux/amd64"></property>
</properties>
<testcase classname="k8s.io/kubernetes/test/integration/apimachinery2" name="TestWatchRestartsIfTimeoutNotReached/group/InformerWatcher_survives_closed_watches" time="30.050000"></testcase>
<testcase classname="k8s.io/kubernetes/test/integration/apimachinery2" name="TestWatchRestartsIfTimeoutNotReached/group/InformerWatcher_survives_closed_watches" time="30.050000">
<system-out>out A</system-out>
<system-err>err B</system-err>
</testcase>
<testcase classname="k8s.io/kubernetes/test/integration/apimachinery2" name="TestSchedulerInformers" time="-0.000000">
<failure message="FailedA" type="">FailureContentA</failure>
</testcase>
@ -108,6 +111,24 @@ func TestPruneTESTS(t *testing.T) {
<failure message="FailedB" type="">FailureContentB</failure>
</testcase>
</testsuite>
<testsuite tests="3" failures="3" time="40.050000" name="k8s.io/kubernetes/test/integration/apimachinery3" timestamp="">
<properties>
<property name="go.version" value="go1.18 linux/amd64"></property>
</properties>
<testcase classname="k8s.io/kubernetes/test/integration/apimachinery3" name="TestWatchRestartsIfTimeoutNotReached/group/InformerWatcher_survives_closed_watches" time="40.050000">
<failure message="Failed" type="">RUNNING TestWatchRestartsIfTimeoutNotReached/group/InformerWatcher_survives_closed_watchesA&#xA;expected foo, got bar</failure>
<system-out>out A</system-out>
<system-err>err A</system-err>
</testcase>
<testcase classname="k8s.io/kubernetes/test/integration/apimachinery3" name="TestWatchRestartsIfTimeoutNotReached/group" time="40.050000">
<failure message="Failed" type="">sub-test failed</failure>
<system-out>out B</system-out>
<system-err>err B</system-err>
</testcase>
<testcase classname="k8s.io/kubernetes/test/integration/apimachinery3" name="TestWatchRestartsIfTimeoutNotReached" time="40.050000">
<failure message="Failed" type="">sub-test failed</failure>
</testcase>
</testsuite>
</testsuites>`
outputXML := `<?xml version="1.0" encoding="UTF-8"?>
@ -131,7 +152,17 @@ func TestPruneTESTS(t *testing.T) {
<property name="go.version" value="go1.18 linux/amd64"></property>
</properties>
<testcase classname="k8s.io/kubernetes/test/integration" name="apimachinery2" time="30.050000">
<failure message="FailedA; FailedB" type="">FailureContentA; FailureContentB</failure>
<failure message="FailedA&#xA;&#xA;FailedB" type="">FailureContentA&#xA;&#xA;FailureContentB</failure>
</testcase>
</testsuite>
<testsuite tests="3" failures="3" time="40.050000" name="k8s.io/kubernetes/test/integration/apimachinery3" timestamp="">
<properties>
<property name="go.version" value="go1.18 linux/amd64"></property>
</properties>
<testcase classname="k8s.io/kubernetes/test/integration" name="apimachinery3" time="40.050000">
<failure message="Failed" type="">RUNNING TestWatchRestartsIfTimeoutNotReached/group/InformerWatcher_survives_closed_watchesA&#xA;expected foo, got bar&#xA;&#xA;sub-test failed</failure>
<system-out>out A&#xA;&#xA;out B</system-out>
<system-err>err A&#xA;&#xA;err B</system-err>
</testcase>
</testsuite>
</testsuites>`