Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
32 changes: 32 additions & 0 deletions api/evfEventFileReader.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { ISequentialFileReader, Parser } from "./src";
import IBMi from "vscode-ibmi/src/api/IBMi";

class EvfEventFileReader implements ISequentialFileReader {
lines: string[];
index = 0;

constructor(lines: string[]) {
this.lines = lines;
}

readNextLine(): string | undefined {
const line = this.lines[this.index];
if (line) {
this.index++;
}

return line;
}
}

export async function getParser(connection: IBMi, library: string, member: string) {
const content = connection.getContent();
const tableData = await content.getTable(`SANJULA`, `EVFEVENT`, `TEMPDET`);
const lines = tableData.map(row => String(row.EVFEVENT));

const evfEventFileReader = new EvfEventFileReader(lines);
const parser = new Parser();
parser.parse(evfEventFileReader);

return parser;
}
45 changes: 41 additions & 4 deletions api/package-lock.json

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

1 change: 1 addition & 0 deletions api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
"tmp": "^0.2.3",
"unzipper": "^0.11.6",
"vscode-ibmi": "github:halcyon-tech/vscode-ibmi",
"@ibm/ibmi-eventf-parser": "github:IBM/ibmi-eventf-parser#feature/resolveLineNumber",
"xml-js": "^1.6.11",
"xml2js": "^0.6.2"
},
Expand Down
7 changes: 6 additions & 1 deletion api/runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { CodeCoverageParser } from "./codeCoverageParser";
import { XMLParser } from "./xmlParser";
import * as xmljs from "xml-js";
import * as path from "path";
import { getParser } from "./evfEventFileReader";

export interface TestCallbacks {
deploy: (workspaceFolderPath: string) => Promise<DeploymentStatus>;
Expand Down Expand Up @@ -678,12 +679,16 @@ export class Runner {
}
}

const parser = await getParser(this.connection, ``, ``);

