Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Don't display destructors as overloaded constructors #252

Draft
wants to merge 10 commits into
base: main
Choose a base branch
from
1 change: 1 addition & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
"proj8_custom_command",
"proj9",
"projA",
"SourceMapRCodeParser",
"SourceMapXrefParser",
"workspace0.code-workspace",
"workspace1.code-workspace",
Expand Down
2 changes: 1 addition & 1 deletion src/ABLResults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { PropathParser } from 'ABLPropath'
import { log } from 'ChannelLogger'
import { RunStatus, ablunitRun } from 'ABLUnitRun'
import { getDLC, IDlc } from 'parse/OpenedgeProjectParser'
import { Duration, gatherAllTestItems, sortLocation } from 'ABLUnitCommon'
import { Duration, gatherAllTestItems } from 'ABLUnitCommon'
import { ITestObj } from 'parse/config/CoreOptions'
import * as FileUtils from 'FileUtils'
import { basename, dirname } from 'path'
Expand Down
3 changes: 1 addition & 2 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
TestRun,
TestRunProfileKind, TestRunRequest,
TestTag,
TextDocument, TextDocumentChangeEvent, Uri, WorkspaceFolder,
TextDocument, Uri, WorkspaceFolder,
commands,
extensions,
languages,
Expand All @@ -26,7 +26,6 @@ import { ABLCompilerError, ABLUnitRuntimeError, TimeoutError } from 'Errors'
import { basename } from 'path'
import * as FileUtils from 'FileUtils'
import { gatherAllTestItems, IExtensionTestReferences, sortLocation } from 'ABLUnitCommon'
import { getDeclarationCoverage, getStatementCoverage } from 'parse/ProfileParser'
import {
// InlineProvider,
SnippetProvider,
Expand Down
15 changes: 14 additions & 1 deletion src/parse/CallStackParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,20 @@ export async function parseCallstack (debugLines: ABLDebugLines, callstackRaw: s

let debugUri: Uri | undefined = Uri.file(debugFile)
if (!FileUtils.doesFileExist(debugUri)) {
const fileinfo = debugLines.propath.search(debugFile)
let fileinfo = debugLines.propath.search(debugFile)
log.info('fileinfo=' + JSON.stringify(fileinfo))
if (!fileinfo && debugFile.endsWith('.r')) {
if (!FileUtils.doesFileExist(debugUri)) {
// file.test.p compiles to file/test.p for some reason... this adjusts for that scenario
if (debugFile.lastIndexOf('/')) {
debugUri = Uri.file(debugFile.substring(0, debugFile.lastIndexOf('/')) + '.' + debugFile.substring(debugFile.lastIndexOf('/') + 1))
}
}
fileinfo = debugLines.propath.search(debugFile.substring(0, debugFile.length - 2) + '.p')
if (!fileinfo) {
fileinfo = debugLines.propath.search(debugFile.substring(0, debugFile.length - 2) + '.cls')
}
}
if (fileinfo) {
debugUri = fileinfo.uri
} else {
Expand Down
4 changes: 4 additions & 0 deletions src/parse/SourceMapRCodeParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,9 @@ export const getSourceMapFromRCode = (propath: PropathParser, uri: Uri) => {
}

const getSourceName = (num: number) => {
log.info('sources.length=' + sources.length + ', num=' + num)
for (const src of sources) {
log.info('src.sourceNum=' + src.sourceNum + ', src.sourceName=' + src.sourceName)
if (src.sourceNum === num) {
return src.sourceName
}
Expand All @@ -247,7 +249,9 @@ export const getSourceMapFromRCode = (propath: PropathParser, uri: Uri) => {


const getSourceUri = (num: number) => {
log.info('sources.length=' + sources.length + ', num=' + num)
for (const src of sources) {
log.info('src.sourceNum=' + src.sourceNum + ', src.sourceName=' + src.sourceName)
if (src.sourceNum === num) {
return src.sourceUri
}
Expand Down
152 changes: 152 additions & 0 deletions test/parse/SourceMapRCodeParser.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
import { commands, Uri } from 'vscode'
import { assert, log, sleep2, toUri } from '../testCommon'
import { PropathParser } from 'ABLPropath'
import { SourceMapItem } from 'parse/SourceMapParser'
import { getSourceMapFromRCode } from 'parse/SourceMapRCodeParser'
import * as FileUtils from 'FileUtils'

suiteSetup('suiteSetup', () => {
log.info('delete xref files')
FileUtils.deleteFile(toUri('test_1/test.p.xref'))
FileUtils.deleteFile(toUri('test_2/test.p.xref'))
FileUtils.deleteFile(toUri('test_3/test.p.xref'))
log.info('ant compile-and-test')
return commands.executeCommand('workbench.action.tasks.build')
.then(() => {
log.info('ant compile-and-test done')
return
}, (e: unknown) => {
log.error('error compiling! (e=' + e + ')')
throw e
})
})

setup('setup', async () => {
log.info('setup ----- start')
while (!FileUtils.doesFileExist(toUri('test_1/test.p.xref'))) {
await sleep2(250, 'waiting for test_1/test.p.xref')
}
})

test('SourceMapRCodeParser.test_0', async () => {
const propath = new PropathParser()
log.info('propath=' + JSON.stringify(propath.propath, null, 2))

const testuri = toUri('test_0/test.p')
const incuri = toUri('test_0/include.i')
const sourceMap = await getSourceMap(propath, toUri('test_0/test.r'))

for (const item of sourceMap.items) {
log.info('item=' + JSON.stringify(item, null, 2))
}

assert.equal(sourceMap.items.length, 1)
// validate the first executable line is number 2
assertLines(sourceMap.items, 2, 2, testuri, testuri)
return
})

test('SourceMapRCodeParser.test_1', async () => {
const propath = new PropathParser()
log.info('propath=' + JSON.stringify(propath.propath, null, 2))

const testuri = toUri('test_1/test.p')
const incuri = toUri('test_1/include.i')
const sourceMap = await getSourceMap(propath, toUri('test_1/test.r'))

for (const item of sourceMap.items) {
log.info('item=' + JSON.stringify(item, null, 2))
}

assert.equal(sourceMap.items.length, 8)
// validate the first executable line is number 4
assertLines([sourceMap.items[0]], 1, 1, testuri, testuri)
assertLines(sourceMap.items, 6, 6, testuri, testuri)
assertLines(sourceMap.items, 7, 1, testuri, incuri)
assertLines(sourceMap.items, 8, 2, testuri, incuri)
assertLines(sourceMap.items, 9, 3, testuri, incuri)
assertLines(sourceMap.items, 10, 4, testuri, incuri)
assertLines(sourceMap.items, 11, 7, testuri, testuri)
assertLines(sourceMap.items, 12, 8, testuri, testuri)
assertLines(sourceMap.items, 13, 9, testuri, testuri)
return
})

test('SourceMapRCodeParser.test_2', async () => {
const propath = new PropathParser()
log.info('propath=' + JSON.stringify(propath.propath, null, 2))

const testuri = toUri('test_2/test.p')
const incuri = toUri('test_2/include.i')
const sourceMap = await getSourceMap(propath, testuri)
assert.equal(sourceMap.items.length, getLineCount(toUri('.dbg/test_2/test.p')))
assertLines(sourceMap.items, 6, 6, testuri, testuri)
assertLines(sourceMap.items, 7, 1, testuri, incuri)
assertLines(sourceMap.items, 8, 2, testuri, incuri)
assertLines(sourceMap.items, 9, 3, testuri, incuri)
assertLines(sourceMap.items, 10, 4, testuri, incuri)
assertLines(sourceMap.items, 11, 7, testuri, testuri)
return
})

test('SourceMapRCodeParser.test_3', async () => {
const propath = new PropathParser()
log.info('propath=' + JSON.stringify(propath.propath, null, 2))

const testuri = toUri('test_3/test.p')
const incuri = toUri('test_3/include.i')
const sourceMap = await getSourceMap(propath, testuri)
assert.equal(sourceMap.items.length, getLineCount(toUri('.dbg/test_3/test.p')))
assertLines(sourceMap.items, 6, 6, testuri, testuri)
assertLines(sourceMap.items, 7, 1, testuri, incuri)
assertLines(sourceMap.items, 8, 2, testuri, incuri)
assertLines(sourceMap.items, 9, 3, testuri, incuri)
assertLines(sourceMap.items, 10, 4, testuri, incuri)
assertLines(sourceMap.items, 11, 5, testuri, incuri)
assertLines(sourceMap.items, 12, 7, testuri, testuri)
return
})

async function getSourceMap (propath: PropathParser, uri: Uri) {
const sourceMap = await getSourceMapFromRCode(propath, uri)
if (!sourceMap) {
throw new Error('no source map found')
}
return sourceMap
}

function getLineCount (uri: Uri) {
const lines = FileUtils.readLinesFromFileSync(uri)
log.info('lines.length=' + lines.length + ' uri=' + uri.fsPath)
log.info('lines[' + lines.length + ']="' + lines[lines.length - 1] + '"')
if (lines[lines.length - 1] == '') {
log.info('returning ' + (lines.length - 1))
return lines.length - 1
}
log.info('returning ' + lines.length)
return lines.length
}

function assertLines (lines: SourceMapItem[], dbgLine: number, srcLine: number, dbgUri?: Uri, srcUri?: Uri) {
const filteredLines = lines.filter((l) => l.debugLine === dbgLine)
if (!filteredLines) {
assert.fail('lines not found ' + dbgUri?.fsPath + ':' + dbgLine)
return
}
if (filteredLines.length > 1) {
assert.fail('multiple lines found ' + dbgUri?.fsPath + ':' + dbgLine)
return
}
const line = filteredLines[0]

log.debug('line=' + JSON.stringify(line, null, 2))

assert.equal(line.debugLine, dbgLine, 'lines.debugLine ' + line.debugLine + ' != ' + dbgLine) // always true
assert.equal(line.sourceLine, srcLine, 'lines.sourceLine ' + line.sourceLine + ' != ' + srcLine + '(dbgLine=' + dbgLine + ')')
if (dbgUri) {
assert.equal(line.debugUri.fsPath, dbgUri.fsPath, 'lines.debugUri (dbgLine=' + dbgLine + ')')
}
if (srcUri) {
assert.equal(line.sourceUri.fsPath, srcUri.fsPath, 'lines.sourceUri (dbgLine=' + dbgLine + ')')
}
}
50 changes: 50 additions & 0 deletions test/suites/proj0.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -503,4 +503,54 @@ suite('proj0 - Extension Test Suite', () => {
assert.tests.failed(0)
})

test('proj0.23 - destructor is not an overload', async () => {
await runTestsInFile('src/destructorClass.test.cls', 1, true)
const res = await getResults()

const profData = res[0].profileJson
const parents = res[0].profileJson.flatMap((p) => p.modules)
const childModules = parents.flatMap((p) => p.childModules)

const modules = childModules.filter(m => m.EntityName == 'destructorClass')
assert.equal(modules?.length, 2, 'modules.length')

assert.equal(modules[0].overloaded, false, 'modules[0].overloaded')
assert.equal(modules[0].Destructor, false, 'modules[0].Destructor')
assert.equal(modules[1].overloaded, false, 'modules[1].overloaded')
assert.equal(modules[1].Destructor, true, 'modules[1].Destructor')
})

test('proj 0.24 - search propath for destructorClass.test.r', async () => {
const res = await getResults()
const fileinfo1 = res[0].debugLines.propath.search('destructorClass.cls')
if (!fileinfo1) {
assert.fail('file not found in propath: destructorClass.cls')
}

const fileinfo2 = res[0].debugLines.propath.search('destructorClass.test.cls')
if (!fileinfo2) {
assert.fail('file not found in propath: destructorClass.test.cls')
}
// This should the result, but the compiler has other ideas....
// assert.equals(fileinfo2?.rcodeUri.fsPath, toUri('src/destructorClass.test.r').fsPath)
assert.equal(fileinfo2?.rcodeUri.fsPath, toUri('src/destructorClass.r').fsPath)

const fileinfo3 = res[0].debugLines.propath.search('destructorClass.r')
if (!fileinfo3) {
assert.fail('file not found in propath: destructorClass.r')
}

const fileinfo4 = res[0].debugLines.propath.search('destructorClass.test.r')
if (!fileinfo4) {
assert.fail('file not found in propath: destructorClass.test.r')
}
assert.equal(fileinfo4?.uri.fsPath, toUri('src/destructorClass.test.cls').fsPath)

const fileinfo5 = res[0].debugLines.propath.search('destructorClass/test.r')
if (!fileinfo5) {
assert.fail('file not found in propath: destructorClass/test.r')
}
assert.equal(fileinfo5?.uri.fsPath, toUri('src/destructorClass.test.cls').fsPath)
})

})
7 changes: 7 additions & 0 deletions test_projects/SourceMapRCodeParser/.vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"ablunit.files.exclude": [
"**/.dbg/**",
],
"files.trimFinalNewlines": false,
"files.insertFinalNewline": false,
}
21 changes: 21 additions & 0 deletions test_projects/SourceMapRCodeParser/.vscode/tasks.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"version": "2.0.0",
"options": {
"env": {
"PATH": "${env:PATH};${env:DLC}/ant/bin",
}
},
"tasks": [
{
"label": "ant compile-and-test",
"type": "shell",
"command": "ant",
"problemMatcher": [],
"group": {
"kind": "build",
"isDefault": true
}

}
]
}
3 changes: 3 additions & 0 deletions test_projects/SourceMapRCodeParser/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# SourceMapXrefParser

This project tests parsing profiles. The r-code generated uses MIN-SIZE and does not contain the source map. The extension falls back to parsing the xref which may be imperfect.
44 changes: 44 additions & 0 deletions test_projects/SourceMapRCodeParser/build.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<project name="SourceMapXrefParser" default="compile-and-test">

<property environment="env" />

<taskdef resource="PCT.properties" classpath="${user.home}/.ant/lib/PCT.jar;/usr/share/ant/lib/PCT.jar" />
<typedef resource="types.properties" classpath="${user.home}/.ant/lib/PCT.jar;/usr/share/ant/lib/PCT.jar" />
<DlcHome value="${env.DLC}" />
<echo>DLC=${env.DLC}</echo>

<target name="compile-and-test">
<antcall target="compile" />
<antcall target="test">
<param name="testdir" value="test_1" />
</antcall>
<antcall target="test">
<param name="testdir" value="test_2" />
</antcall>
<antcall target="test">
<param name="testdir" value="test_3" />
</antcall>

</target>

<target name="test">
<echo>running tests from ${testdir}/</echo>
<ABLUnit writeLog="true" destDir="${testdir}" failOnError="false" haltOnFailure="false">
<!-- <propath>
<path location="." />
</propath> -->
<fileset dir="." includes="${testdir}/*.p" />
<Profiler enabled="true" outputFile="${testdir}/prof.out" coverage="true" />
</ABLUnit>
</target>

<target name="compile">
<echo>Compiling...</echo>
<echo>minSize=${minSize}</echo>

<PCTCompile forceCompile="true" listing="false" keepXref="true" xrefDir="." debugListing="false" minSize="false" flattenDebugListing="false">
<fileset dir="." includes="test_*/*.p" />
</PCTCompile>
</target>

</project>
2 changes: 2 additions & 0 deletions test_projects/SourceMapRCodeParser/test_0/test.p
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// TEST CASE: one executable statement
message 100.
3 changes: 3 additions & 0 deletions test_projects/SourceMapRCodeParser/test_1/include.i
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
message "in test_1/include.i".
define variable cnt as integer no-undo.
cnt = 1.
15 changes: 15 additions & 0 deletions test_projects/SourceMapRCodeParser/test_1/test.p
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// TEST CASE: no trailing lines
block-level on error undo, throw.
using OpenEdge.Core.Assert.

message program-name(1) + 'starting'.
{test_1/include.i}

@Test.
procedure test_A :
if true then
message "in test_A".
else
message "this does not execute".
Assert:isTrue(true).
end procedure.
3 changes: 3 additions & 0 deletions test_projects/SourceMapRCodeParser/test_2/include.i
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
message "in test_2/include.i".
define variable cnt as integer no-undo.
cnt = 1.
Loading