Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 41 additions & 10 deletions pkg/test/ginkgo/junit.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,32 @@ import (
"time"

"github.com/openshift/origin/pkg/test"
"github.com/openshift/origin/pkg/test/extensions"
"github.com/openshift/origin/pkg/test/ginkgo/junitapi"

"github.com/openshift/origin/pkg/version"
)

// populateOTEMetadata adds OTE metadata attributes to a JUnit test case if available
func populateOTEMetadata(testCase *junitapi.JUnitTestCase, extensionResult *extensions.ExtensionTestResult) {
if extensionResult == nil {
return
}

// Set lifecycle attribute
testCase.Lifecycle = string(extensionResult.Lifecycle)

// Set start time attribute if available
if extensionResult.StartTime != nil {
testCase.StartTime = time.Time(*extensionResult.StartTime).UTC().Format(time.RFC3339)
}

// Set end time attribute if available
if extensionResult.EndTime != nil {
testCase.EndTime = time.Time(*extensionResult.EndTime).UTC().Format(time.RFC3339)
}
}

func generateJUnitTestSuiteResults(
name string,
duration time.Duration,
Expand All @@ -36,49 +57,59 @@ func generateJUnitTestSuiteResults(
case test.skipped:
s.NumTests++
s.NumSkipped++
s.TestCases = append(s.TestCases, &junitapi.JUnitTestCase{
testCase := &junitapi.JUnitTestCase{
Name: test.name,
SystemOut: string(test.testOutputBytes),
Duration: test.duration.Seconds(),
SkipMessage: &junitapi.SkipMessage{
Message: lastLinesUntil(string(test.testOutputBytes), 100, "skip ["),
},
})
}
populateOTEMetadata(testCase, test.extensionTestResult)
s.TestCases = append(s.TestCases, testCase)
case test.failed:
s.NumTests++
s.NumFailed++
s.TestCases = append(s.TestCases, &junitapi.JUnitTestCase{
testCase := &junitapi.JUnitTestCase{
Name: test.name,
SystemOut: string(test.testOutputBytes),
Duration: test.duration.Seconds(),
FailureOutput: &junitapi.FailureOutput{
Output: lastLinesUntil(string(test.testOutputBytes), 100, "fail ["),
},
})
}
populateOTEMetadata(testCase, test.extensionTestResult)
s.TestCases = append(s.TestCases, testCase)
case test.flake:
s.NumTests++
s.NumFailed++
s.TestCases = append(s.TestCases, &junitapi.JUnitTestCase{
failedTestCase := &junitapi.JUnitTestCase{
Name: test.name,
SystemOut: string(test.testOutputBytes),
Duration: test.duration.Seconds(),
FailureOutput: &junitapi.FailureOutput{
Output: lastLinesUntil(string(test.testOutputBytes), 100, "flake:"),
},
})
}
populateOTEMetadata(failedTestCase, test.extensionTestResult)
s.TestCases = append(s.TestCases, failedTestCase)

// also add the successful junit result:
s.NumTests++
s.TestCases = append(s.TestCases, &junitapi.JUnitTestCase{
successTestCase := &junitapi.JUnitTestCase{
Name: test.name,
Duration: test.duration.Seconds(),
})
}
populateOTEMetadata(successTestCase, test.extensionTestResult)
s.TestCases = append(s.TestCases, successTestCase)
case test.success:
s.NumTests++
s.TestCases = append(s.TestCases, &junitapi.JUnitTestCase{
testCase := &junitapi.JUnitTestCase{
Name: test.name,
Duration: test.duration.Seconds(),
})
}
populateOTEMetadata(testCase, test.extensionTestResult)
s.TestCases = append(s.TestCases, testCase)
}
}
for _, result := range syntheticTestResults {
Expand Down
170 changes: 169 additions & 1 deletion pkg/test/ginkgo/junit_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
package ginkgo

import "testing"
import (
"encoding/xml"
"testing"
"time"

"github.com/openshift-eng/openshift-tests-extension/pkg/dbtime"
"github.com/openshift-eng/openshift-tests-extension/pkg/extension/extensiontests"
"github.com/openshift/origin/pkg/test/extensions"
"github.com/openshift/origin/pkg/test/ginkgo/junitapi"
)

func Test_lastLines(t *testing.T) {
tests := []struct {
Expand Down Expand Up @@ -33,3 +42,162 @@ func Test_lastLines(t *testing.T) {
})
}
}

