downport and unit tests (#12)
* refactor tests * additional unit tests, closes #10 * refactor * better error messages * add downport step, closes #9 * test
This commit is contained in:
@@ -4,6 +4,7 @@ WORKDIR /opt/test-runner
|
|||||||
COPY . .
|
COPY . .
|
||||||
RUN npm ci
|
RUN npm ci
|
||||||
RUN npm run build
|
RUN npm run build
|
||||||
|
RUN npm install @abaplint/cli -g
|
||||||
RUN npm install @abaplint/transpiler-cli -g
|
RUN npm install @abaplint/transpiler-cli -g
|
||||||
RUN npm install @abaplint/runtime -g
|
RUN npm install @abaplint/runtime -g
|
||||||
ENTRYPOINT ["/opt/test-runner/bin/run.sh"]
|
ENTRYPOINT ["/opt/test-runner/bin/run.sh"]
|
||||||
17
package-lock.json
generated
17
package-lock.json
generated
@@ -9,6 +9,7 @@
|
|||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"license": "AGPL-3.0-or-later",
|
"license": "AGPL-3.0-or-later",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@abaplint/cli": "^2.82.1",
|
||||||
"@abaplint/runtime": "^1.6.66",
|
"@abaplint/runtime": "^1.6.66",
|
||||||
"@abaplint/transpiler": "^1.6.66",
|
"@abaplint/transpiler": "^1.6.66",
|
||||||
"@abaplint/transpiler-cli": "^1.6.66"
|
"@abaplint/transpiler-cli": "^1.6.66"
|
||||||
@@ -26,6 +27,17 @@
|
|||||||
"typescript": "^4.5.2"
|
"typescript": "^4.5.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@abaplint/cli": {
|
||||||
|
"version": "2.82.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@abaplint/cli/-/cli-2.82.1.tgz",
|
||||||
|
"integrity": "sha512-ZG5ykfBbUYw2dfe69k1ON4yQKNpzr+ddV4OwtYYU8/TPyPMkXnzn9hTPigiNK3SxyoKTW2X1KLtfllLAx5V+KA==",
|
||||||
|
"bin": {
|
||||||
|
"abaplint": "abaplint"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@abaplint/core": {
|
"node_modules/@abaplint/core": {
|
||||||
"version": "2.80.10",
|
"version": "2.80.10",
|
||||||
"resolved": "https://registry.npmjs.org/@abaplint/core/-/core-2.80.10.tgz",
|
"resolved": "https://registry.npmjs.org/@abaplint/core/-/core-2.80.10.tgz",
|
||||||
@@ -1211,6 +1223,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@abaplint/cli": {
|
||||||
|
"version": "2.82.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@abaplint/cli/-/cli-2.82.1.tgz",
|
||||||
|
"integrity": "sha512-ZG5ykfBbUYw2dfe69k1ON4yQKNpzr+ddV4OwtYYU8/TPyPMkXnzn9hTPigiNK3SxyoKTW2X1KLtfllLAx5V+KA=="
|
||||||
|
},
|
||||||
"@abaplint/core": {
|
"@abaplint/core": {
|
||||||
"version": "2.80.10",
|
"version": "2.80.10",
|
||||||
"resolved": "https://registry.npmjs.org/@abaplint/core/-/core-2.80.10.tgz",
|
"resolved": "https://registry.npmjs.org/@abaplint/core/-/core-2.80.10.tgz",
|
||||||
|
|||||||
@@ -28,6 +28,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@abaplint/transpiler-cli": "^1.6.66",
|
"@abaplint/transpiler-cli": "^1.6.66",
|
||||||
"@abaplint/transpiler": "^1.6.66",
|
"@abaplint/transpiler": "^1.6.66",
|
||||||
|
"@abaplint/cli": "^2.82.1",
|
||||||
"@abaplint/runtime": "^1.6.66"
|
"@abaplint/runtime": "^1.6.66"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
115
src/index.ts
115
src/index.ts
@@ -26,69 +26,104 @@ export interface ITranspilerConfig {
|
|||||||
options: Transpiler.ITranspilerOptions;
|
options: Transpiler.ITranspilerOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
const COMPILE_RESULT = "_compile_result.txt";
|
const output: IOutput = {
|
||||||
const RUN_RESULT = "_run_result.txt";
|
version: 1,
|
||||||
|
status: "pass",
|
||||||
|
}
|
||||||
|
|
||||||
function run() {
|
class Runner {
|
||||||
|
private readonly tmpDir: string;
|
||||||
|
|
||||||
let config: ITranspilerConfig = {
|
public constructor() {
|
||||||
input_folder: ".",
|
this.tmpDir = fs.mkdtempSync(path.join(tmpdir(), 'exercism-abap-runner-'));
|
||||||
input_filter: [],
|
}
|
||||||
output_folder: "compiled",
|
|
||||||
lib: "",
|
public run() {
|
||||||
write_source_map: true,
|
this.initialize();
|
||||||
write_unit_tests: true,
|
this.syntaxAndDownport();
|
||||||
options: {
|
if (output.status === "pass") {
|
||||||
ignoreSyntaxCheck: false,
|
this.transpile();
|
||||||
addFilenames: true,
|
}
|
||||||
addCommonJS: true,
|
if (output.status === "pass") {
|
||||||
unknownTypes: "runtimeError",
|
this.executeTests();
|
||||||
|
}
|
||||||
|
fs.writeFileSync(outputFile, JSON.stringify(output));
|
||||||
|
}
|
||||||
|
|
||||||
|
private syntaxAndDownport() {
|
||||||
|
const LINT_RESULT = "_abaplint.txt";
|
||||||
|
const abaplintConfig = Transpiler.config;
|
||||||
|
abaplintConfig.rules["downport"] = true;
|
||||||
|
fs.writeFileSync(path.join(this.tmpDir, "abaplint.json"), JSON.stringify(abaplintConfig, null, 2));
|
||||||
|
try {
|
||||||
|
execSync(`abaplint --fix > ` + LINT_RESULT, {
|
||||||
|
stdio: 'pipe',
|
||||||
|
cwd: this.tmpDir});
|
||||||
|
} catch (error) {
|
||||||
|
output.status = "error";
|
||||||
|
output.message = fs.readFileSync(path.join(this.tmpDir, LINT_RESULT), "utf-8");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const tmpDir = fs.mkdtempSync(path.join(tmpdir(), 'exercism-abap-runner-'));
|
private initialize() {
|
||||||
fs.writeFileSync(path.join(tmpDir, "abap_transpile.json"), JSON.stringify(config, null, 2));
|
const config: ITranspilerConfig = {
|
||||||
execSync(`cp ${inputDir}/*.abap ${tmpDir}`, {stdio: 'pipe'});
|
input_folder: ".",
|
||||||
fs.mkdirSync(`${tmpDir}/deps`);
|
input_filter: [],
|
||||||
execSync(`cp open-abap/src/unit/*.clas.abap ${tmpDir}/deps/`, {stdio: 'pipe'});
|
output_folder: "compiled",
|
||||||
execSync(`cp open-abap/src/classrun/*.intf.abap ${tmpDir}/deps/`, {stdio: 'pipe'});
|
lib: "",
|
||||||
|
write_source_map: true,
|
||||||
|
write_unit_tests: true,
|
||||||
|
options: {
|
||||||
|
ignoreSyntaxCheck: false,
|
||||||
|
addFilenames: true,
|
||||||
|
addCommonJS: true,
|
||||||
|
unknownTypes: "runtimeError",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const output: IOutput = {
|
fs.writeFileSync(path.join(this.tmpDir, "abap_transpile.json"), JSON.stringify(config, null, 2));
|
||||||
version: 1,
|
execSync(`cp ${inputDir}/*.abap ${this.tmpDir}`, {stdio: 'pipe'});
|
||||||
status: "pass",
|
fs.mkdirSync(`${this.tmpDir}/deps`);
|
||||||
|
execSync(`cp open-abap/src/unit/*.clas.abap ${this.tmpDir}/deps/`, {stdio: 'pipe'});
|
||||||
|
execSync(`cp open-abap/src/exceptions/* ${this.tmpDir}/deps/`, {stdio: 'pipe'});
|
||||||
|
execSync(`cp open-abap/src/ddic/*.xml ${this.tmpDir}/deps/`, {stdio: 'pipe'});
|
||||||
|
execSync(`cp open-abap/src/classrun/*.intf.abap ${this.tmpDir}/deps/`, {stdio: 'pipe'});
|
||||||
|
execSync(`rm ${this.tmpDir}/deps/*.testclasses.*`, {stdio: 'pipe'});
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
private transpile() {
|
||||||
execSync(`abap_transpile > ` + COMPILE_RESULT, {
|
const COMPILE_RESULT = "_compile_result.txt";
|
||||||
stdio: 'pipe',
|
try {
|
||||||
cwd: tmpDir});
|
execSync(`abap_transpile > ` + COMPILE_RESULT, {
|
||||||
} catch (error) {
|
stdio: 'pipe',
|
||||||
output.status = "error";
|
cwd: this.tmpDir});
|
||||||
output.message = fs.readFileSync(path.join(tmpDir, COMPILE_RESULT), "utf-8");
|
} catch (error) {
|
||||||
output.message = output.message.split("at Transpiler.validate")[0];
|
output.status = "error";
|
||||||
output.message = output.message.trim();
|
output.message = fs.readFileSync(path.join(this.tmpDir, COMPILE_RESULT), "utf-8");
|
||||||
|
output.message = output.message.split("at Transpiler.validate")[0];
|
||||||
|
output.message = output.message.trim();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (output.status === "pass") {
|
private executeTests() {
|
||||||
|
const RUN_RESULT = "_run_result.txt";
|
||||||
execSync(`npm link @abaplint/runtime`, {
|
execSync(`npm link @abaplint/runtime`, {
|
||||||
stdio: 'pipe',
|
stdio: 'pipe',
|
||||||
cwd: tmpDir });
|
cwd: this.tmpDir });
|
||||||
|
|
||||||
try {
|
try {
|
||||||
execSync(`node compiled/index.mjs > ` + RUN_RESULT, {
|
execSync(`node compiled/index.mjs > ` + RUN_RESULT, {
|
||||||
stdio: 'pipe',
|
stdio: 'pipe',
|
||||||
cwd: tmpDir});
|
cwd: this.tmpDir});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
output.status = "fail";
|
output.status = "fail";
|
||||||
output.message = fs.readFileSync(path.join(tmpDir, RUN_RESULT), "utf-8");
|
output.message = fs.readFileSync(path.join(this.tmpDir, RUN_RESULT), "utf-8");
|
||||||
if (output.message.includes("Error: ASSERT failed")) {
|
if (output.message.includes("Error: ASSERT failed")) {
|
||||||
output.message = output.message.split("Error: ASSERT failed")[0] + "Error: ASSERT failed";
|
output.message = output.message.split("Error: ASSERT failed")[0] + "Error: ASSERT failed";
|
||||||
}
|
}
|
||||||
output.message = output.message.trim();
|
output.message = output.message.trim();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fs.writeFileSync(outputFile, JSON.stringify(output));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
run();
|
new Runner().run();
|
||||||
54
test/test.ts
54
test/test.ts
@@ -20,40 +20,40 @@ function checkExpected(expectedFile: string): void {
|
|||||||
expect(act).to.equal(exp);
|
expect(act).to.equal(exp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function test(slug: string, expectedStatus: string) {
|
||||||
|
const path = join(fixtures, slug);
|
||||||
|
const res = spawnSync('bash', [run, slug, path, output], {cwd: root});
|
||||||
|
expect(res.status).to.equal(0);
|
||||||
|
checkExpected(join(path, "expected_results.json"));
|
||||||
|
expect(readResult().status).to.equal(expectedStatus);
|
||||||
|
}
|
||||||
|
|
||||||
describe('abap-test-runner', async () => {
|
describe('abap-test-runner', async () => {
|
||||||
it('simple, pass', async () => {
|
it('simple, pass', async () => {
|
||||||
const slug = "simple-pass";
|
test("simple-pass", "pass");
|
||||||
const path = join(fixtures, slug);
|
|
||||||
const res = spawnSync('bash', [run, slug, path, output], {cwd: root});
|
|
||||||
expect(res.status).to.equal(0);
|
|
||||||
expect(readResult().status).to.equal("pass");
|
|
||||||
checkExpected(join(path, "expected_results.json"));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('simple, fail', async () => {
|
it('simple-fail', async () => {
|
||||||
const slug = "simple-fail";
|
test("simple-fail", "fail");
|
||||||
const path = join(fixtures, slug);
|
|
||||||
const res = spawnSync('bash', [run, slug, path, output], {cwd: root});
|
|
||||||
expect(res.status).to.equal(0);
|
|
||||||
expect(readResult().status).to.equal("fail");
|
|
||||||
checkExpected(join(path, "expected_results.json"));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('simple, syntax error', async () => {
|
it('simple-error', async () => {
|
||||||
const slug = "simple-error";
|
test("simple-error", "error");
|
||||||
const path = join(fixtures, slug);
|
|
||||||
const res = spawnSync('bash', [run, slug, path, output], {cwd: root});
|
|
||||||
expect(res.status).to.equal(0);
|
|
||||||
expect(readResult().status).to.equal("error");
|
|
||||||
checkExpected(join(path, "expected_results.json"));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('hello-world, pass', async () => {
|
it('hello-world-pass', async () => {
|
||||||
const slug = "hello-world-pass";
|
test("hello-world-pass", "pass");
|
||||||
const path = join(fixtures, slug);
|
});
|
||||||
const res = spawnSync('bash', [run, slug, path, output], {cwd: root});
|
|
||||||
expect(res.status).to.equal(0);
|
it('simple-all-fail', async () => {
|
||||||
expect(readResult().status).to.equal("pass");
|
test("simple-all-fail", "fail");
|
||||||
checkExpected(join(path, "expected_results.json"));
|
});
|
||||||
|
|
||||||
|
it('simple-some-fail', async () => {
|
||||||
|
test("simple-some-fail", "fail");
|
||||||
|
});
|
||||||
|
|
||||||
|
it('simple-downport-pass', async () => {
|
||||||
|
test("simple-downport-pass", "pass");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
1
tests/simple-all-fail/expected_results.json
Normal file
1
tests/simple-all-fail/expected_results.json
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"version":1,"status":"fail","message":"ZCL_SIMPLE: running ltcl_simple->test1\n\nError: ASSERT failed"}
|
||||||
12
tests/simple-all-fail/zcl_simple.clas.abap
Normal file
12
tests/simple-all-fail/zcl_simple.clas.abap
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
CLASS zcl_simple DEFINITION PUBLIC.
|
||||||
|
PUBLIC SECTION.
|
||||||
|
METHODS run RETURNING VALUE(res) TYPE i.
|
||||||
|
ENDCLASS.
|
||||||
|
|
||||||
|
CLASS zcl_simple IMPLEMENTATION.
|
||||||
|
|
||||||
|
METHOD run.
|
||||||
|
res = 3.
|
||||||
|
ENDMETHOD.
|
||||||
|
|
||||||
|
ENDCLASS.
|
||||||
23
tests/simple-all-fail/zcl_simple.clas.testclasses.abap
Normal file
23
tests/simple-all-fail/zcl_simple.clas.testclasses.abap
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
CLASS ltcl_simple DEFINITION FOR TESTING RISK LEVEL HARMLESS DURATION SHORT FINAL.
|
||||||
|
|
||||||
|
PRIVATE SECTION.
|
||||||
|
METHODS test1 FOR TESTING RAISING cx_static_check.
|
||||||
|
METHODS test2 FOR TESTING RAISING cx_static_check.
|
||||||
|
|
||||||
|
ENDCLASS.
|
||||||
|
|
||||||
|
CLASS ltcl_simple IMPLEMENTATION.
|
||||||
|
|
||||||
|
METHOD test1.
|
||||||
|
cl_abap_unit_assert=>assert_equals(
|
||||||
|
act = 2
|
||||||
|
exp = 123 ).
|
||||||
|
ENDMETHOD.
|
||||||
|
|
||||||
|
METHOD test2.
|
||||||
|
cl_abap_unit_assert=>assert_equals(
|
||||||
|
act = 2
|
||||||
|
exp = 36 ).
|
||||||
|
ENDMETHOD.
|
||||||
|
|
||||||
|
ENDCLASS.
|
||||||
1
tests/simple-downport-pass/expected_results.json
Normal file
1
tests/simple-downport-pass/expected_results.json
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"version":1,"status":"pass"}
|
||||||
12
tests/simple-downport-pass/zcl_simple.clas.abap
Normal file
12
tests/simple-downport-pass/zcl_simple.clas.abap
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
CLASS zcl_simple DEFINITION PUBLIC.
|
||||||
|
PUBLIC SECTION.
|
||||||
|
METHODS run RETURNING VALUE(res) TYPE i.
|
||||||
|
ENDCLASS.
|
||||||
|
|
||||||
|
CLASS zcl_simple IMPLEMENTATION.
|
||||||
|
|
||||||
|
METHOD run.
|
||||||
|
res = 3.
|
||||||
|
ENDMETHOD.
|
||||||
|
|
||||||
|
ENDCLASS.
|
||||||
17
tests/simple-downport-pass/zcl_simple.clas.testclasses.abap
Normal file
17
tests/simple-downport-pass/zcl_simple.clas.testclasses.abap
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
CLASS ltcl_simple DEFINITION FOR TESTING RISK LEVEL HARMLESS DURATION SHORT FINAL.
|
||||||
|
|
||||||
|
PRIVATE SECTION.
|
||||||
|
METHODS test FOR TESTING RAISING cx_static_check.
|
||||||
|
|
||||||
|
ENDCLASS.
|
||||||
|
|
||||||
|
CLASS ltcl_simple IMPLEMENTATION.
|
||||||
|
|
||||||
|
METHOD test.
|
||||||
|
DATA(simple) = NEW zcl_simple( ).
|
||||||
|
cl_abap_unit_assert=>assert_equals(
|
||||||
|
act = simple->run( )
|
||||||
|
exp = 3 ).
|
||||||
|
ENDMETHOD.
|
||||||
|
|
||||||
|
ENDCLASS.
|
||||||
@@ -1 +1 @@
|
|||||||
{"version":1,"status":"error","message":"Transpiler CLI\nUsing config: abap_transpile.json\n7 files added\n0 files skipped\n\nBuilding\nError: parser_error, Statement does not exist in ABAPopen-abap(or a parser error), \"blah\", zcl_simple.clas.abap:9"}
|
{"version":1,"status":"error","message":"./zcl_simple.clas.abap[9, 5] - Statement does not exist in ABAPopen-abap(or a parser error), \"blah\" (parser_error) [E]\nabaplint: 1 issue(s) found, 59 file(s) analyzed\nFixes applied"}
|
||||||
1
tests/simple-some-fail/expected_results.json
Normal file
1
tests/simple-some-fail/expected_results.json
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"version":1,"status":"fail","message":"ZCL_SIMPLE: running ltcl_simple->test1\nZCL_SIMPLE: running ltcl_simple->test2\n\nError: ASSERT failed"}
|
||||||
12
tests/simple-some-fail/zcl_simple.clas.abap
Normal file
12
tests/simple-some-fail/zcl_simple.clas.abap
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
CLASS zcl_simple DEFINITION PUBLIC.
|
||||||
|
PUBLIC SECTION.
|
||||||
|
METHODS run RETURNING VALUE(res) TYPE i.
|
||||||
|
ENDCLASS.
|
||||||
|
|
||||||
|
CLASS zcl_simple IMPLEMENTATION.
|
||||||
|
|
||||||
|
METHOD run.
|
||||||
|
res = 3.
|
||||||
|
ENDMETHOD.
|
||||||
|
|
||||||
|
ENDCLASS.
|
||||||
23
tests/simple-some-fail/zcl_simple.clas.testclasses.abap
Normal file
23
tests/simple-some-fail/zcl_simple.clas.testclasses.abap
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
CLASS ltcl_simple DEFINITION FOR TESTING RISK LEVEL HARMLESS DURATION SHORT FINAL.
|
||||||
|
|
||||||
|
PRIVATE SECTION.
|
||||||
|
METHODS test1 FOR TESTING RAISING cx_static_check.
|
||||||
|
METHODS test2 FOR TESTING RAISING cx_static_check.
|
||||||
|
|
||||||
|
ENDCLASS.
|
||||||
|
|
||||||
|
CLASS ltcl_simple IMPLEMENTATION.
|
||||||
|
|
||||||
|
METHOD test1.
|
||||||
|
cl_abap_unit_assert=>assert_equals(
|
||||||
|
act = 2
|
||||||
|
exp = 2 ).
|
||||||
|
ENDMETHOD.
|
||||||
|
|
||||||
|
METHOD test2.
|
||||||
|
cl_abap_unit_assert=>assert_equals(
|
||||||
|
act = 2
|
||||||
|
exp = 36 ).
|
||||||
|
ENDMETHOD.
|
||||||
|
|
||||||
|
ENDCLASS.
|
||||||
Reference in New Issue
Block a user