92 lines
2.7 KiB
Dart
92 lines
2.7 KiB
Dart
// SPDX-FileCopyrightText: 2026 FocusFlow contributors
|
|
// SPDX-License-Identifier: AGPL-3.0-only
|
|
|
|
/// Runs the Check Coverage workspace validation tool.
|
|
library;
|
|
|
|
import 'dart:io';
|
|
|
|
/// Checks an LCOV report against a package-library coverage threshold.
|
|
void main(List<String> arguments) {
|
|
if (arguments.length != 2) {
|
|
stderr.writeln('Usage: dart run tool/check_coverage.dart <lcov> <percent>');
|
|
exitCode = 64;
|
|
return;
|
|
}
|
|
|
|
final lcovFile = File(arguments[0]);
|
|
final threshold = double.tryParse(arguments[1]);
|
|
if (threshold == null) {
|
|
stderr.writeln('Coverage threshold must be numeric: ${arguments[1]}');
|
|
exitCode = 64;
|
|
return;
|
|
}
|
|
if (!lcovFile.existsSync()) {
|
|
stderr.writeln('Coverage file not found: ${lcovFile.path}');
|
|
exitCode = 1;
|
|
return;
|
|
}
|
|
|
|
final coverage = _readCoverage(lcovFile);
|
|
if (coverage.totalLines == 0) {
|
|
stderr.writeln('Coverage file has no package lib/ lines.');
|
|
exitCode = 1;
|
|
return;
|
|
}
|
|
|
|
final percent = coverage.percent;
|
|
stdout.writeln(
|
|
'Package lib coverage: ${percent.toStringAsFixed(2)}% '
|
|
'(${coverage.coveredLines}/${coverage.totalLines})',
|
|
);
|
|
if (percent < threshold) {
|
|
stderr.writeln(
|
|
'Coverage ${percent.toStringAsFixed(2)}% is below '
|
|
'${threshold.toStringAsFixed(2)}%.',
|
|
);
|
|
exitCode = 1;
|
|
}
|
|
}
|
|
|
|
/// Runs the `this operation` top-level operation.
|
|
/// This function is part of the file-level API and gives callers a direct way to use the associated scheduler behavior.
|
|
({int coveredLines, int totalLines, double percent}) _readCoverage(File file) {
|
|
var includeFile = false;
|
|
var coveredLines = 0;
|
|
var totalLines = 0;
|
|
|
|
for (final line in file.readAsLinesSync()) {
|
|
if (line.startsWith('SF:')) {
|
|
includeFile = _includeSourceFile(line.substring(3));
|
|
continue;
|
|
}
|
|
if (!includeFile || !line.startsWith('DA:')) {
|
|
continue;
|
|
}
|
|
final comma = line.indexOf(',');
|
|
if (comma == -1) {
|
|
continue;
|
|
}
|
|
totalLines += 1;
|
|
final hits = int.tryParse(line.substring(comma + 1).split(',').first) ?? 0;
|
|
if (hits > 0) {
|
|
coveredLines += 1;
|
|
}
|
|
}
|
|
|
|
final percent = totalLines == 0 ? 0.0 : coveredLines * 100 / totalLines;
|
|
return (
|
|
coveredLines: coveredLines,
|
|
totalLines: totalLines,
|
|
percent: percent,
|
|
);
|
|
}
|
|
|
|
/// Top-level helper that performs the `_includeSourceFile` operation for this file.
|
|
/// Keeping the helper private makes the surrounding flow easier to read while preventing unrelated libraries from depending on implementation detail.
|
|
bool _includeSourceFile(String path) {
|
|
final normalized = path.replaceAll('\\', '/');
|
|
return normalized.contains('/packages/') &&
|
|
normalized.contains('/lib/') &&
|
|
!normalized.endsWith('.g.dart');
|
|
}
|