Skip to content

Commit

Permalink
Upgrading to Brighterscript v1 (#289)
Browse files Browse the repository at this point in the history
* All Typescript and brighterscript errors fixed

* Fixed all tests (except for known bugs with brighterscript-1.0.0-alpha.35)

* Trying to get BSLint working

* All tests pass

* Fixed lint errors in tests

* Made teh project and package files play together better and added script for running rooibos tests from command line

* added some notes about how to do development

* Work out windows bug

* ignore lint warning for now

* Solved windows issue

* Windows test of adds custom test reporters

* Remove it.only

* Removed print statements

* Broke up reporter tests to multiple tests

* Updated package.json version
  • Loading branch information
markwpearce authored Aug 14, 2024
1 parent d81fc64 commit 96d2167
Show file tree
Hide file tree
Showing 61 changed files with 6,272 additions and 2,042 deletions.
5 changes: 4 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@
"/tmp/**": true,
"**/dist": true,
"**/build": true,
"**/out": true
"**/out": true,
"bsc-plugin": true,
"framework": true,
"tests": true
},
"search.exclude": {
"**/.git/objects/**": true,
Expand Down
21 changes: 21 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,27 @@ Rooibos is an independent open-source project maintained exclusively by voluntee

You might want to help! Get in touch via the [slack group](https://join.slack.com/t/rokudevelopers/shared_invite/zt-4vw7rg6v-NH46oY7hTktpRIBM_zGvwA) or [raise issues](https://github.com/rokucommunity/rooibos/issues/new).

### Project Setup

1. Clone the project
2. Run `npm install`
3. Open the workspace file in VSCode: `roobois.code-workspace`

### Running Tests

Rooibos Brighterscript plugin tests can be run either through VSCode's debug configuration `Run Tests (bsc-plugin)` or by running `npm run test` in the directory `rooibos/bsc-plugin`.

Example Rooibos framework tests will wrun on a Roku device.

Create a `.env` in `rooibos/tests` with the following details:

```
ROKU_HOST=<ip of Roku device>
ROKU_PASSWORD=<development password of Roku device>
```

Run tests either from a VSCode debugger, or through `npm run test`.

## Sample project

https://github.com/rokucommunity/rooibos-roku-sample
Expand Down
953 changes: 671 additions & 282 deletions bsc-plugin/package-lock.json

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions bsc-plugin/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "rooibos-roku",
"version": "5.12.0",
"version": "6.0.0-alpha.1",
"description": "simple, flexible, fun brightscript test framework for roku scenegraph apps - roku brighterscript plugin",
"scripts": {
"preversion": "npm run build && npm run lint && npm run test",
Expand Down Expand Up @@ -42,7 +42,7 @@
"@types/node": "^14.18.41",
"@typescript-eslint/eslint-plugin": "^5.27.0",
"@typescript-eslint/parser": "^5.27.0",
"brighterscript": "^0.65.22",
"brighterscript": "^1.0.0-alpha.36",
"chai": "^4.2.0",
"chai-subset": "^1.6.0",
"coveralls": "^3.0.0",
Expand Down
2 changes: 1 addition & 1 deletion bsc-plugin/src/lib/rooibos/Annotation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ export class RooibosAnnotation {
}
try {
if (rawParams) {
this.params.push(new AnnotationParams(annotation, rawParams, annotation.range.start.line, annotation.getArguments() as any, isIgnore, isSolo, noCatch));
this.params.push(new AnnotationParams(annotation, rawParams, annotation.location.range.start.line, annotation.getArguments() as any, isIgnore, isSolo, noCatch));
} else {
diagnosticIllegalParams(file, annotation);
}
Expand Down
79 changes: 68 additions & 11 deletions bsc-plugin/src/lib/rooibos/CodeCoverageProcessor.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import PluginInterface from 'brighterscript/dist/PluginInterface';
import * as fsExtra from 'fs-extra';
import { RooibosPlugin } from '../../plugin';
import undent from 'undent';
import * as path from 'path';

let tmpPath = s`${process.cwd()}/tmp`;
let _rootDir = s`${tmpPath}/rootDir`;
Expand All @@ -27,7 +28,7 @@ describe('RooibosPlugin', () => {
plugin = new RooibosPlugin();
options = {
rootDir: _rootDir,
stagingFolderPath: _stagingFolderPath,
stagingDir: _stagingFolderPath,
rooibos: {
isRecordingCodeCoverage: true,
coverageExcludedFiles: [
Expand All @@ -46,14 +47,14 @@ describe('RooibosPlugin', () => {
builder.program = new Program(builder.options);
program = builder.program;
program.logger = builder.logger;
builder.plugins = new PluginInterface([plugin], { logger: builder.logger });
program.plugins = new PluginInterface([plugin], { logger: builder.logger });
program.plugins.add(plugin);
program.createSourceScope(); //ensure source scope is created
plugin.beforeProgramCreate(builder);
plugin.beforeProgramCreate({ builder: builder });
plugin.afterProgramCreate({ program: program, builder: builder });

});
afterEach(() => {
plugin.afterProgramCreate(program);
plugin.afterProgramCreate({ builder: builder, program: program });
fsExtra.ensureDirSync(tmpPath);
fsExtra.emptyDirSync(tmpPath);
builder.dispose();
Expand Down Expand Up @@ -87,7 +88,7 @@ describe('RooibosPlugin', () => {
`);
program.validate();
expect(program.getDiagnostics()).to.be.empty;
await builder.transpile();
await builder.build();
let a = getContents('source/code.brs');
let b = undent(`
function new(a1, a2)
Expand Down Expand Up @@ -171,7 +172,7 @@ describe('RooibosPlugin', () => {
`);
program.validate();
expect(program.getDiagnostics()).to.be.empty;
await builder.transpile();
await builder.build();
let a = getContents('source/code.brs');
let b = undent(`
function new(a1, a2)
Expand Down Expand Up @@ -264,7 +265,7 @@ describe('RooibosPlugin', () => {
`);
program.validate();
expect(program.getDiagnostics()).to.be.empty;
await builder.transpile();
await builder.build();
let a = getContents('source/code.brs');
let b = undent(`
function __BasicClass_builder()
Expand Down Expand Up @@ -346,7 +347,7 @@ describe('RooibosPlugin', () => {
program.setFile('source/code.bs', source);
program.validate();
expect(program.getDiagnostics()).to.be.empty;
await builder.transpile();
await builder.build();

let a = getContents('source/code.brs');
let b = undent(`
Expand Down Expand Up @@ -420,7 +421,7 @@ describe('RooibosPlugin', () => {
program.setFile('source/code.bs', source);
program.validate();
expect(program.getDiagnostics()).to.be.empty;
await builder.transpile();
await builder.build();

let a = getContents('source/code.brs');
let b = undent(`
Expand Down Expand Up @@ -496,6 +497,62 @@ describe('RooibosPlugin', () => {
});
});

it('adds code coverage in conditional compile statements', async () => {
program.setFile('source/code.bs', `
#const DEBUG = true
sub test()
#if DEBUG
print "debug"
#else
print "not debug"
#end if
end sub
`);
program.validate();
expect(program.getDiagnostics()).to.be.empty;
await builder.build();
let a = getContents('source/code.brs');
let b = undent(`
#const DEBUG = true
sub test()
#if DEBUG
RBS_CC_1_reportLine("3", 4)
RBS_CC_1_reportLine("4", 1)
print "debug"
#else
RBS_CC_1_reportLine("5", 4)
RBS_CC_1_reportLine("6", 1)
print "not debug"
#end if
end sub
function RBS_CC_1_reportLine(lineNumber, reportType = 1)
_rbs_ccn = m._rbs_ccn
if _rbs_ccn <> invalid
_rbs_ccn.entry = {
"f": "1"
"l": lineNumber
"r": reportType
}
return true
end if
_rbs_ccn = m?.global?._rbs_ccn
if _rbs_ccn <> invalid
_rbs_ccn.entry = {
"f": "1"
"l": lineNumber
"r": reportType
}
m._rbs_ccn = _rbs_ccn
return true
end if
return true
end function
`);
expect(a).to.equal(b);
});

it('excludes files from coverage', async () => {
const source = `sub foo()
x = function(y)
Expand All @@ -509,7 +566,7 @@ describe('RooibosPlugin', () => {
program.setFile('source/code.coverageExcluded.bs', source);
program.validate();
expect(program.getDiagnostics()).to.be.empty;
await builder.transpile();
await builder.build();

let a = getContents('source/code.coverageExcluded.brs');
let b = undent(`
Expand Down
69 changes: 51 additions & 18 deletions bsc-plugin/src/lib/rooibos/CodeCoverageProcessor.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/* eslint-disable @typescript-eslint/no-var-requires */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
import type { BrsFile, Editor, ExpressionStatement, Program, ProgramBuilder, Statement } from 'brighterscript';
import { Parser, WalkMode, createVisitor, BinaryExpression, createToken, TokenKind, GroupingExpression, isForStatement, isBlock } from 'brighterscript';
import { Parser, WalkMode, createVisitor, BinaryExpression, createToken, TokenKind, GroupingExpression, isForStatement, isBlock, InternalWalkMode } from 'brighterscript';
import type { RooibosConfig } from './RooibosConfig';
import { RawCodeStatement } from './RawCodeStatement';
import { BrsTranspileState } from 'brighterscript/dist/parser/BrsTranspileState';
Expand All @@ -12,7 +12,8 @@ export enum CodeCoverageLineType {
noCode = 0,
code = 1,
condition = 2,
branch = 3
branch = 3,
conditionalCompile = 4
}

export class CodeCoverageProcessor {
Expand Down Expand Up @@ -75,33 +76,40 @@ export class CodeCoverageProcessor {
this.executableLines = new Map<number, Statement>();
this.processedStatements = new Set<Statement>();
this.astEditor = astEditor;
console.log('Processing file:', this.fileId, file.pkgPath);

file.ast.walk(createVisitor({
ForStatement: (ds, parent, owner, key) => {
this.addStatement(ds);
ds.forToken.text = `${this.getFuncCallText(ds.range.start.line, CodeCoverageLineType.code)}: for`;
ds.tokens.for.text = `${this.getFuncCallText(ds.location.range.start.line, CodeCoverageLineType.code)}: for`;
},
IfStatement: (ifStatement, parent, owner, key) => {
this.addStatement(ifStatement);
(ifStatement as any).condition = new BinaryExpression(
new RawCodeExpression(this.getFuncCallText(ifStatement.condition.range.start.line, CodeCoverageLineType.condition)),
createToken(TokenKind.And),
new GroupingExpression({
left: createToken(TokenKind.LeftParen),
right: createToken(TokenKind.RightParen)
}, ifStatement.condition)
);
(ifStatement as any).condition = new BinaryExpression({
left: new RawCodeExpression(this.getFuncCallText(ifStatement.condition.location.range.start.line, CodeCoverageLineType.condition)),
operator: createToken(TokenKind.And),
right: new GroupingExpression({
leftParen: createToken(TokenKind.LeftParen),
rightParen: createToken(TokenKind.RightParen),
expression: ifStatement.condition
})
});

let blockStatements = ifStatement?.thenBranch?.statements;
if (blockStatements) {
let coverageStatement = new RawCodeStatement(this.getFuncCallText(ifStatement.range.start.line, CodeCoverageLineType.branch));
let coverageStatement = new RawCodeStatement(this.getFuncCallText(ifStatement.location.range.start.line, CodeCoverageLineType.branch));
blockStatements.splice(0, 0, coverageStatement);
}

// Handle the else blocks
let elseBlock = ifStatement.elseBranch;
if (isBlock(elseBlock) && elseBlock.statements) {
let coverageStatement = new RawCodeStatement(this.getFuncCallText(elseBlock.range.start.line - 1, CodeCoverageLineType.branch));
let startRangeLine = elseBlock.location.range.start.line;
if (elseBlock.statements.length > 0) {
// if the else block has statements, then the coverage statement should be inserted before the first statement
startRangeLine -= 1;
}
let coverageStatement = new RawCodeStatement(this.getFuncCallText(startRangeLine, CodeCoverageLineType.branch));
elseBlock.statements.splice(0, 0, coverageStatement);
}

Expand All @@ -112,15 +120,15 @@ export class CodeCoverageProcessor {

},
WhileStatement: (ds, parent, owner, key) => {
ds.tokens.while.text = `${this.getFuncCallText(ds.range.start.line, CodeCoverageLineType.code)}: while`;
ds.tokens.while.text = `${this.getFuncCallText(ds.location.range.start.line, CodeCoverageLineType.code)}: while`;
},
ReturnStatement: (ds, parent, owner, key) => {
this.addStatement(ds);
this.convertStatementToCoverageStatement(ds, CodeCoverageLineType.code, owner, key);
},
ForEachStatement: (ds, parent, owner, key) => {
this.addStatement(ds);
ds.tokens.forEach.text = `${this.getFuncCallText(ds.range.start.line, CodeCoverageLineType.code)}: for each`;
ds.tokens.forEach.text = `${this.getFuncCallText(ds.location.range.start.line, CodeCoverageLineType.code)}: for each`;
},
ExitWhileStatement: (ds, parent, owner, key) => {

Expand Down Expand Up @@ -149,13 +157,38 @@ export class CodeCoverageProcessor {
this.addStatement(ds);
this.convertStatementToCoverageStatement(ds, CodeCoverageLineType.code, owner, key);
}

},
AugmentedAssignmentStatement: (ds, parent, owner, key) => {
this.addStatement(ds);
this.convertStatementToCoverageStatement(ds, CodeCoverageLineType.code, owner, key);
},
ExpressionStatement: (ds, parent, owner, key) => {
this.addStatement(ds);
this.convertStatementToCoverageStatement(ds, CodeCoverageLineType.code, owner, key);
},
ConditionalCompileStatement: (ccStmt, parent, owner, key) => {
this.addStatement(ccStmt);

let blockStatements = ccStmt.thenBranch?.statements;
if (blockStatements) {
let coverageStatement = new RawCodeStatement(this.getFuncCallText(ccStmt.location.range.start.line, CodeCoverageLineType.conditionalCompile));
blockStatements.splice(0, 0, coverageStatement);
}

// Handle the else blocks
let elseBlock = ccStmt.elseBranch;
if (isBlock(elseBlock) && elseBlock.statements) {
let startRangeLine = elseBlock.location.range.start.line;
if (elseBlock.statements.length > 0) {
// if the else block has statements, then the coverage statement should be inserted before the first statement
startRangeLine -= 1;
}
let coverageStatement = new RawCodeStatement(this.getFuncCallText(startRangeLine, CodeCoverageLineType.conditionalCompile));
elseBlock.statements.splice(0, 0, coverageStatement);
}
}
}), { walkMode: WalkMode.visitAllRecursive });
// eslint-disable-next-line no-bitwise
}), { walkMode: WalkMode.visitAllRecursive | InternalWalkMode.visitFalseConditionalCompilationBlocks });

const coverageMapObject = {};
for (let key of this.coverageMap.keys()) {
Expand All @@ -171,7 +204,7 @@ export class CodeCoverageProcessor {
return;
}

const lineNumber = statement.range.start.line;
const lineNumber = statement.location.range.start.line;
this.coverageMap.set(lineNumber, coverageType);
const parsed = Parser.parse(this.getFuncCallText(lineNumber, coverageType)).ast.statements[0] as ExpressionStatement;
this.astEditor.arraySplice(owner, key, 0, parsed);
Expand Down
Loading

0 comments on commit 96d2167

Please sign in to comment.