or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

core-runner.mddjango-integration.mdindex.mdjenkins-compatibility.mdresult-collection.mdxml-utilities.md

jenkins-compatibility.mddocs/

0

# Jenkins Compatibility Tools

1

2

Transformation utilities for ensuring XML report compatibility with various versions of Jenkins xUnit plugins. Addresses schema validation differences and provides tools for adapting reports to specific CI/CD requirements.

3

4

## Capabilities

5

6

### XML Report Transformation

7

8

Transform XML reports to be compatible with specific versions of Jenkins xUnit plugin that have stricter schema validation requirements.

9

10

```python { .api }

11

def transform(xml_data):

12

"""

13

Transform XML report for Jenkins xUnit plugin compatibility.

14

15

Removes attributes that cause validation failures in Jenkins xUnit plugin

16

version 1.104+ which uses stricter XSD validation.

17

18

Parameters:

19

- xml_data: bytes, input XML report data

20

21

Returns:

22

- bytes: transformed XML report data

23

24

Dependencies:

25

- lxml: required for XML transformation

26

"""

27

```

28

29

## Jenkins Plugin Compatibility

30

31

### Jenkins JUnit Plugin

32

33

The standard JUnit plugin (https://plugins.jenkins.io/junit/) has relaxed validation:

34

35

- **Schema Validation**: None (at time of writing)

36

- **Compatibility**: Standard unittest-xml-reporting output works without modification

37

- **Attributes**: Accepts all attributes including file, line, timestamp

38

39

```python

40

import unittest

41

import xmlrunner

42

43

# Standard output works with Jenkins JUnit plugin

44

unittest.main(

45

testRunner=xmlrunner.XMLTestRunner(output='test-reports'),

46

failfast=False, buffer=False, catchbreak=False

47

)

48

```

49

50

### Jenkins xUnit Plugin v1.100

51

52

Older version with more lenient XSD validation:

53

54

- **Schema**: Uses relaxed junit-10.xsd

55

- **Compatibility**: Standard unittest-xml-reporting output works

56

- **Validation**: Performs XSD validation but accepts additional attributes

57

58

### Jenkins xUnit Plugin v1.104+

59

60

Newer version with strict XSD validation:

61

62

- **Schema**: Uses strict junit-10.xsd

63

- **Compatibility**: Requires transformation to remove unsupported attributes

64

- **Validation**: Strict XSD validation fails on extra attributes

65

66

## Usage Examples

67

68

### Basic Transformation for Jenkins xUnit v1.104+

69

70

```python

71

import io

72

import unittest

73

import xmlrunner

74

from xmlrunner.extra.xunit_plugin import transform

75

76

# Generate XML report in memory

77

output = io.BytesIO()

78

unittest.main(

79

testRunner=xmlrunner.XMLTestRunner(output=output),

80

failfast=False, buffer=False, catchbreak=False, exit=False

81

)

82

83

# Transform for Jenkins xUnit plugin compatibility

84

transformed_xml = transform(output.getvalue())

85

86

# Write to file for Jenkins consumption

87

with open('TEST-report.xml', 'wb') as report:

88

report.write(transformed_xml)

89

```

90

91

### CI/CD Pipeline Integration

92

93

```python

94

# ci_test_runner.py

95

import io

96

import sys

97

import unittest

98

import xmlrunner

99

from xmlrunner.extra.xunit_plugin import transform

100

101

def run_tests_for_jenkins():

102

"""Run tests and generate Jenkins-compatible XML reports."""

103

# Capture XML output

104

output = io.BytesIO()

105

106

# Run tests with XML reporting

107

runner = xmlrunner.XMLTestRunner(

108

output=output,

109

verbosity=2,

110

elapsed_times=True

111

)

112

113

# Discover and run tests

114

loader = unittest.TestLoader()

115

suite = loader.discover('tests', pattern='test_*.py')

116

result = runner.run(suite)

117

118

# Transform for Jenkins compatibility

119

xml_data = output.getvalue()

120

transformed_xml = transform(xml_data)

121

122

# Write Jenkins-compatible report

123

with open('junit-report.xml', 'wb') as report_file:

124

report_file.write(transformed_xml)

125

126

# Return exit code based on test results

127

return 0 if result.wasSuccessful() else 1

128

129

if __name__ == '__main__':

130

sys.exit(run_tests_for_jenkins())

131

```

132

133

### Batch File Transformation

134

135

```python

136

import os

137

import glob

138

from xmlrunner.extra.xunit_plugin import transform

139

140

def transform_reports_directory(input_dir, output_dir):

141

"""Transform all XML reports in a directory for Jenkins compatibility."""

142

os.makedirs(output_dir, exist_ok=True)

143

144

for xml_file in glob.glob(os.path.join(input_dir, 'TEST-*.xml')):

145

# Read original report

146

with open(xml_file, 'rb') as f:

147

xml_data = f.read()

148

149

# Transform for compatibility

150

transformed_xml = transform(xml_data)

151

152

# Write transformed report

153

filename = os.path.basename(xml_file)

154

output_path = os.path.join(output_dir, filename)

155

with open(output_path, 'wb') as f:

156

f.write(transformed_xml)

157

158

print(f"Transformed {xml_file} -> {output_path}")

159

160

# Usage

161

transform_reports_directory('raw-reports', 'jenkins-reports')

162

```

163

164

## Transformation Details

165

166

### Removed Attributes

167

168

The transformation removes the following attributes that cause validation failures:

169

170

- **testcase/@file**: Source file path

171

- **testcase/@line**: Source line number

172

- **testcase/@timestamp**: Individual test timestamp

173

174

### Preserved Elements

175

176

All other elements and attributes are preserved:

177

178

- **testsuite** attributes (name, tests, failures, errors, skipped, time, timestamp)

179

- **testcase** attributes (classname, name, time)

180

- **failure/error/skipped** elements and their attributes

181

- **system-out/system-err** content

182

- **properties** elements

183

184

### XSLT Implementation

185

186

The transformation uses XSLT (Extensible Stylesheet Language Transformations):

187

188

```xslt

189

<?xml version="1.0" encoding="UTF-8"?>

190

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">

191

<xsl:output method="xml" indent="yes" />

192

193

<!-- Remove problematic attributes -->

194

<xsl:template match="//testcase/@file" />

195

<xsl:template match="//testcase/@line" />

196

<xsl:template match="//testcase/@timestamp" />

197

198

<!-- Copy everything else -->

199

<xsl:template match="node()|@*">

200

<xsl:copy>

201

<xsl:apply-templates select="node()|@*" />

202

</xsl:copy>

203

</xsl:template>

204

</xsl:stylesheet>

205

```

206

207

## Error Handling

208

209

### Dependency Requirements

210

211

The transformation requires lxml:

212

213

```python

214

try:

215

from xmlrunner.extra.xunit_plugin import transform

216

except ImportError:

217

print("lxml is required for Jenkins compatibility transformation")

218

print("Install with: pip install lxml")

219

sys.exit(1)

220

```

221

222

### Invalid XML Handling

223

224

```python

225

from lxml import etree

226

from xmlrunner.extra.xunit_plugin import transform

227

228

def safe_transform(xml_data):

229

"""Safely transform XML with error handling."""

230

try:

231

return transform(xml_data)

232

except etree.XMLSyntaxError as e:

233

print(f"Invalid XML input: {e}")

234

return xml_data # Return original on error

235

except Exception as e:

236

print(f"Transformation error: {e}")

237

return xml_data

238

```

239

240

## Integration Strategies

241

242

### Conditional Transformation

243

244

```python

245

import os

246

from xmlrunner.extra.xunit_plugin import transform

247

248

def maybe_transform_for_jenkins(xml_data):

249

"""Transform XML only if Jenkins xUnit plugin version requires it."""

250

jenkins_version = os.getenv('JENKINS_XUNIT_VERSION', '1.100')

251

252

if jenkins_version >= '1.104':

253

return transform(xml_data)

254

return xml_data

255

```

256

257

### Custom Transformation Pipeline

258

259

```python

260

from xmlrunner.extra.xunit_plugin import transform

261

import lxml.etree as etree

262

263

def custom_jenkins_transform(xml_data, remove_timestamps=True,

264

remove_file_info=True):

265

"""Custom transformation with configurable options."""

266

if not (remove_timestamps or remove_file_info):

267

return xml_data

268

269

# Parse XML

270

doc = etree.XML(xml_data)

271

272

# Remove attributes based on options

273

if remove_file_info:

274

for elem in doc.xpath('//testcase'):

275

elem.attrib.pop('file', None)

276

elem.attrib.pop('line', None)

277

278

if remove_timestamps:

279

for elem in doc.xpath('//testcase'):

280

elem.attrib.pop('timestamp', None)

281

282

# Return transformed XML

283

return etree.tostring(doc, pretty_print=True, encoding='UTF-8')

284

```

285

286

This transformation system ensures unittest-xml-reporting works seamlessly with various Jenkins plugin versions while maintaining all essential test result information.