Skip to content

Commit 292218f

Browse files
authored
Generate junit report (#1165)
* Enable junit report * Added junit reporter utility * Generate junit report * junit reporter: store time in seconds * Added skip reason in junit report * Added missing end * Fix skip serialize indentation
1 parent 9b4d2c1 commit 292218f

File tree

4 files changed

+145
-9
lines changed

4 files changed

+145
-9
lines changed

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,8 @@
6262
"stoppable": "^1.1.0",
6363
"tap": "^14.4.1",
6464
"tsd": "^0.11.0",
65-
"workq": "^2.1.0"
65+
"workq": "^2.1.0",
66+
"xmlbuilder2": "^2.1.2"
6667
},
6768
"dependencies": {
6869
"debug": "^4.1.1",

test/integration/index.js

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,15 @@
44

55
'use strict'
66

7-
const { readFileSync, accessSync, mkdirSync, readdirSync, statSync } = require('fs')
7+
const { writeFileSync, readFileSync, accessSync, mkdirSync, readdirSync, statSync } = require('fs')
88
const { join, sep } = require('path')
99
const yaml = require('js-yaml')
1010
const Git = require('simple-git')
11+
const ms = require('ms')
1112
const { Client } = require('../../index')
1213
const build = require('./test-runner')
1314
const { sleep } = require('./helper')
14-
const ms = require('ms')
15+
const createJunitReporter = require('./reporter')
1516

1617
const esRepo = 'https://github.com/elastic/elasticsearch.git'
1718
const esFolder = join(__dirname, '..', '..', 'elasticsearch')
@@ -133,6 +134,8 @@ async function start ({ client, isXPack }) {
133134
await withSHA(sha)
134135

135136
log(`Testing ${isXPack ? 'XPack' : 'oss'} api...`)
137+
const junit = createJunitReporter()
138+
const junitTestSuites = junit.testsuites(`Integration test for ${isXPack ? 'XPack' : 'oss'} api`)
136139

137140
const stats = {
138141
total: 0,
@@ -196,31 +199,43 @@ async function start ({ client, isXPack }) {
196199

197200
const cleanPath = file.slice(file.lastIndexOf(apiName))
198201
log(' ' + cleanPath)
202+
const junitTestSuite = junitTestSuites.testsuite(apiName.slice(1) + ' - ' + cleanPath)
199203

200204
for (const test of tests) {
201205
const testTime = now()
202206
const name = Object.keys(test)[0]
203207
if (name === 'setup' || name === 'teardown') continue
208+
const junitTestCase = junitTestSuite.testcase(name)
209+
204210
stats.total += 1
205211
if (shouldSkip(isXPack, file, name)) {
206212
stats.skip += 1
213+
junitTestCase.skip('This test is in the skip list of the client')
214+
junitTestCase.end()
207215
continue
208216
}
209217
log(' - ' + name)
210218
try {
211-
await testRunner.run(setupTest, test[name], teardownTest, stats)
219+
await testRunner.run(setupTest, test[name], teardownTest, stats, junitTestCase)
212220
stats.pass += 1
213221
} catch (err) {
222+
junitTestCase.failure(err)
223+
junitTestCase.end()
224+
junitTestSuite.end()
225+
junitTestSuites.end()
226+
generateJunitXmlReport(junit, isXPack ? 'xpack' : 'oss')
214227
console.error(err)
215228
process.exit(1)
216229
}
217230
const totalTestTime = now() - testTime
231+
junitTestCase.end()
218232
if (totalTestTime > MAX_TEST_TIME) {
219233
log(' took too long: ' + ms(totalTestTime))
220234
} else {
221235
log(' took: ' + ms(totalTestTime))
222236
}
223237
}
238+
junitTestSuite.end()
224239
const totalFileTime = now() - fileTime
225240
if (totalFileTime > MAX_FILE_TIME) {
226241
log(` ${cleanPath} took too long: ` + ms(totalFileTime))
@@ -235,6 +250,8 @@ async function start ({ client, isXPack }) {
235250
log(`${apiName} took: ` + ms(totalApiTime))
236251
}
237252
}
253+
junitTestSuites.end()
254+
generateJunitXmlReport(junit, isXPack ? 'xpack' : 'oss')
238255
log(`Total testing time: ${ms(now() - totalTime)}`)
239256
log(`Test stats:
240257
- Total: ${stats.total}
@@ -359,6 +376,13 @@ function createFolder (name) {
359376
}
360377
}
361378

379+
function generateJunitXmlReport (junit, suite) {
380+
writeFileSync(
381+
join(__dirname, '..', '..', `${suite}-report-junit.xml`),
382+
junit.prettyPrint()
383+
)
384+
}
385+
362386
if (require.main === module) {
363387
const node = process.env.TEST_ES_SERVER || 'http://localhost:9200'
364388
const opts = {

test/integration/reporter.js

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
'use strict'
2+
3+
const assert = require('assert')
4+
const { create } = require('xmlbuilder2')
5+
6+
function createJunitReporter () {
7+
const report = {}
8+
9+
return { testsuites, prettyPrint }
10+
11+
function prettyPrint () {
12+
return create(report).end({ prettyPrint: true })
13+
}
14+
15+
function testsuites (name) {
16+
assert(name, 'The testsuites name is required')
17+
assert(report.testsuites === undefined, 'Cannot set more than one testsuites block')
18+
const startTime = Date.now()
19+
20+
report.testsuites = {
21+
'@id': new Date().toISOString(),
22+
'@name': name
23+
}
24+
25+
const testsuiteList = []
26+
27+
return {
28+
testsuite: createTestSuite(testsuiteList),
29+
end () {
30+
report.testsuites['@time'] = Math.round((Date.now() - startTime) / 1000)
31+
report.testsuites['@tests'] = testsuiteList.reduce((acc, val) => {
32+
acc += val['@tests']
33+
return acc
34+
}, 0)
35+
report.testsuites['@failures'] = testsuiteList.reduce((acc, val) => {
36+
acc += val['@failures']
37+
return acc
38+
}, 0)
39+
report.testsuites['@skipped'] = testsuiteList.reduce((acc, val) => {
40+
acc += val['@skipped']
41+
return acc
42+
}, 0)
43+
if (testsuiteList.length) {
44+
report.testsuites.testsuite = testsuiteList
45+
}
46+
}
47+
}
48+
}
49+
50+
function createTestSuite (testsuiteList) {
51+
return function testsuite (name) {
52+
assert(name, 'The testsuite name is required')
53+
const startTime = Date.now()
54+
const suite = {
55+
'@id': new Date().toISOString(),
56+
'@name': name
57+
}
58+
const testcaseList = []
59+
testsuiteList.push(suite)
60+
return {
61+
testcase: createTestCase(testcaseList),
62+
end () {
63+
suite['@time'] = Math.round((Date.now() - startTime) / 1000)
64+
suite['@tests'] = testcaseList.length
65+
suite['@failures'] = testcaseList.filter(t => t.failure).length
66+
suite['@skipped'] = testcaseList.filter(t => t.skipped).length
67+
if (testcaseList.length) {
68+
suite.testcase = testcaseList
69+
}
70+
}
71+
}
72+
}
73+
}
74+
75+
function createTestCase (testcaseList) {
76+
return function testcase (name) {
77+
assert(name, 'The testcase name is required')
78+
const startTime = Date.now()
79+
const tcase = {
80+
'@id': new Date().toISOString(),
81+
'@name': name
82+
}
83+
testcaseList.push(tcase)
84+
return {
85+
failure (error) {
86+
assert(error, 'The failure error object is required')
87+
tcase.failure = {
88+
'#': error.stack,
89+
'@message': error.message,
90+
'@type': error.code
91+
}
92+
},
93+
skip (reason) {
94+
if (typeof reason !== 'string') {
95+
reason = JSON.stringify(reason, null, 2)
96+
}
97+
tcase.skipped = {
98+
'#': reason
99+
}
100+
},
101+
end () {
102+
tcase['@time'] = Math.round((Date.now() - startTime) / 1000)
103+
}
104+
}
105+
}
106+
}
107+
}
108+
109+
module.exports = createJunitReporter

test/integration/test-runner.js

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -218,11 +218,12 @@ function build (opts = {}) {
218218
* @oaram {object} teardown (null if not needed)
219219
* @returns {Promise}
220220
*/
221-
async function run (setup, test, teardown, stats) {
221+
async function run (setup, test, teardown, stats, junit) {
222222
// if we should skip a feature in the setup/teardown section
223223
// we should skip the entire test file
224224
const skip = getSkip(setup) || getSkip(teardown)
225225
if (skip && shouldSkip(esVersion, skip)) {
226+
junit.skip(skip)
226227
logSkip(skip)
227228
return
228229
}
@@ -240,11 +241,11 @@ function build (opts = {}) {
240241
}
241242
}
242243

243-
if (setup) await exec('Setup', setup, stats)
244+
if (setup) await exec('Setup', setup, stats, junit)
244245

245-
await exec('Test', test, stats)
246+
await exec('Test', test, stats, junit)
246247

247-
if (teardown) await exec('Teardown', teardown, stats)
248+
if (teardown) await exec('Teardown', teardown, stats, junit)
248249

249250
if (isXPack) await cleanupXPack()
250251

@@ -451,11 +452,12 @@ function build (opts = {}) {
451452
* @param {object} the actions to perform
452453
* @returns {Promise}
453454
*/
454-
async function exec (name, actions, stats) {
455+
async function exec (name, actions, stats, junit) {
455456
// tap.comment(name)
456457
for (const action of actions) {
457458
if (action.skip) {
458459
if (shouldSkip(esVersion, action.skip)) {
460+
junit.skip(fillStashedValues(action.skip))
459461
logSkip(fillStashedValues(action.skip))
460462
break
461463
}

0 commit comments

Comments
 (0)