e40cad30a3
Part of removing uses of 'fasta'. TEST=existing Change-Id: Ibf144a126d8ff4ba15b6cfd2c4fb898e9a499362 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/371685 Reviewed-by: Alexander Markov <alexmarkov@google.com> Reviewed-by: Jens Johansen <jensj@google.com> Commit-Queue: Johnni Winther <johnniwinther@google.com> Reviewed-by: Srujan Gaddam <srujzs@google.com>
2872 lines
103 KiB
Dart
2872 lines
103 KiB
Dart
// Copyright (c) 2018, 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:convert' show jsonDecode;
|
|
import 'dart:developer' show debugger;
|
|
import 'dart:io' show Directory, File;
|
|
|
|
import 'package:_fe_analyzer_shared/src/messages/diagnostic_message.dart'
|
|
show DiagnosticMessage, getMessageCodeObject;
|
|
import 'package:_fe_analyzer_shared/src/messages/severity.dart' show Severity;
|
|
import 'package:_fe_analyzer_shared/src/util/colors.dart' as colors;
|
|
import 'package:compiler/src/kernel/dart2js_target.dart' show Dart2jsTarget;
|
|
import "package:dev_compiler/src/kernel/target.dart" show DevCompilerTarget;
|
|
import 'package:expect/expect.dart' show Expect;
|
|
import 'package:front_end/src/api_prototype/compiler_options.dart'
|
|
show CompilerOptions, parseExperimentalArguments, parseExperimentalFlags;
|
|
import 'package:front_end/src/api_prototype/experimental_flags.dart'
|
|
show ExperimentalFlag;
|
|
import 'package:front_end/src/api_prototype/incremental_kernel_generator.dart'
|
|
show IncrementalCompilerResult;
|
|
import "package:front_end/src/api_prototype/memory_file_system.dart"
|
|
show MemoryFileSystem, MemoryFileSystemEntity;
|
|
import 'package:front_end/src/base/nnbd_mode.dart' show NnbdMode;
|
|
import 'package:front_end/src/base/processed_options.dart'
|
|
show ProcessedOptions;
|
|
import 'package:front_end/src/compute_platform_binaries_location.dart'
|
|
show computePlatformBinariesLocation, computePlatformDillName;
|
|
import 'package:front_end/src/codes/cfe_codes.dart'
|
|
show DiagnosticMessageFromJson, FormattedMessage;
|
|
import 'package:front_end/src/fasta/compiler_context.dart' show CompilerContext;
|
|
import 'package:front_end/src/fasta/incremental_compiler.dart'
|
|
show AdvancedInvalidationResult, IncrementalCompiler, RecorderForTesting;
|
|
import 'package:front_end/src/fasta/incremental_serializer.dart'
|
|
show IncrementalSerializer;
|
|
import 'package:front_end/src/fasta/kernel/utils.dart' show ByteSink;
|
|
import 'package:kernel/ast.dart';
|
|
import 'package:kernel/binary/ast_from_binary.dart' show BinaryBuilder;
|
|
import 'package:kernel/binary/ast_to_binary.dart' show BinaryPrinter;
|
|
import 'package:kernel/class_hierarchy.dart'
|
|
show ClassHierarchy, ClosedWorldClassHierarchy, ForTestingClassInfo;
|
|
import 'package:kernel/src/equivalence.dart'
|
|
show
|
|
EquivalenceResult,
|
|
EquivalenceStrategy,
|
|
EquivalenceVisitor,
|
|
checkEquivalence;
|
|
import 'package:kernel/target/targets.dart'
|
|
show
|
|
LateLowering,
|
|
NoneTarget,
|
|
Target,
|
|
TargetFlags,
|
|
TestTargetFlags,
|
|
TestTargetWrapper;
|
|
import 'package:kernel/text/ast_to_text.dart'
|
|
show NameSystem, Printer, componentToString;
|
|
import "package:testing/testing.dart"
|
|
show
|
|
Chain,
|
|
ChainContext,
|
|
Expectation,
|
|
ExpectationSet,
|
|
Result,
|
|
Step,
|
|
TestDescription;
|
|
import "package:vm/modular/target/vm.dart" show VmTarget;
|
|
import "package:yaml/yaml.dart" show YamlMap, loadYamlNode;
|
|
|
|
import 'binary_md_dill_reader.dart' show DillComparer;
|
|
import 'fasta/suite_utils.dart';
|
|
import 'fasta/testing/environment_keys.dart';
|
|
import "incremental_utils.dart" as util;
|
|
import 'test_utils.dart';
|
|
import 'testing_utils.dart' show checkEnvironment;
|
|
import 'utils/io_utils.dart' show computeRepoDir;
|
|
import 'utils/symbolic_language_versions.dart';
|
|
import 'utils/values.dart';
|
|
|
|
void main([List<String> arguments = const []]) => internalMain(createContext,
|
|
arguments: arguments,
|
|
displayName: "incremental suite",
|
|
configurationPath: "../testing.json");
|
|
|
|
/// Top level yaml properties for an incremental test.
|
|
class TestProperties {
|
|
/// Value of [type] for selecting a [basicTest].
|
|
static const String type_basic = 'basic';
|
|
|
|
/// Value of [type] for selecting a [NewWorldTest].
|
|
static const String type_newworld = 'newworld';
|
|
|
|
/// This selects the type of test.
|
|
///
|
|
/// [type_basic] runs a [basicTest] with additional properties defined in
|
|
/// [BasicTestProperties].
|
|
///
|
|
/// [type_newworld] runs a [NewWorldTest] with additional properties defined
|
|
/// in [NewWorldTestProperties].
|
|
static const Property<String> type = Property.required(
|
|
'type', StringValue(options: {type_basic, type_newworld}));
|
|
}
|
|
|
|
/// Additional yaml properties for a [basicTest].
|
|
// TODO(johnniwinther,jensj): Document all properties.
|
|
class BasicTestProperties {
|
|
static const Property<Map<String, String>> sources =
|
|
Property.required('sources', MapValue(StringValue()));
|
|
|
|
static const Property<String> entry =
|
|
Property.required('entry', StringValue());
|
|
|
|
static const Property<List<String>?> invalidate =
|
|
Property.optional('invalidate', ListValue(StringValue()));
|
|
}
|
|
|
|
/// Additional yaml properties for a [NewWorldTest].
|
|
// TODO(johnniwinther,jensj): Document all properties.
|
|
class NewWorldTestProperties {
|
|
static const Property<List<World>> worlds =
|
|
Property.required('worlds', ListValue(CustomValue(World.create)));
|
|
|
|
static const Property<Map<String, Map<String, String>>?> modules =
|
|
Property.optional('modules', MapValue(MapValue(StringValue())));
|
|
|
|
static const Property<bool> omitPlatform =
|
|
Property.optional('omitPlatform', BoolValue(), defaultValue: true);
|
|
|
|
static const Property<bool> forceLateLoweringForTesting = Property.optional(
|
|
'forceLateLoweringForTesting', BoolValue(),
|
|
defaultValue: false);
|
|
|
|
static const Property<bool> trackWidgetCreation = Property.optional(
|
|
'trackWidgetCreation', BoolValue(),
|
|
defaultValue: false);
|
|
|
|
static const Property<bool> incrementalSerialization = Property.optional(
|
|
'incrementalSerialization', BoolValue(),
|
|
defaultValue: false);
|
|
|
|
static const String nnbdMode_weak = 'weak';
|
|
|
|
static const Property<String?> nnbdMode =
|
|
Property.optional('nnbdMode', StringValue(options: {nnbdMode_weak}));
|
|
|
|
static const String target_none = 'none';
|
|
static const String target_dartdevc = 'dartdevc';
|
|
static const String target_dart2js = 'dart2js';
|
|
static const String target_vm = 'vm';
|
|
|
|
static const Property<String?> target = Property.optional(
|
|
'target',
|
|
StringValue(
|
|
options: {target_none, target_dartdevc, target_dart2js, target_vm}));
|
|
}
|
|
|
|
/// Yaml properties for a [World] within a [NewWorldTest].
|
|
// TODO(johnniwinther,jensj): Document all properties.
|
|
class WorldProperties {
|
|
static const Property<List<String>?> modules =
|
|
Property.optional('modules', ListValue(StringValue()));
|
|
|
|
static const String worldType_updated = 'updated';
|
|
|
|
static const Property<String?> worldType =
|
|
Property.optional('worldType', StringValue(options: {worldType_updated}));
|
|
|
|
static const Property<bool> noFullComponent =
|
|
Property.optional('noFullComponent', BoolValue(), defaultValue: false);
|
|
|
|
static const Property<bool?> expectInitializeFromDill =
|
|
Property.optional('expectInitializeFromDill', BoolValue());
|
|
|
|
static const Property<Map<String, String>> sources = Property.optional(
|
|
'sources', MapValue(StringValue()),
|
|
defaultValue: const {});
|
|
|
|
static const Property<bool> badSdk =
|
|
Property.optional('badSdk', BoolValue(), defaultValue: false);
|
|
|
|
static const Property<bool> enableStringReplacement = Property.optional(
|
|
'enableStringReplacement', BoolValue(),
|
|
defaultValue: false);
|
|
|
|
static const Property<String?> packageConfigFile =
|
|
Property.optional('packageConfigFile', StringValue());
|
|
|
|
static const Property<String?> experiments =
|
|
Property.optional("experiments", StringValue());
|
|
|
|
static const String nnbdMode_weak = 'weak';
|
|
|
|
static const Property<String?> nnbdMode =
|
|
Property.optional("nnbdMode", StringValue(options: {nnbdMode_weak}));
|
|
|
|
static const Property<List<String>> entry = Property.required(
|
|
'entry', ListValue(StringValue(), supportSingleton: true));
|
|
|
|
static const Property<bool> outlineOnly =
|
|
Property.optional("outlineOnly", BoolValue(), defaultValue: false);
|
|
|
|
static const Property<bool> skipOutlineBodyCheck = Property.optional(
|
|
"skipOutlineBodyCheck", BoolValue(),
|
|
defaultValue: false);
|
|
|
|
static const Property<bool> fromComponent =
|
|
Property.optional("fromComponent", BoolValue(), defaultValue: false);
|
|
|
|
static const Property<List<String>?> invalidate =
|
|
Property.optional("invalidate", ListValue(StringValue()));
|
|
|
|
static const Property<bool> simulateTransformer = Property.optional(
|
|
"simulateTransformer", BoolValue(),
|
|
defaultValue: false);
|
|
|
|
static const Property<bool?> expectInitializationError =
|
|
Property.optional("expectInitializationError", BoolValue());
|
|
|
|
static const Property<bool> compareToPrevious =
|
|
Property.optional("compareToPrevious", BoolValue(), defaultValue: false);
|
|
|
|
static const Property<List<String>?> uriToSourcesDoesntInclude =
|
|
Property.optional("uriToSourcesDoesntInclude", ListValue(StringValue()));
|
|
|
|
static const Property<List<String>?> uriToSourcesOnlyIncludes =
|
|
Property.optional("uriToSourcesOnlyIncludes", ListValue(StringValue()));
|
|
|
|
static const Property<bool> skipClassHierarchyTest = Property.optional(
|
|
"skipClassHierarchyTest", BoolValue(),
|
|
defaultValue: false);
|
|
|
|
static const Property<bool> expectsPlatform =
|
|
Property.optional("expectsPlatform", BoolValue(), defaultValue: false);
|
|
|
|
static const Property<int?> expectedLibraryCount =
|
|
const Property.optional("expectedLibraryCount", IntValue());
|
|
|
|
static const Property<int?> expectedSyntheticLibraryCount =
|
|
Property.optional("expectedSyntheticLibraryCount", IntValue());
|
|
|
|
/// The expected result of the advanced invalidation.
|
|
///
|
|
/// If omitted, this defaults to `noDirectlyInvalidated` which corresponds
|
|
/// to the advanced invalidation result of the initial compilation. This means
|
|
/// that all subsequent world entries must specify the expected advanced
|
|
/// invalidation result.
|
|
static const Property<AdvancedInvalidationResult> advancedInvalidation =
|
|
Property.optional(
|
|
"advancedInvalidation", EnumValue(AdvancedInvalidationResult.values),
|
|
defaultValue: AdvancedInvalidationResult.noDirectlyInvalidated);
|
|
|
|
static const Property<bool> checkEntries =
|
|
Property.optional("checkEntries", BoolValue(), defaultValue: false);
|
|
|
|
static const Property<bool> checkInvalidatedFiles = Property.optional(
|
|
"checkInvalidatedFiles", BoolValue(),
|
|
defaultValue: true);
|
|
|
|
static const Property<List<String>?> expectedInvalidatedUri =
|
|
Property.optional('expectedInvalidatedUri', ListValue(StringValue()));
|
|
|
|
static const Property<int?> expectSameErrorsAsWorld =
|
|
Property.optional('expectSameErrorsAsWorld', IntValue());
|
|
|
|
static const Property<List<ExpressionCompilation>?> expressionCompilation =
|
|
Property.optional(
|
|
'expressionCompilation',
|
|
ListValue(CustomValue(ExpressionCompilation.create),
|
|
supportSingleton: true));
|
|
|
|
static const Property<bool> compareWithFromScratch = Property.optional(
|
|
'compareWithFromScratch', BoolValue(),
|
|
defaultValue: false);
|
|
|
|
static const Property<bool> brandNewIncrementalSerializationAllowDifferent =
|
|
Property.optional(
|
|
'brandNewIncrementalSerializationAllowDifferent', BoolValue(),
|
|
defaultValue: false);
|
|
|
|
/// Whether the compilation of the world is expected to result in warnings.
|
|
static const Property<bool> warnings =
|
|
Property.optional('warnings', BoolValue(), defaultValue: false);
|
|
|
|
/// Whether the compilation of the world is expected to result in compile-time
|
|
/// errors.
|
|
static const Property<bool> errors =
|
|
Property.optional('errors', BoolValue(), defaultValue: false);
|
|
|
|
static const Property<List<String>?> neededDillLibraries =
|
|
Property.optional('neededDillLibraries', ListValue(StringValue()));
|
|
|
|
static const Property<Map<String, List<String>>?> expectedContent =
|
|
Property.optional('expectedContent', MapValue(ListValue(StringValue())));
|
|
|
|
static const Property<bool> incrementalSerializationDoesWork =
|
|
Property.optional('incrementalSerializationDoesWork', BoolValue(),
|
|
defaultValue: false);
|
|
|
|
static const Property<List<String>?> serializationShouldNotInclude =
|
|
Property.optional(
|
|
'serializationShouldNotInclude', ListValue(StringValue()));
|
|
|
|
static const Property<bool> checkConstantCoverageReferences =
|
|
Property.optional('checkConstantCoverageReferences', BoolValue(),
|
|
defaultValue: true);
|
|
|
|
/// If `true`, the compilation is allowed to report an error twice.
|
|
static const Property<bool> allowDuplicateErrors = Property.optional(
|
|
'allowDuplicateErrors', BoolValue(),
|
|
defaultValue: false);
|
|
|
|
/// If `true`, the compilation is allowed to report a warning twice.
|
|
static const Property<bool> allowDuplicateWarnings = Property.optional(
|
|
'allowDuplicateWarnings', BoolValue(),
|
|
defaultValue: true);
|
|
|
|
/// If `true`, the expect files will contain a print of all errors.
|
|
static const Property<bool> printErrorsInExpect = Property.optional(
|
|
'printErrorsInExpect', BoolValue(),
|
|
defaultValue: false);
|
|
|
|
/// If `true`, the just compiled world will be "rejected", i.e.
|
|
/// not saved as the latest component and the previous non-rejected compile
|
|
/// (i.e. the one saved in the latest component) will be relinked, mimicking
|
|
/// a reject via the frontend server. At least for now all checking after this
|
|
/// point will be skipped.
|
|
static const Property<bool> reject =
|
|
Property.optional('reject', BoolValue(), defaultValue: false);
|
|
}
|
|
|
|
/// Yaml properties for an [ExpressionCompilation] with a [World].
|
|
// TODO(johnniwinther,jensj): Document all properties.
|
|
class ExpressionCompilationProperties {
|
|
static const Property<bool> errors =
|
|
Property.optional('errors', BoolValue(), defaultValue: false);
|
|
|
|
static const Property<bool> warnings =
|
|
Property.optional('warnings', BoolValue(), defaultValue: false);
|
|
|
|
static const Property<String> uri = Property.required('uri', StringValue());
|
|
|
|
static const Property<String> expression =
|
|
Property.required('expression', StringValue());
|
|
|
|
static const Property<String?> className =
|
|
Property.optional('className', StringValue());
|
|
}
|
|
|
|
final ExpectationSet staticExpectationSet =
|
|
new ExpectationSet.fromJsonList(jsonDecode(EXPECTATIONS));
|
|
|
|
const String EXPECTATIONS = '''
|
|
[
|
|
{
|
|
"name": "ExpectationFileMismatch",
|
|
"group": "Fail"
|
|
},
|
|
{
|
|
"name": "ExpectationFileMissing",
|
|
"group": "Fail"
|
|
},
|
|
{
|
|
"name": "MissingErrors",
|
|
"group": "Fail"
|
|
},
|
|
{
|
|
"name": "UnexpectedErrors",
|
|
"group": "Fail"
|
|
},
|
|
{
|
|
"name": "MissingWarnings",
|
|
"group": "Fail"
|
|
},
|
|
{
|
|
"name": "UnexpectedWarnings",
|
|
"group": "Fail"
|
|
},
|
|
{
|
|
"name": "ClassHierarchyError",
|
|
"group": "Fail"
|
|
},
|
|
{
|
|
"name": "NeededDillMismatch",
|
|
"group": "Fail"
|
|
},
|
|
{
|
|
"name": "IncrementalSerializationError",
|
|
"group": "Fail"
|
|
},
|
|
{
|
|
"name": "ContentDataMismatch",
|
|
"group": "Fail"
|
|
},
|
|
{
|
|
"name": "MissingInitializationError",
|
|
"group": "Fail"
|
|
},
|
|
{
|
|
"name": "UnexpectedInitializationError",
|
|
"group": "Fail"
|
|
},
|
|
{
|
|
"name": "ReachableLibrariesError",
|
|
"group": "Fail"
|
|
},
|
|
{
|
|
"name": "EquivalenceError",
|
|
"group": "Fail"
|
|
},
|
|
{
|
|
"name": "UriToSourceError",
|
|
"group": "Fail"
|
|
},
|
|
{
|
|
"name": "MissingPlatformLibraries",
|
|
"group": "Fail"
|
|
},
|
|
{
|
|
"name": "UnexpectedPlatformLibraries",
|
|
"group": "Fail"
|
|
},
|
|
{
|
|
"name": "UnexpectedAdvancedInvalidation",
|
|
"group": "Fail"
|
|
},
|
|
{
|
|
"name": "UnexpectedEntryToLibraryCount",
|
|
"group": "Fail"
|
|
},
|
|
{
|
|
"name": "LibraryCountMismatch",
|
|
"group": "Fail"
|
|
},
|
|
{
|
|
"name": "InitializedFromDillMismatch",
|
|
"group": "Fail"
|
|
},
|
|
{
|
|
"name": "NNBDModeMismatch",
|
|
"group": "Fail"
|
|
},
|
|
{
|
|
"name": "ConstantCoverageReferenceWithoutNode",
|
|
"group": "Fail"
|
|
}
|
|
]
|
|
''';
|
|
|
|
final Expectation ExpectationFileMismatch =
|
|
staticExpectationSet["ExpectationFileMismatch"];
|
|
final Expectation ExpectationFileMissing =
|
|
staticExpectationSet["ExpectationFileMissing"];
|
|
final Expectation MissingErrors = staticExpectationSet["MissingErrors"];
|
|
final Expectation UnexpectedErrors = staticExpectationSet["UnexpectedErrors"];
|
|
final Expectation MissingWarnings = staticExpectationSet["MissingWarnings"];
|
|
final Expectation UnexpectedWarnings =
|
|
staticExpectationSet["UnexpectedWarnings"];
|
|
final Expectation ClassHierarchyError =
|
|
staticExpectationSet["ClassHierarchyError"];
|
|
final Expectation NeededDillMismatch =
|
|
staticExpectationSet["NeededDillMismatch"];
|
|
final Expectation IncrementalSerializationError =
|
|
staticExpectationSet["IncrementalSerializationError"];
|
|
final Expectation ContentDataMismatch =
|
|
staticExpectationSet["ContentDataMismatch"];
|
|
final Expectation MissingInitializationError =
|
|
staticExpectationSet["MissingInitializationError"];
|
|
final Expectation UnexpectedInitializationError =
|
|
staticExpectationSet["UnexpectedInitializationError"];
|
|
final Expectation ReachableLibrariesError =
|
|
staticExpectationSet["ReachableLibrariesError"];
|
|
final Expectation EquivalenceError = staticExpectationSet["EquivalenceError"];
|
|
final Expectation UriToSourceError = staticExpectationSet["UriToSourceError"];
|
|
final Expectation MissingPlatformLibraries =
|
|
staticExpectationSet["MissingPlatformLibraries"];
|
|
final Expectation UnexpectedPlatformLibraries =
|
|
staticExpectationSet["UnexpectedPlatformLibraries"];
|
|
final Expectation UnexpectedAdvancedInvalidation =
|
|
staticExpectationSet["UnexpectedAdvancedInvalidation"];
|
|
final Expectation UnexpectedEntryToLibraryCount =
|
|
staticExpectationSet["UnexpectedEntryToLibraryCount"];
|
|
final Expectation LibraryCountMismatch =
|
|
staticExpectationSet["LibraryCountMismatch"];
|
|
final Expectation InitializedFromDillMismatch =
|
|
staticExpectationSet["InitializedFromDillMismatch"];
|
|
final Expectation NNBDModeMismatch = staticExpectationSet["NNBDModeMismatch"];
|
|
final Expectation ConstantCoverageReferenceWithoutNode =
|
|
staticExpectationSet["ConstantCoverageReferenceWithoutNode"];
|
|
|
|
Future<Context> createContext(Chain suite, Map<String, String> environment) {
|
|
const Set<String> knownEnvironmentKeys = {
|
|
EnvironmentKeys.updateExpectations,
|
|
EnvironmentKeys.addDebugBreaks,
|
|
EnvironmentKeys.skipTests,
|
|
};
|
|
checkEnvironment(environment, knownEnvironmentKeys);
|
|
|
|
// Disable colors to ensure that expectation files are the same across
|
|
// platforms and independent of stdin/stderr.
|
|
colors.enableColors = false;
|
|
Set<String> skipTests =
|
|
environment[EnvironmentKeys.skipTests]?.split(",").toSet() ?? {};
|
|
return new Future.value(new Context(
|
|
environment[EnvironmentKeys.updateExpectations] == "true",
|
|
environment[EnvironmentKeys.addDebugBreaks] == "true",
|
|
skipTests,
|
|
));
|
|
}
|
|
|
|
class Context extends ChainContext {
|
|
@override
|
|
final List<Step> steps = const <Step>[
|
|
const ReadTest(),
|
|
const RunCompilations(),
|
|
];
|
|
|
|
final bool updateExpectations;
|
|
|
|
/// Add a debug break (via dart:developers `debugger()` call) after each
|
|
/// iteration (or 'world run') when doing a "new world test".
|
|
final bool breakBetween;
|
|
|
|
final Set<String> skipTests;
|
|
|
|
Context(this.updateExpectations, this.breakBetween, this.skipTests);
|
|
|
|
@override
|
|
Future<List<TestDescription>> list(Chain suite) async {
|
|
if (skipTests.isEmpty) return await super.list(suite);
|
|
return filterSkipped(await super.list(suite));
|
|
}
|
|
|
|
List<TestDescription> filterSkipped(List<TestDescription> all) {
|
|
List<TestDescription> result = [];
|
|
for (TestDescription testDescription in all) {
|
|
if (!skipTests.contains(testDescription.shortName)) {
|
|
result.add(testDescription);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
@override
|
|
Future<void> cleanUp(TestDescription description, Result result) async {
|
|
await cleanupHelper?.outDir?.delete(recursive: true);
|
|
cleanupHelper?.outDir = null;
|
|
}
|
|
|
|
@override
|
|
final ExpectationSet expectationSet = staticExpectationSet;
|
|
|
|
TestData? cleanupHelper;
|
|
}
|
|
|
|
class TestData {
|
|
YamlMap map;
|
|
Directory? outDir;
|
|
Uri loadedFrom;
|
|
|
|
TestData(this.map, this.outDir, this.loadedFrom);
|
|
}
|
|
|
|
class ReadTest extends Step<TestDescription, TestData, Context> {
|
|
const ReadTest();
|
|
|
|
@override
|
|
String get name => "read test";
|
|
|
|
@override
|
|
Future<Result<TestData>> run(
|
|
TestDescription description, Context context) async {
|
|
Uri uri = description.uri;
|
|
String contents = await new File.fromUri(uri).readAsString();
|
|
TestData data = new TestData(
|
|
loadYamlNode(contents, sourceUrl: uri) as YamlMap,
|
|
Directory.systemTemp.createTempSync("incremental_load_from_dill_test"),
|
|
uri);
|
|
context.cleanupHelper = data;
|
|
return pass(data);
|
|
}
|
|
}
|
|
|
|
class RunCompilations extends Step<TestData, TestData, Context> {
|
|
const RunCompilations();
|
|
|
|
@override
|
|
String get name => "run compilations";
|
|
|
|
@override
|
|
Future<Result<TestData>> run(TestData data, Context context) async {
|
|
Result<TestData>? result;
|
|
YamlMap map = data.map;
|
|
Set<String> keys = new Set<String>.from(map.keys.cast<String>());
|
|
String type = TestProperties.type.read(map, keys);
|
|
switch (type) {
|
|
case TestProperties.type_basic:
|
|
await basicTest(
|
|
BasicTestProperties.sources.read(map, keys),
|
|
BasicTestProperties.entry.read(map, keys),
|
|
BasicTestProperties.invalidate.read(map, keys),
|
|
data.outDir!,
|
|
);
|
|
break;
|
|
case TestProperties.type_newworld:
|
|
result = await new NewWorldTest(
|
|
data: data,
|
|
context: context,
|
|
worlds: NewWorldTestProperties.worlds.read(map, keys),
|
|
omitPlatform: NewWorldTestProperties.omitPlatform.read(map, keys),
|
|
forceLateLoweringForTesting: NewWorldTestProperties
|
|
.forceLateLoweringForTesting
|
|
.read(map, keys),
|
|
trackWidgetCreation:
|
|
NewWorldTestProperties.trackWidgetCreation.read(map, keys),
|
|
incrementalSerialization:
|
|
NewWorldTestProperties.incrementalSerialization.read(map, keys),
|
|
nnbdMode: NewWorldTestProperties.nnbdMode.read(map, keys) ==
|
|
NewWorldTestProperties.nnbdMode_weak
|
|
? NnbdMode.Weak
|
|
: NnbdMode.Strong,
|
|
modules: NewWorldTestProperties.modules.read(map, keys),
|
|
targetName: NewWorldTestProperties.target.read(map, keys),
|
|
).newWorldTest();
|
|
break;
|
|
default:
|
|
throw "Unexpected type: ${type}";
|
|
}
|
|
|
|
if (keys.isNotEmpty) throw "Unknown toplevel keys: $keys";
|
|
return result ?? pass(data);
|
|
}
|
|
}
|
|
|
|
Future<Null> basicTest(Map<String, String> sourceFiles, String entryPoint,
|
|
List<String>? invalidate, Directory outDir) async {
|
|
Uri entryPointUri = outDir.uri.resolve(entryPoint);
|
|
Set<String> invalidateFilenames =
|
|
invalidate == null ? new Set<String>() : new Set<String>.from(invalidate);
|
|
List<Uri> invalidateUris = <Uri>[];
|
|
Uri? packagesUri;
|
|
for (String filename in sourceFiles.keys) {
|
|
Uri uri = outDir.uri.resolve(filename);
|
|
if (invalidateFilenames.contains(filename)) {
|
|
invalidateUris.add(uri);
|
|
invalidateFilenames.remove(filename);
|
|
}
|
|
String source = sourceFiles[filename]!;
|
|
if (filename == ".dart_tool/package_config.json") {
|
|
packagesUri = uri;
|
|
}
|
|
File file = new File.fromUri(uri);
|
|
await file.parent.create(recursive: true);
|
|
await file.writeAsString(source);
|
|
}
|
|
for (String invalidateFilename in invalidateFilenames) {
|
|
if (invalidateFilename.startsWith('package:')) {
|
|
invalidateUris.add(Uri.parse(invalidateFilename));
|
|
} else {
|
|
throw "Error in test yaml: $invalidateFilename was not recognized.";
|
|
}
|
|
}
|
|
|
|
Uri output = outDir.uri.resolve("full.dill");
|
|
Uri initializedOutput = outDir.uri.resolve("full_from_initialized.dill");
|
|
|
|
Stopwatch stopwatch = new Stopwatch()..start();
|
|
CompilerOptions options = getOptions();
|
|
if (packagesUri != null) {
|
|
options.packagesFileUri = packagesUri;
|
|
}
|
|
await normalCompile(entryPointUri, output, options: options);
|
|
print("Normal compile took ${stopwatch.elapsedMilliseconds} ms");
|
|
|
|
stopwatch.reset();
|
|
options = getOptions();
|
|
if (packagesUri != null) {
|
|
options.packagesFileUri = packagesUri;
|
|
}
|
|
bool initializedResult = await initializedCompile(
|
|
entryPointUri, initializedOutput, output, invalidateUris,
|
|
options: options);
|
|
print("Initialized compile(s) from ${output.pathSegments.last} "
|
|
"took ${stopwatch.elapsedMilliseconds} ms");
|
|
Expect.isTrue(initializedResult);
|
|
|
|
// Compare the two files.
|
|
List<int> normalDillData = new File.fromUri(output).readAsBytesSync();
|
|
List<int> initializedDillData =
|
|
new File.fromUri(initializedOutput).readAsBytesSync();
|
|
checkIsEqual(normalDillData, initializedDillData);
|
|
}
|
|
|
|
Future<Map<String, List<int>>> createModules(
|
|
Map<String, Map<String, String>> module,
|
|
final List<int> sdkSummaryData,
|
|
Target target,
|
|
Target originalTarget,
|
|
String sdkSummary,
|
|
{required bool trackNeededDillLibraries,
|
|
required Uri checkoutRoot}) async {
|
|
final Uri base = Uri.parse("org-dartlang-test:///");
|
|
final Uri sdkSummaryUri = base.resolve(sdkSummary);
|
|
|
|
TestMemoryFileSystem fs =
|
|
new TestMemoryFileSystem(base, holePunchBase: checkoutRoot);
|
|
fs.entityForUri(sdkSummaryUri).writeAsBytesSync(sdkSummaryData);
|
|
|
|
// Setup all sources
|
|
for (Map<String, String> moduleSources in module.values) {
|
|
for (String filename in moduleSources.keys) {
|
|
String data = moduleSources[filename]!;
|
|
Uri uri = base.resolve(filename);
|
|
if (await fs.entityForUri(uri).exists()) {
|
|
throw "More than one entry for $filename";
|
|
}
|
|
fs.entityForUri(uri).writeAsStringSync(data);
|
|
}
|
|
}
|
|
|
|
Map<String, List<int>> moduleResult = new Map<String, List<int>>();
|
|
|
|
for (String moduleName in module.keys) {
|
|
List<Uri> moduleSources = <Uri>[];
|
|
Uri? packagesUri;
|
|
for (String filename in module[moduleName]!.keys) {
|
|
Uri uri = base.resolve(filename);
|
|
if (uri.pathSegments.last == "package_config.json") {
|
|
packagesUri = uri;
|
|
} else {
|
|
moduleSources.add(uri);
|
|
}
|
|
}
|
|
bool outlineOnly = false;
|
|
if (originalTarget is DevCompilerTarget) {
|
|
outlineOnly = true;
|
|
}
|
|
CompilerOptions options =
|
|
getOptions(target: target, sdkSummary: sdkSummary);
|
|
options.fileSystem = fs;
|
|
options.sdkRoot = null;
|
|
options.sdkSummary = sdkSummaryUri;
|
|
options.omitPlatform = true;
|
|
options.onDiagnostic = (DiagnosticMessage message) {
|
|
throw message.ansiFormatted;
|
|
};
|
|
if (packagesUri != null) {
|
|
options.packagesFileUri = packagesUri;
|
|
}
|
|
TestIncrementalCompiler compiler = new TestIncrementalCompiler(
|
|
options, moduleSources.first, /* initializeFrom = */ null, outlineOnly);
|
|
IncrementalCompilerResult? compilerResult = await compiler.computeDelta(
|
|
entryPoints: moduleSources,
|
|
trackNeededDillLibraries: trackNeededDillLibraries);
|
|
Component c = compilerResult.component;
|
|
compilerResult = null;
|
|
c.computeCanonicalNames();
|
|
List<Library> wantedLibs = <Library>[];
|
|
for (Library lib in c.libraries) {
|
|
if (moduleSources.contains(lib.importUri) ||
|
|
moduleSources.contains(lib.fileUri)) {
|
|
wantedLibs.add(lib);
|
|
}
|
|
}
|
|
if (wantedLibs.length != moduleSources.length) {
|
|
throw "Module probably not setup right.";
|
|
}
|
|
Component result = new Component(libraries: wantedLibs)
|
|
..setMainMethodAndMode(null, false, c.mode);
|
|
List<int> resultBytes = util.postProcess(result);
|
|
moduleResult[moduleName] = resultBytes;
|
|
}
|
|
|
|
return moduleResult;
|
|
}
|
|
|
|
class ExpressionCompilation {
|
|
final bool errors;
|
|
final bool warnings;
|
|
final String uri;
|
|
final String expression;
|
|
final String? className;
|
|
|
|
ExpressionCompilation(
|
|
{required this.errors,
|
|
required this.warnings,
|
|
required this.uri,
|
|
required this.expression,
|
|
required this.className});
|
|
|
|
static ExpressionCompilation create(Map yaml) {
|
|
Set<String> keys = new Set<String>.from(yaml.keys);
|
|
|
|
bool errors = ExpressionCompilationProperties.errors.read(yaml, keys);
|
|
bool warnings = ExpressionCompilationProperties.warnings.read(yaml, keys);
|
|
String uri = ExpressionCompilationProperties.uri.read(yaml, keys);
|
|
String expression =
|
|
ExpressionCompilationProperties.expression.read(yaml, keys);
|
|
String? className =
|
|
ExpressionCompilationProperties.className.read(yaml, keys);
|
|
|
|
if (keys.isNotEmpty) {
|
|
throw "Unknown key(s) for ExpressionCompilation: $keys";
|
|
}
|
|
return new ExpressionCompilation(
|
|
errors: errors,
|
|
warnings: warnings,
|
|
uri: uri,
|
|
expression: expression,
|
|
className: className);
|
|
}
|
|
}
|
|
|
|
class World {
|
|
final List<String>? modules;
|
|
final bool updateWorldType;
|
|
final bool noFullComponent;
|
|
final bool? expectInitializeFromDill;
|
|
final Map<String, String?> sources;
|
|
final bool useBadSdk;
|
|
final bool enableStringReplacement;
|
|
final String? packageConfigFile;
|
|
final String? experiments;
|
|
final String? nnbdModeString;
|
|
final List<String> entries;
|
|
final bool outlineOnly;
|
|
final bool skipOutlineBodyCheck;
|
|
final bool fromComponent;
|
|
final List<String>? invalidate;
|
|
final bool simulateTransformer;
|
|
final bool? expectInitializationError;
|
|
final bool compareToPrevious;
|
|
final List<String>? uriToSourcesDoesntInclude;
|
|
final List<String>? uriToSourcesOnlyIncludes;
|
|
final bool skipClassHierarchyTest;
|
|
final bool expectsPlatform;
|
|
final int? expectedLibraryCount;
|
|
final int? expectedSyntheticLibraryCount;
|
|
final bool warnings;
|
|
final bool errors;
|
|
final List<String>? neededDillLibraries;
|
|
final Map<String, List<String>>? expectedContent;
|
|
final bool incrementalSerializationDoesWork;
|
|
final bool allowDuplicateErrors;
|
|
final bool allowDuplicateWarnings;
|
|
final bool printErrorsInExpect;
|
|
final bool reject;
|
|
|
|
/// The expected result of the advanced invalidation.
|
|
final AdvancedInvalidationResult advancedInvalidation;
|
|
|
|
final bool checkEntries;
|
|
final bool checkInvalidatedFiles;
|
|
final List<String>? expectedInvalidatedUri;
|
|
final int? expectSameErrorsAsWorld;
|
|
final List<ExpressionCompilation>? expressionCompilation;
|
|
final bool compareWithFromScratch;
|
|
|
|
/// Don't check for equality when we allow it to be different
|
|
/// (e.g. when the old one contains more, and the new one doesn't).
|
|
final bool brandNewIncrementalSerializationAllowDifferent;
|
|
|
|
final List<String>? serializationShouldNotInclude;
|
|
|
|
/// Whether to check if the serialized constant coverage references
|
|
/// points to anything in itself.
|
|
final bool checkConstantCoverageReferences;
|
|
|
|
World({
|
|
required this.modules,
|
|
required this.updateWorldType,
|
|
required this.noFullComponent,
|
|
required this.expectInitializeFromDill,
|
|
required this.sources,
|
|
required this.useBadSdk,
|
|
required this.enableStringReplacement,
|
|
required this.packageConfigFile,
|
|
required this.experiments,
|
|
required this.nnbdModeString,
|
|
required this.entries,
|
|
required this.outlineOnly,
|
|
required this.skipOutlineBodyCheck,
|
|
required this.fromComponent,
|
|
required this.invalidate,
|
|
required this.simulateTransformer,
|
|
required this.expectInitializationError,
|
|
required this.compareToPrevious,
|
|
required this.uriToSourcesDoesntInclude,
|
|
required this.uriToSourcesOnlyIncludes,
|
|
required this.skipClassHierarchyTest,
|
|
required this.expectsPlatform,
|
|
required this.expectedLibraryCount,
|
|
required this.expectedSyntheticLibraryCount,
|
|
required this.advancedInvalidation,
|
|
required this.checkEntries,
|
|
required this.checkInvalidatedFiles,
|
|
required this.expectedInvalidatedUri,
|
|
required this.expectSameErrorsAsWorld,
|
|
required this.expressionCompilation,
|
|
required this.compareWithFromScratch,
|
|
required this.brandNewIncrementalSerializationAllowDifferent,
|
|
required this.warnings,
|
|
required this.errors,
|
|
required this.neededDillLibraries,
|
|
required this.expectedContent,
|
|
required this.incrementalSerializationDoesWork,
|
|
required this.serializationShouldNotInclude,
|
|
required this.checkConstantCoverageReferences,
|
|
required this.allowDuplicateErrors,
|
|
required this.allowDuplicateWarnings,
|
|
required this.printErrorsInExpect,
|
|
required this.reject,
|
|
});
|
|
|
|
static World create(Map world) {
|
|
Set<String> keys = new Set<String>.from(world.keys);
|
|
|
|
List<String>? modules = WorldProperties.modules.read(world, keys);
|
|
|
|
String? worldType = WorldProperties.worldType.read(world, keys);
|
|
bool updateWorldType = worldType == WorldProperties.worldType_updated;
|
|
|
|
bool noFullComponent = WorldProperties.noFullComponent.read(world, keys);
|
|
|
|
bool? expectInitializeFromDill =
|
|
WorldProperties.expectInitializeFromDill.read(world, keys);
|
|
|
|
Map<String, String?> sources = WorldProperties.sources.read(world, keys);
|
|
|
|
bool useBadSdk = WorldProperties.badSdk.read(world, keys);
|
|
|
|
bool enableStringReplacement =
|
|
WorldProperties.enableStringReplacement.read(world, keys);
|
|
|
|
String? packageConfigFile =
|
|
WorldProperties.packageConfigFile.read(world, keys);
|
|
|
|
String? experiments = WorldProperties.experiments.read(world, keys);
|
|
|
|
String? nnbdModeString = WorldProperties.nnbdMode.read(world, keys);
|
|
|
|
List<String> entries = WorldProperties.entry.read(world, keys);
|
|
|
|
bool outlineOnly = WorldProperties.outlineOnly.read(world, keys);
|
|
|
|
bool skipOutlineBodyCheck =
|
|
WorldProperties.skipOutlineBodyCheck.read(world, keys);
|
|
|
|
bool fromComponent = WorldProperties.fromComponent.read(world, keys);
|
|
|
|
List<String>? invalidate = WorldProperties.invalidate.read(world, keys);
|
|
|
|
bool simulateTransformer =
|
|
WorldProperties.simulateTransformer.read(world, keys);
|
|
|
|
bool? expectInitializationError =
|
|
WorldProperties.expectInitializationError.read(world, keys);
|
|
|
|
bool compareToPrevious =
|
|
WorldProperties.compareToPrevious.read(world, keys);
|
|
|
|
List<String>? uriToSourcesDoesntInclude =
|
|
WorldProperties.uriToSourcesDoesntInclude.read(world, keys);
|
|
|
|
List<String>? uriToSourcesOnlyIncludes =
|
|
WorldProperties.uriToSourcesOnlyIncludes.read(world, keys);
|
|
|
|
bool skipClassHierarchyTest =
|
|
WorldProperties.skipClassHierarchyTest.read(world, keys);
|
|
|
|
bool expectsPlatform = WorldProperties.expectsPlatform.read(world, keys);
|
|
|
|
int? expectedLibraryCount =
|
|
WorldProperties.expectedLibraryCount.read(world, keys);
|
|
|
|
int? expectedSyntheticLibraryCount =
|
|
WorldProperties.expectedSyntheticLibraryCount.read(world, keys);
|
|
|
|
AdvancedInvalidationResult advancedInvalidation =
|
|
WorldProperties.advancedInvalidation.read(world, keys);
|
|
|
|
bool checkEntries = WorldProperties.checkEntries.read(world, keys);
|
|
|
|
bool checkInvalidatedFiles =
|
|
WorldProperties.checkInvalidatedFiles.read(world, keys);
|
|
|
|
List<String>? expectedInvalidatedUri =
|
|
WorldProperties.expectedInvalidatedUri.read(world, keys);
|
|
|
|
int? expectSameErrorsAsWorld =
|
|
WorldProperties.expectSameErrorsAsWorld.read(world, keys);
|
|
|
|
List<ExpressionCompilation>? expressionCompilation =
|
|
WorldProperties.expressionCompilation.read(world, keys);
|
|
|
|
bool compareWithFromScratch =
|
|
WorldProperties.compareWithFromScratch.read(world, keys);
|
|
|
|
bool brandNewIncrementalSerializationAllowDifferent = WorldProperties
|
|
.brandNewIncrementalSerializationAllowDifferent
|
|
.read(world, keys);
|
|
|
|
bool warnings = WorldProperties.warnings.read(world, keys);
|
|
|
|
bool errors = WorldProperties.errors.read(world, keys);
|
|
|
|
List<String>? neededDillLibraries =
|
|
WorldProperties.neededDillLibraries.read(world, keys);
|
|
|
|
Map<String, List<String>>? expectedContent =
|
|
WorldProperties.expectedContent.read(world, keys);
|
|
|
|
bool incrementalSerializationDoesWork =
|
|
WorldProperties.incrementalSerializationDoesWork.read(world, keys);
|
|
|
|
List<String>? serializationShouldNotInclude =
|
|
WorldProperties.serializationShouldNotInclude.read(world, keys);
|
|
|
|
bool checkConstantCoverageReferences =
|
|
WorldProperties.checkConstantCoverageReferences.read(world, keys);
|
|
|
|
bool allowDuplicateErrors =
|
|
WorldProperties.allowDuplicateErrors.read(world, keys);
|
|
|
|
bool allowDuplicateWarnings =
|
|
WorldProperties.allowDuplicateWarnings.read(world, keys);
|
|
|
|
bool printErrorsInExpect =
|
|
WorldProperties.printErrorsInExpect.read(world, keys);
|
|
|
|
bool reject = WorldProperties.reject.read(world, keys);
|
|
|
|
if (keys.isNotEmpty) {
|
|
throw "Unknown key(s) for World: $keys";
|
|
}
|
|
|
|
return new World(
|
|
modules: modules,
|
|
updateWorldType: updateWorldType,
|
|
noFullComponent: noFullComponent,
|
|
expectInitializeFromDill: expectInitializeFromDill,
|
|
sources: sources,
|
|
useBadSdk: useBadSdk,
|
|
enableStringReplacement: enableStringReplacement,
|
|
packageConfigFile: packageConfigFile,
|
|
experiments: experiments,
|
|
nnbdModeString: nnbdModeString,
|
|
entries: entries,
|
|
outlineOnly: outlineOnly,
|
|
skipOutlineBodyCheck: skipOutlineBodyCheck,
|
|
fromComponent: fromComponent,
|
|
invalidate: invalidate,
|
|
simulateTransformer: simulateTransformer,
|
|
expectInitializationError: expectInitializationError,
|
|
compareToPrevious: compareToPrevious,
|
|
uriToSourcesDoesntInclude: uriToSourcesDoesntInclude,
|
|
uriToSourcesOnlyIncludes: uriToSourcesOnlyIncludes,
|
|
skipClassHierarchyTest: skipClassHierarchyTest,
|
|
expectsPlatform: expectsPlatform,
|
|
expectedLibraryCount: expectedLibraryCount,
|
|
expectedSyntheticLibraryCount: expectedSyntheticLibraryCount,
|
|
advancedInvalidation: advancedInvalidation,
|
|
checkEntries: checkEntries,
|
|
checkInvalidatedFiles: checkInvalidatedFiles,
|
|
expectedInvalidatedUri: expectedInvalidatedUri,
|
|
expectSameErrorsAsWorld: expectSameErrorsAsWorld,
|
|
expressionCompilation: expressionCompilation,
|
|
compareWithFromScratch: compareWithFromScratch,
|
|
brandNewIncrementalSerializationAllowDifferent:
|
|
brandNewIncrementalSerializationAllowDifferent,
|
|
warnings: warnings,
|
|
errors: errors,
|
|
neededDillLibraries: neededDillLibraries,
|
|
expectedContent: expectedContent,
|
|
incrementalSerializationDoesWork: incrementalSerializationDoesWork,
|
|
serializationShouldNotInclude: serializationShouldNotInclude,
|
|
checkConstantCoverageReferences: checkConstantCoverageReferences,
|
|
allowDuplicateErrors: allowDuplicateErrors,
|
|
allowDuplicateWarnings: allowDuplicateWarnings,
|
|
printErrorsInExpect: printErrorsInExpect,
|
|
reject: reject,
|
|
);
|
|
}
|
|
}
|
|
|
|
class NewWorldTest {
|
|
final TestData data;
|
|
final Context context;
|
|
final List<World> worlds;
|
|
final Map<String, Map<String, String>>? modules;
|
|
final bool omitPlatform;
|
|
final bool forceLateLoweringForTesting;
|
|
final bool trackWidgetCreation;
|
|
final bool incrementalSerialization;
|
|
final NnbdMode nnbdMode;
|
|
final String? targetName;
|
|
|
|
// These are fields in a class to make it easier to track down memory leaks
|
|
// via the leak detector test.
|
|
Component? newestWholeComponent;
|
|
Component? sdk;
|
|
Component? component;
|
|
Component? component2;
|
|
Component? component3;
|
|
|
|
NewWorldTest({
|
|
required this.data,
|
|
required this.context,
|
|
required this.worlds,
|
|
required this.modules,
|
|
required this.omitPlatform,
|
|
required this.forceLateLoweringForTesting,
|
|
required this.trackWidgetCreation,
|
|
required this.incrementalSerialization,
|
|
required this.nnbdMode,
|
|
required this.targetName,
|
|
});
|
|
|
|
Future<Result<TestData>> newWorldTest() async {
|
|
final Uri platformBinariesRoot =
|
|
computePlatformBinariesLocation(forceBuildDir: true);
|
|
|
|
// This is somewhat of a hack but will do for now.
|
|
final Uri checkoutRoot = data.loadedFrom.resolve("../../../../");
|
|
|
|
TestTargetFlags targetFlags = new TestTargetFlags(
|
|
forceLateLoweringsForTesting:
|
|
forceLateLoweringForTesting ? LateLowering.all : null,
|
|
trackWidgetCreation: trackWidgetCreation);
|
|
Target target = new VmTarget(targetFlags);
|
|
if (targetName != null) {
|
|
if (targetName == NewWorldTestProperties.target_none) {
|
|
target = new NoneTarget(targetFlags);
|
|
} else if (targetName == NewWorldTestProperties.target_dartdevc) {
|
|
target = new DevCompilerTarget(targetFlags);
|
|
} else if (targetName == NewWorldTestProperties.target_dart2js) {
|
|
target = new Dart2jsTarget("dart2js", targetFlags);
|
|
} else if (targetName == NewWorldTestProperties.target_vm) {
|
|
// default.
|
|
} else {
|
|
throw "Unknown target name '$targetName'";
|
|
}
|
|
}
|
|
Target originalTarget = target;
|
|
target = new TestTargetWrapper(target, targetFlags);
|
|
|
|
String sdkSummary = computePlatformDillName(
|
|
target,
|
|
nnbdMode,
|
|
() => throw new UnsupportedError(
|
|
"No platform dill for target '${targetName}' with $nnbdMode."))!;
|
|
|
|
final Uri base = Uri.parse("org-dartlang-test:///");
|
|
final Uri sdkSummaryUri = base.resolve(sdkSummary);
|
|
final Uri initializeFrom = base.resolve("initializeFrom.dill");
|
|
Uri platformUri = platformBinariesRoot.resolve(sdkSummary);
|
|
final List<int> sdkSummaryData =
|
|
await new File.fromUri(platformUri).readAsBytes();
|
|
|
|
List<int>? newestWholeComponentData;
|
|
TestMemoryFileSystem? fs;
|
|
Map<String, String?>? sourceFiles;
|
|
CompilerOptions? options;
|
|
TestIncrementalCompiler? compiler;
|
|
IncrementalSerializer? incrementalSerializer;
|
|
|
|
Map<String, List<int>>? moduleData;
|
|
Map<String, Component>? moduleComponents;
|
|
|
|
if (modules != null) {
|
|
moduleData = await createModules(
|
|
modules!, sdkSummaryData, target, originalTarget, sdkSummary,
|
|
checkoutRoot: checkoutRoot, trackNeededDillLibraries: false);
|
|
sdk = newestWholeComponent = new Component();
|
|
new BinaryBuilder(sdkSummaryData,
|
|
filename: null, disableLazyReading: false)
|
|
.readComponent(newestWholeComponent!);
|
|
}
|
|
|
|
int worldNum = 0;
|
|
// TODO: When needed, we can do this for warnings too.
|
|
List<Set<String>> worldErrors = [];
|
|
for (World world in worlds) {
|
|
worldNum++;
|
|
print("----------------");
|
|
print("World #$worldNum");
|
|
print("----------------");
|
|
List<Component>? modulesToUse;
|
|
if (world.modules != null) {
|
|
moduleComponents ??= new Map<String, Component>();
|
|
|
|
sdk!.adoptChildren();
|
|
for (Component c in moduleComponents.values) {
|
|
c.adoptChildren();
|
|
}
|
|
|
|
modulesToUse = <Component>[];
|
|
for (String moduleName in world.modules!) {
|
|
Component? moduleComponent = moduleComponents[moduleName];
|
|
if (moduleComponent != null) {
|
|
modulesToUse.add(moduleComponent);
|
|
}
|
|
}
|
|
for (String moduleName in world.modules!) {
|
|
Component? moduleComponent = moduleComponents[moduleName];
|
|
if (moduleComponent == null) {
|
|
moduleComponent = new Component(nameRoot: sdk!.root);
|
|
new BinaryBuilder(moduleData![moduleName]!,
|
|
filename: null,
|
|
disableLazyReading: false,
|
|
alwaysCreateNewNamedNodes: true)
|
|
.readComponent(moduleComponent);
|
|
moduleComponents[moduleName] = moduleComponent;
|
|
modulesToUse.add(moduleComponent);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!world.updateWorldType) {
|
|
fs = new TestMemoryFileSystem(base, holePunchBase: checkoutRoot);
|
|
}
|
|
fs!.entityForUri(sdkSummaryUri).writeAsBytesSync(sdkSummaryData);
|
|
bool expectInitializeFromDill = false;
|
|
if (newestWholeComponentData != null &&
|
|
newestWholeComponentData.isNotEmpty) {
|
|
fs
|
|
.entityForUri(initializeFrom)
|
|
.writeAsBytesSync(newestWholeComponentData);
|
|
expectInitializeFromDill = true;
|
|
}
|
|
if (world.expectInitializeFromDill != null) {
|
|
expectInitializeFromDill = world.expectInitializeFromDill!;
|
|
}
|
|
if (!world.updateWorldType) {
|
|
sourceFiles = new Map<String, String?>.from(world.sources);
|
|
} else {
|
|
sourceFiles!.addAll(new Map<String, String?>.from(world.sources));
|
|
}
|
|
Uri? packagesUri;
|
|
for (String filename in sourceFiles.keys) {
|
|
String data = sourceFiles[filename] ?? "";
|
|
Uri uri = base.resolve(filename);
|
|
if (filename == ".dart_tool/package_config.json") {
|
|
packagesUri = uri;
|
|
}
|
|
if (world.enableStringReplacement) {
|
|
data = replaceMarkersWithVersions(data);
|
|
}
|
|
fs.entityForUri(uri).writeAsStringSync(data);
|
|
}
|
|
if (world.packageConfigFile != null) {
|
|
packagesUri = base.resolve(world.packageConfigFile!);
|
|
}
|
|
|
|
if (!world.updateWorldType) {
|
|
options = getOptions(target: target, sdkSummary: sdkSummary);
|
|
options.nnbdMode = nnbdMode;
|
|
options.fileSystem = fs;
|
|
options.sdkRoot = null;
|
|
options.sdkSummary = sdkSummaryUri;
|
|
if (world.useBadSdk) {
|
|
options.sdkSummary = sdkSummaryUri.resolve("nonexisting.dill");
|
|
}
|
|
options.omitPlatform = omitPlatform;
|
|
if (world.experiments != null) {
|
|
Map<String, bool> flagsFromOptions =
|
|
parseExperimentalArguments([world.experiments!]);
|
|
// Ensure that we run with non-nullable turned off even when the
|
|
// flag is on by default.
|
|
Map<ExperimentalFlag, bool> explicitExperimentalFlags =
|
|
parseExperimentalFlags(flagsFromOptions,
|
|
onError: (e) =>
|
|
throw "Error on parsing experiments flags: $e");
|
|
options.explicitExperimentalFlags = explicitExperimentalFlags;
|
|
} else {
|
|
options.explicitExperimentalFlags = {};
|
|
}
|
|
// A separate "world" can also change nnbd mode ---
|
|
// notice that the platform is not updated though!
|
|
if (world.nnbdModeString != null) {
|
|
String nnbdMode = world.nnbdModeString!;
|
|
switch (nnbdMode) {
|
|
case WorldProperties.nnbdMode_weak:
|
|
options.nnbdMode = NnbdMode.Weak;
|
|
break;
|
|
default:
|
|
throw "Not supported nnbd mode: $nnbdMode";
|
|
}
|
|
}
|
|
}
|
|
if (packagesUri != null) {
|
|
options!.packagesFileUri = packagesUri;
|
|
}
|
|
bool gotError = false;
|
|
final Set<String> formattedErrors = Set<String>();
|
|
bool gotWarning = false;
|
|
final Set<String> formattedWarnings = Set<String>();
|
|
final Set<String> seenDiagnosticCodes = Set<String>();
|
|
|
|
options!.onDiagnostic = (DiagnosticMessage message) {
|
|
String? code = getMessageCodeObject(message)?.name;
|
|
if (code != null) seenDiagnosticCodes.add(code);
|
|
|
|
String stringId = message.ansiFormatted.join("\n");
|
|
if (message is FormattedMessage) {
|
|
stringId = message.toJsonString();
|
|
} else if (message is DiagnosticMessageFromJson) {
|
|
stringId = message.toJsonString();
|
|
}
|
|
if (message.severity == Severity.error) {
|
|
gotError = true;
|
|
if (!formattedErrors.add(stringId) && !world.allowDuplicateErrors) {
|
|
Expect.fail("Got the same message twice: ${stringId}");
|
|
}
|
|
} else if (message.severity == Severity.warning) {
|
|
gotWarning = true;
|
|
if (!formattedWarnings.add(stringId) &&
|
|
!world.allowDuplicateWarnings) {
|
|
Expect.fail("Got the same message twice: ${stringId}");
|
|
}
|
|
}
|
|
};
|
|
|
|
List<Uri> entries = world.entries.map((e) => base.resolve(e)).toList();
|
|
if (!world.updateWorldType) {
|
|
if (incrementalSerialization == true) {
|
|
incrementalSerializer = new IncrementalSerializer();
|
|
}
|
|
if (world.fromComponent) {
|
|
compiler = new TestIncrementalCompiler.fromComponent(
|
|
options,
|
|
entries.first,
|
|
(modulesToUse != null) ? sdk : newestWholeComponent,
|
|
world.outlineOnly,
|
|
incrementalSerializer);
|
|
} else {
|
|
compiler = new TestIncrementalCompiler(options, entries.first,
|
|
initializeFrom, world.outlineOnly, incrementalSerializer);
|
|
|
|
if (modulesToUse != null) {
|
|
throw "You probably shouldn't do this! "
|
|
"Any modules will have another sdk loaded!";
|
|
}
|
|
}
|
|
}
|
|
|
|
List<Uri> invalidated = <Uri>[];
|
|
if (world.invalidate != null) {
|
|
for (String filename in world.invalidate!) {
|
|
Uri uri = base.resolve(filename);
|
|
invalidated.add(uri);
|
|
compiler!.invalidate(uri);
|
|
}
|
|
}
|
|
|
|
if (modulesToUse != null) {
|
|
compiler!.setModulesToLoadOnNextComputeDelta(modulesToUse);
|
|
compiler.invalidateAllSources();
|
|
}
|
|
|
|
Stopwatch stopwatch = new Stopwatch()..start();
|
|
IncrementalCompilerResult? compilerResult = await compiler!.computeDelta(
|
|
entryPoints: entries,
|
|
fullComponent: !world.updateWorldType
|
|
? false
|
|
: (world.noFullComponent ? false : true),
|
|
trackNeededDillLibraries: modulesToUse != null,
|
|
simulateTransformer: world.simulateTransformer);
|
|
component = compilerResult.component;
|
|
// compilerResult is null'ed out at the end to avoid any
|
|
// "artificial memory leak" on that account.
|
|
if (world.outlineOnly && !world.skipOutlineBodyCheck) {
|
|
for (Library lib in component!.libraries) {
|
|
for (Class c in lib.classes) {
|
|
for (Procedure p in c.procedures) {
|
|
if (p.function.body != null &&
|
|
p.function.body is! EmptyStatement) {
|
|
throw "Got body (${p.function.body.runtimeType})";
|
|
}
|
|
}
|
|
}
|
|
for (Procedure p in lib.procedures) {
|
|
if (p.function.body != null && p.function.body is! EmptyStatement) {
|
|
throw "Got body (${p.function.body.runtimeType})";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
Result<TestData>? result = performErrorAndWarningCheck(world, data,
|
|
gotError, formattedErrors, gotWarning, formattedWarnings);
|
|
if (result != null) return result;
|
|
bool? expectInitializationError = world.expectInitializationError;
|
|
if (expectInitializationError != null) {
|
|
Set<String> seenInitializationError = seenDiagnosticCodes.intersection({
|
|
"InitializeFromDillNotSelfContainedNoDump",
|
|
"InitializeFromDillNotSelfContained",
|
|
"InitializeFromDillUnknownProblem",
|
|
"InitializeFromDillUnknownProblemNoDump",
|
|
});
|
|
if (expectInitializationError) {
|
|
if (seenInitializationError.isEmpty) {
|
|
return new Result<TestData>(data, MissingInitializationError,
|
|
"Expected to see an initialization error but didn't.");
|
|
}
|
|
} else {
|
|
if (seenInitializationError.isNotEmpty) {
|
|
return new Result<TestData>(
|
|
data,
|
|
UnexpectedInitializationError,
|
|
"Expected not to see an initialization error but did: "
|
|
"$seenInitializationError.");
|
|
}
|
|
}
|
|
}
|
|
util.throwOnEmptyMixinBodies(component!);
|
|
await util.throwOnInsufficientUriToSource(component!,
|
|
fileSystem: gotError ? null : fs);
|
|
print("Compile took ${stopwatch.elapsedMilliseconds} ms");
|
|
|
|
Result? contentResult = checkExpectedContent(world, component!);
|
|
if (contentResult != null) return contentResult.copyWithOutput(data);
|
|
result = checkNeededDillLibraries(
|
|
world, data, compilerResult.neededDillLibraries, base);
|
|
if (result != null) return result;
|
|
|
|
Result? nnbdCheck = checkNNBDSettings(component!);
|
|
if (nnbdCheck != null) return nnbdCheck.copyWithOutput(data);
|
|
|
|
if (!world.noFullComponent) {
|
|
Set<Library> allLibraries = new Set<Library>();
|
|
for (Library lib in component!.libraries) {
|
|
computeAllReachableLibrariesFor(lib, allLibraries);
|
|
}
|
|
if (allLibraries.length != component!.libraries.length) {
|
|
return new Result<TestData>(
|
|
data,
|
|
ReachableLibrariesError,
|
|
"Expected for the reachable stuff to be equal to "
|
|
"${component!.libraries} but it was $allLibraries");
|
|
}
|
|
Set<Library> tooMany = allLibraries.toSet()
|
|
..removeAll(component!.libraries);
|
|
if (tooMany.isNotEmpty) {
|
|
return new Result<TestData>(
|
|
data,
|
|
ReachableLibrariesError,
|
|
"Expected for the reachable stuff to be equal to "
|
|
"${component!.libraries} but these were there too: $tooMany "
|
|
"(and others were missing)");
|
|
}
|
|
}
|
|
|
|
util.postProcessComponent(component!);
|
|
String actualSerialized = componentToStringSdkFiltered(component!,
|
|
printErrors: world.printErrorsInExpect ? formattedErrors : null);
|
|
print("*****\n\ncomponent:\n"
|
|
"${actualSerialized}\n\n\n");
|
|
result = checkExpectFile(data, worldNum, "", context, actualSerialized);
|
|
if (result != null) return result;
|
|
|
|
if (world.compareToPrevious && newestWholeComponent != null) {
|
|
EquivalenceResult result = checkEquivalence(
|
|
newestWholeComponent!, component!,
|
|
strategy: const Strategy());
|
|
if (!result.isEquivalent) {
|
|
return new Result<TestData>(
|
|
data, EquivalenceError, result.toString());
|
|
}
|
|
}
|
|
|
|
if (world.reject) {
|
|
util.postProcess(component!);
|
|
|
|
// Reject: We keep the original "newest whole component" and also
|
|
// reject/relink it.
|
|
// This mimics what happens when e.g. flutter "reject"s a compile.
|
|
newestWholeComponent?.relink();
|
|
|
|
// Add errors or assert will trigger in later worlds.
|
|
worldErrors.add(formattedErrors.toSet());
|
|
|
|
// But otherwise skip other stuff - e.g. doing a leak check will find a
|
|
// leak because we (on purpose) have two of the same library at the same
|
|
// time.
|
|
continue;
|
|
} else {
|
|
// Save it.
|
|
newestWholeComponentData = util.postProcess(component!);
|
|
newestWholeComponent = component;
|
|
}
|
|
|
|
if (world.checkConstantCoverageReferences) {
|
|
Result<TestData>? result = checkConstantCoverageReferences(
|
|
newestWholeComponentData, omitPlatform, sdkSummaryData, data);
|
|
if (result != null) return result;
|
|
}
|
|
|
|
if (world.uriToSourcesDoesntInclude != null) {
|
|
for (String filename in world.uriToSourcesDoesntInclude!) {
|
|
Uri uri = base.resolve(filename);
|
|
if (component!.uriToSource[uri] != null) {
|
|
return new Result<TestData>(
|
|
data,
|
|
UriToSourceError,
|
|
"Expected no uriToSource for $uri but found "
|
|
"${component!.uriToSource[uri]}");
|
|
}
|
|
}
|
|
}
|
|
if (world.uriToSourcesOnlyIncludes != null) {
|
|
Set<Uri> allowed = {};
|
|
for (String filename in world.uriToSourcesOnlyIncludes!) {
|
|
Uri uri = base.resolve(filename);
|
|
allowed.add(uri);
|
|
}
|
|
for (Uri uri in component!.uriToSource.keys) {
|
|
// null is always there, so allow it implicitly.
|
|
// Dart scheme uris too.
|
|
if (uri.isScheme("org-dartlang-sdk")) continue;
|
|
if (!allowed.contains(uri)) {
|
|
return new Result<TestData>(
|
|
data,
|
|
UriToSourceError,
|
|
"Expected no uriToSource for $uri but found "
|
|
"${component!.uriToSource[uri]}");
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!world.skipClassHierarchyTest) {
|
|
result = checkClassHierarchy(compilerResult, data, worldNum, context);
|
|
if (result != null) return result;
|
|
}
|
|
|
|
int nonSyntheticLibraries = countNonSyntheticLibraries(component!);
|
|
int nonSyntheticPlatformLibraries =
|
|
countNonSyntheticPlatformLibraries(component!);
|
|
int syntheticLibraries = countSyntheticLibraries(component!);
|
|
if (world.expectsPlatform) {
|
|
if (nonSyntheticPlatformLibraries < 5) {
|
|
return new Result<TestData>(
|
|
data,
|
|
MissingPlatformLibraries,
|
|
"Expected to have at least 5 platform libraries "
|
|
"(actually, the entire sdk), "
|
|
"but got $nonSyntheticPlatformLibraries.");
|
|
}
|
|
} else {
|
|
if (nonSyntheticPlatformLibraries != 0) {
|
|
return new Result<TestData>(
|
|
data,
|
|
UnexpectedPlatformLibraries,
|
|
"Expected to have 0 platform libraries "
|
|
"but got $nonSyntheticPlatformLibraries.");
|
|
}
|
|
}
|
|
if (world.expectedLibraryCount != null) {
|
|
if (nonSyntheticLibraries - nonSyntheticPlatformLibraries !=
|
|
world.expectedLibraryCount!) {
|
|
return new Result<TestData>(
|
|
data,
|
|
LibraryCountMismatch,
|
|
"Expected ${world.expectedLibraryCount} non-synthetic "
|
|
"libraries, got "
|
|
"${nonSyntheticLibraries - nonSyntheticPlatformLibraries} "
|
|
"(not counting platform libraries)");
|
|
}
|
|
}
|
|
if (world.expectedSyntheticLibraryCount != null) {
|
|
if (syntheticLibraries != world.expectedSyntheticLibraryCount!) {
|
|
return new Result<TestData>(
|
|
data,
|
|
LibraryCountMismatch,
|
|
"Expected ${world.expectedSyntheticLibraryCount} synthetic "
|
|
"libraries, got ${syntheticLibraries}");
|
|
}
|
|
}
|
|
|
|
AdvancedInvalidationResult? actualAdvancedInvalidation =
|
|
compiler.recorderForTesting.advancedInvalidationResult;
|
|
if (world.advancedInvalidation != actualAdvancedInvalidation) {
|
|
return new Result<TestData>(
|
|
data,
|
|
UnexpectedAdvancedInvalidation,
|
|
"Expected advancedInvalidation: "
|
|
"${world.advancedInvalidation}, "
|
|
"advancedInvalidation: "
|
|
"${actualAdvancedInvalidation}.");
|
|
}
|
|
|
|
if (!world.noFullComponent) {
|
|
if (world.checkEntries) {
|
|
List<Library> entryLib = component!.libraries
|
|
.where((Library lib) =>
|
|
entries.contains(lib.importUri) ||
|
|
entries.contains(lib.fileUri))
|
|
.toList();
|
|
if (entryLib.length != entries.length) {
|
|
return new Result<TestData>(
|
|
data,
|
|
UnexpectedEntryToLibraryCount,
|
|
"Expected the entries to become libraries. "
|
|
"Got ${entryLib.length} libraries for the expected "
|
|
"${entries.length} entries.");
|
|
}
|
|
}
|
|
}
|
|
if (compiler.initializedFromDillForTesting != expectInitializeFromDill) {
|
|
return new Result<TestData>(
|
|
data,
|
|
InitializedFromDillMismatch,
|
|
"Expected that initializedFromDill would be "
|
|
"$expectInitializeFromDill but was "
|
|
"${compiler.initializedFromDillForTesting}");
|
|
}
|
|
|
|
if (incrementalSerialization == true &&
|
|
compiler.initializedFromDillForTesting) {
|
|
Expect.isTrue(compiler.initializedIncrementalSerializerForTesting);
|
|
} else {
|
|
Expect.isFalse(compiler.initializedIncrementalSerializerForTesting);
|
|
}
|
|
|
|
if (world.checkInvalidatedFiles) {
|
|
Set<Uri>? filteredInvalidated =
|
|
compiler.getFilteredInvalidatedImportUrisForTesting(invalidated);
|
|
if (world.invalidate != null) {
|
|
Expect.equals(
|
|
world.invalidate!.length,
|
|
filteredInvalidated?.length ?? 0,
|
|
"Unexpected invalidated files: ${filteredInvalidated}, "
|
|
"actual: ${world.invalidate}.");
|
|
if (world.expectedInvalidatedUri != null) {
|
|
Expect.setEquals(
|
|
world.expectedInvalidatedUri!.map((s) => base.resolve(s)),
|
|
filteredInvalidated!);
|
|
}
|
|
} else {
|
|
Expect.isNull(filteredInvalidated);
|
|
Expect.isNull(world.expectedInvalidatedUri);
|
|
}
|
|
}
|
|
Result<List<int>?> serializationResult = checkIncrementalSerialization(
|
|
incrementalSerialization, component!, incrementalSerializer, world);
|
|
if (!serializationResult.isPass) {
|
|
return serializationResult.copyWithOutput(data);
|
|
}
|
|
List<int>? incrementalSerializationBytes = serializationResult.output;
|
|
|
|
worldErrors.add(formattedErrors.toSet());
|
|
assert(worldErrors.length == worldNum);
|
|
if (world.expectSameErrorsAsWorld != null) {
|
|
checkErrorsAndWarnings(
|
|
worldErrors[world.expectSameErrorsAsWorld! - 1],
|
|
formattedErrors,
|
|
{},
|
|
{},
|
|
);
|
|
}
|
|
|
|
Set<String> prevFormattedErrors = formattedErrors.toSet();
|
|
Set<String> prevFormattedWarnings = formattedWarnings.toSet();
|
|
|
|
void clearPrevErrorsEtc() {
|
|
gotError = false;
|
|
formattedErrors.clear();
|
|
gotWarning = false;
|
|
formattedWarnings.clear();
|
|
}
|
|
|
|
if (!world.noFullComponent) {
|
|
clearPrevErrorsEtc();
|
|
IncrementalCompilerResult? compilerResult2 =
|
|
await compiler.computeDelta(
|
|
entryPoints: entries,
|
|
fullComponent: true,
|
|
simulateTransformer: world.simulateTransformer);
|
|
component2 = compilerResult2.component;
|
|
compilerResult2 = null;
|
|
Result<TestData>? result = performErrorAndWarningCheck(world, data,
|
|
gotError, formattedErrors, gotWarning, formattedWarnings);
|
|
if (result != null) return result;
|
|
List<int> thisWholeComponent = util.postProcess(component2!);
|
|
String component2String = componentToStringSdkFiltered(component2!,
|
|
printErrors: world.printErrorsInExpect ? formattedErrors : null);
|
|
print("*****\n\ncomponent2:\n$component2String\n\n\n");
|
|
checkIsEqual(newestWholeComponentData, thisWholeComponent);
|
|
checkErrorsAndWarnings(prevFormattedErrors, formattedErrors,
|
|
prevFormattedWarnings, formattedWarnings);
|
|
newestWholeComponent = component2;
|
|
|
|
Result<List<int>?> serializationResult = checkIncrementalSerialization(
|
|
incrementalSerialization,
|
|
component2!,
|
|
incrementalSerializer,
|
|
world);
|
|
if (!serializationResult.isPass) {
|
|
return serializationResult.copyWithOutput(data);
|
|
}
|
|
List<int>? incrementalSerializationBytes2 = serializationResult.output;
|
|
|
|
if ((incrementalSerializationBytes == null &&
|
|
incrementalSerializationBytes2 != null) ||
|
|
(incrementalSerializationBytes != null &&
|
|
incrementalSerializationBytes2 == null)) {
|
|
return new Result<TestData>(
|
|
data,
|
|
IncrementalSerializationError,
|
|
"Incremental serialization gave results in one instance, "
|
|
"but not another.");
|
|
}
|
|
|
|
if (incrementalSerializationBytes != null) {
|
|
checkIsEqual(
|
|
incrementalSerializationBytes, incrementalSerializationBytes2!);
|
|
}
|
|
}
|
|
|
|
if (world.expressionCompilation != null) {
|
|
int expressionCompilationNum = 0;
|
|
for (ExpressionCompilation compilation
|
|
in world.expressionCompilation!) {
|
|
expressionCompilationNum++;
|
|
clearPrevErrorsEtc();
|
|
Uri uri = base.resolve(compilation.uri);
|
|
Procedure procedure = (await compiler.compileExpression(
|
|
compilation.expression,
|
|
{},
|
|
[],
|
|
"debugExpr",
|
|
uri,
|
|
className: compilation.className,
|
|
))!;
|
|
if (gotError && !compilation.errors) {
|
|
return new Result<TestData>(data, UnexpectedErrors,
|
|
"Got error(s) on expression compilation: ${formattedErrors}.");
|
|
} else if (!gotError && compilation.errors) {
|
|
return new Result<TestData>(
|
|
data, MissingErrors, "Didn't get any errors.");
|
|
}
|
|
if (gotWarning && !compilation.warnings) {
|
|
return new Result<TestData>(
|
|
data,
|
|
UnexpectedWarnings,
|
|
"Got warning(s) on expression compilation: "
|
|
"${formattedWarnings}.");
|
|
} else if (!gotWarning && compilation.warnings) {
|
|
return new Result<TestData>(
|
|
data, MissingWarnings, "Didn't get any warnings.");
|
|
}
|
|
Result<TestData>? result = checkExpectFile(
|
|
data,
|
|
worldNum,
|
|
".expression.$expressionCompilationNum",
|
|
context,
|
|
nodeToString(procedure));
|
|
if (result != null) return result;
|
|
}
|
|
}
|
|
|
|
if (!world.noFullComponent &&
|
|
(incrementalSerialization == true || world.compareWithFromScratch)) {
|
|
// Do compile from scratch and compare.
|
|
clearPrevErrorsEtc();
|
|
TestIncrementalCompiler? compilerFromScratch;
|
|
|
|
IncrementalSerializer? incrementalSerializer2;
|
|
if (incrementalSerialization == true) {
|
|
incrementalSerializer2 = new IncrementalSerializer();
|
|
}
|
|
|
|
if (world.fromComponent || modulesToUse != null) {
|
|
compilerFromScratch = new TestIncrementalCompiler.fromComponent(
|
|
options,
|
|
entries.first,
|
|
sdk,
|
|
world.outlineOnly,
|
|
incrementalSerializer2);
|
|
} else {
|
|
compilerFromScratch = new TestIncrementalCompiler(options,
|
|
entries.first, null, world.outlineOnly, incrementalSerializer2);
|
|
}
|
|
|
|
if (modulesToUse != null) {
|
|
compilerFromScratch.setModulesToLoadOnNextComputeDelta(modulesToUse);
|
|
compilerFromScratch.invalidateAllSources();
|
|
}
|
|
|
|
Stopwatch stopwatch = new Stopwatch()..start();
|
|
IncrementalCompilerResult? compilerResult3 =
|
|
await compilerFromScratch.computeDelta(
|
|
entryPoints: entries,
|
|
trackNeededDillLibraries: modulesToUse != null,
|
|
simulateTransformer: world.simulateTransformer);
|
|
component3 = compilerResult3.component;
|
|
compilerResult3 = null;
|
|
compilerFromScratch = null;
|
|
Result<TestData>? result = performErrorAndWarningCheck(world, data,
|
|
gotError, formattedErrors, gotWarning, formattedWarnings);
|
|
if (result != null) return result;
|
|
util.throwOnEmptyMixinBodies(component3!);
|
|
await util.throwOnInsufficientUriToSource(component3!);
|
|
print("Compile took ${stopwatch.elapsedMilliseconds} ms");
|
|
|
|
List<int> thisWholeComponent = util.postProcess(component3!);
|
|
String component3String = componentToStringSdkFiltered(component3!,
|
|
printErrors: world.printErrorsInExpect ? formattedErrors : null);
|
|
print("*****\n\ncomponent3:\n$component3String\n\n\n");
|
|
if (world.compareWithFromScratch) {
|
|
checkIsEqual(newestWholeComponentData, thisWholeComponent);
|
|
}
|
|
checkErrorsAndWarnings(prevFormattedErrors, formattedErrors,
|
|
prevFormattedWarnings, formattedWarnings);
|
|
|
|
Result<List<int>?> serializationResult = checkIncrementalSerialization(
|
|
incrementalSerialization,
|
|
component3!,
|
|
incrementalSerializer2,
|
|
world);
|
|
if (!serializationResult.isPass) {
|
|
return serializationResult.copyWithOutput(data);
|
|
}
|
|
List<int>? incrementalSerializationBytes3 = serializationResult.output;
|
|
|
|
if ((incrementalSerializationBytes == null &&
|
|
incrementalSerializationBytes3 != null) ||
|
|
(incrementalSerializationBytes != null &&
|
|
incrementalSerializationBytes3 == null)) {
|
|
return new Result<TestData>(
|
|
data,
|
|
IncrementalSerializationError,
|
|
"Incremental serialization gave results in one instance, "
|
|
"but not another.");
|
|
}
|
|
|
|
if (incrementalSerializationBytes != null) {
|
|
if (world.brandNewIncrementalSerializationAllowDifferent) {
|
|
// Don't check for equality when we allow it to be different
|
|
// (e.g. when the old one contains more, and the new one doesn't).
|
|
} else {
|
|
checkIsEqual(
|
|
incrementalSerializationBytes, incrementalSerializationBytes3!);
|
|
}
|
|
newestWholeComponentData = incrementalSerializationBytes;
|
|
}
|
|
}
|
|
|
|
component = null;
|
|
compilerResult = null;
|
|
component2 = null;
|
|
component3 = null;
|
|
// Dummy tree nodes can (currently) leak though the parent pointer.
|
|
// To avoid that (here) (for leak testing) we'll null them out.
|
|
for (TreeNode treeNode in dummyTreeNodes) {
|
|
treeNode.parent = null;
|
|
}
|
|
|
|
if (context.breakBetween) {
|
|
debugger();
|
|
print("Continuing after debug break");
|
|
}
|
|
}
|
|
return new Result<TestData>.pass(data);
|
|
}
|
|
}
|
|
|
|
Result<TestData>? checkConstantCoverageReferences(
|
|
List<int> newestWholeComponentData,
|
|
bool omitPlatform,
|
|
List<int> sdkSummaryData,
|
|
TestData data) {
|
|
// Note that this is in a method to avoid "semi-leaks".
|
|
Component loadedComponent = new Component();
|
|
if (omitPlatform) {
|
|
new BinaryBuilder(sdkSummaryData, filename: null)
|
|
.readComponent(loadedComponent);
|
|
}
|
|
new BinaryBuilder(newestWholeComponentData, filename: null)
|
|
.readComponent(loadedComponent);
|
|
|
|
for (MapEntry<Uri, Source> source in loadedComponent.uriToSource.entries) {
|
|
Set<Reference>? references = source.value.constantCoverageConstructors;
|
|
if (references != null) {
|
|
for (Reference reference in references) {
|
|
if (reference.node == null) {
|
|
return new Result<TestData>(
|
|
data,
|
|
ConstantCoverageReferenceWithoutNode,
|
|
"Constant coverage reference without node: "
|
|
"${reference.canonicalName} from ${source.value.importUri} "
|
|
"indexed in ${source.key}");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
class Strategy extends EquivalenceStrategy {
|
|
const Strategy();
|
|
|
|
@override
|
|
bool checkComponent_libraries(
|
|
EquivalenceVisitor visitor, Component node, Component other) {
|
|
return visitor.checkSets(node.libraries.toSet(), other.libraries.toSet(),
|
|
visitor.matchNamedNodes, visitor.checkNodes, 'libraries');
|
|
}
|
|
|
|
@override
|
|
bool checkClass_procedures(
|
|
EquivalenceVisitor visitor, Class node, Class other) {
|
|
// Check procedures as a set instead of a list to allow for reordering.
|
|
return visitor.checkSets(node.procedures.toSet(), other.procedures.toSet(),
|
|
visitor.matchNamedNodes, visitor.checkNodes, 'procedures');
|
|
}
|
|
|
|
@override
|
|
bool checkVariableDeclaration_binaryOffsetNoTag(EquivalenceVisitor visitor,
|
|
VariableDeclaration node, VariableDeclaration other) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
Result? checkNNBDSettings(Component component) {
|
|
NonNullableByDefaultCompiledMode mode = component.mode;
|
|
if (mode == NonNullableByDefaultCompiledMode.Invalid) return null;
|
|
for (Library lib in component.libraries) {
|
|
if (mode == lib.nonNullableByDefaultCompiledMode) continue;
|
|
|
|
if (mode == NonNullableByDefaultCompiledMode.Strong ||
|
|
lib.nonNullableByDefaultCompiledMode ==
|
|
NonNullableByDefaultCompiledMode.Strong) {
|
|
// Non agnostic and one (but not both) are strong => error.
|
|
return new Result(
|
|
null,
|
|
NNBDModeMismatch,
|
|
"Component mode was $mode but ${lib.importUri} had mode "
|
|
"${lib.nonNullableByDefaultCompiledMode}.");
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
Result<TestData>? checkExpectFile(TestData data, int worldNum,
|
|
String extraUriString, Context context, String actualSerialized) {
|
|
Uri uri = data.loadedFrom.resolve(data.loadedFrom.pathSegments.last +
|
|
".world.$worldNum${extraUriString}.expect");
|
|
String? expected;
|
|
File file = new File.fromUri(uri);
|
|
if (file.existsSync()) {
|
|
expected = file.readAsStringSync();
|
|
}
|
|
if (expected?.replaceAll("\r\n", "\n") != actualSerialized) {
|
|
if (context.updateExpectations) {
|
|
file.writeAsStringSync(actualSerialized);
|
|
} else {
|
|
String extra = "";
|
|
if (expected == null) extra = "Expect file did not exist.\n";
|
|
return new Result<TestData>(
|
|
data,
|
|
expected == null ? ExpectationFileMissing : ExpectationFileMismatch,
|
|
"${extra}Unexpected serialized representation. "
|
|
"Fix or update $uri to contain the below:\n\n"
|
|
"$actualSerialized",
|
|
autoFixCommand: "updateExpectations=true",
|
|
canBeFixWithUpdateExpectations: true);
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/// Check that the class hierarchy is up-to-date with reality.
|
|
///
|
|
/// This has the option to do expect files, but it's disabled by default
|
|
/// while we're trying to figure out if it's useful or not.
|
|
Result<TestData>? checkClassHierarchy(IncrementalCompilerResult compilerResult,
|
|
TestData data, int worldNum, Context context,
|
|
{bool checkExpectFile = false}) {
|
|
ClassHierarchy? classHierarchy = compilerResult.classHierarchy;
|
|
if (classHierarchy is! ClosedWorldClassHierarchy) {
|
|
return new Result<TestData>(
|
|
data,
|
|
ClassHierarchyError,
|
|
"Expected the class hierarchy to be ClosedWorldClassHierarchy "
|
|
"but it wasn't. It was ${classHierarchy.runtimeType}");
|
|
}
|
|
List<ForTestingClassInfo> classHierarchyData =
|
|
classHierarchy.getTestingClassInfo();
|
|
Map<Class, ForTestingClassInfo> classHierarchyMap =
|
|
new Map<Class, ForTestingClassInfo>();
|
|
for (ForTestingClassInfo info in classHierarchyData) {
|
|
if (classHierarchyMap[info.classNode] != null) {
|
|
return new Result<TestData>(
|
|
data, ClassHierarchyError, "Two entries for ${info.classNode}");
|
|
}
|
|
classHierarchyMap[info.classNode] = info;
|
|
}
|
|
|
|
Component component = compilerResult.component;
|
|
StringBuffer sb = new StringBuffer();
|
|
for (Library library in component.libraries) {
|
|
if (library.importUri.isScheme("dart")) continue;
|
|
sb.writeln("Library ${library.importUri}");
|
|
for (Class c in library.classes) {
|
|
sb.writeln(" - Class ${c.name}");
|
|
|
|
Set<Class> checkedSupertypes = <Class>{};
|
|
Result<TestData>? checkSupertype(Supertype? supertype) {
|
|
if (supertype == null) return null;
|
|
Class superclass = supertype.classNode;
|
|
if (checkedSupertypes.add(superclass)) {
|
|
Supertype? asSuperClass =
|
|
classHierarchy.getClassAsInstanceOf(c, superclass);
|
|
if (asSuperClass == null) {
|
|
return new Result<TestData>(data, ClassHierarchyError,
|
|
"${superclass} not found as a superclass of $c");
|
|
}
|
|
Result<TestData>? result = checkSupertype(superclass.supertype);
|
|
if (result != null) return result;
|
|
result = checkSupertype(superclass.mixedInType);
|
|
if (result != null) return result;
|
|
for (Supertype interface in superclass.implementedTypes) {
|
|
result = checkSupertype(interface);
|
|
if (result != null) return result;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
Result<TestData>? result = checkSupertype(c.asThisSupertype);
|
|
if (result != null) return result;
|
|
|
|
ForTestingClassInfo? info = classHierarchyMap[c];
|
|
if (info == null) {
|
|
return new Result<TestData>(data, ClassHierarchyError,
|
|
"Didn't find any class hierarchy info for $c");
|
|
}
|
|
|
|
if (info.lazyDeclaredGettersAndCalls != null) {
|
|
sb.writeln(" - lazyDeclaredGettersAndCalls:");
|
|
for (Member member in info.lazyDeclaredGettersAndCalls!) {
|
|
sb.writeln(" - ${member.name.text}");
|
|
}
|
|
|
|
// Expect these to be the same as in the class.
|
|
Set<Member> members = info.lazyDeclaredGettersAndCalls!.toSet();
|
|
for (Field f in c.fields) {
|
|
if (f.isStatic) continue;
|
|
if (!members.remove(f)) {
|
|
return new Result<TestData>(
|
|
data,
|
|
ClassHierarchyError,
|
|
"Didn't find ${f.name.text} in lazyDeclaredGettersAndCalls "
|
|
"for ${c.name} in ${library.importUri}");
|
|
}
|
|
}
|
|
for (Procedure p in c.procedures) {
|
|
if (p.isStatic) continue;
|
|
if (p.isSetter) continue;
|
|
if (!members.remove(p)) {
|
|
return new Result<TestData>(
|
|
data,
|
|
ClassHierarchyError,
|
|
"Didn't find ${p.name.text} in lazyDeclaredGettersAndCalls "
|
|
"for ${c.name} in ${library.importUri}");
|
|
}
|
|
}
|
|
if (members.isNotEmpty) {
|
|
return new Result<TestData>(
|
|
data,
|
|
ClassHierarchyError,
|
|
"Still have ${members.map((m) => m.name.text)} left "
|
|
"for ${c.name} in ${library.importUri}");
|
|
}
|
|
}
|
|
if (info.lazyDeclaredSetters != null) {
|
|
sb.writeln(" - lazyDeclaredSetters:");
|
|
for (Member member in info.lazyDeclaredSetters!) {
|
|
sb.writeln(" - ${member.name.text}");
|
|
}
|
|
|
|
// Expect these to be the same as in the class.
|
|
Set<Member> members = info.lazyDeclaredSetters!.toSet();
|
|
for (Field f in c.fields) {
|
|
if (f.isStatic) continue;
|
|
if (!f.hasSetter) continue;
|
|
if (!members.remove(f)) {
|
|
return new Result<TestData>(data, ClassHierarchyError,
|
|
"Didn't find $f in lazyDeclaredSetters for $c");
|
|
}
|
|
}
|
|
for (Procedure p in c.procedures) {
|
|
if (p.isStatic) continue;
|
|
if (!p.isSetter) continue;
|
|
if (!members.remove(p)) {
|
|
return new Result<TestData>(data, ClassHierarchyError,
|
|
"Didn't find $p in lazyDeclaredSetters for $c");
|
|
}
|
|
}
|
|
if (members.isNotEmpty) {
|
|
return new Result<TestData>(
|
|
data,
|
|
ClassHierarchyError,
|
|
"Still have ${members.map((m) => m.name.text)} left "
|
|
"for ${c.name} in ${library.importUri}");
|
|
}
|
|
}
|
|
if (info.lazyImplementedGettersAndCalls != null) {
|
|
sb.writeln(" - lazyImplementedGettersAndCalls:");
|
|
for (Member member in info.lazyImplementedGettersAndCalls!) {
|
|
sb.writeln(" - ${member.name.text}");
|
|
}
|
|
}
|
|
if (info.lazyImplementedSetters != null) {
|
|
sb.writeln(" - lazyImplementedSetters:");
|
|
for (Member member in info.lazyImplementedSetters!) {
|
|
sb.writeln(" - ${member.name.text}");
|
|
}
|
|
}
|
|
if (info.lazyInterfaceGettersAndCalls != null) {
|
|
sb.writeln(" - lazyInterfaceGettersAndCalls:");
|
|
for (Member member in info.lazyInterfaceGettersAndCalls!) {
|
|
sb.writeln(" - ${member.name.text}");
|
|
}
|
|
}
|
|
if (info.lazyInterfaceSetters != null) {
|
|
sb.writeln(" - lazyInterfaceSetters:");
|
|
for (Member member in info.lazyInterfaceSetters!) {
|
|
sb.writeln(" - ${member.name.text}");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (checkExpectFile) {
|
|
String actualClassHierarchy = sb.toString();
|
|
Uri uri = data.loadedFrom.resolve(data.loadedFrom.pathSegments.last +
|
|
".world.$worldNum.class_hierarchy.expect");
|
|
String? expected;
|
|
File file = new File.fromUri(uri);
|
|
if (file.existsSync()) {
|
|
expected = file.readAsStringSync();
|
|
}
|
|
if (expected != actualClassHierarchy) {
|
|
if (context.updateExpectations) {
|
|
file.writeAsStringSync(actualClassHierarchy);
|
|
} else {
|
|
String extra = "";
|
|
if (expected == null) extra = "Expect file did not exist.\n";
|
|
return new Result<TestData>(
|
|
data,
|
|
ClassHierarchyError,
|
|
"${extra}Unexpected serialized representation. "
|
|
"Fix or update $uri to contain the below:\n\n"
|
|
"$actualClassHierarchy");
|
|
}
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
void checkErrorsAndWarnings(
|
|
Set<String> prevFormattedErrors,
|
|
Set<String> formattedErrors,
|
|
Set<String> prevFormattedWarnings,
|
|
Set<String> formattedWarnings) {
|
|
if (prevFormattedErrors.length != formattedErrors.length) {
|
|
Expect.fail("Previously had ${prevFormattedErrors.length} errors, "
|
|
"now had ${formattedErrors.length}.\n\n"
|
|
"Before:\n"
|
|
"${prevFormattedErrors.join("\n")}"
|
|
"\n\n"
|
|
"Now:\n"
|
|
"${formattedErrors.join("\n")}");
|
|
}
|
|
if ((prevFormattedErrors.toSet()..removeAll(formattedErrors)).isNotEmpty) {
|
|
Expect.fail("Previously got error messages $prevFormattedErrors, "
|
|
"now had ${formattedErrors}.");
|
|
}
|
|
if (prevFormattedWarnings.length != formattedWarnings.length) {
|
|
Expect.fail("Previously had ${prevFormattedWarnings.length} errors, "
|
|
"now had ${formattedWarnings.length}.");
|
|
}
|
|
if ((prevFormattedWarnings.toSet()..removeAll(formattedWarnings))
|
|
.isNotEmpty) {
|
|
Expect.fail("Previously got error messages $prevFormattedWarnings, "
|
|
"now had ${formattedWarnings}.");
|
|
}
|
|
}
|
|
|
|
Result<List<int>?> checkIncrementalSerialization(
|
|
bool? incrementalSerialization,
|
|
Component component,
|
|
IncrementalSerializer? incrementalSerializer,
|
|
World world) {
|
|
if (incrementalSerialization == true) {
|
|
Component c = new Component(nameRoot: component.root)
|
|
..setMainMethodAndMode(null, false, component.mode);
|
|
c.libraries.addAll(component.libraries);
|
|
c.uriToSource.addAll(component.uriToSource);
|
|
Map<String, Set<String>> originalContent = buildMapOfContent(c);
|
|
ByteSink sink = new ByteSink();
|
|
int librariesBefore = c.libraries.length;
|
|
incrementalSerializer!.writePackagesToSinkAndTrimComponent(c, sink);
|
|
int librariesAfter = c.libraries.length;
|
|
if (librariesAfter > librariesBefore) {
|
|
return new Result<List<int>>(null, IncrementalSerializationError,
|
|
"Incremental serialization added libraries!");
|
|
}
|
|
if (librariesBefore == librariesAfter &&
|
|
world.incrementalSerializationDoesWork) {
|
|
return new Result<List<int>>(null, IncrementalSerializationError,
|
|
"Incremental serialization didn't remove any libraries!");
|
|
}
|
|
if (librariesAfter < librariesBefore && sink.builder.isEmpty) {
|
|
return new Result<List<int>>(
|
|
null,
|
|
IncrementalSerializationError,
|
|
"Incremental serialization didn't output any bytes, "
|
|
"but did remove libraries");
|
|
} else if (librariesAfter == librariesBefore && !sink.builder.isEmpty) {
|
|
return new Result<List<int>>(
|
|
null,
|
|
IncrementalSerializationError,
|
|
"Incremental serialization did output bytes, "
|
|
"but didn't remove libraries");
|
|
}
|
|
if (librariesAfter < librariesBefore) {
|
|
// If we actually did incrementally serialize anything, check the output!
|
|
BinaryPrinter printer = new BinaryPrinter(sink);
|
|
printer.writeComponentFile(c);
|
|
List<int> bytes = sink.builder.takeBytes();
|
|
|
|
// Load the bytes back in.
|
|
Component loadedComponent = new Component();
|
|
new BinaryBuilder(bytes, filename: null).readComponent(loadedComponent);
|
|
|
|
// Check that it doesn't contain anything we said it shouldn't.
|
|
if (world.serializationShouldNotInclude != null) {
|
|
Set<Uri> includedImportUris =
|
|
loadedComponent.libraries.map((l) => l.importUri).toSet();
|
|
for (String uriString in world.serializationShouldNotInclude!) {
|
|
Uri uri = Uri.parse(uriString);
|
|
if (includedImportUris.contains(uri)) {
|
|
return new Result<List<int>>(
|
|
null,
|
|
IncrementalSerializationError,
|
|
"Incremental serialization shouldn't include "
|
|
"$uriString but did.");
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check that it contains at least what we want.
|
|
Map<String, Set<String>> afterContent =
|
|
buildMapOfContent(loadedComponent);
|
|
// Remove any keys in afterContent not in the original as the written
|
|
// one is allowed to contain *more*.
|
|
Set<String> newKeys = afterContent.keys.toSet()
|
|
..removeAll(originalContent.keys);
|
|
for (String key in newKeys) {
|
|
afterContent.remove(key);
|
|
}
|
|
Result? result = checkExpectedContentData(afterContent, originalContent);
|
|
if (result != null) return result.copyWithOutput<List<int>?>(null);
|
|
|
|
// Check that the result is self-contained.
|
|
result = checkSelfContained(loadedComponent);
|
|
if (result != null) return result.copyWithOutput<List<int>?>(null);
|
|
|
|
return new Result<List<int>>.pass(bytes);
|
|
}
|
|
}
|
|
return new Result<List<int>?>.pass(null);
|
|
}
|
|
|
|
Result? checkSelfContained(Component component) {
|
|
Set<Library> got = new Set<Library>.from(component.libraries);
|
|
for (Library lib in component.libraries) {
|
|
for (LibraryDependency dependency in lib.dependencies) {
|
|
if (dependency.importedLibraryReference.node == null ||
|
|
!got.contains(dependency.targetLibrary)) {
|
|
if (dependency.importedLibraryReference.canonicalName
|
|
.toString()
|
|
.startsWith("root::dart:")) {
|
|
continue;
|
|
}
|
|
return Result(
|
|
null,
|
|
IncrementalSerializationError,
|
|
"Component didn't contain ${dependency.importedLibraryReference} "
|
|
"and it should have.");
|
|
}
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
void computeAllReachableLibrariesFor(Library lib, Set<Library> allLibraries) {
|
|
Set<Library> libraries = new Set<Library>();
|
|
List<Library> workList = <Library>[];
|
|
allLibraries.add(lib);
|
|
libraries.add(lib);
|
|
workList.add(lib);
|
|
while (workList.isNotEmpty) {
|
|
Library library = workList.removeLast();
|
|
for (LibraryDependency dependency in library.dependencies) {
|
|
if (dependency.targetLibrary.importUri.isScheme("dart")) continue;
|
|
if (libraries.add(dependency.targetLibrary)) {
|
|
workList.add(dependency.targetLibrary);
|
|
allLibraries.add(dependency.targetLibrary);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Result? checkExpectedContent(World world, Component component) {
|
|
if (world.expectedContent != null) {
|
|
Map<String, Set<String>> actualContent = buildMapOfContent(component);
|
|
return checkExpectedContentData(actualContent, world.expectedContent!);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
Result? checkExpectedContentData(Map<String, Set<String>> actualContent,
|
|
Map<String, Iterable<String>> expectedContent) {
|
|
Result<TestData> createFailureResult() {
|
|
return new Result(
|
|
null,
|
|
ContentDataMismatch,
|
|
"Expected and actual content not the same.\n"
|
|
"Expected $expectedContent.\n"
|
|
"Got $actualContent");
|
|
}
|
|
|
|
if (actualContent.length != expectedContent.length) {
|
|
return createFailureResult();
|
|
}
|
|
Set<String> missingKeys = actualContent.keys.toSet()
|
|
..removeAll(expectedContent.keys);
|
|
if (missingKeys.isNotEmpty) {
|
|
return createFailureResult();
|
|
}
|
|
for (MapEntry<String, Iterable<String>> entry in expectedContent.entries) {
|
|
Set<String> expected = new Set<String>.from(entry.value);
|
|
Set<String> actual = actualContent[entry.key]!.toSet();
|
|
if (expected.length != actual.length) {
|
|
return createFailureResult();
|
|
}
|
|
actual.removeAll(expected);
|
|
if (actual.isNotEmpty) {
|
|
return createFailureResult();
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
Map<String, Set<String>> buildMapOfContent(Component component) {
|
|
Map<String, Set<String>> actualContent = new Map<String, Set<String>>();
|
|
for (Library lib in component.libraries) {
|
|
Set<String> libContent =
|
|
actualContent[lib.importUri.toString()] = new Set<String>();
|
|
for (Class c in lib.classes) {
|
|
libContent.add("Class ${c.name}");
|
|
}
|
|
for (Procedure p in lib.procedures) {
|
|
libContent.add("Procedure ${p.name.text}");
|
|
}
|
|
for (Field f in lib.fields) {
|
|
libContent.add("Field ${f.name.text}");
|
|
}
|
|
}
|
|
return actualContent;
|
|
}
|
|
|
|
Result<TestData>? checkNeededDillLibraries(
|
|
World world, TestData data, Set<Library>? neededDillLibraries, Uri base) {
|
|
if (world.neededDillLibraries != null) {
|
|
List<Uri> actualContent = <Uri>[];
|
|
for (Library lib in neededDillLibraries!) {
|
|
if (lib.importUri.isScheme("dart")) continue;
|
|
actualContent.add(lib.importUri);
|
|
}
|
|
|
|
List<Uri> expectedContent = <Uri>[];
|
|
for (String entry in world.neededDillLibraries!) {
|
|
expectedContent.add(base.resolve(entry));
|
|
}
|
|
|
|
Result<TestData> createFailureResult() {
|
|
return new Result<TestData>(
|
|
data,
|
|
NeededDillMismatch,
|
|
"Expected and actual content not the same.\n"
|
|
"Expected $expectedContent.\n"
|
|
"Got $actualContent");
|
|
}
|
|
|
|
if (actualContent.length != expectedContent.length) {
|
|
return createFailureResult();
|
|
}
|
|
Set<Uri> notInExpected =
|
|
actualContent.toSet().difference(expectedContent.toSet());
|
|
Set<Uri> notInActual =
|
|
expectedContent.toSet().difference(actualContent.toSet());
|
|
if (notInExpected.isNotEmpty) {
|
|
return createFailureResult();
|
|
}
|
|
if (notInActual.isNotEmpty) {
|
|
return createFailureResult();
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
String nodeToString(TreeNode node) {
|
|
StringBuffer buffer = new StringBuffer();
|
|
new Printer(buffer, syntheticNames: new NameSystem()).writeNode(node);
|
|
return '$buffer';
|
|
}
|
|
|
|
String componentToStringSdkFiltered(Component component,
|
|
{required final Set<String>? printErrors}) {
|
|
Component c = new Component();
|
|
List<Uri> dartUris = <Uri>[];
|
|
for (Library lib in component.libraries) {
|
|
if (lib.importUri.isScheme("dart")) {
|
|
dartUris.add(lib.importUri);
|
|
} else {
|
|
if (lib.fileUri.isScheme("holePunch")) {
|
|
// Skip this.
|
|
continue;
|
|
}
|
|
c.libraries.add(lib);
|
|
}
|
|
}
|
|
c.setMainMethodAndMode(component.mainMethodName, true, component.mode);
|
|
c.problemsAsJson = component.problemsAsJson;
|
|
|
|
StringBuffer s = new StringBuffer();
|
|
s.write(componentToString(c));
|
|
|
|
addConstantCoverageToExpectation(component, s,
|
|
skipImportUri: (Uri? importUri) => importUri?.scheme == "dart");
|
|
|
|
if (dartUris.isNotEmpty) {
|
|
s.writeln("");
|
|
s.writeln("And ${dartUris.length} platform libraries:");
|
|
for (Uri uri in dartUris) {
|
|
s.writeln(" - $uri");
|
|
}
|
|
}
|
|
|
|
if (printErrors != null && printErrors.isNotEmpty) {
|
|
s.writeln("");
|
|
s.writeln("A total of ${printErrors.length} errors:");
|
|
for (String error in printErrors) {
|
|
// Make the error more readable if it's a json.
|
|
Map<String, dynamic>? decoded;
|
|
try {
|
|
decoded = jsonDecode(error);
|
|
} catch (_) {}
|
|
if (decoded != null && decoded["plainTextFormatted"] is List) {
|
|
List plainTextFormatted = decoded["plainTextFormatted"] as List;
|
|
if (plainTextFormatted.isNotEmpty &&
|
|
plainTextFormatted.first is String) {
|
|
error = plainTextFormatted.first;
|
|
}
|
|
}
|
|
s.writeln(" - $error");
|
|
}
|
|
}
|
|
|
|
return s.toString();
|
|
}
|
|
|
|
int countNonSyntheticLibraries(Component c) {
|
|
int result = 0;
|
|
for (Library lib in c.libraries) {
|
|
if (!lib.isSynthetic) result++;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
int countNonSyntheticPlatformLibraries(Component c) {
|
|
int result = 0;
|
|
for (Library lib in c.libraries) {
|
|
if (!lib.isSynthetic && lib.importUri.isScheme("dart")) result++;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
int countSyntheticLibraries(Component c) {
|
|
int result = 0;
|
|
for (Library lib in c.libraries) {
|
|
if (lib.isSynthetic) result++;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
Result<TestData>? performErrorAndWarningCheck(
|
|
World world,
|
|
TestData data,
|
|
bool gotError,
|
|
Set<String> formattedErrors,
|
|
bool gotWarning,
|
|
Set<String> formattedWarnings) {
|
|
if (world.errors && !gotError) {
|
|
return new Result<TestData>(
|
|
data, MissingErrors, "Expected error, but didn't get any.");
|
|
} else if (!world.errors && gotError) {
|
|
return new Result<TestData>(
|
|
data, UnexpectedErrors, "Got unexpected error(s): $formattedErrors.");
|
|
}
|
|
if (world.warnings && !gotWarning) {
|
|
return new Result<TestData>(
|
|
data, MissingWarnings, "Expected warning, but didn't get any.");
|
|
} else if (!world.warnings && gotWarning) {
|
|
return new Result<TestData>(data, UnexpectedWarnings,
|
|
"Got unexpected warnings(s): $formattedWarnings.");
|
|
}
|
|
return null;
|
|
}
|
|
|
|
void checkIsEqual(List<int> a, List<int> b) {
|
|
int length = a.length;
|
|
if (b.length < length) {
|
|
length = b.length;
|
|
}
|
|
for (int i = 0; i < length; ++i) {
|
|
if (a[i] != b[i]) {
|
|
print("Data differs at byte ${i + 1}.");
|
|
|
|
StringBuffer message = new StringBuffer();
|
|
message.writeln("Data differs at byte ${i + 1}.");
|
|
message.writeln("");
|
|
message.writeln("Will try to find more useful information:");
|
|
|
|
final String repoDir = computeRepoDir();
|
|
File binaryMd = new File("$repoDir/pkg/kernel/binary.md");
|
|
String binaryMdContent = binaryMd.readAsStringSync();
|
|
|
|
DillComparer dillComparer = new DillComparer();
|
|
if (dillComparer.compare(a, b, binaryMdContent, message)) {
|
|
message.writeln(
|
|
"Somehow the two different byte-lists compared to the same.");
|
|
}
|
|
|
|
Expect.fail(message.toString());
|
|
}
|
|
}
|
|
Expect.equals(a.length, b.length);
|
|
}
|
|
|
|
CompilerOptions getOptions({Target? target, String? sdkSummary}) {
|
|
target ??= new VmTarget(new TargetFlags());
|
|
sdkSummary ??= 'vm_platform_strong.dill';
|
|
final Uri sdkRoot = computePlatformBinariesLocation(forceBuildDir: true);
|
|
CompilerOptions options = new CompilerOptions()
|
|
..target = target
|
|
..librariesSpecificationUri = Uri.base.resolve("sdk/lib/libraries.json")
|
|
..omitPlatform = true
|
|
..onDiagnostic = (DiagnosticMessage message) {
|
|
if (message.severity == Severity.error ||
|
|
message.severity == Severity.warning) {
|
|
Expect.fail(
|
|
"Unexpected error: ${message.plainTextFormatted.join('\n')}");
|
|
}
|
|
}
|
|
..sdkSummary = sdkRoot.resolve(sdkSummary)
|
|
..environmentDefines = const {};
|
|
return options;
|
|
}
|
|
|
|
Future<bool> normalCompile(Uri input, Uri output,
|
|
{CompilerOptions? options}) async {
|
|
options ??= getOptions();
|
|
TestIncrementalCompiler compiler =
|
|
new TestIncrementalCompiler(options, input);
|
|
List<int> bytes =
|
|
await normalCompileToBytes(input, options: options, compiler: compiler);
|
|
new File.fromUri(output).writeAsBytesSync(bytes);
|
|
return compiler.initializedFromDillForTesting;
|
|
}
|
|
|
|
Future<List<int>> normalCompileToBytes(Uri input,
|
|
{CompilerOptions? options, IncrementalCompiler? compiler}) async {
|
|
Component component = await normalCompileToComponent(input,
|
|
options: options, compiler: compiler);
|
|
return util.postProcess(component);
|
|
}
|
|
|
|
Future<Component> normalCompileToComponent(Uri input,
|
|
{CompilerOptions? options, IncrementalCompiler? compiler}) async {
|
|
Component component =
|
|
await normalCompilePlain(input, options: options, compiler: compiler);
|
|
util.throwOnEmptyMixinBodies(component);
|
|
await util.throwOnInsufficientUriToSource(component);
|
|
return component;
|
|
}
|
|
|
|
Future<Component> normalCompilePlain(Uri input,
|
|
{CompilerOptions? options, IncrementalCompiler? compiler}) async {
|
|
options ??= getOptions();
|
|
compiler ??= new TestIncrementalCompiler(options, input);
|
|
return (await compiler.computeDelta()).component;
|
|
}
|
|
|
|
Future<bool> initializedCompile(
|
|
Uri input, Uri output, Uri initializeWith, List<Uri> invalidateUris,
|
|
{CompilerOptions? options}) async {
|
|
options ??= getOptions();
|
|
TestIncrementalCompiler compiler =
|
|
new TestIncrementalCompiler(options, input, initializeWith);
|
|
for (Uri invalidateUri in invalidateUris) {
|
|
compiler.invalidate(invalidateUri);
|
|
}
|
|
IncrementalCompilerResult initializedCompilerResult =
|
|
await compiler.computeDelta();
|
|
Component initializedComponent = initializedCompilerResult.component;
|
|
util.throwOnEmptyMixinBodies(initializedComponent);
|
|
await util.throwOnInsufficientUriToSource(initializedComponent);
|
|
bool result = compiler.initializedFromDillForTesting;
|
|
new File.fromUri(output)
|
|
.writeAsBytesSync(util.postProcess(initializedComponent));
|
|
int actuallyInvalidatedCount = compiler
|
|
.getFilteredInvalidatedImportUrisForTesting(invalidateUris)
|
|
?.length ??
|
|
0;
|
|
if (result && actuallyInvalidatedCount < invalidateUris.length) {
|
|
Expect.fail("Expected at least ${invalidateUris.length} invalidated uris, "
|
|
"got $actuallyInvalidatedCount");
|
|
}
|
|
|
|
IncrementalCompilerResult initializedFullCompilerResult =
|
|
await compiler.computeDelta(fullComponent: true);
|
|
Component initializedFullComponent = initializedFullCompilerResult.component;
|
|
util.throwOnEmptyMixinBodies(initializedFullComponent);
|
|
await util.throwOnInsufficientUriToSource(initializedFullComponent);
|
|
Expect.equals(initializedComponent.libraries.length,
|
|
initializedFullComponent.libraries.length);
|
|
Expect.equals(initializedComponent.uriToSource.length,
|
|
initializedFullComponent.uriToSource.length);
|
|
|
|
for (Uri invalidateUri in invalidateUris) {
|
|
compiler.invalidate(invalidateUri);
|
|
}
|
|
|
|
IncrementalCompilerResult partialResult = await compiler.computeDelta();
|
|
Component partialComponent = partialResult.component;
|
|
util.throwOnEmptyMixinBodies(partialComponent);
|
|
await util.throwOnInsufficientUriToSource(partialComponent);
|
|
actuallyInvalidatedCount = (compiler
|
|
.getFilteredInvalidatedImportUrisForTesting(invalidateUris)
|
|
?.length ??
|
|
0);
|
|
if (actuallyInvalidatedCount < invalidateUris.length) {
|
|
Expect.fail("Expected at least ${invalidateUris.length} invalidated uris, "
|
|
"got $actuallyInvalidatedCount");
|
|
}
|
|
|
|
IncrementalCompilerResult emptyResult = await compiler.computeDelta();
|
|
Component emptyComponent = emptyResult.component;
|
|
util.throwOnEmptyMixinBodies(emptyComponent);
|
|
await util.throwOnInsufficientUriToSource(emptyComponent);
|
|
|
|
List<Uri> fullLibUris =
|
|
initializedComponent.libraries.map((lib) => lib.importUri).toList();
|
|
List<Uri> partialLibUris =
|
|
partialComponent.libraries.map((lib) => lib.importUri).toList();
|
|
List<Uri> emptyLibUris =
|
|
emptyComponent.libraries.map((lib) => lib.importUri).toList();
|
|
|
|
Expect.isTrue(fullLibUris.length > partialLibUris.length ||
|
|
partialLibUris.length == invalidateUris.length);
|
|
Expect.isTrue(partialLibUris.isNotEmpty || invalidateUris.isEmpty);
|
|
|
|
Expect.isTrue(emptyLibUris.isEmpty);
|
|
|
|
return result;
|
|
}
|
|
|
|
class TestIncrementalCompiler extends IncrementalCompiler {
|
|
@override
|
|
final TestRecorderForTesting recorderForTesting =
|
|
new TestRecorderForTesting();
|
|
final Uri entryPoint;
|
|
|
|
/// Filter out the automatically added entryPoint, unless it's explicitly
|
|
/// specified as being invalidated.
|
|
/// Also filter out uris with "nonexisting.dart" in the name as synthetic
|
|
/// libraries are invalidated automatically too.
|
|
/// This is not perfect, but works for what it's currently used for.
|
|
Set<Uri>? getFilteredInvalidatedImportUrisForTesting(
|
|
List<Uri> invalidatedUris) {
|
|
if (recorderForTesting.invalidatedImportUrisForTesting == null) return null;
|
|
|
|
Set<String> invalidatedFilenames =
|
|
invalidatedUris.map((uri) => uri.pathSegments.last).toSet();
|
|
Set<Uri> result = new Set<Uri>();
|
|
for (Uri uri in recorderForTesting.invalidatedImportUrisForTesting!) {
|
|
if (uri.pathSegments.isNotEmpty &&
|
|
uri.pathSegments.last == "nonexisting.dart") {
|
|
continue;
|
|
}
|
|
if (invalidatedFilenames.contains(entryPoint.pathSegments.last) ||
|
|
invalidatedFilenames.contains(uri.pathSegments.last)) {
|
|
result.add(uri);
|
|
}
|
|
}
|
|
|
|
return result.isEmpty ? null : result;
|
|
}
|
|
|
|
TestIncrementalCompiler(CompilerOptions options, this.entryPoint,
|
|
[Uri? initializeFrom,
|
|
bool? outlineOnly,
|
|
IncrementalSerializer? incrementalSerializer])
|
|
: super(
|
|
new CompilerContext(
|
|
new ProcessedOptions(options: options, inputs: [entryPoint])),
|
|
initializeFrom,
|
|
outlineOnly,
|
|
incrementalSerializer);
|
|
|
|
TestIncrementalCompiler.fromComponent(CompilerOptions options,
|
|
this.entryPoint, Component? componentToInitializeFrom,
|
|
[bool? outlineOnly, IncrementalSerializer? incrementalSerializer])
|
|
: super.fromComponent(
|
|
new CompilerContext(
|
|
new ProcessedOptions(options: options, inputs: [entryPoint])),
|
|
componentToInitializeFrom,
|
|
outlineOnly,
|
|
incrementalSerializer);
|
|
|
|
@override
|
|
Future<IncrementalCompilerResult> computeDelta(
|
|
{List<Uri>? entryPoints,
|
|
bool fullComponent = false,
|
|
bool trackNeededDillLibraries = false,
|
|
bool? simulateTransformer}) async {
|
|
IncrementalCompilerResult result = await super.computeDelta(
|
|
entryPoints: entryPoints,
|
|
fullComponent: fullComponent,
|
|
trackNeededDillLibraries: trackNeededDillLibraries);
|
|
|
|
// We should at least have the SDK builders available. Slight smoke test.
|
|
if (!dillTargetForTesting!.loader.libraryImportUris
|
|
.map((uri) => uri.toString())
|
|
.contains("dart:core")) {
|
|
throw "Loaders builder should contain the sdk, "
|
|
"but didn't even contain dart:core.";
|
|
}
|
|
|
|
if (simulateTransformer == true) {
|
|
doSimulateTransformer(result.component);
|
|
}
|
|
return result;
|
|
}
|
|
}
|
|
|
|
class TestRecorderForTesting extends RecorderForTesting {
|
|
Set<Uri>? invalidatedImportUrisForTesting;
|
|
AdvancedInvalidationResult? advancedInvalidationResult;
|
|
int? rebuildBodiesCount;
|
|
|
|
@override
|
|
void recordInvalidatedImportUris(List<Uri> uris) {
|
|
invalidatedImportUrisForTesting = uris.isEmpty ? null : uris.toSet();
|
|
}
|
|
|
|
@override
|
|
void recordNonFullComponent(Component component) {
|
|
// It should at least contain the sdk. Slight smoke test.
|
|
if (!component.libraries
|
|
.map((lib) => lib.importUri.toString())
|
|
.contains("dart:core")) {
|
|
throw "Loaders builder should contain the sdk, "
|
|
"but didn't even contain dart:core.";
|
|
}
|
|
}
|
|
|
|
@override
|
|
void recordRebuildBodiesCount(int count) {
|
|
rebuildBodiesCount = count;
|
|
assert(
|
|
count == 0 ||
|
|
advancedInvalidationResult == AdvancedInvalidationResult.bodiesOnly,
|
|
"Unexpected successful advanced invalidation: "
|
|
"${advancedInvalidationResult}");
|
|
}
|
|
|
|
@override
|
|
void recordAdvancedInvalidationResult(AdvancedInvalidationResult result) {
|
|
advancedInvalidationResult = result;
|
|
}
|
|
|
|
@override
|
|
void recordTemporaryFile(Uri uri) {
|
|
File f = new File.fromUri(uri);
|
|
if (f.existsSync()) f.deleteSync();
|
|
}
|
|
}
|
|
|
|
void doSimulateTransformer(Component c) {
|
|
for (Library lib in c.libraries) {
|
|
if (lib.fields
|
|
.where((f) => f.name.text == "unique_SimulateTransformer")
|
|
.toList()
|
|
.isNotEmpty) {
|
|
continue;
|
|
}
|
|
Name fieldName = new Name("unique_SimulateTransformer");
|
|
Field field = new Field.immutable(fieldName,
|
|
isFinal: true,
|
|
fieldReference: lib.reference.canonicalName
|
|
?.getChildFromFieldWithName(fieldName)
|
|
.reference,
|
|
getterReference: lib.reference.canonicalName
|
|
?.getChildFromFieldGetterWithName(fieldName)
|
|
.reference,
|
|
fileUri: lib.fileUri);
|
|
lib.addField(field);
|
|
for (Class c in lib.classes) {
|
|
if (c.fields
|
|
.where((f) => f.name.text == "unique_SimulateTransformer")
|
|
.toList()
|
|
.isNotEmpty) {
|
|
continue;
|
|
}
|
|
fieldName = new Name("unique_SimulateTransformer");
|
|
field = new Field.immutable(fieldName,
|
|
isFinal: true,
|
|
fieldReference: lib.reference.canonicalName
|
|
?.getChildFromFieldWithName(fieldName)
|
|
.reference,
|
|
getterReference: c.reference.canonicalName
|
|
?.getChildFromFieldGetterWithName(fieldName)
|
|
.reference,
|
|
fileUri: c.fileUri);
|
|
c.addField(field);
|
|
}
|
|
}
|
|
}
|
|
|
|
class TestMemoryFileSystem extends MemoryFileSystem {
|
|
Uri holePunchBase;
|
|
|
|
TestMemoryFileSystem(Uri currentDirectory, {required this.holePunchBase})
|
|
: super(currentDirectory);
|
|
|
|
@override
|
|
MemoryFileSystemEntity entityForUri(Uri uri) {
|
|
if (uri.isScheme("holePunch")) {
|
|
// "Copy" the file into the memory file system.
|
|
Uri holePunchResolved = holePunchBase.resolve(uri.path);
|
|
File f = new File.fromUri(holePunchResolved);
|
|
MemoryFileSystemEntity entity = super.entityForUri(uri);
|
|
if (f.existsSync()) {
|
|
entity.writeAsBytesSync(f.readAsBytesSync());
|
|
}
|
|
return entity;
|
|
}
|
|
// Try to "sanitize" the uri as a real file system does, namely
|
|
// "a/b.dart" and "a//b.dart" returns the same file.
|
|
if (uri.pathSegments.contains("")) {
|
|
Uri newUri = uri.replace(
|
|
pathSegments: uri.pathSegments.where((element) => element != ""));
|
|
return super.entityForUri(newUri);
|
|
}
|
|
return super.entityForUri(uri);
|
|
}
|
|
}
|