func Test_populateOTEMetadata(t *testing.T) {
startTime := time.Date(2023, 12, 25, 10, 0, 0, 0, time.UTC)
endTime := time.Date(2023, 12, 25, 10, 5, 0, 0, time.UTC)

tests := []struct {
name string
extensionResult *extensions.ExtensionTestResult
expectedLifecycle string
expectedStartTime string
expectedEndTime string
}{
{
name: "nil extension result",
extensionResult: nil,
expectedLifecycle: "",
expectedStartTime: "",
expectedEndTime: "",
},
{
name: "complete extension result",
extensionResult: &extensions.ExtensionTestResult{
ExtensionTestResult: &extensiontests.ExtensionTestResult{
Name: "test-case",
Lifecycle: extensiontests.LifecycleBlocking,
StartTime: dbtime.Ptr(startTime),
EndTime: dbtime.Ptr(endTime),
},
},
expectedLifecycle: "blocking",
expectedStartTime: "2023-12-25T10:00:00Z",
expectedEndTime: "2023-12-25T10:05:00Z",
},
{
name: "informing lifecycle",
extensionResult: &extensions.ExtensionTestResult{
ExtensionTestResult: &extensiontests.ExtensionTestResult{
Name: "test-case",
Lifecycle: extensiontests.LifecycleInforming,
StartTime: dbtime.Ptr(startTime),
EndTime: dbtime.Ptr(endTime),
},
},
expectedLifecycle: "informing",
expectedStartTime: "2023-12-25T10:00:00Z",
expectedEndTime: "2023-12-25T10:05:00Z",
},
{
name: "missing time fields",
extensionResult: &extensions.ExtensionTestResult{
ExtensionTestResult: &extensiontests.ExtensionTestResult{
Name: "test-case",
Lifecycle: extensiontests.LifecycleBlocking,
StartTime: nil,
EndTime: nil,
},
},
expectedLifecycle: "blocking",
expectedStartTime: "",
expectedEndTime: "",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
testCase := &junitapi.JUnitTestCase{
Name: "test-case",
Duration: 300.0, // 5 minutes
}

populateOTEMetadata(testCase, tt.extensionResult)

if testCase.Lifecycle != tt.expectedLifecycle {
t.Errorf("Lifecycle = %q, want %q", testCase.Lifecycle, tt.expectedLifecycle)
}
if testCase.StartTime != tt.expectedStartTime {
t.Errorf("StartTime = %q, want %q", testCase.StartTime, tt.expectedStartTime)
}
if testCase.EndTime != tt.expectedEndTime {
t.Errorf("EndTime = %q, want %q", testCase.EndTime, tt.expectedEndTime)
}
})
}
}

func Test_junitXMLWithOTEAttributes(t *testing.T) {
startTime := time.Date(2023, 12, 25, 10, 0, 0, 0, time.UTC)
endTime := time.Date(2023, 12, 25, 10, 5, 0, 0, time.UTC)

// Create a JUnit test case and populate it with OTE metadata
extensionResult := &extensions.ExtensionTestResult{
ExtensionTestResult: &extensiontests.ExtensionTestResult{
Name: "example-test",
Lifecycle: extensiontests.LifecycleBlocking,
StartTime: dbtime.Ptr(startTime),
EndTime: dbtime.Ptr(endTime),
},
}

junitTestCase := &junitapi.JUnitTestCase{
Name: "example-test",
Duration: 300.0, // 5 minutes
}

// Populate the OTE metadata
populateOTEMetadata(junitTestCase, extensionResult)

// Create a test suite containing our test case
suite := &junitapi.JUnitTestSuite{
Name: "test-suite",
NumTests: 1,
Duration: 300.0,
TestCases: []*junitapi.JUnitTestCase{junitTestCase},
}

// Verify OTE metadata is present
if junitTestCase.Lifecycle != "blocking" {
t.Errorf("Lifecycle = %q, want %q", junitTestCase.Lifecycle, "blocking")
}
if junitTestCase.StartTime != "2023-12-25T10:00:00Z" {
t.Errorf("StartTime = %q, want %q", junitTestCase.StartTime, "2023-12-25T10:00:00Z")
}
if junitTestCase.EndTime != "2023-12-25T10:05:00Z" {
t.Errorf("EndTime = %q, want %q", junitTestCase.EndTime, "2023-12-25T10:05:00Z")
}

// Verify XML marshaling includes the new attributes
xmlData, err := xml.MarshalIndent(suite, "", " ")
if err != nil {
t.Fatalf("Failed to marshal XML: %v", err)
}

xmlString := string(xmlData)

// Check that our custom attributes are in the XML
expectedAttributes := []string{
`lifecycle="blocking"`,
`start-time="2023-12-25T10:00:00Z"`,
`end-time="2023-12-25T10:05:00Z"`,
}

for _, attr := range expectedAttributes {
if !contains(xmlString, attr) {
t.Errorf("XML does not contain expected attribute: %s\nXML output:\n%s", attr, xmlString)
}
}
}

// Helper function to check if a string contains a substring
func contains(s, substr string) bool {
return len(s) >= len(substr) && func() bool {
for i := 0; i <= len(s)-len(substr); i++ {
if s[i:i+len(substr)] == substr {
return true
}
}
return false
}()
}
5 changes: 5 additions & 0 deletions pkg/test/ginkgo/junitapi/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,11 @@ type JUnitTestCase struct {
// Duration is the time taken in seconds to run the test
Duration float64 `xml:"time,attr"`

// OTE metadata attributes
StartTime string `xml:"start-time,attr,omitempty"`
EndTime string `xml:"end-time,attr,omitempty"`
Lifecycle string `xml:"lifecycle,attr,omitempty"`

// SkipMessage holds the reason why the test was skipped
SkipMessage *SkipMessage `xml:"skipped"`

Expand Down