Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
ac24971
Allow parsing European number formats for test execution times
kenherring Apr 28, 2026
97e20bd
Bump version to prerelease 1.4.39
kenherring Apr 28, 2026
b064130
Allow parsing European number formats for test execution times
kenherring Apr 29, 2026
a2ab832
Allow parsing European number formats for test execution times
kenherring Apr 29, 2026
216f226
Allow parsing European number formats for test execution times
kenherring Apr 29, 2026
fc8b683
Allow parsing European number formats for test execution times
kenherring Apr 29, 2026
4c1a19e
Allow parsing European number formats for test execution times
kenherring Apr 29, 2026
de252ab
Allow parsing European number formats for test execution times
kenherring Apr 29, 2026
e91347e
Allow parsing European number formats for test execution times
kenherring Apr 29, 2026
efad5a3
Allow parsing European number formats for test execution times
kenherring Apr 29, 2026
3555768
Allow parsing European number formats for test execution times
kenherring Apr 29, 2026
ced72c5
Allow parsing European number formats for test execution times
kenherring May 1, 2026
aab8e6f
Allow parsing European number formats for test execution times
kenherring May 1, 2026
c2bd662
Allow parsing European number formats for test execution times
kenherring May 1, 2026
4e99499
Allow parsing European number formats for test execution times
kenherring May 1, 2026
e0e262d
Allow parsing European number formats for test execution times
kenherring May 1, 2026
1e6278e
Allow parsing European number formats for test execution times
kenherring May 1, 2026
e81c0a6
Allow parsing European number formats for test execution times
kenherring May 1, 2026
7f95009
Allow parsing European number formats for test execution times
kenherring May 1, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# [1.4.37](https://github.com/kenherring/ablunit-test-runner/releases/tag/1.4.37) - 2026-04-15 (pre-release)
# [1.4.39](https://github.com/kenherring/ablunit-test-runner/releases/tag/1.4.39) - 2026-04-28 (pre-release)

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

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

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

