diff --git a/bsc-plugin/src/lib/rooibos/CodeCoverageProcessor.spec.ts b/bsc-plugin/src/lib/rooibos/CodeCoverageProcessor.spec.ts index 1a038d3d..a1d7f9b3 100644 --- a/bsc-plugin/src/lib/rooibos/CodeCoverageProcessor.spec.ts +++ b/bsc-plugin/src/lib/rooibos/CodeCoverageProcessor.spec.ts @@ -67,8 +67,8 @@ describe('RooibosPlugin', () => { it('adds code coverage to a brs file', async () => { program.setFile('source/code.brs', ` function new(a1, a2) - c = 0 - text = "" + c = 0 + text = "" for i = 0 to 10 text = text + "hello" c++ @@ -91,50 +91,100 @@ describe('RooibosPlugin', () => { let a = getContents('source/code.brs'); let b = undent(` function new(a1, a2) - RBS_CC_1_reportLine("2", 1) + RBS_CC_0_reportFunction(0) + RBS_CC_0_reportLine(2) c = 0 - RBS_CC_1_reportLine("3", 1) + RBS_CC_0_reportLine(3) text = "" - RBS_CC_1_reportLine("4", 1): for i = 0 to 10 - RBS_CC_1_reportLine("5", 1) + RBS_CC_0_reportLine(4): for i = 0 to 10 + RBS_CC_0_reportBranch(0, 0) + RBS_CC_0_reportLine(5) text = text + "hello" - RBS_CC_1_reportLine("6", 1) + RBS_CC_0_reportLine(6) c++ - RBS_CC_1_reportLine("7", 1) + RBS_CC_0_reportLine(7) c += 1 - if RBS_CC_1_reportLine("8", 2) and (c = 2) - RBS_CC_1_reportLine("8", 3) - RBS_CC_1_reportLine("9", 1) + if RBS_CC_0_reportLine(8) and (c = 2) + RBS_CC_0_reportBranch(1, 1) + RBS_CC_0_reportLine(9) ? "is true" end if - if RBS_CC_1_reportLine("12", 2) and (c = 3) - RBS_CC_1_reportLine("12", 3) - RBS_CC_1_reportLine("13", 1) + if RBS_CC_0_reportLine(12) and (c = 3) + RBS_CC_0_reportBranch(2, 2) + RBS_CC_0_reportLine(13) ? "free" else - RBS_CC_1_reportLine("14", 3) - RBS_CC_1_reportLine("15", 1) + RBS_CC_0_reportBranch(3, 3) + RBS_CC_0_reportLine(15) ? "not free" end if end for end function - function RBS_CC_1_reportLine(lineNumber, reportType = 1) + function RBS_CC_0_reportLine(lineNumber) _rbs_ccn = m._rbs_ccn if _rbs_ccn <> invalid _rbs_ccn.entry = { - "f": "1" + "f": 0 "l": lineNumber - "r": reportType + "r": 1 } return true end if _rbs_ccn = m?.global?._rbs_ccn if _rbs_ccn <> invalid _rbs_ccn.entry = { - "f": "1" + "f": 0 "l": lineNumber - "r": reportType + "r": 1 + } + m._rbs_ccn = _rbs_ccn + return true + end if + return true + end function + + function RBS_CC_0_reportBranch(blockId, branchId) + _rbs_ccn = m._rbs_ccn + if _rbs_ccn <> invalid + _rbs_ccn.entry = { + "f": 0 + "bl": blockId + "br": branchId + "r": 3 + } + return true + end if + _rbs_ccn = m?.global?._rbs_ccn + if _rbs_ccn <> invalid + _rbs_ccn.entry = { + "f": 0 + "bl": blockId + "br": branchId + "r": 3 + } + m._rbs_ccn = _rbs_ccn + return true + end if + return true + end function + + function RBS_CC_0_reportFunction(functionId) + _rbs_ccn = m._rbs_ccn + if _rbs_ccn <> invalid + _rbs_ccn.entry = { + "f": 0 + "fn": functionId + "r": 4 + } + return true + end if + _rbs_ccn = m?.global?._rbs_ccn + if _rbs_ccn <> invalid + _rbs_ccn.entry = { + "f": 0 + "fn": functionId + "r": 4 } m._rbs_ccn = _rbs_ccn return true @@ -175,50 +225,100 @@ describe('RooibosPlugin', () => { let a = getContents('source/code.brs'); let b = undent(` function new(a1, a2) - RBS_CC_1_reportLine("2", 1) + RBS_CC_0_reportFunction(0) + RBS_CC_0_reportLine(2) c = 0 - RBS_CC_1_reportLine("3", 1) + RBS_CC_0_reportLine(3) text = "" - RBS_CC_1_reportLine("4", 1): for i = 0 to 10 - RBS_CC_1_reportLine("5", 1) + RBS_CC_0_reportLine(4): for i = 0 to 10 + RBS_CC_0_reportBranch(0, 0) + RBS_CC_0_reportLine(5) text = text + "hello" - RBS_CC_1_reportLine("6", 1) + RBS_CC_0_reportLine(6) c++ - RBS_CC_1_reportLine("7", 1) + RBS_CC_0_reportLine(7) c += 1 - if RBS_CC_1_reportLine("8", 2) and (c = 2) - RBS_CC_1_reportLine("8", 3) - RBS_CC_1_reportLine("9", 1) + if RBS_CC_0_reportLine(8) and (c = 2) + RBS_CC_0_reportBranch(1, 1) + RBS_CC_0_reportLine(9) ? "is true" end if - if RBS_CC_1_reportLine("12", 2) and (c = 3) - RBS_CC_1_reportLine("12", 3) - RBS_CC_1_reportLine("13", 1) + if RBS_CC_0_reportLine(12) and (c = 3) + RBS_CC_0_reportBranch(2, 2) + RBS_CC_0_reportLine(13) ? "free" else - RBS_CC_1_reportLine("14", 3) - RBS_CC_1_reportLine("15", 1) + RBS_CC_0_reportBranch(3, 3) + RBS_CC_0_reportLine(15) ? "not free" end if end for end function - function RBS_CC_1_reportLine(lineNumber, reportType = 1) + function RBS_CC_0_reportLine(lineNumber) _rbs_ccn = m._rbs_ccn if _rbs_ccn <> invalid _rbs_ccn.entry = { - "f": "1" + "f": 0 "l": lineNumber - "r": reportType + "r": 1 } return true end if _rbs_ccn = m?.global?._rbs_ccn if _rbs_ccn <> invalid _rbs_ccn.entry = { - "f": "1" + "f": 0 "l": lineNumber - "r": reportType + "r": 1 + } + m._rbs_ccn = _rbs_ccn + return true + end if + return true + end function + + function RBS_CC_0_reportBranch(blockId, branchId) + _rbs_ccn = m._rbs_ccn + if _rbs_ccn <> invalid + _rbs_ccn.entry = { + "f": 0 + "bl": blockId + "br": branchId + "r": 3 + } + return true + end if + _rbs_ccn = m?.global?._rbs_ccn + if _rbs_ccn <> invalid + _rbs_ccn.entry = { + "f": 0 + "bl": blockId + "br": branchId + "r": 3 + } + m._rbs_ccn = _rbs_ccn + return true + end if + return true + end function + + function RBS_CC_0_reportFunction(functionId) + _rbs_ccn = m._rbs_ccn + if _rbs_ccn <> invalid + _rbs_ccn.entry = { + "f": 0 + "fn": functionId + "r": 4 + } + return true + end if + _rbs_ccn = m?.global?._rbs_ccn + if _rbs_ccn <> invalid + _rbs_ccn.entry = { + "f": 0 + "fn": functionId + "r": 4 } m._rbs_ccn = _rbs_ccn return true @@ -267,68 +367,118 @@ describe('RooibosPlugin', () => { await builder.transpile(); let a = getContents('source/code.brs'); let b = undent(` - function __BasicClass_builder() - instance = {} - instance.new = function(a1, a2) - m.field1 = invalid - m.field2 = invalid - RBS_CC_1_reportLine("6", 1) - c = 0 - RBS_CC_1_reportLine("7", 1) - text = "" - RBS_CC_1_reportLine("8", 1): for i = 0 to 10 - RBS_CC_1_reportLine("9", 1) - text = text + "hello" - RBS_CC_1_reportLine("10", 1) - c++ - RBS_CC_1_reportLine("11", 1) - c += 1 - if RBS_CC_1_reportLine("12", 2) and (c = 2) - RBS_CC_1_reportLine("12", 3) - RBS_CC_1_reportLine("13", 1) - ? "is true" - end if - if RBS_CC_1_reportLine("16", 2) and (c = 3) - RBS_CC_1_reportLine("16", 3) - RBS_CC_1_reportLine("17", 1) - ? "free" - else - RBS_CC_1_reportLine("18", 3) - RBS_CC_1_reportLine("19", 1) - ? "not free" - end if - end for + function __BasicClass_builder() + instance = {} + instance.new = function(a1, a2) + m.field1 = invalid + m.field2 = invalid + RBS_CC_0_reportLine(6) + RBS_CC_0_reportFunction(0) + c = 0 + RBS_CC_0_reportLine(7) + text = "" + RBS_CC_0_reportLine(8): for i = 0 to 10 + RBS_CC_0_reportBranch(0, 0) + RBS_CC_0_reportLine(9) + text = text + "hello" + RBS_CC_0_reportLine(10) + c++ + RBS_CC_0_reportLine(11) + c += 1 + if RBS_CC_0_reportLine(12) and (c = 2) + RBS_CC_0_reportBranch(1, 1) + RBS_CC_0_reportLine(13) + ? "is true" + end if + if RBS_CC_0_reportLine(16) and (c = 3) + RBS_CC_0_reportBranch(2, 2) + RBS_CC_0_reportLine(17) + ? "free" + else + RBS_CC_0_reportBranch(3, 3) + RBS_CC_0_reportLine(19) + ? "not free" + end if + end for + end function + return instance + end function + function BasicClass(a1, a2) + instance = __BasicClass_builder() + instance.new(a1, a2) + return instance + end function + + function RBS_CC_0_reportLine(lineNumber) + _rbs_ccn = m._rbs_ccn + if _rbs_ccn <> invalid + _rbs_ccn.entry = { + "f": 0 + "l": lineNumber + "r": 1 + } + return true + end if + _rbs_ccn = m?.global?._rbs_ccn + if _rbs_ccn <> invalid + _rbs_ccn.entry = { + "f": 0 + "l": lineNumber + "r": 1 + } + m._rbs_ccn = _rbs_ccn + return true + end if + return true end function - return instance - end function - function BasicClass(a1, a2) - instance = __BasicClass_builder() - instance.new(a1, a2) - return instance - end function - 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 - } + function RBS_CC_0_reportBranch(blockId, branchId) + _rbs_ccn = m._rbs_ccn + if _rbs_ccn <> invalid + _rbs_ccn.entry = { + "f": 0 + "bl": blockId + "br": branchId + "r": 3 + } + return true + end if + _rbs_ccn = m?.global?._rbs_ccn + if _rbs_ccn <> invalid + _rbs_ccn.entry = { + "f": 0 + "bl": blockId + "br": branchId + "r": 3 + } + m._rbs_ccn = _rbs_ccn + return true + end if 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 + end function + + function RBS_CC_0_reportFunction(functionId) + _rbs_ccn = m._rbs_ccn + if _rbs_ccn <> invalid + _rbs_ccn.entry = { + "f": 0 + "fn": functionId + "r": 4 + } + return true + end if + _rbs_ccn = m?.global?._rbs_ccn + if _rbs_ccn <> invalid + _rbs_ccn.entry = { + "f": 0 + "fn": functionId + "r": 4 + } + m._rbs_ccn = _rbs_ccn + return true + end if return true - end if - return true - end function + end function `); expect(a).to.equal(b); }); @@ -351,34 +501,84 @@ describe('RooibosPlugin', () => { let a = getContents('source/code.brs'); let b = undent(` sub foo() - RBS_CC_1_reportLine("1", 1) + RBS_CC_0_reportFunction(0) + RBS_CC_0_reportLine(1) x = function(y) - if RBS_CC_1_reportLine("2", 2) and ((true)) then - RBS_CC_1_reportLine("2", 3) - RBS_CC_1_reportLine("3", 1) + RBS_CC_0_reportFunction(1) + if RBS_CC_0_reportLine(2) and ((true)) then + RBS_CC_0_reportBranch(0, 0) + RBS_CC_0_reportLine(3) return 1 end if - RBS_CC_1_reportLine("5", 1) + RBS_CC_0_reportLine(5) return 0 end function end sub - function RBS_CC_1_reportLine(lineNumber, reportType = 1) + function RBS_CC_0_reportLine(lineNumber) _rbs_ccn = m._rbs_ccn if _rbs_ccn <> invalid _rbs_ccn.entry = { - "f": "1" + "f": 0 "l": lineNumber - "r": reportType + "r": 1 } return true end if _rbs_ccn = m?.global?._rbs_ccn if _rbs_ccn <> invalid _rbs_ccn.entry = { - "f": "1" + "f": 0 "l": lineNumber - "r": reportType + "r": 1 + } + m._rbs_ccn = _rbs_ccn + return true + end if + return true + end function + + function RBS_CC_0_reportBranch(blockId, branchId) + _rbs_ccn = m._rbs_ccn + if _rbs_ccn <> invalid + _rbs_ccn.entry = { + "f": 0 + "bl": blockId + "br": branchId + "r": 3 + } + return true + end if + _rbs_ccn = m?.global?._rbs_ccn + if _rbs_ccn <> invalid + _rbs_ccn.entry = { + "f": 0 + "bl": blockId + "br": branchId + "r": 3 + } + m._rbs_ccn = _rbs_ccn + return true + end if + return true + end function + + function RBS_CC_0_reportFunction(functionId) + _rbs_ccn = m._rbs_ccn + if _rbs_ccn <> invalid + _rbs_ccn.entry = { + "f": 0 + "fn": functionId + "r": 4 + } + return true + end if + _rbs_ccn = m?.global?._rbs_ccn + if _rbs_ccn <> invalid + _rbs_ccn.entry = { + "f": 0 + "fn": functionId + "r": 4 } m._rbs_ccn = _rbs_ccn return true @@ -425,65 +625,114 @@ describe('RooibosPlugin', () => { let a = getContents('source/code.brs'); let b = undent(` sub foo(action as string) - if RBS_CC_1_reportLine("2", 2) and (action = "action1") then - RBS_CC_1_reportLine("2", 3) - RBS_CC_1_reportLine("3", 1) + RBS_CC_0_reportFunction(0) + if RBS_CC_0_reportLine(2) and (action = "action1") then + RBS_CC_0_reportBranch(0, 0) + RBS_CC_0_reportLine(3) print "action1" - else if RBS_CC_1_reportLine("4", 2) and (action = "action2" or action = "action2") then - RBS_CC_1_reportLine("4", 3) - RBS_CC_1_reportLine("5", 1) + else if RBS_CC_0_reportLine(4) and (action = "action2" or action = "action2") then + RBS_CC_0_reportBranch(1, 1) + RBS_CC_0_reportLine(5) print "action2" - else if RBS_CC_1_reportLine("6", 2) and (action = "action3") then - RBS_CC_1_reportLine("6", 3) - RBS_CC_1_reportLine("7", 1) + else if RBS_CC_0_reportLine(6) and (action = "action3") then + RBS_CC_0_reportBranch(2, 2) + RBS_CC_0_reportLine(7) print "action3" - else if RBS_CC_1_reportLine("8", 2) and (action = "action4") then - RBS_CC_1_reportLine("8", 3) - else if RBS_CC_1_reportLine("9", 2) and (action = "action5") then - RBS_CC_1_reportLine("9", 3) - RBS_CC_1_reportLine("10", 1) + else if RBS_CC_0_reportLine(8) and (action = "action4") then + RBS_CC_0_reportBranch(3, 3) + else if RBS_CC_0_reportLine(9) and (action = "action5") then + RBS_CC_0_reportBranch(4, 4) + RBS_CC_0_reportLine(10) print "action5" - else if RBS_CC_1_reportLine("11", 2) and (action = "action6") then - RBS_CC_1_reportLine("11", 3) - RBS_CC_1_reportLine("12", 1) + else if RBS_CC_0_reportLine(11) and (action = "action6") then + RBS_CC_0_reportBranch(5, 5) + RBS_CC_0_reportLine(12) print "action6" - else if RBS_CC_1_reportLine("13", 2) and (action = "action7") then - RBS_CC_1_reportLine("13", 3) - RBS_CC_1_reportLine("14", 1) + else if RBS_CC_0_reportLine(13) and (action = "action7") then + RBS_CC_0_reportBranch(6, 6) + RBS_CC_0_reportLine(14) print "action7" - else if RBS_CC_1_reportLine("15", 2) and (action = "action8") then - RBS_CC_1_reportLine("15", 3) - RBS_CC_1_reportLine("16", 1) + else if RBS_CC_0_reportLine(15) and (action = "action8") then + RBS_CC_0_reportBranch(7, 7) + RBS_CC_0_reportLine(16) print "action8" - else if RBS_CC_1_reportLine("17", 2) and (action = "action9") then - RBS_CC_1_reportLine("17", 3) - RBS_CC_1_reportLine("18", 1) + else if RBS_CC_0_reportLine(17) and (action = "action9") then + RBS_CC_0_reportBranch(8, 8) + RBS_CC_0_reportLine(18) print "action9" - else if RBS_CC_1_reportLine("19", 2) and (action = "action10") then - RBS_CC_1_reportLine("19", 3) - RBS_CC_1_reportLine("20", 1) + else if RBS_CC_0_reportLine(19) and (action = "action10") then + RBS_CC_0_reportBranch(9, 9) + RBS_CC_0_reportLine(20) print "action10" else - RBS_CC_1_reportLine("21", 3) + RBS_CC_0_reportBranch(10, 10) end if end sub - function RBS_CC_1_reportLine(lineNumber, reportType = 1) + function RBS_CC_0_reportLine(lineNumber) _rbs_ccn = m._rbs_ccn if _rbs_ccn <> invalid _rbs_ccn.entry = { - "f": "1" + "f": 0 "l": lineNumber - "r": reportType + "r": 1 } return true end if _rbs_ccn = m?.global?._rbs_ccn if _rbs_ccn <> invalid _rbs_ccn.entry = { - "f": "1" + "f": 0 "l": lineNumber - "r": reportType + "r": 1 + } + m._rbs_ccn = _rbs_ccn + return true + end if + return true + end function + + function RBS_CC_0_reportBranch(blockId, branchId) + _rbs_ccn = m._rbs_ccn + if _rbs_ccn <> invalid + _rbs_ccn.entry = { + "f": 0 + "bl": blockId + "br": branchId + "r": 3 + } + return true + end if + _rbs_ccn = m?.global?._rbs_ccn + if _rbs_ccn <> invalid + _rbs_ccn.entry = { + "f": 0 + "bl": blockId + "br": branchId + "r": 3 + } + m._rbs_ccn = _rbs_ccn + return true + end if + return true + end function + + function RBS_CC_0_reportFunction(functionId) + _rbs_ccn = m._rbs_ccn + if _rbs_ccn <> invalid + _rbs_ccn.entry = { + "f": 0 + "fn": functionId + "r": 4 + } + return true + end if + _rbs_ccn = m?.global?._rbs_ccn + if _rbs_ccn <> invalid + _rbs_ccn.entry = { + "f": 0 + "fn": functionId + "r": 4 } m._rbs_ccn = _rbs_ccn return true diff --git a/bsc-plugin/src/lib/rooibos/CodeCoverageProcessor.ts b/bsc-plugin/src/lib/rooibos/CodeCoverageProcessor.ts index b0a48488..f742af6f 100644 --- a/bsc-plugin/src/lib/rooibos/CodeCoverageProcessor.ts +++ b/bsc-plugin/src/lib/rooibos/CodeCoverageProcessor.ts @@ -1,9 +1,8 @@ /* 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 type { BrsFile, Editor, ExpressionStatement, FunctionExpression, Program, ProgramBuilder, Statement } from 'brighterscript'; +import { Parser, WalkMode, createVisitor, BinaryExpression, createToken, TokenKind, GroupingExpression, isForStatement, isFunctionExpression, ParseMode, isFunctionStatement, isCallExpression, isVariableExpression } from 'brighterscript'; import type { RooibosConfig } from './RooibosConfig'; -import { RawCodeStatement } from './RawCodeStatement'; import { BrsTranspileState } from 'brighterscript/dist/parser/BrsTranspileState'; import { RawCodeExpression } from './RawCodeExpression'; import type { FileFactory } from './FileFactory'; @@ -12,22 +11,55 @@ export enum CodeCoverageLineType { noCode = 0, code = 1, condition = 2, - branch = 3 + branch = 3, + function = 4 } export class CodeCoverageProcessor { private coverageBrsTemplate = ` - function RBS_CC_#ID#_reportLine(lineNumber, reportType = 1) + function RBS_CC_#ID#_reportLine(lineNumber) _rbs_ccn = m._rbs_ccn if _rbs_ccn <> invalid - _rbs_ccn.entry = { "f": "#ID#", "l": lineNumber, "r": reportType } + _rbs_ccn.entry = { "f": #ID#, "l": lineNumber, "r": ${CodeCoverageLineType.code} } return true end if _rbs_ccn = m?.global?._rbs_ccn if _rbs_ccn <> invalid - _rbs_ccn.entry = { "f": "#ID#", "l": lineNumber, "r": reportType } + _rbs_ccn.entry = { "f": #ID#, "l": lineNumber, "r": ${CodeCoverageLineType.code} } + m._rbs_ccn = _rbs_ccn + return true + end if + return true + end function + + function RBS_CC_#ID#_reportBranch(blockId, branchId) + _rbs_ccn = m._rbs_ccn + if _rbs_ccn <> invalid + _rbs_ccn.entry = { "f": #ID#, "bl": blockId, "br": branchId, "r": ${CodeCoverageLineType.branch} } + return true + end if + + _rbs_ccn = m?.global?._rbs_ccn + if _rbs_ccn <> invalid + _rbs_ccn.entry = { "f": #ID#, "bl": blockId, "br": branchId, "r": ${CodeCoverageLineType.branch} } + m._rbs_ccn = _rbs_ccn + return true + end if + return true + end function + + function RBS_CC_#ID#_reportFunction(functionId) + _rbs_ccn = m._rbs_ccn + if _rbs_ccn <> invalid + _rbs_ccn.entry = { "f": #ID#, "fn": functionId, "r": ${CodeCoverageLineType.function} } + return true + end if + + _rbs_ccn = m?.global?._rbs_ccn + if _rbs_ccn <> invalid + _rbs_ccn.entry = { "f": #ID#, "fn": functionId, "r": ${CodeCoverageLineType.function} } m._rbs_ccn = _rbs_ccn return true end if @@ -37,54 +69,90 @@ export class CodeCoverageProcessor { constructor(builder: ProgramBuilder, fileFactory: FileFactory) { this.config = (builder.options as any).rooibos as RooibosConfig || {}; - this.expectedCoverageMap = {}; - this.filePathMap = {}; + this.functionMap = []; this.fileId = 0; this.fileFactory = fileFactory; + this.processedFunctions = new Set(); + this.baseCoverageReport = { + files: [] + }; try { } catch (e) { console.log('Error:', e.stack); } } + private baseCoverageReport: CoverageMap; private config: RooibosConfig; private fileId: number; - private filePathMap: any; - private expectedCoverageMap: any; + private blockId: number; + private branchId: number; + private functionMap: Array>; private executableLines: Map; private transpileState: BrsTranspileState; - private coverageMap: Map; private fileFactory: FileFactory; private processedStatements: Set; + private processedFunctions: Set; private astEditor: Editor; + private foundLines: Array; + private foundFunctions: Array; + private foundBlocks: Array; + public generateMetadata(isUsingCoverage: boolean, program: Program) { - this.fileFactory.createCoverageComponent(program, this.expectedCoverageMap, this.filePathMap); + this.fileFactory.createCoverageComponent(program, this.baseCoverageReport); } public addCodeCoverage(file: BrsFile, astEditor: Editor) { if (this.config.isRecordingCodeCoverage) { this.transpileState = new BrsTranspileState(file); + this.blockId = 0; + this.branchId = 0; this._processFile(file, astEditor); + this.fileId++; } } public _processFile(file: BrsFile, astEditor: Editor) { - this.fileId++; - this.coverageMap = new Map(); + this.foundLines = []; + this.foundFunctions = []; + this.foundBlocks = []; + this.executableLines = new Map(); this.processedStatements = new Set(); this.astEditor = astEditor; file.ast.walk(createVisitor({ + FunctionStatement: (statement, parent, owner, key) => { + this.getFunctionIdInFile(statement, ParseMode.BrighterScript, owner, key); + }, + Block: (statement, parent, owner, key) => { + if (!isFunctionExpression(parent)) { + + const lineNumber = statement.range.start.line; + const parsed = Parser.parse(this.getReportBranchHitFuncCallText(this.blockId, this.branchId, statement, owner, key)).ast.statements[0] as ExpressionStatement; + this.astEditor.addToArray(statement.statements, 0, parsed); + this.foundBlocks.push({ + id: this.branchId, + branches: [{ + id: this.branchId, + line: lineNumber, + totalHit: 0 + }] + }); + this.blockId++; + this.branchId++; + + } + }, ForStatement: (ds, parent, owner, key) => { - this.addStatement(ds); - ds.forToken.text = `${this.getFuncCallText(ds.range.start.line, CodeCoverageLineType.code)}: for`; + this.addStatement(ds, ds.range.start.line); + ds.forToken.text = `${this.getReportLineHitFuncCallText(ds.range.start.line, CodeCoverageLineType.code, ds, owner, key)}: for`; }, IfStatement: (ifStatement, parent, owner, key) => { - this.addStatement(ifStatement); + this.addStatement(ifStatement, ifStatement.range.start.line); (ifStatement as any).condition = new BinaryExpression( - new RawCodeExpression(this.getFuncCallText(ifStatement.condition.range.start.line, CodeCoverageLineType.condition)), + new RawCodeExpression(this.getReportLineHitFuncCallText(ifStatement.condition.range.start.line, CodeCoverageLineType.condition, ifStatement, owner, key)), createToken(TokenKind.And), new GroupingExpression({ left: createToken(TokenKind.LeftParen), @@ -92,78 +160,89 @@ export class CodeCoverageProcessor { }, ifStatement.condition) ); - let blockStatements = ifStatement?.thenBranch?.statements; - if (blockStatements) { - let coverageStatement = new RawCodeStatement(this.getFuncCallText(ifStatement.range.start.line, CodeCoverageLineType.branch)); - blockStatements.splice(0, 0, coverageStatement); - } + // let blockStatements = ifStatement?.thenBranch?.statements; + // if (blockStatements) { + // let coverageStatement = new RawCodeStatement(this.getReportLineHitFuncCallText(ifStatement.range.start.line, CodeCoverageLineType.branch, ifStatement, owner, key)); + // 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)); - elseBlock.statements.splice(0, 0, coverageStatement); - } + // // Handle the else blocks + // let elseBlock = ifStatement.elseBranch; + // if (isBlock(elseBlock) && elseBlock.statements) { + // let coverageStatement = new RawCodeStatement(this.getReportLineHitFuncCallText(elseBlock.range.start.line - 1, CodeCoverageLineType.branch, elseBlock, owner, key)); + // elseBlock.statements.splice(0, 0, coverageStatement); + // } }, GotoStatement: (ds, parent, owner, key) => { - this.addStatement(ds); + this.addStatement(ds, ds.range.start.line); this.convertStatementToCoverageStatement(ds, CodeCoverageLineType.code, owner, key); }, WhileStatement: (ds, parent, owner, key) => { - ds.tokens.while.text = `${this.getFuncCallText(ds.range.start.line, CodeCoverageLineType.code)}: while`; + ds.tokens.while.text = `${this.getReportLineHitFuncCallText(ds.range.start.line, CodeCoverageLineType.code, ds, owner, key)}: while`; }, ReturnStatement: (ds, parent, owner, key) => { - this.addStatement(ds); + this.addStatement(ds, ds.range.start.line); 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`; + this.addStatement(ds, ds.range.start.line); + ds.tokens.forEach.text = `${this.getReportLineHitFuncCallText(ds.range.start.line, CodeCoverageLineType.code, ds, owner, key)}: for each`; }, ExitWhileStatement: (ds, parent, owner, key) => { }, PrintStatement: (ds, parent, owner, key) => { - this.addStatement(ds); + this.addStatement(ds, ds.range.start.line); this.convertStatementToCoverageStatement(ds, CodeCoverageLineType.code, owner, key); }, DottedSetStatement: (ds, parent, owner, key) => { - this.addStatement(ds); + this.addStatement(ds, ds.range.start.line); this.convertStatementToCoverageStatement(ds, CodeCoverageLineType.code, owner, key); }, IndexedSetStatement: (ds, parent, owner, key) => { - this.addStatement(ds); + this.addStatement(ds, ds.range.start.line); this.convertStatementToCoverageStatement(ds, CodeCoverageLineType.code, owner, key); }, IncrementStatement: (ds, parent, owner, key) => { - this.addStatement(ds); + this.addStatement(ds, ds.range.start.line); this.convertStatementToCoverageStatement(ds, CodeCoverageLineType.code, owner, key); }, AssignmentStatement: (ds, parent, owner, key) => { if (!isForStatement(parent)) { - this.addStatement(ds); + this.addStatement(ds, ds.range.start.line); this.convertStatementToCoverageStatement(ds, CodeCoverageLineType.code, owner, key); } }, ExpressionStatement: (ds, parent, owner, key) => { - this.addStatement(ds); + if (isCallExpression(ds.expression) && isVariableExpression(ds.expression.callee) && ds.expression.callee.name.text.startsWith('RBS_CC_')) { + return; + } + + this.addStatement(ds, ds.range.start.line); this.convertStatementToCoverageStatement(ds, CodeCoverageLineType.code, owner, key); } }), { walkMode: WalkMode.visitAllRecursive }); - const coverageMapObject = {}; - for (let key of this.coverageMap.keys()) { - coverageMapObject[key] = this.coverageMap.get(key); - } - this.expectedCoverageMap[this.fileId.toString().trim()] = coverageMapObject; - this.filePathMap[this.fileId] = file.pkgPath; this.addBrsAPIText(file, astEditor); + + this.baseCoverageReport.files[this.fileId] = { + sourceFile: file.pkgPath.replace('pkg:', '.').replace('\\', '/'), + lines: this.foundLines.sort((a, b) => a.lineNumber - b.lineNumber), + lineTotalFound: this.foundLines.length, + lineTotalHit: 0, + functions: this.foundFunctions.sort((a, b) => a.startLine - b.startLine), + functionTotalFound: this.foundFunctions.length, + functionTotalHit: 0, + blocks: this.foundBlocks, + branchTotalFound: this.foundBlocks.reduce((currentCount, block) => currentCount + block.branches.length, 0), + branchTotalHit: 0 + }; } private convertStatementToCoverageStatement(statement: Statement, coverageType: CodeCoverageLineType, owner: any, key: any) { @@ -172,8 +251,7 @@ export class CodeCoverageProcessor { } const lineNumber = statement.range.start.line; - this.coverageMap.set(lineNumber, coverageType); - const parsed = Parser.parse(this.getFuncCallText(lineNumber, coverageType)).ast.statements[0] as ExpressionStatement; + const parsed = Parser.parse(this.getReportLineHitFuncCallText(lineNumber, coverageType, statement, owner, key)).ast.statements[0] as ExpressionStatement; this.astEditor.arraySplice(owner, key, 0, parsed); // store the statement in a set to avoid handling again after inserting statement above this.processedStatements.add(statement); @@ -184,14 +262,351 @@ export class CodeCoverageProcessor { astEditor.arrayPush(file.ast.statements, ...astCodeToInject); } - private addStatement(statement: Statement, lineNumber?: number) { + private addStatement(statement: Statement, lineNumber: number) { if (!this.executableLines.has(lineNumber)) { this.executableLines.set(lineNumber, statement); + + this.foundLines.push({ + lineNumber: lineNumber, + totalHit: 0 + }); + } + } + + private getReportLineHitFuncCallText(lineNumber: number, lineType: CodeCoverageLineType, statement: Statement, owner: any, key: any) { + const funcId = this.getFunctionIdInFile(statement, ParseMode.BrighterScript, owner, key); + return `RBS_CC_${this.fileId}_reportLine(${lineNumber})`; + } + + private getReportBranchHitFuncCallText(blockId: number, branchId: number, statement: Statement, owner: any, key: any) { + const funcId = this.getFunctionIdInFile(statement, ParseMode.BrighterScript, owner, key); + return `RBS_CC_${this.fileId}_reportBranch(${blockId}, ${branchId})`; + } + + private getReportFunctionHitFuncCallText(functionId: number, statement: Statement) { + return `RBS_CC_${this.fileId}_reportFunction(${functionId})`; + } + + private getFunctionIdInFile(statement: Statement, parseMode: ParseMode, owner: any, key: any) { + let originalFunc: FunctionExpression; + if (isFunctionStatement(statement)) { + originalFunc = statement.func; + } else { + originalFunc = statement.findAncestor(isFunctionExpression); + } + let func: FunctionExpression = originalFunc; + + let nameParts = []; + while (func.parentFunction) { + let index = func.parentFunction.childFunctionExpressions.indexOf(func); + nameParts.unshift(`anon${index}`); + func = func.parentFunction; + } + //get the index of this function in its parent + nameParts.unshift( + func.functionStatement.getName(parseMode) + ); + + const name = nameParts.join('$'); + + if (!this.processedFunctions.has(originalFunc)) { + this.processedFunctions.add(originalFunc); + if (!this.functionMap[this.fileId]) { + this.functionMap[this.fileId] = []; + } + + const parsed = Parser.parse(this.getReportFunctionHitFuncCallText(this.functionMap[this.fileId].length, statement)).ast.statements[0] as ExpressionStatement; + this.astEditor.addToArray(originalFunc.body.statements, 0, parsed); + + this.foundFunctions.push({ + name: name, + startLine: originalFunc.range.start.line, + endLine: originalFunc.range.end.line, + totalHit: 0 + }); + this.functionMap[this.fileId].push(name); } + + return this.functionMap[this.fileId].indexOf(name); } +} + + +export interface CoverageMap { + files: Array; +} + +interface FileCoverage { + sourceFile: string; + lineTotalFound: number; + lineTotalHit: number; + lines: Array; + functionTotalFound: number; + functionTotalHit: number; + functions: Array; + branchTotalFound: number; + branchTotalHit: number; + blocks: Array; +} + +interface BranchCoverage { + id: number; + branches: Array<{ + id: number; + totalHit: number; + line: number; + }>; +} +interface FunctionCoverage { + name: string; + totalHit: number; + startLine: number; + endLine: number; +} + +interface LineCoverage { + lineNumber: number; + totalHit: number; +} + +function createCovMap(files: Array) { + + let report = ''; + + let covrageMap: CoverageMap = { + files: [] + }; + + for (const file of files) { + report += `TN:\n`; + report += `SF:${file.sourceFile}\n`; + report += `VER:\n`; + + // Add all the found functions for the file + for (const func of file.functions) { + report += `FN:${func.startLine},${func.endLine},${func.name}\n`; + } - private getFuncCallText(lineNumber: number, lineType: CodeCoverageLineType) { - this.coverageMap.set(lineNumber, lineType); - return `RBS_CC_${this.fileId}_reportLine("${lineNumber.toString().trim()}", ${lineType.toString().trim()})`; + // Write function related data + for (const func of file.functions) { + if (func.totalHit > 0) { + report += `FNDA:${func.totalHit},${func.name}\n`; + } + } + + report += `FNF:${file.functionTotalFound}\n`; + report += `FNH:${file.functionTotalHit}\n`; + + // Write branch related data + for (const block of file.blocks) { + for (const branch of block.branches) { + if (branch.totalHit > 0) { + report += `BRDA:${branch.line},${block.id},${branch.id},${branch.totalHit}\n`; + } + } + } + + report += `BRF:${file.branchTotalFound}\n`; + report += `BRH:${file.branchTotalHit}\n`; + + // Write the per line related data + for (const line of file.lines) { + report += `DA:${line.lineNumber},${line.totalHit}`; + } + + report += `LF:${file.lineTotalFound}\n`; + report += `LH:${file.lineTotalHit}\n`; + report += `end_of_record\n`; } } +// function write_info($$$) { +// my $self = $_[0]; +// local *INFO_HANDLE = $_[1]; +// my $checksum = defined($_[2]) ? $_[2] : 0; +// my $br_found; +// my $br_hit; +// my $ln_total_found = 0; +// my $ln_total_hit = 0; +// my $fn_total_found = 0; +// my $fn_total_hit = 0; +// my $br_total_found = 0; +// my $br_total_hit = 0; + +// my $srcReader = ReadCurrentSource->new() +// if (lcovutil::is_filter_enabled()); + +// foreach my $source_file (sort($self->files())) { +// next if lcovutil::is_external($source_file); +// my $entry = $self->data($source_file); +// die("expected TraceInfo, got '" . ref($entry) . "'") +// unless('TraceInfo' eq ref($entry)); + +// my ($testdata, $sumcount, $funcdata, $checkdata, $testfncdata, +// $testbrdata, $sumbrcount, $found, $hit, +// $f_found, $f_hit, $br_found, $br_hit) = $entry->get_info(); + +// # munge the source file name, if requested +// $source_file = lcovutil::subst_file_name($source_file); +// # Add to totals +// $ln_total_found += $found; +// $ln_total_hit += $hit; +// $fn_total_found += $f_found; +// $fn_total_hit += $f_hit; +// $br_total_found += $br_found; +// $br_total_hit += $br_hit; + +// foreach my $testname (sort($testdata->keylist())) { +// my $testcount = $testdata->value($testname); +// my $testfnccount = $testfncdata->value($testname); +// my $testbrcount = $testbrdata->value($testname); +// $found = 0; +// $hit = 0; + +// print(INFO_HANDLE "TN:$testname\n"); +// print(INFO_HANDLE "SF:$source_file\n"); +// print(INFO_HANDLE "VER:" . $entry->version() . "\n") +// if defined($entry->version()); +// if (defined($srcReader)) { +// $srcReader->close(); +// if (is_c_file($source_file)) { +// lcovutil::debug("reading $source_file for lcov filtering\n"); +// if (-e $source_file) { +// $srcReader->open($source_file); +// } else { +// lcovutil::ignorable_error($lcovutil::ERROR_SOURCE, +// "'$source_file' not found (for filtering)") +// if (lcovutil::warn_once($source_file)); +// } +// } else { +// lcovutil::debug("not reading $source_file: no ext match\n"); +// } +// } +// my $functionMap = $testfncdata->{$testname}; +// # Write function related data - sort by line number +// foreach my $key ( sort({$functionMap->findKey($a)->line() <=> $functionMap->findKey($b)->line()} +// $functionMap->keylist())) { +// my $data = $functionMap->findKey($key); +// my $aliases = $data->aliases(); +// foreach my $alias (keys %$aliases) { +// print(INFO_HANDLE "FN:" . $data->line(). ",$alias\n"); +// } +// } +// my $f_found = 0; +// my $f_hit = 0; +// foreach my $key ($functionMap->keylist()) { +// my $data = $functionMap->findKey($key); +// my $aliases = $data->aliases(); +// foreach my $alias (keys %$aliases) { +// my $hit = $aliases->{$alias}; +// ++ $f_found; +// ++ $f_hit if $hit > 0; +// print(INFO_HANDLE "FNDA:$hit,$alias\n"); +// } +// } +// print(INFO_HANDLE "FNF:$f_found\n"); +// print(INFO_HANDLE "FNH:$f_hit\n"); + +// # Write branch related data +// $br_found = 0; +// $br_hit = 0; +// my $currentBranchLine; +// my $skipBranch = 0; +// my $reader = $srcReader +// if (defined($srcReader) && $srcReader->notEmpty()); +// my $branchHistogram = $cov_filter[$FILTER_BRANCH_NO_COND] +// if $reader; + +// foreach my $line (sort({$a <=> $b} +// $testbrcount->keylist())) { + +// # omit if line excluded or branches excluded on this line +// next +// if (defined($reader) && +// ($reader->isOutOfRange($line, 'branch') || +// $reader->isExcluded($line, 1))); + +// my $brdata = $testbrcount->value($line); +// if (defined($branchHistogram)) { +// $skipBranch = ! $reader->containsConditional($line); +// if ($skipBranch) { +// ++ $branchHistogram->[0]; # one line where we skip +// $branchHistogram->[1] += scalar($brdata->blocks()); +// lcovutil::info(2, "skip BRDA '" . +// $reader->getLine($line) . +// "' $source_file:$line\n"); +// next; +// } +// } +// # want the block_id to be treated as 32-bit unsigned integer +// # (need masking to match regression tests) +// my $mask = (1<<32) -1; +// foreach my $block_id ($brdata->blocks()) { +// my $blockData = $brdata->getBlock($block_id); +// $block_id &= $mask; +// foreach my $br (@$blockData) { +// my $taken = $br->data(); +// my $branch_id = $br->id(); +// my $branch_expr = $br->expr(); +// # mostly for Verilog: if there is a branch expression: use it. +// printf(INFO_HANDLE "BRDA:%u,%u,%s,%s\n", +// $line, $block_id, +// defined($branch_expr) ? $branch_expr : $branch_id, $taken); +// $br_found++; +// $br_hit++ +// if ($taken ne '-' && $taken > 0); +// } +// } +// } +// if ($br_found > 0) { +// print(INFO_HANDLE "BRF:$br_found\n"); +// print(INFO_HANDLE "BRH:$br_hit\n"); +// } + +// # Write line related data +// my ($brace_histogram, $blank_histogram); +// if (defined($reader)) { +// $brace_histogram = $cov_filter[$FILTER_LINE_CLOSE_BRACE]; +// $blank_histogram = $cov_filter[$FILTER_BLANK_LINE]; +// } +// foreach my $line (sort({$a <=> $b} $testcount->keylist())) { +// next +// if (defined($reader) && +// ($reader->isOutOfRange($line, 'line') || $reader->isExcluded($line))); + +// my $l_hit = $testcount->value($line); +// if ( ! defined($sumbrcount->value($line))) { +// # don't suppresss if this line has associated branch data + +// if ($brace_histogram && +// $reader->suppressCloseBrace($line, $l_hit, $testcount)) { +// lcovutil::info(2, "skip DA '" . $reader->getLine($line) +// . "' $source_file:$line\n"); +// ++$brace_histogram->[0]; # one location where this applied +// ++$brace_histogram->[1]; # one coverpoint suppressed +// next; +// } elsif ($blank_histogram && +// $l_hit == 0 && +// $reader->isBlank($line)) { +// lcovutil::info(2, "skip DA (empty) $source_file:$line\n"); +// ++ $blank_histogram->[0]; # one location where this applied +// ++ $blank_histogram->[1]; # one coverpoint suppressed +// next; +// } +// } +// my $chk = $checkdata->{$line}; +// print(INFO_HANDLE "DA:$line,$l_hit" . +// (defined($chk) && $checksum ? ",". $chk : "") +// ."\n"); +// $found++; +// $hit++ +// if ($l_hit > 0); +// } +// print(INFO_HANDLE "LF:$found\n"); +// print(INFO_HANDLE "LH:$hit\n"); +// print(INFO_HANDLE "end_of_record\n"); +// } +// } + +// return ($ln_total_found, $ln_total_hit, $fn_total_found, $fn_total_hit, +// $br_total_found, $br_total_hit); +// } diff --git a/bsc-plugin/src/lib/rooibos/FileFactory.ts b/bsc-plugin/src/lib/rooibos/FileFactory.ts index 24b16c59..09b56d29 100644 --- a/bsc-plugin/src/lib/rooibos/FileFactory.ts +++ b/bsc-plugin/src/lib/rooibos/FileFactory.ts @@ -3,6 +3,7 @@ import { standardizePath as s } from 'brighterscript'; import * as path from 'path'; import * as fs from 'fs'; import * as fse from 'fs-extra'; +import type { CoverageMap } from './CodeCoverageProcessor'; import type { TestSuite } from './TestSuite'; export class FileFactory { @@ -106,10 +107,9 @@ export class FileFactory { return contents; } - public createCoverageComponent(program: Program, coverageMap: any, filepathMap: Map) { + public createCoverageComponent(program: Program, baseCoverageReport: CoverageMap) { let template = this.coverageComponentBrsTemplate; - template = template.replace(/\"\#EXPECTED_MAP\#\"/g, JSON.stringify(coverageMap ?? {})); - template = template.replace(/\"\#FILE_PATH_MAP\#\"/g, JSON.stringify(filepathMap ?? {})); + template = template.replace(/\"\#BASE_COVERAGE_REPORT\#\"/g, JSON.stringify(baseCoverageReport ?? { files: [] })); this.addFileToRootDir(program, path.join('components/rooibos', 'CodeCoverage.brs'), template); this.addFileToRootDir(program, path.join('components/rooibos', 'CodeCoverage.xml'), this.coverageComponentXmlTemplate); diff --git a/framework/src/source/CodeCoverage.brs b/framework/src/source/CodeCoverage.brs index 8bb7d5e0..909e045f 100644 --- a/framework/src/source/CodeCoverage.brs +++ b/framework/src/source/CodeCoverage.brs @@ -1,39 +1,122 @@ function init() - m.resolvedMap = {} - m.top.observeField("entry", "onEntryChange") - m.top.observeField("save", "onSave") - m.results = [] + m.coverageMap = "#BASE_COVERAGE_REPORT#" + m.port = createObject("roMessagePort") + m.top.observeFieldScoped("entry", m.port) + m.top.functionName = "runTaskThread" + m.top.control = "RUN" end function -function setExpectedMap() - m.top.expectedMap = "#EXPECTED_MAP#" -end function +function runTaskThread() as void + while true + events = [] + saving = false + message = getMessage(m.port) + if message <> invalid + events.push(message) + end if + if m.top.save = true + saving = true + ? "Saving unprocessed code cov events..." + 'Get All the unprocessed messages + while true + message = getMessage(m.port, 3) + if message = invalid + exit while + else + events.push(message) + end if + end while + + ? "Found" events.count() " unprocessed events..." + end if + + ' enum CodeCoverageLineType + ' noCode = 0 + ' code = 1 + ' condition = 2 + ' branch = 3 + ' function = 4 + ' end enum + + for each event in events + entry = event.getData() + if entry <> invalid + file = m.coverageMap.files[entry.f] + if entry.r = 4 ' CodeCoverageLineType.function + if file.functions[entry.fn].totalHit = 0 + file.functionTotalHit ++ + end if + file.functions[entry.fn].totalHit ++ + else if entry.r = 3 ' CodeCoverageLineType.branch + for each branch in file.blocks[entry.bl].branches + if branch.id = entry.br + if branch.totalHit = 0 + file.branchTotalHit ++ + end if + branch.totalHit ++ + exit for + end if + end for + else if entry.r = 1 ' CodeCoverageLineType.code + for each line in file.lines + if line.lineNumber = entry.l + if line.totalHit = 0 + file.lineTotalHit ++ + end if + line.totalHit ++ + exit for + end if + end for + end if + m.coverageMap.files[entry.f] = file + end if + end for + + if saving = true + m.top.coverageResults = m.coverageMap + return + end if + end while -function setFilePathMap() - m.top.filePathMap = "#FILE_PATH_MAP#" end function -function onEntryChange() - entry = m.top.entry - ' defer till later - m.results.push(entry) + +' Gets the next message from the message port and applies a short sleep if no message was returned. +' @param {roMessagePort} port - The active message port to get messages from. +' @param {Integer} [sleepInterval] - How long to sleep if there was no message returned. +' @return {Dynamic} Any resulting message from the message port. +function getMessage(port as object, sleepInterval = 20 as integer) as dynamic + message = port.getMessage() + ' I know I will always get something from the port so no need for the uninitialized check in isInvalid + if message = invalid then sleep(sleepInterval) + return message end function -function onSave() - ? "saving data" - for each entry in m.results - if entry <> invalid - fileId = entry.f - lineMap = m.resolvedMap[fileId] +#if false + sub test() + player = m.player - if lineMap = invalid - lineMap = {} - m.resolvedMap[fileId] = lineMap - end if - lineMap[entry.l] = entry.r + report = false + if player = invalid or (player.duration > 0 and player.state = "playing") then + report = true + end if + + if report = true then + player.control = "stop" + end if + end sub + + + sub test() + player = m.player + + report = false + if RBS_CC_0_reportCondition(109, 1, player = invalid) or RBS_CC_0_reportCondition(109, 2, (RBS_CC_0_reportCondition(109, 3, player.duration > 0) and RBS_CC_0_reportCondition(109, 4, player.state = "playing"))) then + report = true + end if + + if report = true then + player.control = "stop" end if - end for - m.top.resolvedMap = m.resolvedMap - setExpectedMap() - setFilePathMap() -end function \ No newline at end of file + end sub +#end if diff --git a/framework/src/source/CodeCoverage.xml b/framework/src/source/CodeCoverage.xml index e948d05f..e0dcd3b0 100644 --- a/framework/src/source/CodeCoverage.xml +++ b/framework/src/source/CodeCoverage.xml @@ -1,13 +1,12 @@