allow functions to call other functions #53
This commit is contained in:
@@ -0,0 +1,192 @@
|
||||
import 'mocha';
|
||||
import { expect } from 'chai';
|
||||
import { ISharedFunction } from '@/application/Parser/Script/Compiler/Function/ISharedFunction';
|
||||
import { FunctionData } from 'js-yaml-loader!*';
|
||||
import { IFunctionCallCompiler } from '@/application/Parser/Script/Compiler/FunctionCall/IFunctionCallCompiler';
|
||||
import { FunctionCompiler } from '@/application/Parser/Script/Compiler/Function/FunctionCompiler';
|
||||
import { FunctionCallCompilerStub } from '../../../../../stubs/FunctionCallCompilerStub';
|
||||
import { FunctionDataStub } from '../../../../../stubs/FunctionDataStub';
|
||||
|
||||
describe('FunctionsCompiler', () => {
|
||||
describe('compileFunctions', () => {
|
||||
describe('validates functions', () => {
|
||||
it('throws if one of the functions is undefined', () => {
|
||||
// arrange
|
||||
const expectedError = `some functions are undefined`;
|
||||
const functions = [ FunctionDataStub.createWithCode(), undefined ];
|
||||
const sut = new MockableFunctionCompiler();
|
||||
// act
|
||||
const act = () => sut.compileFunctions(functions);
|
||||
// assert
|
||||
expect(act).to.throw(expectedError);
|
||||
});
|
||||
it('throws when functions have same names', () => {
|
||||
// arrange
|
||||
const name = 'same-func-name';
|
||||
const expectedError = `duplicate function name: "${name}"`;
|
||||
const functions = [
|
||||
FunctionDataStub.createWithCode().withName(name),
|
||||
FunctionDataStub.createWithCode().withName(name),
|
||||
];
|
||||
const sut = new MockableFunctionCompiler();
|
||||
// act
|
||||
const act = () => sut.compileFunctions(functions);
|
||||
// assert
|
||||
expect(act).to.throw(expectedError);
|
||||
});
|
||||
it('throws when function parameters have same names', () => {
|
||||
// arrange
|
||||
const parameterName = 'duplicate-parameter';
|
||||
const func = FunctionDataStub.createWithCall()
|
||||
.withParameters(parameterName, parameterName);
|
||||
const expectedError = `"${func.name}": duplicate parameter name: "${parameterName}"`;
|
||||
const sut = new MockableFunctionCompiler();
|
||||
// act
|
||||
const act = () => sut.compileFunctions([ func ]);
|
||||
// assert
|
||||
expect(act).to.throw(expectedError);
|
||||
});
|
||||
describe('throws when when function have duplicate code', () => {
|
||||
it('code', () => {
|
||||
// arrange
|
||||
const code = 'duplicate-code';
|
||||
const expectedError = `duplicate "code" in functions: "${code}"`;
|
||||
const functions = [
|
||||
FunctionDataStub.createWithoutCallOrCodes().withName('func-1').withCode(code),
|
||||
FunctionDataStub.createWithoutCallOrCodes().withName('func-2').withCode(code),
|
||||
];
|
||||
const sut = new MockableFunctionCompiler();
|
||||
// act
|
||||
const act = () => sut.compileFunctions(functions);
|
||||
// assert
|
||||
expect(act).to.throw(expectedError);
|
||||
});
|
||||
it('revertCode', () => {
|
||||
// arrange
|
||||
const revertCode = 'duplicate-revert-code';
|
||||
const expectedError = `duplicate "revertCode" in functions: "${revertCode}"`;
|
||||
const functions = [
|
||||
FunctionDataStub.createWithoutCallOrCodes()
|
||||
.withName('func-1').withCode('code-1').withRevertCode(revertCode),
|
||||
FunctionDataStub.createWithoutCallOrCodes()
|
||||
.withName('func-2').withCode('code-2').withRevertCode(revertCode),
|
||||
];
|
||||
const sut = new MockableFunctionCompiler();
|
||||
// act
|
||||
const act = () => sut.compileFunctions(functions);
|
||||
// assert
|
||||
expect(act).to.throw(expectedError);
|
||||
});
|
||||
});
|
||||
it('both code and call are defined', () => {
|
||||
// arrange
|
||||
const functionName = 'invalid-function';
|
||||
const expectedError = `both "code" and "call" are defined in "${functionName}"`;
|
||||
const invalidFunction = FunctionDataStub.createWithoutCallOrCodes()
|
||||
.withName(functionName)
|
||||
.withCode('code')
|
||||
.withMockCall();
|
||||
const sut = new MockableFunctionCompiler();
|
||||
// act
|
||||
const act = () => sut.compileFunctions([ invalidFunction ]);
|
||||
// assert
|
||||
expect(act).to.throw(expectedError);
|
||||
});
|
||||
it('neither code and call is defined', () => {
|
||||
// arrange
|
||||
const functionName = 'invalid-function';
|
||||
const expectedError = `neither "code" or "call" is defined in "${functionName}"`;
|
||||
const invalidFunction = FunctionDataStub.createWithoutCallOrCodes()
|
||||
.withName(functionName);
|
||||
const sut = new MockableFunctionCompiler();
|
||||
// act
|
||||
const act = () => sut.compileFunctions([ invalidFunction ]);
|
||||
// assert
|
||||
expect(act).to.throw(expectedError);
|
||||
});
|
||||
});
|
||||
it('returns empty with empty functions', () => {
|
||||
// arrange
|
||||
const emptyValues = [ [], undefined ];
|
||||
const sut = new MockableFunctionCompiler();
|
||||
for (const emptyFunctions of emptyValues) {
|
||||
// act
|
||||
const actual = sut.compileFunctions(emptyFunctions);
|
||||
// assert
|
||||
expect(actual).to.not.equal(undefined);
|
||||
}
|
||||
});
|
||||
it('parses single function with code as expected', () => {
|
||||
// arrange
|
||||
const name = 'function-name';
|
||||
const expected = FunctionDataStub
|
||||
.createWithoutCallOrCodes()
|
||||
.withName(name)
|
||||
.withCode('expected-code')
|
||||
.withRevertCode('expected-revert-code')
|
||||
.withParameters('expected-parameter-1', 'expected-parameter-2');
|
||||
const sut = new MockableFunctionCompiler();
|
||||
// act
|
||||
const collection = sut.compileFunctions([ expected ]);
|
||||
// expect
|
||||
const actual = collection.getFunctionByName(name);
|
||||
expectEqualFunctions(expected, actual);
|
||||
});
|
||||
it('parses function with call as expected', () => {
|
||||
// arrange
|
||||
const calleeName = 'callee-function';
|
||||
const caller = FunctionDataStub.createWithoutCallOrCodes()
|
||||
.withName('caller-function')
|
||||
.withCall({ function: calleeName });
|
||||
const callee = FunctionDataStub.createWithoutCallOrCodes()
|
||||
.withName(calleeName)
|
||||
.withCode('expected-code')
|
||||
.withRevertCode('expected-revert-code');
|
||||
const sut = new MockableFunctionCompiler();
|
||||
// act
|
||||
const collection = sut.compileFunctions([ caller, callee ]);
|
||||
// expect
|
||||
const actual = collection.getFunctionByName(caller.name);
|
||||
expectEqualFunctionCode(callee, actual);
|
||||
});
|
||||
it('parses multiple functions with call as expected', () => {
|
||||
// arrange
|
||||
const calleeName = 'callee-function';
|
||||
const caller1 = FunctionDataStub.createWithoutCallOrCodes()
|
||||
.withName('caller-function')
|
||||
.withCall({ function: calleeName });
|
||||
const caller2 = FunctionDataStub.createWithoutCallOrCodes()
|
||||
.withName('caller-function-2')
|
||||
.withCall({ function: calleeName });
|
||||
const callee = FunctionDataStub.createWithoutCallOrCodes()
|
||||
.withName(calleeName)
|
||||
.withCode('expected-code')
|
||||
.withRevertCode('expected-revert-code');
|
||||
const sut = new MockableFunctionCompiler();
|
||||
// act
|
||||
const collection = sut.compileFunctions([ caller1, caller2, callee ]);
|
||||
// expect
|
||||
const compiledCaller1 = collection.getFunctionByName(caller1.name);
|
||||
const compiledCaller2 = collection.getFunctionByName(caller2.name);
|
||||
expectEqualFunctionCode(callee, compiledCaller1);
|
||||
expectEqualFunctionCode(callee, compiledCaller2);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function expectEqualFunctions(expected: FunctionData, actual: ISharedFunction) {
|
||||
expect(actual.name).to.equal(expected.name);
|
||||
expect(actual.parameters).to.deep.equal(expected.parameters);
|
||||
expectEqualFunctionCode(expected, actual);
|
||||
}
|
||||
|
||||
function expectEqualFunctionCode(expected: FunctionData, actual: ISharedFunction) {
|
||||
expect(actual.code).to.equal(expected.code);
|
||||
expect(actual.revertCode).to.equal(expected.revertCode);
|
||||
}
|
||||
|
||||
class MockableFunctionCompiler extends FunctionCompiler {
|
||||
constructor(functionCallCompiler: IFunctionCallCompiler = new FunctionCallCompilerStub()) {
|
||||
super(functionCallCompiler);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
import 'mocha';
|
||||
import { expect } from 'chai';
|
||||
import { SharedFunction } from '@/application/Parser/Script/Compiler/Function/SharedFunction';
|
||||
|
||||
describe('SharedFunction', () => {
|
||||
describe('name', () => {
|
||||
it('sets as expected', () => {
|
||||
// arrange
|
||||
const expected = 'expected-function-name';
|
||||
// act
|
||||
const sut = new SharedFunctionBuilder()
|
||||
.withName(expected)
|
||||
.build();
|
||||
// assert
|
||||
expect(sut.name).equal(expected);
|
||||
});
|
||||
it('throws if empty or undefined', () => {
|
||||
// arrange
|
||||
const expectedError = 'undefined function name';
|
||||
const invalidValues = [ undefined, '' ];
|
||||
for (const invalidValue of invalidValues) {
|
||||
// act
|
||||
const act = () => new SharedFunctionBuilder()
|
||||
.withName(invalidValue)
|
||||
.build();
|
||||
// assert
|
||||
expect(act).to.throw(expectedError);
|
||||
}
|
||||
});
|
||||
});
|
||||
describe('parameters', () => {
|
||||
it('sets as expected', () => {
|
||||
// arrange
|
||||
const expected = [ 'expected-parameter' ];
|
||||
// act
|
||||
const sut = new SharedFunctionBuilder()
|
||||
.withParameters(expected)
|
||||
.build();
|
||||
// assert
|
||||
expect(sut.parameters).to.deep.equal(expected);
|
||||
});
|
||||
it('returns empty array if undefined', () => {
|
||||
// arrange
|
||||
const expected = [ ];
|
||||
const value = undefined;
|
||||
// act
|
||||
const sut = new SharedFunctionBuilder()
|
||||
.withParameters(value)
|
||||
.build();
|
||||
// assert
|
||||
expect(sut.parameters).to.not.equal(undefined);
|
||||
expect(sut.parameters).to.deep.equal(expected);
|
||||
});
|
||||
});
|
||||
describe('code', () => {
|
||||
it('sets as expected', () => {
|
||||
// arrange
|
||||
const expected = 'expected-code';
|
||||
// act
|
||||
const sut = new SharedFunctionBuilder()
|
||||
.withCode(expected)
|
||||
.build();
|
||||
// assert
|
||||
expect(sut.code).equal(expected);
|
||||
});
|
||||
it('throws if empty or undefined', () => {
|
||||
// arrange
|
||||
const functionName = 'expected-function-name';
|
||||
const expectedError = `undefined function ("${functionName}") code`;
|
||||
const invalidValues = [ undefined, '' ];
|
||||
for (const invalidValue of invalidValues) {
|
||||
// act
|
||||
const act = () => new SharedFunctionBuilder()
|
||||
.withName(functionName)
|
||||
.withCode(invalidValue)
|
||||
.build();
|
||||
// assert
|
||||
expect(act).to.throw(expectedError);
|
||||
}
|
||||
});
|
||||
});
|
||||
describe('revertCode', () => {
|
||||
it('sets as expected', () => {
|
||||
// arrange
|
||||
const testData = [ 'expected-revert-code', undefined, '' ];
|
||||
for (const data of testData) {
|
||||
// act
|
||||
const sut = new SharedFunctionBuilder()
|
||||
.withRevertCode(data)
|
||||
.build();
|
||||
// assert
|
||||
expect(sut.revertCode).equal(data);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
class SharedFunctionBuilder {
|
||||
private name = 'name';
|
||||
private parameters: readonly string[] = [ 'parameter' ];
|
||||
private code = 'code';
|
||||
private revertCode = 'revert-code';
|
||||
|
||||
public build(): SharedFunction {
|
||||
return new SharedFunction(
|
||||
this.name,
|
||||
this.parameters,
|
||||
this.code,
|
||||
this.revertCode,
|
||||
);
|
||||
}
|
||||
public withName(name: string) {
|
||||
this.name = name;
|
||||
return this;
|
||||
}
|
||||
public withParameters(parameters: readonly string[]) {
|
||||
this.parameters = parameters;
|
||||
return this;
|
||||
}
|
||||
public withCode(code: string) {
|
||||
this.code = code;
|
||||
return this;
|
||||
}
|
||||
public withRevertCode(revertCode: string) {
|
||||
this.revertCode = revertCode;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
import 'mocha';
|
||||
import { expect } from 'chai';
|
||||
import { SharedFunctionCollection } from '@/application/Parser/Script/Compiler/Function/SharedFunctionCollection';
|
||||
import { SharedFunctionStub } from '../../../../../stubs/SharedFunctionStub';
|
||||
|
||||
describe('SharedFunctionCollection', () => {
|
||||
describe('addFunction', () => {
|
||||
it('throws if function is undefined', () => {
|
||||
// arrange
|
||||
const expectedError = 'undefined function';
|
||||
const func = undefined;
|
||||
const sut = new SharedFunctionCollection();
|
||||
// act
|
||||
const act = () => sut.addFunction(func);
|
||||
// assert
|
||||
expect(act).to.throw(expectedError);
|
||||
});
|
||||
it('throws if function with same name already exists', () => {
|
||||
// arrange
|
||||
const functionName = 'duplicate-function';
|
||||
const expectedError = `function with name ${functionName} already exists`;
|
||||
const func = new SharedFunctionStub()
|
||||
.withName('duplicate-function');
|
||||
const sut = new SharedFunctionCollection();
|
||||
sut.addFunction(func);
|
||||
// act
|
||||
const act = () => sut.addFunction(func);
|
||||
// assert
|
||||
expect(act).to.throw(expectedError);
|
||||
|
||||
});
|
||||
});
|
||||
describe('getFunctionByName', () => {
|
||||
it('throws if name is undefined', () => {
|
||||
// arrange
|
||||
const expectedError = 'undefined function name';
|
||||
const invalidValues = [ undefined, '' ];
|
||||
const sut = new SharedFunctionCollection();
|
||||
for (const invalidValue of invalidValues) {
|
||||
const name = invalidValue;
|
||||
// act
|
||||
const act = () => sut.getFunctionByName(name);
|
||||
// assert
|
||||
expect(act).to.throw(expectedError);
|
||||
}
|
||||
});
|
||||
it('throws if function does not exist', () => {
|
||||
// arrange
|
||||
const name = 'unique-name';
|
||||
const expectedError = `called function is not defined "${name}"`;
|
||||
const func = new SharedFunctionStub()
|
||||
.withName('unexpected-name');
|
||||
const sut = new SharedFunctionCollection();
|
||||
sut.addFunction(func);
|
||||
// act
|
||||
const act = () => sut.getFunctionByName(name);
|
||||
// assert
|
||||
expect(act).to.throw(expectedError);
|
||||
});
|
||||
it('returns existing function', () => {
|
||||
// arrange
|
||||
const name = 'expected-function-name';
|
||||
const expected = new SharedFunctionStub()
|
||||
.withName(name);
|
||||
const sut = new SharedFunctionCollection();
|
||||
sut.addFunction(new SharedFunctionStub().withName('another-function-name'));
|
||||
sut.addFunction(expected);
|
||||
// act
|
||||
const actual = sut.getFunctionByName(name);
|
||||
// assert
|
||||
expect(actual).to.equal(expected);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user