Skip to content

Commit b6d4e79

Browse files
committed
containerized-testing-tool: Save the test results in Junit format
- Add support for saving the test results in Junit format - Fix documentation - Add .gitignore Signed-off-by: Ankita Pareek <ankitapareek@microsoft.com>
1 parent 35e73e4 commit b6d4e79

File tree

5 files changed

+146
-24
lines changed

5 files changed

+146
-24
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
kata-containerized-test-tool
2+
results/
3+
*.tar
4+
*.tar.gz

tests/kata-containerized-test-tool/Dockerfile

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,5 @@ RUN tdnf update -y && tdnf install -y \
1010
iproute \
1111
&& tdnf clean all
1212

13-
# Default environment variables
14-
ENV ENABLED_TESTS=""
15-
1613
ENTRYPOINT ["/app/kata-containerized-test-tool"]
1714
CMD ["--output", "/results"]

tests/kata-containerized-test-tool/cmd/katatest/main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ func getTestsToRun(availableTests map[string]core.Test) []core.Test {
7373
return testsToRun
7474
}
7575

76-
// Retrieve the expected values for a specific test from environment variables
76+
// getExpectedValues retrieves the expected values for a specific test from environment variables
7777
func getExpectedValues(testName string) map[string]interface{} {
7878
expectedValues := map[string]interface{}{}
7979

tests/kata-containerized-test-tool/internal/core/framework.go

Lines changed: 96 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ package core
22

33
import (
44
"context"
5-
"encoding/json"
5+
"encoding/xml"
66
"fmt"
77
"os"
88
"strings"
@@ -15,25 +15,25 @@ type Framework struct {
1515
outputDir string
1616
}
1717

18-
// Create a new testing framework
18+
// NewFrameworkCreate a new testing framework
1919
func NewFramework(outputDir string) *Framework {
2020
return &Framework{
2121
tests: make(map[string]Test),
2222
outputDir: outputDir,
2323
}
2424
}
2525

26-
// Add a test to the framework
26+
// RegisterTest adds a test to the framework
2727
func (f *Framework) RegisterTest(test Test) {
2828
f.tests[test.Name()] = test
2929
}
3030

31-
// Return all registered tests
31+
// GetAvailableTests returns all registered tests
3232
func (f *Framework) GetAvailableTests() map[string]Test {
3333
return f.tests
3434
}
3535

36-
// Check if the provided test configurations are valid
36+
// ValidateConfiguration checks if the provided test configurations are valid
3737
func (f *Framework) ValidateConfiguration(testsToRun []Test,
3838
getExpectedValuesFunc func(string) map[string]interface{}) []string {
3939

@@ -84,12 +84,12 @@ func (f *Framework) ValidateConfiguration(testsToRun []Test,
8484
return warnings
8585
}
8686

87-
// Execute a single test with the given expected values
87+
// RunTest executes a single test with the given expected values
8888
func (f *Framework) RunTest(ctx context.Context, test Test, expectedValues map[string]interface{}) TestResult {
8989
return test.Run(ctx, expectedValues)
9090
}
9191

92-
// Execute the given tests with their expected values
92+
// RunTests executes the given tests with their expected values
9393
func (f *Framework) RunTests(ctx context.Context, testsToRun []Test,
9494
getExpectedValuesFunc func(string) map[string]interface{}) ([]TestResult, error) {
9595

@@ -112,21 +112,104 @@ func (f *Framework) RunTests(ctx context.Context, testsToRun []Test,
112112
return results, nil
113113
}
114114

115-
// Write test results to a JSON file
115+
// saveResults saves test results in JUnit XML format
116116
func (f *Framework) saveResults(results []TestResult) error {
117-
filename := fmt.Sprintf("%s/results_%s.json",
117+
// Create the root element
118+
testSuites := JUnitTestSuites{}
119+
120+
// Create a test suite
121+
testSuite := JUnitTestSuite{
122+
Name: "KataContainerTests",
123+
Tests: len(results),
124+
Failures: 0,
125+
Time: 0,
126+
}
127+
128+
// Process each test result
129+
for _, result := range results {
130+
// Calculate test duration
131+
duration := result.EndTime.Sub(result.StartTime)
132+
133+
// Create test case
134+
testCase := JUnitTestCase{
135+
Name: result.Name,
136+
ClassName: "KataContainerTest",
137+
Time: duration.Seconds(),
138+
}
139+
140+
// Store metrics and expected values as properties
141+
var properties []JUnitProperty
142+
143+
// Add metrics properties
144+
for key, value := range result.Metrics {
145+
properties = append(properties, JUnitProperty{
146+
Name: fmt.Sprintf("metric.%s", key),
147+
Value: fmt.Sprintf("%v", value),
148+
})
149+
}
150+
151+
// Add expected value properties (if any)
152+
for key, value := range result.ExpectedValues {
153+
properties = append(properties, JUnitProperty{
154+
Name: fmt.Sprintf("expected.%s", key),
155+
Value: fmt.Sprintf("%v", value),
156+
})
157+
}
158+
159+
// Only add properties if we have any
160+
if len(properties) > 0 {
161+
testCase.Properties = JUnitProperties{
162+
Properties: properties,
163+
}
164+
}
165+
166+
// Add failure information if test failed
167+
if !result.Success {
168+
testSuite.Failures++
169+
testCase.Failure = &JUnitFailure{
170+
Message: result.Error,
171+
Type: "AssertionFailure",
172+
Value: fmt.Sprintf("Test failed: %s", result.Error),
173+
}
174+
}
175+
176+
// Add test case to suite
177+
testSuite.TestCases = append(testSuite.TestCases, testCase)
178+
179+
// Add to total time
180+
testSuite.Time += duration.Seconds()
181+
}
182+
183+
// Add suite to root element
184+
testSuites.Suites = append(testSuites.Suites, testSuite)
185+
186+
// Create the XML file
187+
filename := fmt.Sprintf("%s/results_%s.xml",
118188
f.outputDir,
119189
time.Now().Format("20060102_150405"))
120190

121-
data, err := json.MarshalIndent(results, "", " ")
191+
file, err := os.Create(filename)
122192
if err != nil {
123-
return err
193+
return fmt.Errorf("failed to create JUnit XML file: %v", err)
194+
}
195+
defer file.Close()
196+
197+
// Write XML header
198+
file.WriteString(xml.Header)
199+
200+
// Create encoder with indentation for readability
201+
encoder := xml.NewEncoder(file)
202+
encoder.Indent("", " ")
203+
204+
// Encode and write
205+
if err := encoder.Encode(testSuites); err != nil {
206+
return fmt.Errorf("failed to encode JUnit XML: %v", err)
124207
}
125208

126-
return os.WriteFile(filename, data, 0644)
209+
return nil
127210
}
128211

129-
// Helper function to get a human-readable result string
212+
// getResultString is a helper function to get a human-readable result string
130213
func getResultString(result TestResult) string {
131214
if result.Success {
132215
return "PASSED"

tests/kata-containerized-test-tool/internal/core/types.go

Lines changed: 45 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,58 @@ package core
22

33
import (
44
"context"
5+
"encoding/xml"
56
"time"
67
)
78

89
type TestResult struct {
9-
Name string `json:"name"`
10-
StartTime time.Time `json:"start_time"`
11-
EndTime time.Time `json:"end_time"`
12-
Success bool `json:"success"`
13-
Metrics map[string]interface{} `json:"metrics"`
14-
ExpectedValues map[string]interface{} `json:"expected_values"`
15-
Error string `json:"error,omitempty"`
10+
Name string
11+
StartTime time.Time
12+
EndTime time.Time
13+
Success bool
14+
Metrics map[string]interface{}
15+
ExpectedValues map[string]interface{}
16+
Error string
1617
}
1718

1819
type Test interface {
1920
Name() string
2021
Run(ctx context.Context, expectedValues map[string]interface{}) TestResult
2122
}
23+
24+
type JUnitTestSuites struct {
25+
XMLName xml.Name `xml:"testsuites"`
26+
Suites []JUnitTestSuite `xml:"testsuite"`
27+
}
28+
29+
type JUnitTestSuite struct {
30+
XMLName xml.Name `xml:"testsuite"`
31+
Tests int `xml:"tests,attr"`
32+
Failures int `xml:"failures,attr"`
33+
Time float64 `xml:"time,attr"`
34+
Name string `xml:"name,attr"`
35+
TestCases []JUnitTestCase `xml:"testcase"`
36+
}
37+
38+
type JUnitTestCase struct {
39+
Name string `xml:"name,attr"`
40+
ClassName string `xml:"classname,attr"`
41+
Time float64 `xml:"time,attr"`
42+
Failure *JUnitFailure `xml:"failure,omitempty"`
43+
Properties JUnitProperties `xml:"properties,omitempty"`
44+
}
45+
46+
type JUnitProperties struct {
47+
Properties []JUnitProperty `xml:"property"`
48+
}
49+
50+
type JUnitProperty struct {
51+
Name string `xml:"name,attr"`
52+
Value string `xml:"value,attr"`
53+
}
54+
55+
type JUnitFailure struct {
56+
Message string `xml:"message,attr"`
57+
Type string `xml:"type,attr"`
58+
Value string `xml:",chardata"`
59+
}

0 commit comments

Comments
 (0)