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

xml-utilities.mddocs/

0

# XML Document Construction

1

2

Low-level utilities for building XML test reports with proper JUnit schema compliance. Provides context management, counter tracking, and CDATA section handling for generating well-formed XML documents.

3

4

## Capabilities

5

6

### TestXMLBuilder Class

7

8

Main XML document builder that encapsulates rules for creating JUnit-compatible XML test reports with proper hierarchy and formatting.

9

10

```python { .api }

11

class TestXMLBuilder:

12

def __init__(self):

13

"""Initialize XML document builder with empty document."""

14

15

def begin_context(self, tag, name):

16

"""

17

Begin new XML context (testsuites, testsuite, or testcase).

18

19

Parameters:

20

- tag: str, XML tag name

21

- name: str, context name attribute

22

"""

23

24

def end_context(self):

25

"""

26

End current context and append to parent.

27

28

Returns:

29

- bool: True if context was ended, False if no context

30

"""

31

32

def current_context(self):

33

"""

34

Get current XML context.

35

36

Returns:

37

- TestXMLContext: current context or None

38

"""

39

40

def context_tag(self):

41

"""

42

Get tag name of current context.

43

44

Returns:

45

- str: current context tag name

46

"""

47

48

def append(self, tag, content, **kwargs):

49

"""

50

Append XML element with attributes to current context.

51

52

Parameters:

53

- tag: str, XML tag name

54

- content: str, element content

55

- **kwargs: element attributes

56

57

Returns:

58

- Element: created XML element

59

"""

60

61

def append_cdata_section(self, tag, content):

62

"""

63

Append element with CDATA content to current context.

64

65

Parameters:

66

- tag: str, XML tag name

67

- content: str, CDATA content

68

69

Returns:

70

- Element: created XML element

71

"""

72

73

def increment_counter(self, counter_name):

74

"""

75

Increment counter in current context and all parents.

76

77

Parameters:

78

- counter_name: str, counter name ('tests', 'errors', 'failures', 'skipped')

79

"""

80

81

def finish(self):

82

"""

83

End all open contexts and return formatted XML document.

84

85

Returns:

86

- bytes: pretty-printed XML document

87

"""

88

```

89

90

#### Usage Examples

91

92

**Basic XML Report Construction**

93

```python

94

from xmlrunner.builder import TestXMLBuilder

95

96

builder = TestXMLBuilder()

97

98

# Create testsuites root

99

builder.begin_context('testsuites', 'all_tests')

100

101

# Create testsuite

102

builder.begin_context('testsuite', 'test_module.TestClass')

103

104

# Add testcase

105

builder.begin_context('testcase', 'test_example')

106

builder.append('success', '', message='Test passed')

107

builder.increment_counter('tests')

108

builder.end_context() # End testcase

109

110

builder.end_context() # End testsuite

111

builder.end_context() # End testsuites

112

113

# Generate XML

114

xml_content = builder.finish()

115

print(xml_content.decode('utf-8'))

116

```

117

118

**Error Reporting with CDATA**

119

```python

120

builder = TestXMLBuilder()

121

builder.begin_context('testsuites', 'all_tests')

122

builder.begin_context('testsuite', 'test_module.TestClass')

123

builder.begin_context('testcase', 'test_failure')

124

125

# Add failure with traceback

126

failure_info = """Traceback (most recent call last):

127

File "test.py", line 10, in test_failure

128

self.assertTrue(False)

129

AssertionError: False is not true"""

130

131

builder.append('failure', '', type='AssertionError', message='False is not true')

132

builder.append_cdata_section('failure', failure_info)

133

134

builder.increment_counter('tests')

135

builder.increment_counter('failures')

136

137

xml_content = builder.finish()

138

```

139

140

### TestXMLContext Class

141

142

Represents XML document hierarchy context with automatic counter tracking and time measurement.

143

144