Expand Down
2 changes: 1 addition & 1 deletion docker/run_tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ initialize () {
if [ "${ABLUNIT_TEST_RUNNER_OE_VERSION,,}" = "all" ]; then
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)
elif [ "$ABLUNIT_TEST_RUNNER_OE_VERSION" != '12.2.12' ] &&
[ "$ABLUNIT_TEST_RUNNER_OE_VERSION" != '12.1.19' ] &&
[ "$ABLUNIT_TEST_RUNNER_OE_VERSION" != '12.2.19' ] &&
[ "$ABLUNIT_TEST_RUNNER_OE_VERSION" != '12.7.0' ] &&
[ "$ABLUNIT_TEST_RUNNER_OE_VERSION" != '12.8.1' ] &&
[ "$ABLUNIT_TEST_RUNNER_OE_VERSION" != '12.8.3' ] &&
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "ablunit-test-runner",
"displayName": "ABLUnit Test Runner",
"description": "OpenEdge ABLUnit test runner for VSCode",
"version": "1.4.37",
"version": "1.4.39",
"engineStrict": true,
"galleryBanner": {
"color": "#007ACC",
Expand Down
2 changes: 1 addition & 1 deletion scripts/run_test_wrapper.sh
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ update_oe_version () {
local SHORT_VERSION=${ABLUNIT_TEST_RUNNER_OE_VERSION%.*}
log_it "SHORT_VERSION=$SHORT_VERSION"

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

Expand Down
7 changes: 6 additions & 1 deletion src/ABLUnitRun.ts
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,12 @@ function runCommand (res: ABLResults, options: TestRun, cancellation: Cancellati
log.info(prefix + '🔹 ' + ablunitStatus.entityName, {testRun: options})
break
case 'TEST_END': {
const dur = Number(ablunitStatus.duration ?? '0') * 1000
if (ablunitStatus.duration?.includes(',') && !ablunitStatus.duration.includes('.')) {
// European number format (-E)
ablunitStatus.duration = ablunitStatus.duration.replace(/,/, '.')
}

const dur = Math.round(Number(ablunitStatus.duration ?? '0') * 1000)
options.passed(currentTestItems[0], dur)
log.info(prefix + '✅ ' + ablunitStatus.entityName + ' (' + dur + ' ms)', {testRun: options})
break
Expand Down
17 changes: 15 additions & 2 deletions src/parse/ResultsParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,12 @@ export class ABLResultsParser {
namePathSep = workspace.asRelativePath(namePathSep, false)
}

let time: string = res[idx].$.time
if (Number.isNaN(Number(time)) && time.includes(',')) {
// European number format (-E)
time = time.replace(/,/, '.')
}

suites[idx] = {
name: namePathSep,
classname: res[idx].$.classname ?? undefined,
Expand All @@ -171,7 +177,7 @@ export class ABLResultsParser {
errors: Number(res[idx].$.errors),
failures: Number(res[idx].$.failures),
skipped: Number(res[idx].$.skipped ?? 0),
time: Number(res[idx].$.time) * 1000,
time: Math.round(Number.parseFloat(time) * 1000),
properties: this.parseProperties(res[idx].properties),
testsuite: testsuite,
testcases: testcases
Expand Down Expand Up @@ -202,11 +208,18 @@ export class ABLResultsParser {
const cases: ITestCase[] = []

for (let idx=0; idx<res.length; idx++) {

let time: string = res[idx].$.time
if (Number.isNaN(Number(time)) && time.includes(',')) {
// European number format (-E)
time = time.replace(/,/, '.')
}

cases[idx] = {
name: res[idx].$.name,
classname: res[idx].$.classname ?? undefined,
status: res[idx].$.status,
time: Number(res[idx].$.time),
time: Number(time),
failures: await this.parseFailOrError(res[idx]),
skipped: this.parseSkipped(res[idx]),
}
Expand Down
27 changes: 14 additions & 13 deletions test/openedgeAblCommands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ export async function restartLangServer (rcodeCount = 0): Promise<number> {
log.info('status=' + JSON.stringify(status))

return await ablExtExports!.restartLanguageServer()
.then(() => sleep(100))
.then(() => waitForLangServerReady())
.then(() => waitForRcode(rcodeCount))
}
Expand Down Expand Up @@ -230,22 +231,22 @@ async function waitForLangServerReady () {
let status = await ablExtExports!.status()
while (waitTime.elapsed() < maxWait * 1000) {
if (!status?.projects || status.projects.length === 0) {
log.info('language server not ready yet...' + waitTime)
continue
}

let isReady = true
for (const project of status.projects) {
if (!project.initialized || project.rcodeTasks !== 0 || project.sourceTasks !== 0) {
isReady = false
log.info('language server not ready yet... ' + waitTime +
'\n\tstatus=' + JSON.stringify(status))
} else {
let isReady = true
for (const project of status.projects) {
if (!project.initialized || project.rcodeTasks !== 0 || project.sourceTasks !== 0) {
isReady = false
}
}
if (isReady) {
log.info('Language server is ready!')
return
}
}
if (isReady) {
log.info('Language server is ready!')
return
}

await sleep(250)
await sleep(250, null)
.then(() => ablExtExports!.status())
.then((response) => {
status = response
Expand Down
55 changes: 41 additions & 14 deletions test/suites/proj0.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,21 @@ function createTempFile () {
return tempFile
}

async function restoreProjectConfig () {
log.info('restoring openedge-project.json file')
FileUtils.copyFile(backupProjectFile, 'openedge-project.json', { preserveTimestamps: true})
await deleteRcode()
await restartLangServer(32)
}

const backupProjectFile = 'oeproject.bk'
const disposables: vscode.Disposable[] = []
let firstSetup = true
let ext: Extension<ABLUnitTestRunner>

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

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

setup('proj0 - setup', async () => {
log.info('---------- setup ----------')
FileUtils.copyFile('.vscode/settings.json.bk', '.vscode/settings.json')
FileUtils.copyFile(backupProjectFile, 'openedge-project.json')
if (FileUtils.getFileModifiedTime(backupProjectFile).valueOf() !== FileUtils.getFileModifiedTime('openedge-project.json').valueOf()) {
await restoreProjectConfig()
}
await commands.executeCommand('workbench.action.closeAllEditors')

if (firstSetup) {
const oever = process.env['ABLUNIT_TEST_RUNNER_OE_VERSION'] ?? process.env['OE_VERSION']
Expand All @@ -57,10 +68,11 @@ setup('proj0 - setup', async () => {
}
firstSetup = false
}
log.info('---------- setup complete ----------')
})

teardown('proj0 - afterEach', () => {
log.info('proj0 teardown')
log.info('---------- teardown start ----------')
FileUtils.deleteFile([
toUri('.vscode/ablunit-test-profile.json'),
toUri('results.json'),
Expand All @@ -77,6 +89,8 @@ teardown('proj0 - afterEach', () => {
log.warn('disposables.length != 0')
}
}

log.info('---------- teardown complete ----------')
})

suiteTeardown('proj0 - after', () => {
Expand Down Expand Up @@ -303,11 +317,6 @@ test('proj0.11 - timeout 5s', () => {
test('proj0.12 - timeout 1500ms fail', () => {
log.info('---------- proj0.12 ----------')
const prom = updateConfig('ablunit.files.exclude', '**/.{builder,pct}/**')
.then(() => {
const cfg = workspace.getConfiguration('ablunit.files.exclude')
log.debug('files.exclude=' + JSON.stringify(cfg))
return
})
.then(() => { return updateTestProfile('timeout', 1500) })
.then(() => { return runTestAtLine('src/timeout.p', 37, 0) })
.then(() => { return commands.executeCommand('_ablunit.getTestRunError') })
Expand Down Expand Up @@ -361,8 +370,30 @@ test('proj0.14 - timeout invalid -5s', async () => {
return
})

test('proj0.15 - european numbers (-E)', () => {
log.info('---------- proj0.15 -----------')
FileUtils.copyFile('openedge-project.test15.json', 'openedge-project.json')

const prom = updateConfig('ablunit.files.exclude', '**/.{builder,pct}/**')
.then(() => sleep(100))
.then(() => runTestAtLine('src/timeout.p', 37, 0))
.then(() => getResults())
.then((recentResults) => {
const resultsXml = FileUtils.readFileSync(toUri('results.xml')).toString()
log.info('resultsXml=' + resultsXml)
const time = resultsXml.search(/time="[\d]+,[\d]+"/)
assert.ok(time > 0, 'could not find time attribute with european number format in results.xml')
assert.ok(!isNaN(Number(recentResults[0].ablResults?.resultsJson[0].testsuite?.[0].time)), 'testsuite time should not be NaN due to european number format')
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')
return true
}, (e: unknown) => {
assert.fail('unexpected test error (e=' + e + ')')
})
return prom
})

test('proj0.17 - coverage in class property getters/setters', async () => {
log.info('proj0.17')
log.info('---------- proj0.17 ----------')
FileUtils.deleteFile(['results.xml', 'results.json'], { force: true })
FileUtils.copyFile('.vscode/ablunit-test-profile.proj0.17.json', '.vscode/ablunit-test-profile.json')
await runTestAtLine('src/test_17.cls', 33, 1, TestRunProfileKind.Coverage)
Expand Down Expand Up @@ -436,7 +467,7 @@ test('proj0.19 - program runs external source', async () => {
test('proj0.20 - build directory', async () => {
FileUtils.copyFile('openedge-project.test20.json', 'openedge-project.json')
await deleteRcode()
await restartLangServer(23)
await restartLangServer(32)

assert.fileExists('d1/test_20.r')
assert.fileExists('d2/test_20.p.xref')
Expand All @@ -446,10 +477,6 @@ test('proj0.20 - build directory', async () => {
assert.tests.count(1)
assert.coverageProcessingMethod(toUri('src/test_20.p'), 'rcode')
})

FileUtils.copyFile(backupProjectFile, 'openedge-project.json')
await deleteRcode()
await restartLangServer(23)
})

test('proj0.21 - overloaded method coverage', async () => {
Expand Down
21 changes: 21 additions & 0 deletions test_projects/proj0/openedge-project.test15.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"name": "proj0",
"version": "0",
"oeversion": "12.8",
"charset": "utf-8",
"extraParameters": "-E",
"buildPath": [
{ "type": "source", "documentation": "docs/src.json", "path": "src", "includes": "**/*.p,**/*.cls", "excludes": "emptyClass.cls", "xref": "." },
{ "type": "source", "documentation": "docs/test.json", "path": "test" }
],
//this is a comment!
"dbConnections": [
{
"name": "sp2k",
"aliases": [ "dbalias", "third" ], //here's another comment. why is this field required per the schema?
"connect": "-db target/db/sp2k -RO",
"schemaFile": "target/sp2k.df"
}
],
"numThreads": 1
}
Loading