blob: 3f7fcc281684dfb21e567df70c5ad4e10bed1628 [file] [log] [blame]
// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'dart:async';
import 'dart:io';
import 'package:testing/testing.dart'
show
Chain,
ChainContext,
Result,
Step,
TestDescription,
ExpectationSet,
Expectation;
import '../tool/coverage_merger.dart' as coverageMerger;
import 'coverage_helper.dart';
import 'testing/environment_keys.dart';
import 'testing_utils.dart' show checkEnvironment;
import 'utils/kernel_chain.dart';
import 'utils/suite_utils.dart';
import 'vm_service_coverage.dart';
void main([List<String> arguments = const []]) => internalMain(
createContext,
arguments: arguments,
displayName: "coverage merger suite",
configurationPath: "../testing.json",
);
const List<Map<String, String>> EXPECTATIONS = [
{"name": "ExpectationFileMismatch", "group": "Fail"},
{"name": "ExpectationFileMissing", "group": "Fail"},
];
Future<Context> createContext(Chain suite, Map<String, String> environment) {
const Set<String> knownEnvironmentKeys = {EnvironmentKeys.updateExpectations};
checkEnvironment(environment, knownEnvironmentKeys);
return new Future.value(new Context(suite.name, environment));
}
class CheckCoverageData extends Step<TestDescription, void, Context> {
const CheckCoverageData();
@override
String get name => "CheckCoverageData";
@override
Future<Result<void>> run(TestDescription description, Context context) async {
Directory tmpDir = Directory.systemTemp.createTempSync(
"coverage_merger_test",
);
try {
Directory dartToolDir = new Directory.fromUri(
tmpDir.uri.resolve(".dart_tool/"),
);
dartToolDir.createSync(recursive: true);
File packageConfig = new File.fromUri(
tmpDir.uri.resolve(".dart_tool/package_config.json"),
);
// We claim it being called 'front_end' as that's (currently at least) the
// only package we process (almost) all files for.
packageConfig.writeAsStringSync("""{
"configVersion": 2,
"packages": [
{
"name": "front_end",
"rootUri": "../",
"packageUri": "lib/",
"languageVersion": "3.8"
}
]
}""");
Directory libDir = new Directory.fromUri(tmpDir.uri.resolve("lib/"));
libDir.createSync(recursive: true);
File main = new File.fromUri(tmpDir.uri.resolve("lib/main.dart"));
File sourceFile = new File.fromUri(description.uri);
main.writeAsStringSync(sourceFile.readAsStringSync().trim());
CollectingCoverageHelper helper = new CollectingCoverageHelper();
await helper.start(
[
"--disable-dart-dev",
"--enable-asserts",
"--pause_isolates_on_exit",
main.path,
],
stdoutReceiver: (String line) {
print(" > $line");
},
stderrReceiver: (String line) {
print("e> $line");
},
);
Coverage coverage = await helper.completer.future;
Directory coverageDir = new Directory.fromUri(
tmpDir.uri.resolve("coverage/"),
);
coverageDir.createSync(recursive: true);
File coverageFile = new File.fromUri(
tmpDir.uri.resolve("coverage/coverage.json"),
);
coverage.writeToFile(coverageFile);
Map<Uri, coverageMerger.CoverageInfo> coverageData = (await coverageMerger
.mergeFromDirUri(
packageConfig.uri,
coverageDir.uri,
silent: true,
extraCoverageIgnores: ["coverage-ignore(suite):"],
extraCoverageBlockIgnores: ["coverage-ignore-block(suite):"],
addAndRemoveCommentsInFiles: false,
stdoutReceiver: (String line) {
print(" > $line");
},
stderrReceiver: (String line) {
print("e> $line");
},
))!;
if (coverageData.values.first.error) {
print("Warning: Got an error.");
}
Result<TestDescription> expectMatch = await context
.match<TestDescription>(
".visualization.expect",
coverageData.values.first.visualization.trim(),
description.uri,
description,
);
if (expectMatch.outcome != Expectation.pass) return expectMatch;
coverageData = (await coverageMerger.mergeFromDirUri(
packageConfig.uri,
coverageDir.uri,
silent: true,
extraCoverageIgnores: ["coverage-ignore(suite):"],
extraCoverageBlockIgnores: ["coverage-ignore-block(suite):"],
addAndRemoveCommentsInFiles: true,
stdoutReceiver: (String line) {
print(" > $line");
},
stderrReceiver: (String line) {
print("e> $line");
},
))!;
print("Reading ${main.path}");
String outputWithComments = main.readAsStringSync().trim();
return await context.match<TestDescription>(
".commented.expect",
outputWithComments,
description.uri,
description,
);
} finally {
try {
tmpDir.deleteSync(recursive: true);
} catch (e) {
// Wait a little and retry.
sleep(const Duration(milliseconds: 42));
try {
tmpDir.deleteSync(recursive: true);
} catch (e) {
print('Warning: $e');
}
}
}
}
}
class CollectingCoverageHelper extends CoverageHelper {
Completer<Coverage> completer = new Completer();
CollectingCoverageHelper() : super(doPrint: false, forceCompilation: true);
@override
void gotCoverage(Coverage coverage) {
completer.complete(coverage);
}
}
class Context extends ChainContext with MatchContext {
final String suiteName;
@override
final List<Step> steps = const <Step>[const CheckCoverageData()];
@override
final bool updateExpectations;
@override
final ExpectationSet expectationSet = new ExpectationSet.fromJsonList(
EXPECTATIONS,
);
Context(this.suiteName, Map<String, String> environment)
: updateExpectations =
environment[EnvironmentKeys.updateExpectations] == "true";
@override
bool get canBeFixWithUpdateExpectations => true;
@override
String get updateExpectationsOption =>
'${EnvironmentKeys.updateExpectations}=true';
}