```python { .api }

145

class TestXMLContext:

146

def __init__(self, xml_doc, parent_context=None):

147

"""

148

Initialize XML context.

149

150

Parameters:

151

- xml_doc: Document, XML document instance

152

- parent_context: TestXMLContext or None, parent context

153

"""

154

155

def begin(self, tag, name):

156

"""

157

Begin context by creating XML element.

158

159

Parameters:

160

- tag: str, XML tag name

161

- name: str, name attribute value

162

"""

163

164

def end(self):

165

"""

166

End context and set timing/counter attributes.

167

168

Returns:

169

- Element: completed XML element

170

"""

171

172

def element_tag(self):

173

"""

174

Get tag name of this context's element.

175

176

Returns:

177

- str: tag name

178

"""

179

180

def increment_counter(self, counter_name):

181

"""

182

Increment counter if valid for this context type.

183

184

Parameters:

185

- counter_name: str, counter name

186

"""

187

188

def elapsed_time(self):

189

"""

190

Get formatted elapsed time for this context.

191

192

Returns:

193

- str: elapsed time in seconds (3 decimal places)

194

"""

195

196

def timestamp(self):

197

"""

198

Get ISO-8601 formatted timestamp for context end.

199

200

Returns:

201

- str: timestamp string

202

"""

203

204

# Attributes

205

xml_doc: Document

206

parent: TestXMLContext | None

207

element: Element

208

counters: dict[str, int]

209

```

210

211

#### Counter Rules

212

213

Different XML elements support different counters:

214

215

- **testsuites**: supports tests, errors, failures counters

216

- **testsuite**: supports tests, errors, failures, skipped counters

217

- **testcase**: no counters (individual test level)

218

219

### Text Processing Utilities

220

221

Functions for cleaning and processing text content for XML compatibility.

222

223

```python { .api }

224

def replace_nontext(text, replacement='\uFFFD'):

225

"""

226

Replace invalid XML characters in text.

227

228

Parameters:

229

- text: str, input text

230

- replacement: str, replacement character

231

232

Returns:

233

- str: cleaned text with invalid XML characters replaced

234

"""

235

236

# Constants

237

UTF8: str = 'UTF-8' # Default encoding for XML documents

238

INVALID_XML_1_0_UNICODE_RE: Pattern # Regex for invalid XML 1.0 characters

239

```

240

241

#### Usage Examples

242

243

**Text Cleaning**

244

```python

245

from xmlrunner.builder import replace_nontext

246

247

# Clean problematic characters from test output

248

raw_output = "Test output with \x00 null character"

249

clean_output = replace_nontext(raw_output)

250

# Result: "Test output with � null character"

251

252

# Use in XML building

253

builder.append_cdata_section('system-out', clean_output)

254

```

255

256

### CDATA Section Handling

257

258

The builder properly handles CDATA sections, including splitting content that contains the CDATA end marker.

259

260

```python

261

# Content with CDATA end marker is automatically split

262

problematic_content = "Some content ]]> with end marker ]]> inside"

263

builder.append_cdata_section('system-out', problematic_content)

264

265

# Results in multiple CDATA sections:

266

# <system-out><![CDATA[Some content ]]]]><![CDATA[> with end marker ]]]]><![CDATA[> inside]]></system-out>

267

```

268

269

### XML Schema Compliance

270

271

The builder generates XML that complies with JUnit schema requirements:

272

273

- Proper element hierarchy (testsuites → testsuite → testcase)

274

- Required attributes (name, tests, time, timestamp)

275

- Valid counter attributes based on element type

276

- Proper CDATA escaping for content with special characters

277

- UTF-8 encoding with XML declaration

278

279

### Performance Considerations

280

281

The XML builder is optimized for typical test report sizes:

282

283

- Uses DOM for structured building (suitable for reports with thousands of tests)

284

- Incremental counter updates avoid full tree traversal

285

- CDATA splitting handles edge cases without performance impact

286

- Memory usage scales linearly with test count

287

288

### Integration with Result System

289

290

The builder is used internally by _XMLTestResult but can be used independently:

291

292

```python

293

from xmlrunner.builder import TestXMLBuilder

294

from xml.dom.minidom import Document

295

296

# Direct usage for custom XML generation

297

builder = TestXMLBuilder()

298

299

# Build custom structure

300

builder.begin_context('testsuites', 'custom_run')

301

builder.begin_context('testsuite', 'my_tests')

302

303

for test_name, result in test_results.items():

304

builder.begin_context('testcase', test_name)

305

if result['passed']:

306

builder.increment_counter('tests')

307

else:

308

builder.append('failure', '', type='CustomError', message=result['error'])

309

builder.increment_counter('tests')

310

builder.increment_counter('failures')

311

builder.end_context()

312

313

xml_bytes = builder.finish()

314

```