Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
georgejecook committed Oct 25, 2023
1 parent cfdaf2f commit 316e84c
Show file tree
Hide file tree
Showing 8 changed files with 347 additions and 217 deletions.
67 changes: 39 additions & 28 deletions bsc-plugin/src/lib/rooibos/MockUtil.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ function trimLeading(text: string) {
return text.split('\n').map((line) => line.trimStart()).join('\n');
}

describe.only('MockUtil', () => {
describe('MockUtil', () => {
let program: Program;
let builder: ProgramBuilder;
let plugin: RooibosPlugin;
Expand Down Expand Up @@ -78,14 +78,14 @@ describe.only('MockUtil', () => {
await builder.transpile();
let a = getContents('source/code.brs');
let b = trimLeading(`function sayHello(a1, a2)
if RBS_CC_1_getMocksByFunctionName()["sayHello"] <> invalid
result = RBS_CC_1_getMocksByFunctionName()["sayHello"].callback(a1,a2)
if RBS_SM_1_getMocksByFunctionName()["sayhello"] <> invalid
result = RBS_SM_1_getMocksByFunctionName()["sayhello"].callback(a1,a2)
return result
end if
print "hello"
end function
function RBS_CC_1_getMocksByFunctionName()
function RBS_SM_1_getMocksByFunctionName()
if m._rMocksByFunctionName = invalid
m._rMocksByFunctionName = {}
end if
Expand All @@ -109,14 +109,14 @@ describe.only('MockUtil', () => {
await builder.transpile();
let a = getContents('source/code.brs');
let b = trimLeading(`function sayHello(a1, a2)
if RBS_CC_1_getMocksByFunctionName()["sayHello"] <> invalid
result = RBS_CC_1_getMocksByFunctionName()["sayHello"].callback(a1,a2)
if RBS_SM_1_getMocksByFunctionName()["sayhello"] <> invalid
result = RBS_SM_1_getMocksByFunctionName()["sayhello"].callback(a1,a2)
return result
end if
print "hello"
end function
function RBS_CC_1_getMocksByFunctionName()
function RBS_SM_1_getMocksByFunctionName()
if m._rMocksByFunctionName = invalid
m._rMocksByFunctionName = {}
end if
Expand All @@ -126,7 +126,7 @@ describe.only('MockUtil', () => {
expect(a).to.equal(b);

});
it.only('weird raletracker task issue I saw', async () => {
it('weird raletracker task issue I saw', async () => {
program.setFile('source/code.bs', `
Sub RedLines_SetRulerLines(rulerLines)
For Each line In rulerLines.Items()
Expand All @@ -142,15 +142,26 @@ describe.only('MockUtil', () => {
expect(program.getDiagnostics()).to.be.empty;
await builder.transpile();
let a = getContents('source/code.brs');
let b = trimLeading(`function sayHello(a1, a2)
if RBS_CC_1_getMocksByFunctionName()["sayHello"] <> invalid
result = RBS_CC_1_getMocksByFunctionName()["sayHello"].callback(a1,a2)
let b = trimLeading(`Sub RedLines_SetRulerLines(rulerLines)
if RBS_SM_1_getMocksByFunctionName()["redlines_setrulerlines"] <> invalid
result = RBS_SM_1_getMocksByFunctionName()["redlines_setrulerlines"].callback(rulerLines)
return
end if
For Each line In rulerLines.Items()
RedLines_AddLine(line.key, line.value.position, line.value.coords, m.node, m.childMap)
End For
end Sub
Sub RedLines_AddLine(id, position, coords, node, childMap) as Object
if RBS_SM_1_getMocksByFunctionName()["redlines_addline"] <> invalid
result = RBS_SM_1_getMocksByFunctionName()["redlines_addline"].callback(id,position,coords,node,childMap)
return result
end if
print "hello"
end function
line = CreateObject("roSGNode", "Rectangle")
line.setField("id", id)
end sub
function RBS_CC_1_getMocksByFunctionName()
function RBS_SM_1_getMocksByFunctionName()
if m._rMocksByFunctionName = invalid
m._rMocksByFunctionName = {}
end if
Expand All @@ -172,14 +183,14 @@ describe.only('MockUtil', () => {
await builder.transpile();
let a = getContents('source/code.brs');
let b = trimLeading(`sub sayHello(a1, a2)
if RBS_CC_1_getMocksByFunctionName()["sayHello"] <> invalid
result = RBS_CC_1_getMocksByFunctionName()["sayHello"].callback(a1,a2)
if RBS_SM_1_getMocksByFunctionName()["sayhello"] <> invalid
result = RBS_SM_1_getMocksByFunctionName()["sayhello"].callback(a1,a2)
return
end if
print "hello"
end sub
function RBS_CC_1_getMocksByFunctionName()
function RBS_SM_1_getMocksByFunctionName()
if m._rMocksByFunctionName = invalid
m._rMocksByFunctionName = {}
end if
Expand All @@ -203,14 +214,14 @@ describe.only('MockUtil', () => {
await builder.transpile();
let a = getContents('source/code.brs');
let b = trimLeading(`function person_utils_sayHello(a1, a2)
if RBS_CC_1_getMocksByFunctionName()["person_utils_sayHello"] <> invalid
result = RBS_CC_1_getMocksByFunctionName()["person_utils_sayHello"].callback(a1,a2)
if RBS_SM_1_getMocksByFunctionName()["person_utils_sayhello"] <> invalid
result = RBS_SM_1_getMocksByFunctionName()["person_utils_sayhello"].callback(a1,a2)
return result
end if
print "hello"
end function
function RBS_CC_1_getMocksByFunctionName()
function RBS_SM_1_getMocksByFunctionName()
if m._rMocksByFunctionName = invalid
m._rMocksByFunctionName = {}
end if
Expand All @@ -234,14 +245,14 @@ describe.only('MockUtil', () => {
await builder.transpile();
let a = getContents('source/code.brs');
let b = trimLeading(`sub person_utils_sayHello(a1, a2)
if RBS_CC_1_getMocksByFunctionName()["person_utils_sayHello"] <> invalid
result = RBS_CC_1_getMocksByFunctionName()["person_utils_sayHello"].callback(a1,a2)
if RBS_SM_1_getMocksByFunctionName()["person_utils_sayhello"] <> invalid
result = RBS_SM_1_getMocksByFunctionName()["person_utils_sayhello"].callback(a1,a2)
return
end if
print "hello"
end sub
function RBS_CC_1_getMocksByFunctionName()
function RBS_SM_1_getMocksByFunctionName()
if m._rMocksByFunctionName = invalid
m._rMocksByFunctionName = {}
end if
Expand Down Expand Up @@ -317,22 +328,22 @@ describe.only('MockUtil', () => {
end function
function beings_sayHello()
if RBS_CC_1_getMocksByFunctionName()["beings_sayHello"] <> invalid
result = RBS_CC_1_getMocksByFunctionName()["beings_sayHello"].callback()
if RBS_SM_1_getMocksByFunctionName()["beings_sayhello"] <> invalid
result = RBS_SM_1_getMocksByFunctionName()["beings_sayhello"].callback()
return result
end if
print "hello2"
end function
function sayHello()
if RBS_CC_1_getMocksByFunctionName()["sayHello"] <> invalid
result = RBS_CC_1_getMocksByFunctionName()["sayHello"].callback()
if RBS_SM_1_getMocksByFunctionName()["sayhello"] <> invalid
result = RBS_SM_1_getMocksByFunctionName()["sayhello"].callback()
return result
end if
print "hello3"
end function
function RBS_CC_1_getMocksByFunctionName()
function RBS_SM_1_getMocksByFunctionName()
if m._rMocksByFunctionName = invalid
m._rMocksByFunctionName = {}
end if
Expand Down
130 changes: 110 additions & 20 deletions bsc-plugin/src/lib/rooibos/MockUtil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ import { Range } from 'vscode-languageserver-types';
import type { FileFactory } from './FileFactory';
import undent from 'undent';
import type { RooibosSession } from './RooibosSession';
import { BrsTranspileState } from 'brighterscript/dist/parser/BrsTranspileState';
import { diagnosticErrorProcessingFile } from '../utils/Diagnostics';
import type { TestCase } from './TestCase';
import type { TestSuite } from './TestSuite';
import { getAllDottedGetParts, getRootObjectFromDottedGet, getStringPathFromDottedGet, overrideAstTranspile } from './Utils';

export class MockUtil {

Expand All @@ -22,7 +27,7 @@ export class MockUtil {
session: RooibosSession;

private brsFileAdditions = `
function RBS_CC_#ID#_getMocksByFunctionName()
function RBS_SM_#ID#_getMocksByFunctionName()
if m._rMocksByFunctionName = invalid
m._rMocksByFunctionName = {}
end if
Expand All @@ -34,21 +39,21 @@ export class MockUtil {
private fileId: number;
private filePathMap: any;
private fileFactory: FileFactory;
private processedStatements: Set<brighterscript.FunctionExpression>;
private processedStatements: Set<brighterscript.FunctionStatement>;
private astEditor: Editor;

public enableGlobalMethodMocks(file: BrsFile, astEditor: Editor) {
enableGlobalMethodMocks(file: BrsFile, astEditor: Editor) {
if (this.config.isGlobalMethodMockingEnabled) {
this._processFile(file, astEditor);
}
}

public _processFile(file: BrsFile, astEditor: Editor) {
_processFile(file: BrsFile, astEditor: Editor) {
this.fileId++;
this.processedStatements = new Set<brighterscript.FunctionExpression>();
this.processedStatements = new Set<brighterscript.FunctionStatement>();
this.astEditor = astEditor;

for (let fs of file.parser.references.functionExpressions) {
// console.log('processing global methods on ', file.pkgPath);
for (let fs of file.parser.references.functionStatements) {
this.enableMockOnFunction(fs);
}

Expand All @@ -58,44 +63,129 @@ export class MockUtil {
}
}

private enableMockOnFunction(functionExpression: brighterscript.FunctionExpression) {
if (isClassStatement(functionExpression.parent?.parent)) {
console.log('skipping class', functionExpression.parent?.parent.name.text)
private enableMockOnFunction(functionStatement: brighterscript.FunctionStatement) {
if (isClassStatement(functionStatement.parent?.parent)) {
// console.log('skipping class', functionStatement.parent?.parent?.name?.text);
return;
}
if (this.processedStatements.has(functionExpression)) {
if (this.processedStatements.has(functionStatement)) {
// console.log('skipping processed expression');
return;
}

const methodName = functionExpression?.functionStatement?.getName(brighterscript.ParseMode.BrightScript) || '';
const methodName = functionStatement?.getName(brighterscript.ParseMode.BrightScript).toLowerCase() || '';
// console.log('MN', methodName);
if (this.config.isGlobalMethodMockingEfficientMode && !this.session.globalStubbedMethods.has(methodName)) {
// console.log('skipping method that is not stubbed', methodName);
return;
}

// console.log('processing stubbed method', methodName);
//TODO check if the user has actually mocked or stubbed this function, otherwise leave it alone!

for (let param of functionExpression.parameters) {
for (let param of functionStatement.func.parameters) {
param.asToken = null;
}
const paramNames = functionExpression.parameters.map((param) => param.name.text).join(',');
const paramNames = functionStatement.func.parameters.map((param) => param.name.text).join(',');

const returnStatement = ((functionExpression.functionType?.kind === brighterscript.TokenKind.Sub && (functionExpression.returnTypeToken === undefined || functionExpression.returnTypeToken?.kind === brighterscript.TokenKind.Void)) || functionExpression.returnTypeToken?.kind === brighterscript.TokenKind.Void) ? 'return' : 'return result';
this.astEditor.addToArray(functionExpression.body.statements, 0, new RawCodeStatement(undent`
if RBS_CC_${this.fileId}_getMocksByFunctionName()["${methodName}"] <> invalid
result = RBS_CC_${this.fileId}_getMocksByFunctionName()["${methodName}"].callback(${paramNames})
const returnStatement = ((functionStatement.func.functionType?.kind === brighterscript.TokenKind.Sub && (functionStatement.func.returnTypeToken === undefined || functionStatement.func.returnTypeToken?.kind === brighterscript.TokenKind.Void)) || functionStatement.func.returnTypeToken?.kind === brighterscript.TokenKind.Void) ? 'return' : 'return result';
this.astEditor.addToArray(functionStatement.func.body.statements, 0, new RawCodeStatement(undent`
if RBS_SM_${this.fileId}_getMocksByFunctionName()["${methodName}"] <> invalid
result = RBS_SM_${this.fileId}_getMocksByFunctionName()["${methodName}"].callback(${paramNames})
${returnStatement}
end if
`));

this.processedStatements.add(functionExpression);
this.processedStatements.add(functionStatement);
}

public addBrsAPIText(file: BrsFile) {
addBrsAPIText(file: BrsFile) {
//TODO should use ast editor!
const func = new RawCodeStatement(this.brsFileAdditions.replace(/\#ID\#/g, this.fileId.toString().trim()), file, Range.create(Position.create(1, 1), Position.create(1, 1)));
file.ast.statements.push(func);
}


gatherGlobalMethodMocks(testSuite: TestSuite) {
// console.log('gathering global method mocks for testSuite', testSuite.name);
for (let group of [...testSuite.testGroups.values()].filter((tg) => tg.isIncluded)) {
for (let testCase of [...group.testCases.values()].filter((tc) => tc.isIncluded)) {
this.gatherMockedGlobalMethods(testSuite, testCase);
}
}

}
private gatherMockedGlobalMethods(testSuite: TestSuite, testCase: TestCase) {
try {
let func = testSuite.classStatement.methods.find((m) => m.name.text.toLowerCase() === testCase.funcName.toLowerCase());
func.walk(brighterscript.createVisitor({
ExpressionStatement: (expressionStatement, parent, owner) => {
let callExpression = expressionStatement.expression as brighterscript.CallExpression;
if (brighterscript.isCallExpression(callExpression) && brighterscript.isDottedGetExpression(callExpression.callee)) {
let dge = callExpression.callee;
let assertRegex = /(?:fail|assert(?:[a-z0-9]*)|expect(?:[a-z0-9]*)|stubCall)/i;
if (dge && assertRegex.test(dge.name.text)) {
if (dge.name.text === 'stubCall') {
this.processGlobalStubbedMethod(callExpression);
return expressionStatement;

} else {

if (dge.name.text === 'expectCalled' || dge.name.text === 'expectNotCalled') {
this.processGlobalStubbedMethod(callExpression);
}
}
}
}
}
}), {
walkMode: brighterscript.WalkMode.visitStatementsRecursive
});
} catch (e) {
// console.log(e);
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
diagnosticErrorProcessingFile(testSuite.file, e.message);
}
}

private processGlobalStubbedMethod(callExpression: brighterscript.CallExpression) {
let isNotCalled = false;
let isStubCall = false;
const namespaceLookup = this.session.namespaceLookup;
if (brighterscript.isDottedGetExpression(callExpression.callee)) {
const nameText = callExpression.callee.name.text;
isNotCalled = nameText === 'expectNotCalled';
isStubCall = nameText === 'stubCall';
}
//modify args
let arg0 = callExpression.args[0];
if (brighterscript.isCallExpression(arg0) && brighterscript.isDottedGetExpression(arg0.callee)) {

//is it a namespace?
let dg = arg0.callee;
let nameParts = getAllDottedGetParts(dg);
let name = nameParts.pop();

// console.log('found expect with name', name);
if (name) {
//is a namespace?
if (nameParts[0] && namespaceLookup.has(nameParts[0].toLowerCase())) {
//then this must be a namespace method
let fullPathName = nameParts.join('.').toLowerCase();
let ns = namespaceLookup.get(fullPathName);
if (!ns) {
//TODO this is an error condition!
}
nameParts.push(name);
let functionName = nameParts.join('_').toLowerCase();
this.session.globalStubbedMethods.add(functionName);
}
}
} else if (brighterscript.isCallExpression(arg0) && brighterscript.isVariableExpression(arg0.callee)) {
let functionName = arg0.callee.getName(brighterscript.ParseMode.BrightScript).toLowerCase();
this.session.globalStubbedMethods.add(functionName);
}
}

}

Loading

0 comments on commit 316e84c

Please sign in to comment.