This extension provides LCOV code coverage reporting capabilities for Lucee CFML applications.
Requires Lucee 7.0.1.7 or newer (requires AST enchancements)
What is line coverage? It a report of what code was executed either by some sort of testing, which allows you to see which parts of the code based aren't being used or tested.
Note
CURRENTLY still in early BETA, WIP subject to change, not yet published! CURRENTLY still in early BETA, WIP subject to changed, not yet published!
- Parse execution log files (.exl) generated by Lucee's Execution Logging https://docs.lucee.org/recipes/execution-log.html
- Generate LCOV format reports compatible with standard coverage tools
- Create HTML reports with detailed line-by-line coverage visualization
- Support for multiple time unit displays (seconds, milliseconds, microseconds, nanoseconds)
- AST-based source code analysis for accurate line counting (matches what Lucee's execution logger tracks)
- Coverage statistics and reporting utilities
- Handles processing large logs from a single test suite execution run
option ={ separateFiles: true }
This extension consumes the Execution Logs from Lucee, it currently doesn't consider some block elmements as being executed, i.e. switch, component, function, etc.
Execution logs use character offsets, which needs to be translated to per line records.
In addition, they contain overlapping blocks which need to be filtered out in order to produce useful reports.
Processing large logs (ie.e 500Mb+) from test suite runs, requires a lot of filtering and de-duplication, be patient, it's been tuned to be fast, use the verbose: true
option to see progress/details.

The LCOV files produced by this extension can be visualised in VS Code via the Coverage Gutters extension

The extension provides easy-to-use functions for code coverage analysis:
Any directories being passed as arguments must already exist!
lcovStartLogging( adminPassword, executionLogDir="", options={} )
- Start execution logging with ResourceExecutionLoglcovStopLogging( adminPassword, className="lucee.runtime.engine.ResourceExecutionLog" )
- Stop execution logging
Execution Logging Options
- className - Lucee Java class name for execution logging, default is
lucee.runtime.engine.ResourceExecutionLog
- executionLogDir Directory for storing .exl execution log files, (string, default: auto-generated)
lcovGenerateAllReports( executionLogDir, outputDir, options={} )
- Generate LCOV, HTML, and JSON reportslcovGenerateLcov( executionLogDir, outputFile, options={} )
- Generate LCOV format file onlylcovGenerateHtml( executionLogDir, outputDir, options={} )
- Generate HTML reports onlylcovGenerateJson( executionLogDir, outputDir, options={} )
- Generate JSON reports onlylcovGenerateSummary( executionLogDir, options={} )
- Generate coverage statistics only
The options
struct can include the following configuration parameters:
verbose
Enables verbose logging during processing, defaultfalse
displayUnit
- Time unit for display:auto
,nano
,micro
,milli
,second
- defaultfalse
unit
- Time unit for execution logging:nano
,micro
ormilli
, defaultmicro
minTime
- Minimum execution time threshold for logging, default0
File Filtering
allowList
- Array of file paths/patterns to include (when specified, only these files are processed)blocklist
- Array of file paths/patterns to exclude from processing, i.e. frameworks, testbox, etc
HTML Report Options
separateFiles
- Generate separate HTML files per source file instead of files per request, defaultfalse
title
Title for HTML reportsincludeTimestamp
- Include timestamp in report headers,boolean=true
AST Options
LCOV differentiates between executable lines and non-executable lines, aka linesFound
. I.e. comment is non-executable and therefore ignored so it doesn't count towards missing line coverage.
useAstForLinesFound
(boolean, default: true) - Use AST-based analysis for accurate executable line counting, otherwise it uses a simpler non AST approach.
source/components/lucee/extension/lcov/ExecutableLineCounter.cfc
var options = {
verbose: true,
displayUnit: "milli",
allowList: ["/app", "/lib"],
blocklist: ["/testbox", "/specs", "{lucee-config}"],
separateFiles: true,
title: "My Application Coverage Report",
useAstForLinesFound: true,
minTime: 100 // Only log executions > 100 microseconds
};
- ExecutionLogParser - Parses Lucee .exl execution log files with verbose logging
- CoverageBlockProcessor - Utility functions for coverage calculations and data processing
- HtmlReporter - Generates HTML coverage reports with dependency checks
- HtmlWriter - Handles HTML content generation with customizable time units
- ExecutableLineCounter - AST-based source code analysis for line counting
- exeLogger - Utility component to enable/disable execution logs
- LcovFunctions - Main component providing all extension functions
The extension functions are automatically available once installed.
IMPORTANT: ResourceExecutionLog only works on NEW requests after enableExecutionLog()
. You must use internalRequest()
, cfhttp
, or similar to make a new request for logging to occur. Code executed in the same request as enableExecutionLog()
will NOT be logged.
Use them in your CFML code like:
codeCoverageDir = ""/path/to/resourceExecutionlogs";
// Start execution logging
var logDirectory = lcovStartLogging(
adminPassword = "your-admin-password",
executionLogDir = codeCoverageDir, // Optional: specify directory, or leave empty for auto-generated
options = {
unit: "milli",
minTime: 0,
className: "lucee.runtime.engine.ResourceExecutionLog"
}
);
// Run your tests or application code here
// CRITICAL: Must use internalRequest() for logging to work, logging only works for subsequent requests!
internalRequest(
template = "/your/test/runner.cfm",
throwonerror = true
);
// or
internalRequest(
template = "/your/application/entry-point.cfm",
throwonerror = true
);
// Stop execution logging
lcovStopLogging(adminPassword = "your-admin-password");
var options = {
verbose: true,
allowList: [],
blocklist: [expandPath("/testbox"), expandPath("/specs"), expandPath("{lucee-config}")],
displayUnit: "auto"
};
reportDir = "/path/to/reports";
if ( !directoryExists( reportDir ) )
directoryCreate( reportDir );
// Generate all report types (LCOV, HTML, JSON)
var result = lcovGenerateAllReports( executionLogDir=codeCoverageDir, outputDir=reportDir, options=options );
// Or generate individual report types:
// lcovGenerateLcov(executionLogDir=codeCoverageDir, outputFile="/path/to/lcov.info", options=options);
// lcovGenerateHtml(executionLogDir=codeCoverageDir, outputDir=reportDir, options=options);
// lcovGenerateJson(executionLogDir=codeCoverageDir, outputDir=reportDir, options=options);
Install the extension through the Lucee Administrator or deploy the .lex file manually.
- Lucee 7.0.0.372-SNAPSHOT or higher
- Java 11+, 21+ recommend for performance reasons
GNU Lesser General Public License, Version 2.1
Contributions are welcome! Please submit issues and pull requests to the GitHub repository.