Skip to content

Commit 9c824db

Browse files
authored
Allow parsing European number formats for test execution times (#574)
1 parent 7b649e4 commit 9c824db

10 files changed

Lines changed: 108 additions & 37 deletions

File tree

CHANGELOG.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
1-
# [1.4.37](https://github.com/kenherring/ablunit-test-runner/releases/tag/1.4.37) - 2026-04-15 (pre-release)
1+
# [1.4.39](https://github.com/kenherring/ablunit-test-runner/releases/tag/1.4.39) - 2026-04-28 (pre-release)
22

3+
* Allow parsing European number formats for test execution times (#574)
4+
* build(deps-dev): bump @vscode/vsce from 3.7.1 to 3.9.1 (#570)
5+
* build(deps-dev): bump typescript-eslint from 8.58.2 to 8.59.0 (#573)
6+
* build(deps-dev): bump typescript-eslint from 8.58.1 to 8.58.2 (#571)
37
* Various fixes for development tooling (#566)
48
* Trim `windows-` from codepage (#567)
59
* Clear environment variable when `initializationProcedure` is no longer set (#565)
@@ -72,7 +76,7 @@
7276
* Bump esbuild from 0.27.0 to 0.27.1 (#457)
7377
* Bump jws from 3.2.2 to 3.2.3 in the npm_and_yarn group across 1 directory (#455)
7478

75-
**Full Changelog**: [1.4.4...1.4.37](https://github.com/kenherring/ablunit-test-runner/compare/1.4.4...1.4.37)
79+
**Full Changelog**: [1.4.4...1.4.39](https://github.com/kenherring/ablunit-test-runner/compare/1.4.4...1.4.39)
7680

7781
# [1.4.4](https://github.com/kenherring/ablunit-test-runner/releases/tag/1.4.4) - 2025-11-26 (pre-release)
7882

docker/run_tests.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ initialize () {
163163
if [ "${ABLUNIT_TEST_RUNNER_OE_VERSION,,}" = "all" ]; then
164164
OE_VERSIONS=(12.2.12 12.7.0 12.8.1 12.8.3 12.8.4 12.8.5 12.8.6 12.8.7 12.8.8 12.8.9 12.8.11 13.0.0)
165165
elif [ "$ABLUNIT_TEST_RUNNER_OE_VERSION" != '12.2.12' ] &&
166-
[ "$ABLUNIT_TEST_RUNNER_OE_VERSION" != '12.1.19' ] &&
166+
[ "$ABLUNIT_TEST_RUNNER_OE_VERSION" != '12.2.19' ] &&
167167
[ "$ABLUNIT_TEST_RUNNER_OE_VERSION" != '12.7.0' ] &&
168168
[ "$ABLUNIT_TEST_RUNNER_OE_VERSION" != '12.8.1' ] &&
169169
[ "$ABLUNIT_TEST_RUNNER_OE_VERSION" != '12.8.3' ] &&

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "ablunit-test-runner",
33
"displayName": "ABLUnit Test Runner",
44
"description": "OpenEdge ABLUnit test runner for VSCode",
5-
"version": "1.4.37",
5+
"version": "1.4.39",
66
"engineStrict": true,
77
"galleryBanner": {
88
"color": "#007ACC",

scripts/run_test_wrapper.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ update_oe_version () {
5757
local SHORT_VERSION=${ABLUNIT_TEST_RUNNER_OE_VERSION%.*}
5858
log_it "SHORT_VERSION=$SHORT_VERSION"
5959

60-
sed -i "s|\"oeversion\": *\"12.[0-9]\"|\"oeversion\": \"$SHORT_VERSION\"|g" test_projects/*/openedge-project.json
60+
sed -i "s|\"oeversion\": *\"12.[0-9]\"|\"oeversion\": \"$SHORT_VERSION\"|g" test_projects/*/openedge-project.*json
6161
# ls -al test_projects/*/openedge-project.json
6262
}
6363

src/ABLUnitRun.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,12 @@ function runCommand (res: ABLResults, options: TestRun, cancellation: Cancellati
292292
log.info(prefix + '🔹 ' + ablunitStatus.entityName, {testRun: options})
293293
break
294294
case 'TEST_END': {
295-
const dur = Number(ablunitStatus.duration ?? '0') * 1000
295+
if (ablunitStatus.duration?.includes(',') && !ablunitStatus.duration.includes('.')) {
296+
// European number format (-E)
297+
ablunitStatus.duration = ablunitStatus.duration.replace(/,/, '.')
298+
}
299+
300+
const dur = Math.round(Number(ablunitStatus.duration ?? '0') * 1000)
296301
options.passed(currentTestItems[0], dur)
297302
log.info(prefix + '✅ ' + ablunitStatus.entityName + ' (' + dur + ' ms)', {testRun: options})
298303
break

src/parse/ResultsParser.ts

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,12 @@ export class ABLResultsParser {
162162
namePathSep = workspace.asRelativePath(namePathSep, false)
163163
}
164164

165+
let time: string = res[idx].$.time
166+
if (Number.isNaN(Number(time)) && time.includes(',')) {
167+
// European number format (-E)
168+
time = time.replace(/,/, '.')
169+
}
170+
165171
suites[idx] = {
166172
name: namePathSep,
167173
classname: res[idx].$.classname ?? undefined,
@@ -171,7 +177,7 @@ export class ABLResultsParser {
171177
errors: Number(res[idx].$.errors),
172178
failures: Number(res[idx].$.failures),
173179
skipped: Number(res[idx].$.skipped ?? 0),
174-
time: Number(res[idx].$.time) * 1000,
180+
time: Math.round(Number.parseFloat(time) * 1000),
175181
properties: this.parseProperties(res[idx].properties),
176182
testsuite: testsuite,
177183
testcases: testcases
@@ -202,11 +208,18 @@ export class ABLResultsParser {
202208
const cases: ITestCase[] = []
203209

204210
for (let idx=0; idx<res.length; idx++) {
211+
212+
let time: string = res[idx].$.time
213+
if (Number.isNaN(Number(time)) && time.includes(',')) {
214+
// European number format (-E)
215+
time = time.replace(/,/, '.')
216+
}
217+
205218
cases[idx] = {
206219
name: res[idx].$.name,
207220
classname: res[idx].$.classname ?? undefined,
208221
status: res[idx].$.status,
209-
time: Number(res[idx].$.time),
222+
time: Number(time),
210223
failures: await this.parseFailOrError(res[idx]),
211224
skipped: this.parseSkipped(res[idx]),
212225
}

test/openedgeAblCommands.ts

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ export async function restartLangServer (rcodeCount = 0): Promise<number> {
118118
log.info('status=' + JSON.stringify(status))
119119

120120
return await ablExtExports!.restartLanguageServer()
121+
.then(() => sleep(100))
121122
.then(() => waitForLangServerReady())
122123
.then(() => waitForRcode(rcodeCount))
123124
}
@@ -230,22 +231,22 @@ async function waitForLangServerReady () {
230231
let status = await ablExtExports!.status()
231232
while (waitTime.elapsed() < maxWait * 1000) {
232233
if (!status?.projects || status.projects.length === 0) {
233-
log.info('language server not ready yet...' + waitTime)
234-
continue
235-
}
236-
237-
let isReady = true
238-
for (const project of status.projects) {
239-
if (!project.initialized || project.rcodeTasks !== 0 || project.sourceTasks !== 0) {
240-
isReady = false
234+
log.info('language server not ready yet... ' + waitTime +
235+
'\n\tstatus=' + JSON.stringify(status))
236+
} else {
237+
let isReady = true
238+
for (const project of status.projects) {
239+
if (!project.initialized || project.rcodeTasks !== 0 || project.sourceTasks !== 0) {
240+
isReady = false
241+
}
242+
}
243+
if (isReady) {
244+
log.info('Language server is ready!')
245+
return
241246
}
242-
}
243-
if (isReady) {
244-
log.info('Language server is ready!')
245-
return
246247
}
247248

248-
await sleep(250)
249+
await sleep(250, null)
249250
.then(() => ablExtExports!.status())
250251
.then((response) => {
251252
status = response

test/suites/proj0.test.ts

Lines changed: 41 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,21 @@ function createTempFile () {
1313
return tempFile
1414
}
1515

16+
async function restoreProjectConfig () {
17+
log.info('restoring openedge-project.json file')
18+
FileUtils.copyFile(backupProjectFile, 'openedge-project.json', { preserveTimestamps: true})
19+
await deleteRcode()
20+
await restartLangServer(32)
21+
}
22+
1623
const backupProjectFile = 'oeproject.bk'
1724
const disposables: vscode.Disposable[] = []
1825
let firstSetup = true
1926
let ext: Extension<ABLUnitTestRunner>
2027

2128
suiteSetup('proj0 - before', async () => {
2229
FileUtils.copyFile('.vscode/settings.json', '.vscode/settings.json.bk')
23-
FileUtils.copyFile('openedge-project.json', backupProjectFile)
30+
FileUtils.copyFile('openedge-project.json', backupProjectFile, { preserveTimestamps: true})
2431

2532
FileUtils.deleteDir(toUri('d1'))
2633
FileUtils.deleteDir(toUri('d2'))
@@ -47,8 +54,12 @@ suiteSetup('proj0 - before', async () => {
4754
})
4855

4956
setup('proj0 - setup', async () => {
57+
log.info('---------- setup ----------')
5058
FileUtils.copyFile('.vscode/settings.json.bk', '.vscode/settings.json')
51-
FileUtils.copyFile(backupProjectFile, 'openedge-project.json')
59+
if (FileUtils.getFileModifiedTime(backupProjectFile).valueOf() !== FileUtils.getFileModifiedTime('openedge-project.json').valueOf()) {
60+
await restoreProjectConfig()
61+
}
62+
await commands.executeCommand('workbench.action.closeAllEditors')
5263

5364
if (firstSetup) {
5465
const oever = process.env['ABLUNIT_TEST_RUNNER_OE_VERSION'] ?? process.env['OE_VERSION']
@@ -57,10 +68,11 @@ setup('proj0 - setup', async () => {
5768
}
5869
firstSetup = false
5970
}
71+
log.info('---------- setup complete ----------')
6072
})
6173

6274
teardown('proj0 - afterEach', () => {
63-
log.info('proj0 teardown')
75+
log.info('---------- teardown start ----------')
6476
FileUtils.deleteFile([
6577
toUri('.vscode/ablunit-test-profile.json'),
6678
toUri('results.json'),
@@ -77,6 +89,8 @@ teardown('proj0 - afterEach', () => {
7789
log.warn('disposables.length != 0')
7890
}
7991
}
92+
93+
log.info('---------- teardown complete ----------')
8094
})
8195

8296
suiteTeardown('proj0 - after', () => {
@@ -303,11 +317,6 @@ test('proj0.11 - timeout 5s', () => {
303317
test('proj0.12 - timeout 1500ms fail', () => {
304318
log.info('---------- proj0.12 ----------')
305319
const prom = updateConfig('ablunit.files.exclude', '**/.{builder,pct}/**')
306-
.then(() => {
307-
const cfg = workspace.getConfiguration('ablunit.files.exclude')
308-
log.debug('files.exclude=' + JSON.stringify(cfg))
309-
return
310-
})
311320
.then(() => { return updateTestProfile('timeout', 1500) })
312321
.then(() => { return runTestAtLine('src/timeout.p', 37, 0) })
313322
.then(() => { return commands.executeCommand('_ablunit.getTestRunError') })
@@ -361,8 +370,30 @@ test('proj0.14 - timeout invalid -5s', async () => {
361370
return
362371
})
363372

373+
test('proj0.15 - european numbers (-E)', () => {
374+
log.info('---------- proj0.15 -----------')
375+
FileUtils.copyFile('openedge-project.test15.json', 'openedge-project.json')
376+
377+
const prom = updateConfig('ablunit.files.exclude', '**/.{builder,pct}/**')
378+
.then(() => sleep(100))
379+
.then(() => runTestAtLine('src/timeout.p', 37, 0))
380+
.then(() => getResults())
381+
.then((recentResults) => {
382+
const resultsXml = FileUtils.readFileSync(toUri('results.xml')).toString()
383+
log.info('resultsXml=' + resultsXml)
384+
const time = resultsXml.search(/time="[\d]+,[\d]+"/)
385+
assert.ok(time > 0, 'could not find time attribute with european number format in results.xml')
386+
assert.ok(!isNaN(Number(recentResults[0].ablResults?.resultsJson[0].testsuite?.[0].time)), 'testsuite time should not be NaN due to european number format')
387+
assert.ok(!isNaN(Number(recentResults[0].ablResults?.resultsJson[0].testsuite?.[0].testcases?.[0].time)), 'testcase time should not be NaN due to european number format')
388+
return true
389+
}, (e: unknown) => {
390+
assert.fail('unexpected test error (e=' + e + ')')
391+
})
392+
return prom
393+
})
394+
364395
test('proj0.17 - coverage in class property getters/setters', async () => {
365-
log.info('proj0.17')
396+
log.info('---------- proj0.17 ----------')
366397
FileUtils.deleteFile(['results.xml', 'results.json'], { force: true })
367398
FileUtils.copyFile('.vscode/ablunit-test-profile.proj0.17.json', '.vscode/ablunit-test-profile.json')
368399
await runTestAtLine('src/test_17.cls', 33, 1, TestRunProfileKind.Coverage)
@@ -436,7 +467,7 @@ test('proj0.19 - program runs external source', async () => {
436467
test('proj0.20 - build directory', async () => {
437468
FileUtils.copyFile('openedge-project.test20.json', 'openedge-project.json')
438469
await deleteRcode()
439-
await restartLangServer(23)
470+
await restartLangServer(32)
440471

441472
assert.fileExists('d1/test_20.r')
442473
assert.fileExists('d2/test_20.p.xref')
@@ -446,10 +477,6 @@ test('proj0.20 - build directory', async () => {
446477
assert.tests.count(1)
447478
assert.coverageProcessingMethod(toUri('src/test_20.p'), 'rcode')
448479
})
449-
450-
FileUtils.copyFile(backupProjectFile, 'openedge-project.json')
451-
await deleteRcode()
452-
await restartLangServer(23)
453480
})
454481

455482
test('proj0.21 - overloaded method coverage', async () => {
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"name": "proj0",
3+
"version": "0",
4+
"oeversion": "12.8",
5+
"charset": "utf-8",
6+
"extraParameters": "-E",
7+
"buildPath": [
8+
{ "type": "source", "documentation": "docs/src.json", "path": "src", "includes": "**/*.p,**/*.cls", "excludes": "emptyClass.cls", "xref": "." },
9+
{ "type": "source", "documentation": "docs/test.json", "path": "test" }
10+
],
11+
//this is a comment!
12+
"dbConnections": [
13+
{
14+
"name": "sp2k",
15+
"aliases": [ "dbalias", "third" ], //here's another comment. why is this field required per the schema?
16+
"connect": "-db target/db/sp2k -RO",
17+
"schemaFile": "target/sp2k.df"
18+
}
19+
],
20+
"numThreads": 1
21+
}

0 commit comments

Comments
 (0)