Flutter Live Coverage
English
A Flutter runtime code coverage plugin that supports Release mode. Collects line-level coverage through source code instrumentation at runtime, generates LCOV format files, and supports S3 upload.
Features
- Release Mode Support: Collect coverage in Release builds through compile-time code instrumentation
- Line-Level Coverage: Precisely records execution status of each line of code
- LCOV Format Output: Standard format compatible with genhtml, codecov, and other tools
- Local Storage: Automatically saves coverage files to device local storage
- S3 Upload: Supports configuring AWS S3 for remote data collection
- High Performance: Uses BitSet and inline optimization to minimize runtime overhead
Installation
Add dependency in pubspec.yaml:
dependencies:
flutter_live_coverage:
git:
url: https://github.com/WilliamSkyWalker/flutter_live_coverage.git
Usage
1. Instrument Your Project
Before building, use the CLI tool to instrument your project source code:
# Basic usage - in-place modification
dart run flutter_live_coverage:coverage_instrument -p ./your_flutter_app
# Output to separate directory (recommended, doesn't modify original code)
dart run flutter_live_coverage:coverage_instrument \
-p ./your_flutter_app \
-o ./your_flutter_app_instrumented
# Custom include/exclude patterns
dart run flutter_live_coverage:coverage_instrument \
-p ./your_flutter_app \
-i "lib/**/*.dart" \
-e "**/*.g.dart" \
-e "**/*.freezed.dart"
CLI Options:
-p, --project: Flutter project root directory (default: current directory)-o, --output: Output directory (default: in-place modification)-i, --include: Glob patterns to include (default:lib/**/*.dart)-e, --exclude: Glob patterns to exclude-b, --backup: Backup original files-v, --verbose: Show verbose output
After instrumentation, the following files are generated:
coverage_source_map.json: Source map filelib/coverage_init.dart: Initialization code
2. Initialize in Your App
import 'package:flutter/material.dart';
import 'package:flutter_live_coverage/flutter_live_coverage.dart';
import 'coverage_init.dart'; // CLI-generated initialization file
void main() {
// Initialize coverage collection
initCoverage();
// Optional: Configure S3 upload
CodeCoverageApi.instance.configureS3(S3Config(
bucket: 'my-coverage-bucket',
region: 'us-west-2',
accessKeyId: const String.fromEnvironment('AWS_ACCESS_KEY_ID'),
secretAccessKey: const String.fromEnvironment('AWS_SECRET_ACCESS_KEY'),
prefix: 'app-coverage', // Optional prefix
));
// Optional: Enable auto upload
CodeCoverageApi.instance.enableAutoUpload(
interval: Duration(minutes: 5),
testName: 'production-session',
);
runApp(MyApp());
}
3. Export Coverage Data
// Get current coverage statistics
final stats = CodeCoverageApi.instance.getStats();
print('Coverage: ${stats.coveragePercentage}%');
print('Executed: ${stats.executedLines}/${stats.totalLines} lines');
// Save to local file
final file = await CodeCoverageApi.instance.saveToFile(
testName: 'user-session-123',
);
print('Saved to: ${file.path}');
// Upload to S3
final result = await CodeCoverageApi.instance.uploadToS3(
testName: 'user-session-123',
);
if (result.success) {
print('Uploaded to: ${result.url}');
}
// Or export at once (local + S3)
// By default, local file is deleted after successful S3 upload
final exportResult = await CodeCoverageApi.instance.exportCoverage(
testName: 'user-session-123',
uploadToS3: true,
deleteLocalOnUploadSuccess: true, // Default is true
);
4. Build Your App
# Build with instrumented code
flutter build apk --release \
--dart-define=AWS_ACCESS_KEY_ID=xxx \
--dart-define=AWS_SECRET_ACCESS_KEY=xxx
# Or iOS
flutter build ios --release \
--dart-define=AWS_ACCESS_KEY_ID=xxx \
--dart-define=AWS_SECRET_ACCESS_KEY=xxx
API Reference
CodeCoverageApi
Main API interface:
// Initialize
CodeCoverageApi.instance.initialize(sourceMapJson);
// S3 Configuration
CodeCoverageApi.instance.configureS3(config);
CodeCoverageApi.instance.isS3Configured; // bool
// Auto Upload (default: delete local file after successful upload)
CodeCoverageApi.instance.enableAutoUpload(
interval: Duration(minutes: 5),
deleteLocalOnSuccess: true,
);
CodeCoverageApi.instance.disableAutoUpload();
// Statistics
CodeCoverageApi.instance.getStats(); // CoverageStats
CodeCoverageApi.instance.getFileCoverage(fileId); // FileCoverageData?
CodeCoverageApi.instance.getAllFileCoverage(); // List<FileCoverageData>
// Generate Reports
CodeCoverageApi.instance.generateLcov(testName: 'test'); // String
CodeCoverageApi.instance.generateJson(testName: 'test'); // String
// File Operations
CodeCoverageApi.instance.saveToFile(); // Future<File>
CodeCoverageApi.instance.uploadToS3(); // Future<S3UploadResult>
CodeCoverageApi.instance.exportCoverage(); // Future<CoverageExportResult>
CodeCoverageApi.instance.listLocalFiles(); // Future<List<CoverageFileInfo>>
CodeCoverageApi.instance.cleanupLocalFiles(keepCount: 10);
// Reset
CodeCoverageApi.instance.reset(); // Clear execution data, keep source map
CodeCoverageApi.instance.clear(); // Complete clear
S3Config
S3Config(
bucket: 'bucket-name',
region: 'us-west-2',
accessKeyId: 'AKIA...',
secretAccessKey: 'secret',
sessionToken: null, // Optional, for temporary credentials
prefix: 'coverage', // Optional, object key prefix
endpoint: null, // Optional, custom endpoint for S3-compatible services
usePathStyle: false, // Whether to use path style
)
LCOV Output Format
Generated LCOV file format:
TN:test-name
SF:lib/main.dart
FNF:0
FNH:0
DA:1,1
DA:5,1
DA:10,0
LF:3
LH:2
end_of_record
Process with standard tools:
# Generate HTML report
genhtml coverage.lcov -o coverage_report
# Upload to Codecov
codecov -f coverage.lcov
Notes
-
Performance Impact: Code instrumentation adds about 5-10% to code size, runtime performance impact is minimal (using BitSet and inline optimization)
-
Exclude Generated Code: Recommended to exclude
.g.dart,.freezed.dart, and other generated code files -
Security: Do not hardcode AWS credentials in code, use
--dart-defineor environment variables -
Storage Cleanup: Periodically call
cleanupLocalFiles()to clean up old coverage files
License
BSD-3-Clause License
中文
一个支持 Release 模式的 Flutter 运行时代码覆盖率收集插件。通过源代码插桩在运行时收集行级别覆盖率,生成 LCOV 格式文件,并支持 S3 上传。
功能特性
- Release 模式支持:通过编译时代码插桩,支持在 Release 打包后收集覆盖率
- 行级别覆盖率:精确记录每一行代码的执行情况
- LCOV 格式输出:标准格式,兼容 genhtml、codecov 等工具
- 本地存储:自动保存覆盖率文件到设备本地
- S3 上传:支持配置 AWS S3 进行远程数据收集
- 高性能:使用 BitSet 和内联优化,最小化运行时开销
安装
在 pubspec.yaml 中添加依赖:
dependencies:
flutter_live_coverage:
git:
url: https://github.com/WilliamSkyWalker/flutter_live_coverage.git
使用方法
1. 对项目进行代码插桩
在构建之前,使用 CLI 工具对项目源代码进行插桩:
# 基本用法 - 原地修改
dart run flutter_live_coverage:coverage_instrument -p ./your_flutter_app
# 输出到单独目录(推荐,不修改原始代码)
dart run flutter_live_coverage:coverage_instrument \
-p ./your_flutter_app \
-o ./your_flutter_app_instrumented
# 自定义包含/排除模式
dart run flutter_live_coverage:coverage_instrument \
-p ./your_flutter_app \
-i "lib/**/*.dart" \
-e "**/*.g.dart" \
-e "**/*.freezed.dart"
CLI 选项:
-p, --project: Flutter 项目根目录(默认当前目录)-o, --output: 输出目录(默认原地修改)-i, --include: 要包含的 glob 模式(默认lib/**/*.dart)-e, --exclude: 要排除的 glob 模式-b, --backup: 备份原始文件-v, --verbose: 显示详细输出
插桩后会生成:
coverage_source_map.json: 源码映射文件lib/coverage_init.dart: 初始化代码
2. 在应用中初始化
import 'package:flutter/material.dart';
import 'package:flutter_live_coverage/flutter_live_coverage.dart';
import 'coverage_init.dart'; // CLI 生成的初始化文件
void main() {
// 初始化覆盖率收集
initCoverage();
// 可选:配置 S3 上传
CodeCoverageApi.instance.configureS3(S3Config(
bucket: 'my-coverage-bucket',
region: 'us-west-2',
accessKeyId: const String.fromEnvironment('AWS_ACCESS_KEY_ID'),
secretAccessKey: const String.fromEnvironment('AWS_SECRET_ACCESS_KEY'),
prefix: 'app-coverage', // 可选前缀
));
// 可选:启用自动上传
CodeCoverageApi.instance.enableAutoUpload(
interval: Duration(minutes: 5),
testName: 'production-session',
);
runApp(MyApp());
}
3. 导出覆盖率数据
// 获取当前覆盖率统计
final stats = CodeCoverageApi.instance.getStats();
print('Coverage: ${stats.coveragePercentage}%');
print('Executed: ${stats.executedLines}/${stats.totalLines} lines');
// 保存到本地文件
final file = await CodeCoverageApi.instance.saveToFile(
testName: 'user-session-123',
);
print('Saved to: ${file.path}');
// 上传到 S3
final result = await CodeCoverageApi.instance.uploadToS3(
testName: 'user-session-123',
);
if (result.success) {
print('Uploaded to: ${result.url}');
}
// 或者一次性导出(本地 + S3)
// 默认 S3 上传成功后会自动删除本地文件以节省空间
final exportResult = await CodeCoverageApi.instance.exportCoverage(
testName: 'user-session-123',
uploadToS3: true,
deleteLocalOnUploadSuccess: true, // 默认为 true
);
4. 构建应用
# 使用插桩后的代码构建
flutter build apk --release \
--dart-define=AWS_ACCESS_KEY_ID=xxx \
--dart-define=AWS_SECRET_ACCESS_KEY=xxx
# 或者 iOS
flutter build ios --release \
--dart-define=AWS_ACCESS_KEY_ID=xxx \
--dart-define=AWS_SECRET_ACCESS_KEY=xxx
API 参考
CodeCoverageApi
主要的 API 接口:
// 初始化
CodeCoverageApi.instance.initialize(sourceMapJson);
// S3 配置
CodeCoverageApi.instance.configureS3(config);
CodeCoverageApi.instance.isS3Configured; // bool
// 自动上传(默认上传成功后删除本地文件)
CodeCoverageApi.instance.enableAutoUpload(
interval: Duration(minutes: 5),
deleteLocalOnSuccess: true,
);
CodeCoverageApi.instance.disableAutoUpload();
// 统计信息
CodeCoverageApi.instance.getStats(); // CoverageStats
CodeCoverageApi.instance.getFileCoverage(fileId); // FileCoverageData?
CodeCoverageApi.instance.getAllFileCoverage(); // List<FileCoverageData>
// 生成报告
CodeCoverageApi.instance.generateLcov(testName: 'test'); // String
CodeCoverageApi.instance.generateJson(testName: 'test'); // String
// 文件操作
CodeCoverageApi.instance.saveToFile(); // Future<File>
CodeCoverageApi.instance.uploadToS3(); // Future<S3UploadResult>
CodeCoverageApi.instance.exportCoverage(); // Future<CoverageExportResult>
CodeCoverageApi.instance.listLocalFiles(); // Future<List<CoverageFileInfo>>
CodeCoverageApi.instance.cleanupLocalFiles(keepCount: 10);
// 重置
CodeCoverageApi.instance.reset(); // 清除执行数据,保留源码映射
CodeCoverageApi.instance.clear(); // 完全清除
S3Config
S3Config(
bucket: 'bucket-name',
region: 'us-west-2',
accessKeyId: 'AKIA...',
secretAccessKey: 'secret',
sessionToken: null, // 可选,用于临时凭证
prefix: 'coverage', // 可选,对象键前缀
endpoint: null, // 可选,自定义 endpoint(用于 S3 兼容服务)
usePathStyle: false, // 是否使用 path style
)
LCOV 输出格式
生成的 LCOV 文件格式如下:
TN:test-name
SF:lib/main.dart
FNF:0
FNH:0
DA:1,1
DA:5,1
DA:10,0
LF:3
LH:2
end_of_record
可以使用标准工具处理:
# 生成 HTML 报告
genhtml coverage.lcov -o coverage_report
# 上传到 Codecov
codecov -f coverage.lcov
注意事项
-
性能影响:代码插桩会增加约 5-10% 的代码体积,运行时性能影响很小(使用 BitSet 和内联优化)
-
排除生成代码:建议排除
.g.dart、.freezed.dart等生成的代码文件 -
安全性:不要在代码中硬编码 AWS 凭证,使用
--dart-define或环境变量 -
存储清理:定期调用
cleanupLocalFiles()清理旧的覆盖率文件
许可证
BSD-3-Clause License
Libraries
- flutter_live_coverage
- Flutter Runtime Code Coverage Plugin
- flutter_live_coverage_method_channel
- flutter_live_coverage_platform_interface