// Parse XML test case results
let testCaseResults: TestCaseResult[] = [];
try {
const xmlStmfContent = (await content.downloadStreamfileRaw(resolvedXmlStmf)).toString();
const xmlAsJs = xmljs.xml2js(xmlStmfContent, { compact: false, alwaysChildren: true });
testCaseResults = XMLParser.parseTestResults(xmlAsJs, testSuite.uri.scheme === 'file' || testSuite.uri.scheme === 'streamfile');
const isStreamFile = testSuite.uri.scheme === 'file' || testSuite.uri.scheme === 'streamfile';
const xmlParser = new XMLParser(xmlAsJs, isStreamFile, parser);
testCaseResults = xmlParser.parseTestResults();
} catch (error: any) {
for (const testCase of testSuite.testCases) {
const assertionResult: AssertionResult[] = [{
Expand Down
76 changes: 46 additions & 30 deletions api/xmlParser.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,21 @@
import { Parser } from "./src";
import { AssertionResult, CallstackItem, TestCaseResult, ValueInfo } from "./types";

export namespace XMLParser {
export function parseTestResults(xml: any, isStreamFile: boolean): TestCaseResult[] {
export class XMLParser {
private xml: any;
private isStreamFile: boolean;
private parser: Parser;

constructor(xml: any, isStreamFile: boolean, parser: Parser) {
this.xml = xml;
this.isStreamFile = isStreamFile;
this.parser = parser;
}

parseTestResults(): TestCaseResult[] {
const results: TestCaseResult[] = [];

for (const testcase of xml.elements[0].elements) {
for (const testcase of this.xml.elements[0].elements) {
if (testcase.name !== 'testcase') {
continue;
}
Expand All @@ -19,13 +30,13 @@ export namespace XMLParser {

for (const assertion of testcase.elements) {
if (assertion.name === 'success') {
const assertionResult = parseSuccess(assertion, isStreamFile);
const assertionResult = this.parseSuccess(assertion);
result.results!.push(assertionResult);
} else if (assertion.name === 'failure') {
const assertionResult = parseFailure(assertion, isStreamFile);
const assertionResult = this.parseFailure(assertion);
result.results!.push(assertionResult);
} else if (assertion.name === 'error') {
const assertionResult = parseError(assertion, isStreamFile);
const assertionResult = this.parseError(assertion);
result.results!.push(assertionResult);
}
}
Expand All @@ -36,16 +47,16 @@ export namespace XMLParser {
return results;
}

function parseSuccess(success: any, isStreamFile: boolean): AssertionResult {
private parseSuccess(success: any): AssertionResult {
const assertionResult: AssertionResult = {
name: success.attributes.name,
outcome: success.name,
line: convertLineNumbers(success.attributes.line, isStreamFile)
line: this.convertLineNumbers(success.attributes.line)
};

for (const child of success.elements) {
if (child.name === 'expected') {
const expectedValue = parseValueInfo(child);
const expectedValue = this.parseValueInfo(child);
if (expectedValue) {
assertionResult.expected = expectedValue;
}
Expand All @@ -55,32 +66,32 @@ export namespace XMLParser {
return assertionResult;
}

function parseFailure(failure: any, isStreamFile: boolean): AssertionResult {
private parseFailure(failure: any): AssertionResult {
const assertionResult: AssertionResult = {
name: failure.attributes.name,
outcome: failure.name,
line: convertLineNumbers(failure.attributes.line, isStreamFile),
line: this.convertLineNumbers(failure.attributes.line),
message: failure.attributes.message
};

for (const child of failure.elements) {
if (child.name === 'callstack') {
const callstack = parseCallstack(child, isStreamFile);
const callstack = this.parseCallstack(child);
if (callstack) {
assertionResult.callstack = callstack;
}
} else if (child.name === 'expected') {
const expectedValue = parseValueInfo(child);
const expectedValue = this.parseValueInfo(child);
if (expectedValue) {
assertionResult.expected = expectedValue;
}
} else if (child.name === 'actual') {
const actualValue = parseValueInfo(child);
const actualValue = this.parseValueInfo(child);
if (actualValue) {
assertionResult.actual = actualValue;
}
} else if (child.name === 'diagnosticMessages') {
const diagnosticMessages = parseDiagnosticMessages(child);
const diagnosticMessages = this.parseDiagnosticMessages(child);
if (diagnosticMessages) {
assertionResult.diagnosticMessages = diagnosticMessages;
}
Expand All @@ -90,7 +101,7 @@ export namespace XMLParser {
return assertionResult;
}

function parseError(error: any, isStreamFile: boolean): AssertionResult {
private parseError(error: any): AssertionResult {
const assertionResult: AssertionResult = {
outcome: error.name,
message: error.attributes.message,
Expand All @@ -100,12 +111,12 @@ export namespace XMLParser {

for (const child of error.elements) {
if (child.name === 'messageSender') {
const callstack = parseCallstackItem(child, isStreamFile);
const callstack = this.parseCallstackItem(child);
if (callstack) {
assertionResult.messageSender = callstack;
}
} else if (child.name === 'messageReceiver') {
const callstack = parseCallstackItem(child, isStreamFile);
const callstack = this.parseCallstackItem(child);
if (callstack) {
assertionResult.messageReceiver = callstack;
}
Expand All @@ -115,30 +126,30 @@ export namespace XMLParser {
return assertionResult;
}

function parseCallstack(callstack: any, isStreamFile: boolean): CallstackItem[] | undefined {
private parseCallstack(callstack: any): CallstackItem[] | undefined {
const callstackItems: CallstackItem[] = [];

for (const child of callstack.elements) {
callstackItems.push(parseCallstackItem(child, isStreamFile));
callstackItems.push(this.parseCallstackItem(child));
}

if (callstackItems.length > 0) {
return callstackItems;
}
}

function parseCallstackItem(callstackItem: any, isStreamFile: boolean): CallstackItem {
private parseCallstackItem(callstackItem: any): CallstackItem {
return {
program: callstackItem.attributes.program,
programLibrary: callstackItem.attributes.programLibrary,
module: callstackItem.attributes.module,
moduleLibrary: callstackItem.attributes.moduleLibrary,
procedure: callstackItem.attributes.procedure,
line: convertLineNumbers(callstackItem.attributes.line, isStreamFile)!
line: this.convertLineNumbers(callstackItem.attributes.line)!
};
}

function parseDiagnosticMessages(diagnosticMessages: any): string[] | undefined {
private parseDiagnosticMessages(diagnosticMessages: any): string[] | undefined {
const messages: string[] = [];

for (const child of diagnosticMessages.elements) {
Expand All @@ -156,7 +167,7 @@ export namespace XMLParser {
}


function parseValueInfo(expectedOrActual: any): ValueInfo | undefined {
private parseValueInfo(expectedOrActual: any): ValueInfo | undefined {
const valueInfo: ValueInfo = {
value: '',
type: '',
Expand Down Expand Up @@ -188,13 +199,18 @@ export namespace XMLParser {
}
}

function convertLineNumbers(rawLine: string | undefined, isStreamFile: boolean): number | undefined {
private convertLineNumbers(rawLine: string | undefined): number | undefined {
let line = rawLine ? parseInt(rawLine) : undefined;
if (!isStreamFile && line) {
// Stream files: Line numbers match line numbers of the source code
// Source members: Line numbers must be divided by 100 because they are specified with 2 decimal positions
// https://github.com/tools-400/irpgunit/issues/15#issuecomment-2871972032
line = line / 100;
if (line) {
if (this.isStreamFile) {
// Resolve the expanded source line number to its true line number in the original source
line = this.parser.resolveLineNumber(line);
} else {
// Stream files: Line numbers match line numbers of the source code
// Source members: Line numbers must be divided by 100 because they are specified with 2 decimal positions
// https://github.com/tools-400/irpgunit/issues/15#issuecomment-2871972032
line = line / 100;
}
}

return line;
Expand Down
Loading