132 lines
3.4 KiB
Dart
132 lines
3.4 KiB
Dart
import 'dart:io';
|
|
|
|
/// Enforces FLUTTER-TDD-PLAN.md Section 2 line-coverage targets for admin portal files.
|
|
void main() {
|
|
final String root = _repoRoot();
|
|
final List<_CoverageGate> gates = <_CoverageGate>[
|
|
_CoverageGate(
|
|
label: 'server admin logic',
|
|
lcovPath: '$root/server/coverage/lcov.info',
|
|
pathContains: 'market_history_admin_logic.dart',
|
|
minLinePercent: 95,
|
|
),
|
|
_CoverageGate(
|
|
label: 'server admin handler',
|
|
lcovPath: '$root/server/coverage/lcov.info',
|
|
pathContains: 'market_history_admin_handler.dart',
|
|
minLinePercent: 85,
|
|
),
|
|
_CoverageGate(
|
|
label: 'server admin actions',
|
|
lcovPath: '$root/server/coverage/lcov.info',
|
|
pathContains: 'market_history_admin_actions.dart',
|
|
minLinePercent: 85,
|
|
),
|
|
_CoverageGate(
|
|
label: 'flutter admin models',
|
|
lcovPath: '$root/coverage/lcov.info',
|
|
pathContains: 'lib/admin/models/',
|
|
minLinePercent: 90,
|
|
),
|
|
_CoverageGate(
|
|
label: 'flutter admin repositories',
|
|
lcovPath: '$root/coverage/lcov.info',
|
|
pathContains: 'lib/admin/repositories/',
|
|
minLinePercent: 90,
|
|
),
|
|
_CoverageGate(
|
|
label: 'flutter admin widgets',
|
|
lcovPath: '$root/coverage/lcov.info',
|
|
pathContains: 'lib/admin/widgets/',
|
|
minLinePercent: 75,
|
|
),
|
|
_CoverageGate(
|
|
label: 'flutter admin screens',
|
|
lcovPath: '$root/coverage/lcov.info',
|
|
pathContains: 'lib/admin/screens/',
|
|
minLinePercent: 75,
|
|
),
|
|
];
|
|
|
|
final List<String> failures = <String>[];
|
|
for (final _CoverageGate gate in gates) {
|
|
final File file = File(gate.lcovPath);
|
|
if (!file.existsSync()) {
|
|
failures.add('${gate.label}: missing ${gate.lcovPath}');
|
|
continue;
|
|
}
|
|
final double pct = _lineCoveragePercent(
|
|
file.readAsLinesSync(),
|
|
gate.pathContains,
|
|
);
|
|
stdout.writeln(
|
|
'${gate.label}: ${pct.toStringAsFixed(1)}% (min ${gate.minLinePercent}%)',
|
|
);
|
|
if (pct + 0.05 < gate.minLinePercent) {
|
|
failures.add(
|
|
'${gate.label}: ${pct.toStringAsFixed(1)}% < ${gate.minLinePercent}%',
|
|
);
|
|
}
|
|
}
|
|
|
|
if (failures.isNotEmpty) {
|
|
stderr.writeln('Admin portal coverage gates failed:');
|
|
for (final String failure in failures) {
|
|
stderr.writeln(' - $failure');
|
|
}
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
String _repoRoot() {
|
|
final String script = Platform.script.toFilePath();
|
|
return File(script).parent.parent.path;
|
|
}
|
|
|
|
double _lineCoveragePercent(List<String> lines, String pathContains) {
|
|
double total = 0;
|
|
double hit = 0;
|
|
String? currentFile;
|
|
for (final String line in lines) {
|
|
if (line.startsWith('SF:')) {
|
|
currentFile = line.substring(3);
|
|
continue;
|
|
}
|
|
if (line == 'end_of_record') {
|
|
currentFile = null;
|
|
continue;
|
|
}
|
|
if (currentFile == null || !currentFile.contains(pathContains)) {
|
|
continue;
|
|
}
|
|
if (line.startsWith('DA:')) {
|
|
final List<String> parts = line.substring(3).split(',');
|
|
if (parts.length != 2) {
|
|
continue;
|
|
}
|
|
total++;
|
|
if (int.tryParse(parts[1]) != 0) {
|
|
hit++;
|
|
}
|
|
}
|
|
}
|
|
if (total == 0) {
|
|
return 0;
|
|
}
|
|
return hit * 100 / total;
|
|
}
|
|
|
|
class _CoverageGate {
|
|
const _CoverageGate({
|
|
required this.label,
|
|
required this.lcovPath,
|
|
required this.pathContains,
|
|
required this.minLinePercent,
|
|
});
|
|
|
|
final String label;
|
|
final String lcovPath;
|
|
final String pathContains;
|
|
final int minLinePercent;
|
|
}
|