• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

rokucommunity / rooibos / 26598497209

28 May 2026 07:52PM UTC coverage: 84.234% (+0.06%) from 84.179%
26598497209

push

github

web-flow
Make tests less brittle - removes full transpile string checks (#396)

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

669 of 861 branches covered (77.7%)

Branch coverage included in aggregate %.

0 of 2 new or added lines in 2 files covered. (0.0%)

1030 of 1156 relevant lines covered (89.1%)

176.02 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

85.32
/src/lib/rooibos/CodeCoverageProcessor.ts
1
import type { BrsFile, Editor, ExpressionStatement, Program, ProgramBuilder, Statement } from 'brighterscript';
2
import { Parser, WalkMode, createVisitor, BinaryExpression, createToken, TokenKind, GroupingExpression, isForStatement, isBlock } from 'brighterscript';
1✔
3
import type { RooibosConfig } from './RooibosConfig';
4
import { RawCodeStatement } from './RawCodeStatement';
1✔
5
import { RawCodeExpression } from './RawCodeExpression';
1✔
6
import type { FileFactory } from './FileFactory';
7

8
export enum CodeCoverageLineType {
1✔
9
    noCode = 0,
1✔
10
    code = 1,
1✔
11
    condition = 2,
1✔
12
    branch = 3
1✔
13
}
14

15
export class CodeCoverageProcessor {
1✔
16

17
    private coverageBrsTemplate = `
91✔
18
        function RBS_CC_#ID#_reportLine(lineNumber, reportType = 1)
19
            _rbs_ccn = m._rbs_ccn
20
            if _rbs_ccn <> invalid
21
                _rbs_ccn.entry = { "f": "#ID#", "l": lineNumber, "r": reportType }
22
                return true
23
            end if
24

25
            _rbs_ccn = m?.global?._rbs_ccn
26
            if _rbs_ccn <> invalid
27
                _rbs_ccn.entry = { "f": "#ID#", "l": lineNumber, "r": reportType }
28
                m._rbs_ccn = _rbs_ccn
29
                return true
30
            end if
31
            return true
32
        end function
33
    `;
34

35
    constructor(builder: ProgramBuilder, fileFactory: FileFactory) {
36
        this.config = (builder.options as any).rooibos as RooibosConfig || {};
91✔
37
        this.expectedCoverageMap = {};
91✔
38
        this.filePathMap = {};
91✔
39
        this.fileId = 0;
91✔
40
        this.fileFactory = fileFactory;
91✔
41
        try {
91✔
42
        } catch (e) {
NEW
43
            console.log('Error:', (e as Error).stack);
×
44
        }
45
    }
46

47
    private config: RooibosConfig;
48
    private fileId: number;
49
    private filePathMap: any;
50
    private expectedCoverageMap: any;
51
    private executableLines: Map<number, Statement>;
52
    private coverageMap: Map<number, number>;
53
    private fileFactory: FileFactory;
54
    private processedStatements: Set<Statement>;
55
    private addedStatements: Set<Statement>;
56
    private astEditor: Editor;
57

58
    public generateMetadata(isUsingCoverage: boolean, program: Program) {
59
        this.fileFactory.createCoverageComponent(program, this.expectedCoverageMap, this.filePathMap);
65✔
60
    }
61

62
    public addCodeCoverage(file: BrsFile, astEditor: Editor) {
63
        if (this.config.isRecordingCodeCoverage) {
7!
64
            this._processFile(file, astEditor);
7✔
65
        }
66
    }
67

68
    public _processFile(file: BrsFile, astEditor: Editor) {
69
        this.fileId++;
7✔
70
        this.coverageMap = new Map<number, number>();
7✔
71
        this.executableLines = new Map<number, Statement>();
7✔
72
        this.processedStatements = new Set<Statement>();
7✔
73
        this.addedStatements = new Set<Statement>();
7✔
74
        this.astEditor = astEditor;
7✔
75

76
        file.ast.walk(createVisitor({
7✔
77
            ForStatement: (ds, parent, owner, key) => {
78
                this.addStatement(ds);
3✔
79
                ds.forToken.text = `${this.getFuncCallText(ds.range.start.line, CodeCoverageLineType.code)}: for`;
3✔
80
            },
81
            IfStatement: (ifStatement, parent, owner, key) => {
82
                this.addStatement(ifStatement);
18✔
83
                (ifStatement as any).condition = new BinaryExpression(
18✔
84
                    new RawCodeExpression(this.getFuncCallText(ifStatement.condition.range.start.line, CodeCoverageLineType.condition)),
85
                    createToken(TokenKind.And),
86
                    new GroupingExpression({
87
                        left: createToken(TokenKind.LeftParen),
88
                        right: createToken(TokenKind.RightParen)
89
                    }, ifStatement.condition)
90
                );
91

92
                let blockStatements = ifStatement?.thenBranch?.statements;
18!
93
                if (blockStatements) {
18!
94
                    let coverageStatement = new RawCodeStatement(this.getFuncCallText(ifStatement.range.start.line, CodeCoverageLineType.branch));
18✔
95
                    blockStatements.splice(0, 0, coverageStatement);
18✔
96
                }
97

98
                // Handle the else blocks
99
                let elseBlock = ifStatement.elseBranch;
18✔
100
                if (isBlock(elseBlock) && elseBlock.statements) {
18✔
101
                    let coverageStatement = new RawCodeStatement(this.getFuncCallText(elseBlock.range.start.line - 1, CodeCoverageLineType.branch));
5✔
102
                    elseBlock.statements.splice(0, 0, coverageStatement);
5✔
103
                }
104

105
            },
106
            GotoStatement: (ds, parent, owner, key) => {
107
                this.addStatement(ds);
×
108
                this.convertStatementToCoverageStatement(ds, CodeCoverageLineType.code, owner, key);
×
109

110
            },
111
            WhileStatement: (ds, parent, owner, key) => {
112
                ds.tokens.while.text = `${this.getFuncCallText(ds.range.start.line, CodeCoverageLineType.code)}: while`;
×
113
            },
114
            ReturnStatement: (ds, parent, owner, key) => {
115
                this.addStatement(ds);
×
116
                this.convertStatementToCoverageStatement(ds, CodeCoverageLineType.code, owner, key);
×
117
            },
118
            ForEachStatement: (ds, parent, owner, key) => {
119
                this.addStatement(ds);
×
120
                ds.tokens.forEach.text = `${this.getFuncCallText(ds.range.start.line, CodeCoverageLineType.code)}: for each`;
×
121
            },
122
            ExitWhileStatement: (ds, parent, owner, key) => {
123

124
            },
125
            PrintStatement: (ds, parent, owner, key) => {
126
                this.addStatement(ds);
23✔
127
                this.convertStatementToCoverageStatement(ds, CodeCoverageLineType.code, owner, key);
23✔
128
            },
129
            DottedSetStatement: (ds, parent, owner, key) => {
130
                this.addStatement(ds);
×
131
                this.convertStatementToCoverageStatement(ds, CodeCoverageLineType.code, owner, key);
×
132

133
            },
134
            IndexedSetStatement: (ds, parent, owner, key) => {
135
                this.addStatement(ds);
×
136
                this.convertStatementToCoverageStatement(ds, CodeCoverageLineType.code, owner, key);
×
137

138
            },
139
            IncrementStatement: (ds, parent, owner, key) => {
140
                this.addStatement(ds);
3✔
141
                this.convertStatementToCoverageStatement(ds, CodeCoverageLineType.code, owner, key);
3✔
142

143
            },
144
            AssignmentStatement: (ds, parent, owner, key) => {
145
                if (!isForStatement(parent)) {
17✔
146
                    this.addStatement(ds);
14✔
147
                    this.convertStatementToCoverageStatement(ds, CodeCoverageLineType.code, owner, key);
14✔
148
                }
149

150
            },
151
            ExpressionStatement: (ds, parent, owner, key) => {
152
                this.addStatement(ds);
40✔
153
                this.convertStatementToCoverageStatement(ds, CodeCoverageLineType.code, owner, key);
40✔
154
            }
155
        }), { walkMode: WalkMode.visitAllRecursive });
156

157
        const coverageMapObject = {};
7✔
158
        for (let key of this.coverageMap.keys()) {
7✔
159
            coverageMapObject[key] = this.coverageMap.get(key);
66✔
160
        }
161
        this.expectedCoverageMap[this.fileId.toString().trim()] = coverageMapObject;
7✔
162
        this.filePathMap[this.fileId] = file.pkgPath;
7✔
163
        this.addBrsAPIText(file, astEditor);
7✔
164
    }
165

166
    private convertStatementToCoverageStatement(statement: Statement, coverageType: CodeCoverageLineType, owner: any, key: any) {
167
        if (this.processedStatements.has(statement) || this.addedStatements.has(statement)) {
80✔
168
            return;
40✔
169
        }
170

171
        const lineNumber = statement.range.start.line;
40✔
172
        this.coverageMap.set(lineNumber, coverageType);
40✔
173
        const parsed = Parser.parse(this.getFuncCallText(lineNumber, coverageType)).ast.statements[0] as ExpressionStatement;
40✔
174
        this.astEditor.arraySplice(owner, key, 0, parsed);
40✔
175
        this.addedStatements.add(parsed);
40✔
176
        // store the statement in a set to avoid handling again after inserting statement above
177
        this.processedStatements.add(statement);
40✔
178
    }
179

180
    public addBrsAPIText(file: BrsFile, astEditor: Editor) {
181
        const astCodeToInject = Parser.parse(this.coverageBrsTemplate.replace(/\#ID\#/g, this.fileId.toString().trim())).ast.statements;
7✔
182
        astEditor.arrayPush(file.ast.statements, ...astCodeToInject);
7✔
183
        for (let statement of astCodeToInject) {
7✔
184
            this.addedStatements.add(statement);
7✔
185
        }
186
    }
187

188
    private addStatement(statement: Statement, lineNumber?: number) {
189
        if (!this.executableLines.has(lineNumber)) {
101✔
190
            this.executableLines.set(lineNumber, statement);
7✔
191
        }
192
    }
193

194
    private getFuncCallText(lineNumber: number, lineType: CodeCoverageLineType) {
195
        this.coverageMap.set(lineNumber, lineType);
84✔
196
        return `RBS_CC_${this.fileId}_reportLine("${lineNumber.toString().trim()}", ${lineType.toString().trim()})`;
84✔
197
    }
198
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc