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

Side by Side Diff: pkg/front_end/test/fasta/shaker_test.dart

Issue 2893493004: First step for modular output in fasta. (Closed)
Patch Set: 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 unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE.md file.
4
5 /// Tests basic functionality of the API tree-shaker.
6 ///
7 /// Each input file is built and run through [trimProgram], then we check that
8 /// the set of libraries, classes, and members that are retained match those
9 /// declared in an expectations file.
10 ///
11 /// Input files can contain some markers to turn on flags that configure this
12 /// runner. These markers are recognized:
13 /// @@SHOW_CORE_LIBRARIES@@ - whether to check for retained information from
14 /// the core libraries. By default this runner only checks for members of
15 /// pkg/front_end/testcases/shaker/lib/lib.dart.
16 ///
17 /// @@RETAIN_MEMBERS_OF_CLASSES@@ - turns on the retainClassMembers option
18 /// of [trimProgram] for that specific test.
19 library fasta.test.shaker_test;
20
21 import 'dart:async' show Future;
22 import 'dart:convert' show JSON;
23 import 'dart:io' show File;
24
25 export 'package:testing/testing.dart' show Chain, runMe;
26 import 'package:front_end/physical_file_system.dart';
27 import 'package:front_end/src/fasta/dill/dill_target.dart' show DillTarget;
28 import 'package:front_end/src/fasta/errors.dart' show InputError;
29 import 'package:front_end/src/fasta/kernel/kernel_outline_shaker.dart'
30 show trimProgram;
31 import 'package:front_end/src/fasta/kernel/kernel_target.dart'
32 show KernelTarget;
33 import 'package:front_end/src/fasta/kernel/verifier.dart' show verifyProgram;
34 import 'package:front_end/src/fasta/testing/kernel_chain.dart' show runDiff;
35 import 'package:front_end/src/fasta/testing/patched_sdk_location.dart';
36 import 'package:front_end/src/fasta/ticker.dart' show Ticker;
37 import 'package:front_end/src/fasta/translate_uri.dart' show TranslateUri;
38 import 'package:front_end/src/fasta/util/relativize.dart' show relativizeUri;
39 import 'package:kernel/ast.dart' show Program;
40 import 'package:kernel/kernel.dart' show loadProgramFromBytes;
41 import 'package:testing/testing.dart'
42 show Chain, ChainContext, ExpectationSet, Result, Step, TestDescription;
43 import 'testing/suite.dart';
44
45 main(List<String> arguments) => runMe(arguments, createContext, "testing.json");
46
47 Future<TreeShakerContext> createContext(
48 Chain suite, Map<String, String> environment) {
49 return TreeShakerContext.create(environment);
50 }
51
52 /// Context used to run the tree-shaking test suite.
53 class TreeShakerContext extends ChainContext {
54 final TranslateUri uriTranslator;
55 final Uri outlineUri;
56 final List<Step> steps;
57 final List<int> outlineBytes;
58
59 final ExpectationSet expectationSet =
60 new ExpectationSet.fromJsonList(JSON.decode(EXPECTATIONS));
61
62 TreeShakerContext(this.outlineUri, this.uriTranslator, this.outlineBytes,
63 bool updateExpectations)
64 : steps = <Step>[
65 const BuildProgram(),
66 new CheckShaker(updateExpectations: updateExpectations),
67 ];
68
69 Program loadPlatformOutline() {
70 // Note: we rebuild the platform outline on every test because the compiler
71 // currently mutates the in-memory representation of the program without
72 // cloning it.
73 return loadProgramFromBytes(outlineBytes);
74 }
75
76 static create(Map<String, String> environment) async {
77 environment[ENABLE_FULL_COMPILE] = "";
78 environment[AST_KIND_INDEX] = "${AstKind.Kernel.index}";
79 bool updateExpectations = environment["updateExpectations"] == "true";
80 Uri sdk = await computePatchedSdk();
81 Uri outlineUri = sdk.resolve('outline.dill');
82 Uri packages = Uri.base.resolve(".packages");
83 TranslateUri uriTranslator =
84 await TranslateUri.parse(PhysicalFileSystem.instance, packages);
85 List<int> outlineBytes = new File.fromUri(outlineUri).readAsBytesSync();
86 return new TreeShakerContext(
87 outlineUri, uriTranslator, outlineBytes, updateExpectations);
88 }
89 }
90
91 /// Step that extracts the test-specific options and builds the program without
92 /// applying tree-shaking.
93 class BuildProgram
94 extends Step<TestDescription, _IntermediateData, TreeShakerContext> {
95 const BuildProgram();
96 String get name => "build program";
97 Future<Result<_IntermediateData>> run(
98 TestDescription description, TreeShakerContext context) async {
99 var platformOutline = context.loadPlatformOutline();
100 platformOutline.unbindCanonicalNames();
101
102 var dillTarget =
103 new DillTarget(new Ticker(isVerbose: false), context.uriTranslator);
104 dillTarget.loader.appendLibraries(platformOutline);
105 var sourceTarget = new KernelTarget(
106 PhysicalFileSystem.instance, dillTarget, context.uriTranslator, false);
107
108 try {
109 await dillTarget.buildOutlines();
110 var inputUri = description.uri;
111 sourceTarget.read(inputUri);
112 var contents = new File.fromUri(inputUri).readAsStringSync();
113 var showCoreLibraries = contents.contains("@@SHOW_CORE_LIBRARIES@@");
114 var retainMembers = contents.contains("@@RETAIN_MEMBERS_OF_CLASSES@@");
115 await sourceTarget.buildOutlines();
116 // Note: We run the tree-shaker as a separate step on this suite to be
117 // able to specify what libraries to tree shake (by default only the code
118 // in the dillTarget gets tree-shaken). We could apply the tree-shaker
119 // twice, but that seems unnecessary.
120 var program = await sourceTarget.buildProgram(trimDependencies: false);
121 return pass(new _IntermediateData(
122 inputUri, program, showCoreLibraries, retainMembers));
123 } on InputError catch (e, s) {
124 return fail(null, e.error, s);
125 }
126 }
127 }
128
129 /// Intermediate result from the testing chain.
130 class _IntermediateData {
131 /// The input URI provided to the test.
132 final Uri uri;
133
134 /// Program built by [BuildProgram].
135 final Program program;
136
137 /// Whether the output should include tree-shaking information about the core
138 /// libraries. This is specified in a comment on individual test files where
139 /// we believe that information is relevant.
140 final bool showCoreLibraries;
141
142 /// What to provide to [trimProgram] for the
143 /// `retainClassMembers` flag.
144 final bool retainClassMembers;
145
146 _IntermediateData(
147 this.uri, this.program, this.showCoreLibraries, this.retainClassMembers);
148 }
149
150 /// A step that runs the tree-shaker and checks againt an expectation file for
151 /// the list of members and classes that should be preserved by the tree-shaker.
152 class CheckShaker extends Step<_IntermediateData, String, ChainContext> {
153 final bool updateExpectations;
154 const CheckShaker({this.updateExpectations: false});
155
156 String get name => "match shaker expectation";
157
158 /// Tree-shake dart:* libraries and the library under [_specialLibraryPath].
159 bool _isTreeShaken(Uri uri) =>
160 uri.isScheme('dart') ||
161 Uri.base.resolveUri(uri).path.endsWith(_specialLibraryPath);
162
163 Future<Result<String>> run(
164 _IntermediateData data, ChainContext context) async {
165 String actualResult;
166 var entryUri = data.program.libraries
167 .firstWhere((l) => !l.importUri.isScheme('dart'))
168 .importUri;
169
170 var program = data.program;
171 trimProgram(program, (uri) => !_isTreeShaken(uri),
172 retainClassMembers: data.retainClassMembers);
173
174 var errors = verifyProgram(program, isOutline: false);
175 if (!errors.isEmpty) {
176 return new Result<String>(
177 null, context.expectationSet["VerificationError"], errors, null);
178 }
179
180 // Build a text representation of what we expect to be retained.
181 var buffer = new StringBuffer();
182 buffer.writeln('DO NOT EDIT -- this file is autogenerated ---');
183 buffer.writeln('Tree-shaker preserved the following:');
184 for (var library in program.libraries) {
185 var importUri = library.importUri;
186 if (!_isTreeShaken(importUri)) continue;
187 if (importUri.isScheme('dart') && !data.showCoreLibraries) continue;
188 String uri = relativizeUri(library.importUri);
189 buffer.writeln('\nlibrary $uri:');
190 for (var member in library.members) {
191 buffer.writeln(' - member ${member.name}');
192 }
193 for (var typedef_ in library.typedefs) {
194 buffer.writeln(' - typedef ${typedef_.name}');
195 }
196 for (var cls in library.classes) {
197 buffer.writeln(' - class ${cls.name}');
198 for (var member in cls.members) {
199 var name = '${member.name}';
200 if (name == "") {
201 buffer.writeln(' - (default consturctor)');
202 } else {
203 buffer.writeln(' - $name');
204 }
205 }
206 }
207 }
208
209 actualResult = "$buffer";
210
211 // Compare against expectations using the text representation.
212 File expectedFile = new File("${entryUri.toFilePath()}.shaker");
213 if (await expectedFile.exists()) {
214 String expected = await expectedFile.readAsString();
215 if (expected.trim() != actualResult.trim()) {
216 if (!updateExpectations) {
217 String diff = await runDiff(expectedFile.uri, actualResult);
218 return fail(
219 null, "$entryUri doesn't match ${expectedFile.uri}\n$diff");
220 }
221 } else {
222 return pass(actualResult);
223 }
224 }
225 if (updateExpectations) {
226 expectedFile.writeAsStringSync(actualResult);
227 return pass(actualResult);
228 } else {
229 return fail(
230 actualResult,
231 """
232 Please create file ${expectedFile.path} with this content:
233 $buffer""");
234 }
235 }
236 }
237
238 /// A special library used only to test the shaker. The suite above will
239 /// tree-shake the contents of this library.
240 const _specialLibraryPath = 'pkg/front_end/testcases/shaker/lib/lib.dart';
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698