Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(732)

Unified Diff: pkg/front_end/test/fasta/shaker_test.dart

Issue 2894283002: Reapply CL: first step for modular support in fasta (Closed)
Patch Set: new changes since first CL Created 3 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: pkg/front_end/test/fasta/shaker_test.dart
diff --git a/pkg/front_end/test/fasta/shaker_test.dart b/pkg/front_end/test/fasta/shaker_test.dart
new file mode 100644
index 0000000000000000000000000000000000000000..85c29ad6b3f830ef6d9651dea1d752b9736768de
--- /dev/null
+++ b/pkg/front_end/test/fasta/shaker_test.dart
@@ -0,0 +1,241 @@
+// Copyright (c) 2017, 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.md file.
+
+/// Tests basic functionality of the API tree-shaker.
+///
+/// Each input file is built and tree-shaken, then we check that the set of
+/// libraries, classes, and members that are retained match those declared in an
+/// expectations file.
+///
+/// Input files may contain markers to turn on flags that configure this
+/// runner. Currently only the following marker is recognized:
+/// @@SHOW_CORE_LIBRARIES@@ - whether to check for retained information from
+/// the core libraries. By default this runner only checks for members of
+/// pkg/front_end/testcases/shaker/lib/lib.dart.
+library fasta.test.shaker_test;
+
+import 'dart:async' show Future;
+import 'dart:convert' show JSON;
+import 'dart:io' show File;
+
+export 'package:testing/testing.dart' show Chain, runMe;
+import 'package:front_end/physical_file_system.dart';
+import 'package:front_end/src/fasta/dill/dill_target.dart' show DillTarget;
+import 'package:front_end/src/fasta/errors.dart' show InputError;
+import 'package:front_end/src/fasta/kernel/kernel_target.dart'
+ show KernelTarget;
+import 'package:front_end/src/fasta/kernel/verifier.dart' show verifyProgram;
+import 'package:front_end/src/fasta/testing/kernel_chain.dart' show runDiff;
+import 'package:front_end/src/fasta/testing/patched_sdk_location.dart';
+import 'package:front_end/src/fasta/ticker.dart' show Ticker;
+import 'package:front_end/src/fasta/translate_uri.dart' show TranslateUri;
+import 'package:front_end/src/fasta/util/relativize.dart' show relativizeUri;
+import 'package:kernel/ast.dart' show Program;
+import 'package:kernel/kernel.dart' show loadProgramFromBytes;
+import 'package:testing/testing.dart'
+ show Chain, ChainContext, ExpectationSet, Result, Step, TestDescription;
+import 'testing/suite.dart';
+
+main(List<String> arguments) => runMe(arguments, createContext, "testing.json");
+
+Future<TreeShakerContext> createContext(
+ Chain suite, Map<String, String> environment) {
+ return TreeShakerContext.create(environment);
+}
+
+/// Context used to run the tree-shaking test suite.
+class TreeShakerContext extends ChainContext {
ahe 2017/05/22 11:49:04 This reminds me of code that I've seen before. Is
Siggi Cherem (dart-lang) 2017/05/22 23:15:48 It was inspired by FastaContext under pkg/front_en
+ final TranslateUri uriTranslator;
+ final Uri outlineUri;
+ final List<Step> steps;
+ final List<int> outlineBytes;
+
+ final ExpectationSet expectationSet =
+ new ExpectationSet.fromJsonList(JSON.decode(EXPECTATIONS));
+
+ TreeShakerContext(this.outlineUri, this.uriTranslator, this.outlineBytes,
+ bool updateExpectations)
+ : steps = <Step>[
+ const BuildProgram(),
+ new CheckShaker(updateExpectations: updateExpectations),
+ ];
+
+ Program loadPlatformOutline() {
+ // Note: we rebuild the platform outline on every test because the compiler
+ // currently mutates the in-memory representation of the program without
+ // cloning it.
+ return loadProgramFromBytes(outlineBytes);
+ }
+
+ static create(Map<String, String> environment) async {
+ environment[ENABLE_FULL_COMPILE] = "";
+ environment[AST_KIND_INDEX] = "${AstKind.Kernel.index}";
+ bool updateExpectations = environment["updateExpectations"] == "true";
+ Uri sdk = await computePatchedSdk();
+ Uri outlineUri = sdk.resolve('outline.dill');
+ Uri packages = Uri.base.resolve(".packages");
+ TranslateUri uriTranslator =
+ await TranslateUri.parse(PhysicalFileSystem.instance, packages);
+ List<int> outlineBytes = new File.fromUri(outlineUri).readAsBytesSync();
+ return new TreeShakerContext(
+ outlineUri, uriTranslator, outlineBytes, updateExpectations);
+ }
+}
+
+/// Step that extracts the test-specific options and builds the program without
+/// applying tree-shaking.
+class BuildProgram
+ extends Step<TestDescription, _IntermediateData, TreeShakerContext> {
+ const BuildProgram();
+ String get name => "build program";
+ Future<Result<_IntermediateData>> run(
+ TestDescription description, TreeShakerContext context) async {
+ try {
+ var platformOutline = context.loadPlatformOutline();
+ platformOutline.unbindCanonicalNames();
+ var dillTarget =
+ new DillTarget(new Ticker(isVerbose: false), context.uriTranslator);
+ dillTarget.loader.appendLibraries(platformOutline);
+ var sourceTarget = new KernelTarget(PhysicalFileSystem.instance,
+ dillTarget, context.uriTranslator, false);
+ await dillTarget.buildOutlines();
+
+ var inputUri = description.uri;
+
+ /// We treat the lib.dart library as a special dependency that was
+ /// previously built. To do so, we build it and append it back to the
+ /// dillTarget before building the actual test.
+ var libUri = inputUri.resolve('lib/lib.dart');
+ sourceTarget.read(libUri);
+ dillTarget.loader.appendLibraries(
+ await sourceTarget.buildOutlines(), (uri) => uri == libUri);
+
+ /// This new KernelTarget contains only sources from the test without
+ /// lib.dart.
+ sourceTarget = new KernelTarget(PhysicalFileSystem.instance, dillTarget,
+ context.uriTranslator, false);
+
+ await dillTarget.buildOutlines();
+ sourceTarget.read(inputUri);
+ var contents = new File.fromUri(inputUri).readAsStringSync();
+ var showCoreLibraries = contents.contains("@@SHOW_CORE_LIBRARIES@@");
+
+ await sourceTarget.buildOutlines();
+ // Note: We run the tree-shaker as a separate step on this suite to be
+ // able to specify what libraries to tree shake (by default only the code
+ // in the dillTarget gets tree-shaken). We could apply the tree-shaker
+ // twice, but that seems unnecessary.
+ var program = await sourceTarget.buildProgram(trimDependencies: true);
+ return pass(new _IntermediateData(inputUri, program, showCoreLibraries));
+ } on InputError catch (e, s) {
+ return fail(null, e.error, s);
+ }
+ }
+}
+
+/// Intermediate result from the testing chain.
+class _IntermediateData {
+ /// The input URI provided to the test.
+ final Uri uri;
+
+ /// Program built by [BuildProgram].
+ final Program program;
+
+ /// Whether the output should include tree-shaking information about the core
+ /// libraries. This is specified in a comment on individual test files where
+ /// we believe that information is relevant.
+ final bool showCoreLibraries;
+
+ _IntermediateData(this.uri, this.program, this.showCoreLibraries);
+}
+
+/// A step that runs the tree-shaker and checks againt an expectation file for
+/// the list of members and classes that should be preserved by the tree-shaker.
+class CheckShaker extends Step<_IntermediateData, String, ChainContext> {
+ final bool updateExpectations;
+ const CheckShaker({this.updateExpectations: false});
+
+ String get name => "match shaker expectation";
+
+ /// Tree-shake dart:* libraries and the library under [_specialLibraryPath].
+ bool _isTreeShaken(Uri uri) =>
+ uri.isScheme('dart') ||
+ Uri.base.resolveUri(uri).path.endsWith(_specialLibraryPath);
+
+ Future<Result<String>> run(
+ _IntermediateData data, ChainContext context) async {
+ String actualResult;
+ var entryUri = data.program.libraries
+ .firstWhere((l) => !l.importUri.isScheme('dart'))
+ .importUri;
+
+ var program = data.program;
+
+ var errors = verifyProgram(program, isOutline: false);
+ if (!errors.isEmpty) {
+ return new Result<String>(
+ null, context.expectationSet["VerificationError"], errors, null);
+ }
+
+ // Build a text representation of what we expect to be retained.
+ var buffer = new StringBuffer();
+ buffer.writeln('DO NOT EDIT -- this file is autogenerated ---');
+ buffer.writeln('Tree-shaker preserved the following:');
+ for (var library in program.libraries) {
+ var importUri = library.importUri;
+ if (!_isTreeShaken(importUri)) continue;
+ if (importUri.isScheme('dart') && !data.showCoreLibraries) continue;
+ String uri = relativizeUri(library.importUri);
+ buffer.writeln('\nlibrary $uri:');
+ for (var member in library.members) {
+ buffer.writeln(' - member ${member.name}');
+ }
+ for (var typedef_ in library.typedefs) {
+ buffer.writeln(' - typedef ${typedef_.name}');
+ }
+ for (var cls in library.classes) {
+ buffer.writeln(' - class ${cls.name}');
+ for (var member in cls.members) {
+ var name = '${member.name}';
+ if (name == "") {
+ buffer.writeln(' - (default constructor)');
+ } else {
+ buffer.writeln(' - $name');
+ }
+ }
+ }
+ }
+
+ actualResult = "$buffer";
+
+ // Compare against expectations using the text representation.
+ File expectedFile = new File("${entryUri.toFilePath()}.shaker");
+ if (await expectedFile.exists()) {
+ String expected = await expectedFile.readAsString();
+ if (expected.trim() != actualResult.trim()) {
+ if (!updateExpectations) {
+ String diff = await runDiff(expectedFile.uri, actualResult);
+ return fail(
+ null, "$entryUri doesn't match ${expectedFile.uri}\n$diff");
+ }
+ } else {
+ return pass(actualResult);
+ }
+ }
+ if (updateExpectations) {
+ expectedFile.writeAsStringSync(actualResult);
+ return pass(actualResult);
+ } else {
+ return fail(
+ actualResult,
+ """
+Please create file ${expectedFile.path} with this content:
+$buffer""");
+ }
+ }
+}
+
+/// A special library used only to test the shaker. The suite above will
+/// tree-shake the contents of this library.
+const _specialLibraryPath = 'pkg/front_end/testcases/shaker/lib/lib.dart';

Powered by Google App Engine
This is Rietveld 408576698