Skip to content

Commit 06e581c

Browse files
Initial engine
1 parent 935a0f9 commit 06e581c

25 files changed

+903
-0
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
/build/*
2+
/node_modules/*

assertions/index.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { RunResult } from "../engine/run_result";
2+
import { Step } from "../engine/step";
3+
import { Command } from "../engine/command";
4+
import { NoErrorCode } from "./noErrorCode";
5+
import { NoException } from "./noException";
6+
7+
8+
export class Assertions{
9+
10+
public noErrorCode(result: RunResult): Assertions {
11+
NoErrorCode.run(result);
12+
return this;
13+
}
14+
15+
public noException(result: RunResult): Assertions {
16+
NoException.run(result);
17+
return this;
18+
}
19+
}

assertions/noErrorCode.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { RunResult } from "../engine/run_result";
2+
3+
export class NoErrorCode{
4+
public static run(result: RunResult): void {
5+
if(result.returnCode != 0){
6+
throw new Error("returnCode is not 0");
7+
}
8+
}
9+
}

assertions/noException.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { RunResult } from "../engine/run_result";
2+
3+
export class NoException {
4+
public static run(result: RunResult): void {
5+
if(result.exceptions.length > 0){
6+
throw new Error("Unexpected exception.");
7+
}
8+
}
9+
}

buildRun.ps1

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
tsc
2+
Copy-Item -Force -Recurse -Path $PSScriptRoot\engine\parser.def -Destination $PSScriptRoot\build\engine\parser.def
3+
Copy-Item -Force -Recurse -Path $PSScriptRoot\playbooks\ -Destination $PSScriptRoot\build
4+
Copy-Item -Force -Recurse -Path $PSScriptRoot\environments\ -Destination $PSScriptRoot\build
5+
Copy-Item -Force -Recurse -Exclude *.ts -Path $PSScriptRoot\runners\ -Destination $PSScriptRoot\build
6+
node $PSScriptRoot\build\engine\run.js

engine/command.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export class Command{
2+
public name: string;
3+
public parameters: string;
4+
}

engine/engine.ts

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import { Environment } from "./environment";
2+
import { Playbook } from "./playbook";
3+
import { Runner } from "./runner";
4+
import { RunResult } from "./run_result";
5+
6+
7+
export class Engine {
8+
9+
private runners: Map<string, Runner> = new Map<string, Runner>();
10+
private variables: Map<string, any> = new Map<string, any>();
11+
12+
constructor(private environmentName: string, private environment: Environment, private playbook: Playbook) { }
13+
14+
async run() {
15+
console.log("Environment: " + this.environmentName);
16+
if (! await this.isEnvironmentComplete()) {
17+
if (this.environment.failOnIncomplete) {
18+
throw "Environment incomplete: " + this.environmentName;
19+
}
20+
console.log("Environment incomplete: " + this.environmentName);
21+
return;
22+
}
23+
for (let runnerIndex in this.environment.runners) {
24+
(await this.getRunner(this.environment.runners[runnerIndex])).init(this.playbook);
25+
}
26+
27+
for (let stepIndex in this.playbook.steps) {
28+
for (let lineIndex in this.playbook.steps[stepIndex].lines) {
29+
for (let runnerIndex in this.environment.runners) {
30+
let runner = await this.getRunner(this.environment.runners[runnerIndex]);
31+
if (runner.supports(this.playbook.steps[stepIndex].lines[lineIndex].name)) {
32+
var result = new RunResult();
33+
try {
34+
result = runner.run(this.playbook.steps[stepIndex], this.playbook.steps[stepIndex].lines[lineIndex]);
35+
}
36+
catch (e) {
37+
result.exceptions.push(e);
38+
}
39+
await runner.assert(this.playbook.steps[stepIndex], this.playbook.steps[stepIndex].lines[lineIndex], result);
40+
break;
41+
}
42+
}
43+
}
44+
}
45+
46+
for (let runnerIndex in this.environment.runners) {
47+
(await this.getRunner(this.environment.runners[runnerIndex])).destroy(this.playbook);
48+
}
49+
}
50+
51+
public setVariable(name: string, value: any) {
52+
this.variables.set(name, value);
53+
}
54+
55+
private async isEnvironmentComplete(): Promise<boolean> {
56+
for (let stepIndex in this.playbook.steps) {
57+
for (let lineIndex in this.playbook.steps[stepIndex].lines) {
58+
let isSupported = false;
59+
for (let runnerIndex in this.environment.runners) {
60+
if ((await this.getRunner(this.environment.runners[runnerIndex])).supports(this.playbook.steps[stepIndex].lines[lineIndex].name)) {
61+
isSupported = true;
62+
break;
63+
}
64+
}
65+
if (!isSupported) {
66+
return false;
67+
}
68+
}
69+
}
70+
71+
return true;
72+
}
73+
74+
private async getRunner(name: string): Promise<Runner> {
75+
if (!this.runners.has(name)) {
76+
await this.loadRunner(name);
77+
}
78+
return this.runners.get(name);
79+
}
80+
81+
private async loadRunner(name: string) {
82+
let imp = await import("../runners/" + name + "/index");
83+
let map = new Map<string, any>();
84+
for (let index in imp) {
85+
map.set(index.toLowerCase(), imp[index]);
86+
}
87+
let runner: Runner = new (map.get(name.toLowerCase()));
88+
runner.registerGetVariableCallback((name) => this.variables.get(name));
89+
runner.registerSetVariableCallback((name, value) => this.setVariable(name, value));
90+
runner.path = __dirname + "/../runners/" + name + "/";
91+
runner.name = name;
92+
runner.playbookName = this.playbook.name;
93+
runner.playbookPath = this.playbook.path;
94+
this.runners.set(name, runner);
95+
}
96+
}

engine/environment.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
2+
export interface Environment{
3+
failOnIncomplete: boolean;
4+
runners: string[];
5+
}

engine/parser.def

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
start
2+
= headline
3+
description
4+
step+
5+
6+
headline
7+
= "=" _ string ___
8+
9+
description
10+
= "====" ___
11+
descriptionlines
12+
"====" ___
13+
14+
descriptionlines
15+
= descriptionline+ { return { "descriptionlines": text()}; }
16+
17+
descriptionline
18+
= !"====" string __
19+
20+
step
21+
= (
22+
"====" ___
23+
stepinner
24+
steptextafterlines
25+
"====" __
26+
)
27+
/ stepinner
28+
29+
stepinner
30+
= steptextlines?
31+
"[step]" ___
32+
"--" ___
33+
steplines
34+
"--" __
35+
36+
steptextlines
37+
= steptextline* { return { "steptextlines": text()}; }
38+
39+
steptextline
40+
= !"[step]" string __
41+
42+
steptextafterlines
43+
= steptextafterline* { return { "steptextafterlines": text()}; }
44+
45+
steptextafterline
46+
= !"====" string __
47+
48+
steplines
49+
= stepline+
50+
51+
stepline
52+
= !"--" string __ { return { "stepline": text()}; }
53+
54+
string "string"
55+
= [a-zA-Z0-9.(),;:/-_- ]+ { return text(); }
56+
57+
_ "whitespace"
58+
= [ \t]*
59+
60+
__ "linebreak"
61+
= [ \t\n\r]*
62+
63+
___ "linebreak"
64+
= [ \t\n\r]+

engine/parser.ts

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import { Playbook } from "./playbook";
2+
import { Step } from "./step";
3+
import { Command } from "./command";
4+
const pegjs = require("pegjs");
5+
const tspegjs = require("ts-pegjs");
6+
const fs = require('fs');
7+
8+
9+
export class Parser {
10+
private parser;
11+
12+
constructor() {
13+
let def = fs.readFileSync(__dirname + "/parser.def", 'utf8');
14+
this.parser = pegjs.generate(def);
15+
16+
}
17+
18+
parse(inputFile: string): Playbook {
19+
let input = fs.readFileSync(inputFile, 'utf8');
20+
let parseResult = this.parser.parse(input);
21+
let result = new Playbook();
22+
result.title = parseResult[0][2];
23+
result.description = parseResult[1][2].descriptionlines;
24+
for(let index in parseResult[2]){
25+
let step = new Step();
26+
step.text = this.getText(parseResult,index);;
27+
step.lines = this.getLines(parseResult,index);
28+
step.textAfter = this.getTextAfter(parseResult, index);
29+
30+
result.steps.push(step);
31+
}
32+
33+
return result;
34+
}
35+
36+
getText(parseResult, index){
37+
try {
38+
return parseResult[2][index][0].steptextlines || parseResult[2][index][2][0].steptextlines;
39+
} catch (error) {
40+
return parseResult[2][index][2][0].steptextlines;
41+
}
42+
}
43+
44+
getLines(parseResult, index):Command[]{
45+
try {
46+
return (parseResult[2][index][5][0].stepline || parseResult[2][index][2][5][0].stepline).split("\r\n").filter(e => e != '').map(e => this.createCommand(e));
47+
} catch (error) {
48+
return parseResult[2][index][2][5][0].stepline.split("\r\n").filter(e => e != '').map(e => this.createCommand(e));
49+
}
50+
}
51+
52+
createCommand(line: string): Command{
53+
let re =/([^(]+)\(([^)]*)\)/;
54+
let result = re.exec(line);
55+
let retVal = new Command();
56+
retVal.name = result[1].trim();
57+
retVal.parameters = result[2];
58+
return retVal;
59+
}
60+
61+
getTextAfter(parseResult, index){
62+
try {
63+
return parseResult[2][index][3].steptextafterlines || "";
64+
} catch (error) {
65+
return "";
66+
}
67+
}
68+
}

0 commit comments

Comments
 (0)