flutter_live_coverage 1.1.0 copy "flutter_live_coverage: ^1.1.0" to clipboard
flutter_live_coverage: ^1.1.0 copied to clipboard

Flutter runtime code coverage plugin supporting Release mode. Collects line-level coverage data with LCOV output and S3 upload support.

Flutter Live Coverage #

English | 中文


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 file
  • lib/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 #

  1. Performance Impact: Code instrumentation adds about 5-10% to code size, runtime performance impact is minimal (using BitSet and inline optimization)

  2. Exclude Generated Code: Recommended to exclude .g.dart, .freezed.dart, and other generated code files

  3. Security: Do not hardcode AWS credentials in code, use --dart-define or environment variables

  4. 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

注意事项 #

  1. 性能影响:代码插桩会增加约 5-10% 的代码体积,运行时性能影响很小(使用 BitSet 和内联优化)

  2. 排除生成代码:建议排除 .g.dart.freezed.dart 等生成的代码文件

  3. 安全性:不要在代码中硬编码 AWS 凭证,使用 --dart-define 或环境变量

  4. 存储清理:定期调用 cleanupLocalFiles() 清理旧的覆盖率文件

许可证 #

BSD-3-Clause License

0
likes
150
points
0
downloads

Publisher

unverified uploader

Weekly Downloads

Flutter runtime code coverage plugin supporting Release mode. Collects line-level coverage data with LCOV output and S3 upload support.

Repository (GitHub)
View/report issues

Documentation

API reference

License

BSD-3-Clause (license)

Dependencies

analyzer, args, aws_common, aws_signature_v4, flutter, flutter_web_plugins, glob, path, path_provider, plugin_platform_interface, web

More

Packages that depend on flutter_live_coverage

Packages that implement flutter_live_coverage