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

Side by Side Diff: pkg/barback/test/utils.dart

Issue 5695057915019264: Make barback more package-aware. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Re-upload Created 7 years, 5 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 | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 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 file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 library barback.test.utils; 5 library barback.test.utils;
6 6
7 import 'dart:async'; 7 import 'dart:async';
8 import 'dart:collection'; 8 import 'dart:collection';
9 import 'dart:io'; 9 import 'dart:io';
10 10
11 import 'package:barback/barback.dart'; 11 import 'package:barback/barback.dart';
12 import 'package:barback/src/asset_graph.dart'; 12 import 'package:barback/src/asset_graph.dart';
13 import 'package:barback/src/asset_graph_manager.dart';
14 import 'package:barback/src/utils.dart';
13 import 'package:pathos/path.dart' as pathos; 15 import 'package:pathos/path.dart' as pathos;
14 import 'package:scheduled_test/scheduled_test.dart'; 16 import 'package:scheduled_test/scheduled_test.dart';
17 import 'package:stack_trace/stack_trace.dart';
15 18
16 // TODO(rnystrom): Get rid of this or find a better path for it. 19 // TODO(rnystrom): Get rid of this or find a better path for it.
17 import '../../../sdk/lib/_internal/pub/test/command_line_config.dart'; 20 import '../../../sdk/lib/_internal/pub/test/command_line_config.dart';
18 21
19 var _configured = false; 22 var _configured = false;
20 23
21 MockProvider _provider; 24 MockProvider _provider;
22 AssetGraph _graph; 25 AssetGraphManager _graphManager;
23 26
24 /// Calls to [buildShouldSucceed] and [buildShouldFail] set expectations on 27 /// Calls to [buildShouldSucceed] and [buildShouldFail] set expectations on
25 /// successive [BuildResult]s from [_graph]. This keeps track of how many calls 28 /// successive [BuildResult]s from [_graph]. This keeps track of how many calls
26 /// have already been made so later calls know which result to look for. 29 /// have already been made so later calls know which result to look for.
27 int _nextBuildResult; 30 int _nextBuildResult;
28 31
29 void initConfig() { 32 void initConfig() {
30 if (_configured) return; 33 if (_configured) return;
31 _configured = true; 34 _configured = true;
32 unittestConfiguration = new CommandLineConfiguration(); 35 unittestConfiguration = new CommandLineConfiguration();
33 } 36 }
34 37
35 /// Creates a new [AssetProvider] and [AssetGraph] with the given [assets] and 38 /// Creates a new [AssetProvider] and [AssetGraph] with the given [assets] and
36 /// [transformers]. 39 /// [transformers].
37 /// 40 ///
38 /// This graph is used internally by most of the other functions in this 41 /// This graph is used internally by most of the other functions in this
39 /// library so you must call it in the test before calling any of the other 42 /// library so you must call it in the test before calling any of the other
40 /// functions. 43 /// functions.
41 /// 44 ///
42 /// [assets] may either be an [Iterable] or a [Map]. If it's an [Iterable], 45 /// [assets] may either be an [Iterable] or a [Map]. If it's an [Iterable],
43 /// each element may either be an [AssetId] or a string that can be parsed to 46 /// each element may either be an [AssetId] or a string that can be parsed to
44 /// one. If it's a [Map], each key should be a string that can be parsed to an 47 /// one. If it's a [Map], each key should be a string that can be parsed to an
45 /// [AssetId] and the value should be a string defining the contents of that 48 /// [AssetId] and the value should be a string defining the contents of that
46 /// asset. 49 /// asset.
47 void initGraph([assets, Iterable<Iterable<Transformer>> transformers]) { 50 ///
51 /// [transformers] is a map from package names to the transformers for each
52 /// package.
53 void initGraph([assets,
54 Map<String, Iterable<Iterable<Transformer>>> transformers]) {
48 if (assets == null) assets = []; 55 if (assets == null) assets = [];
49 if (transformers == null) transformers = []; 56 if (transformers == null) transformers = {};
50 57
51 _provider = new MockProvider(assets); 58 _provider = new MockProvider(assets, transformers);
52 _graph = new AssetGraph(_provider, transformers); 59 _graphManager = new AssetGraphManager(_provider);
53 _nextBuildResult = 0; 60 _nextBuildResult = 0;
54 } 61 }
55 62
56 /// Updates [assets] in the current [AssetProvider]. 63 /// Updates [assets] in the current [AssetProvider].
57 /// 64 ///
58 /// Each item in the list may either be an [AssetId] or a string that can be 65 /// Each item in the list may either be an [AssetId] or a string that can be
59 /// parsed as one. Note that this method is not automatically scheduled. 66 /// parsed as one. Note that this method is not automatically scheduled.
60 void updateSources(Iterable assets) { 67 void updateSources(Iterable assets) {
61 // Allow strings as asset IDs. 68 // Allow strings as asset IDs.
62 assets = assets.map((asset) { 69 assets = assets.map((asset) {
63 if (asset is String) return new AssetId.parse(asset); 70 if (asset is String) return new AssetId.parse(asset);
64 return asset; 71 return asset;
65 }); 72 });
66 73
67 _graph.updateSources(assets); 74 _graphManager.updateSources(assets);
68 } 75 }
69 76
70 /// Removes [assets] from the current [AssetProvider]. 77 /// Removes [assets] from the current [AssetProvider].
71 /// 78 ///
72 /// Each item in the list may either be an [AssetId] or a string that can be 79 /// Each item in the list may either be an [AssetId] or a string that can be
73 /// parsed as one. Note that this method is not automatically scheduled. 80 /// parsed as one. Note that this method is not automatically scheduled.
74 void removeSources(Iterable assets) { 81 void removeSources(Iterable assets) {
75 // Allow strings as asset IDs. 82 // Allow strings as asset IDs.
76 assets = assets.map((asset) { 83 assets = assets.map((asset) {
77 if (asset is String) return new AssetId.parse(asset); 84 if (asset is String) return new AssetId.parse(asset);
78 return asset; 85 return asset;
79 }); 86 });
80 87
81 _graph.removeSources(assets); 88 _graphManager.removeSources(assets);
82 } 89 }
83 90
84 /// Schedules a change to the contents of an asset identified by [name] to 91 /// Schedules a change to the contents of an asset identified by [name] to
85 /// [contents]. 92 /// [contents].
86 /// 93 ///
87 /// Does not update it in the graph. 94 /// Does not update it in the graph.
88 void modifyAsset(String name, String contents) { 95 void modifyAsset(String name, String contents) {
89 schedule(() { 96 schedule(() {
90 _provider._modifyAsset(name, contents); 97 _provider._modifyAsset(name, contents);
91 }, "modify asset $name"); 98 }, "modify asset $name");
92 } 99 }
93 100
94 /// Schedules a pause of the internally created [AssetProvider]. 101 /// Schedules a pause of the internally created [AssetProvider].
95 /// 102 ///
96 /// All asset requests that the [AssetGraph] makes to the provider after this 103 /// All asset requests that the [AssetGraph] makes to the provider after this
97 /// will not complete until [resumeProvider] is called. 104 /// will not complete until [resumeProvider] is called.
98 void pauseProvider() { 105 void pauseProvider() {
99 schedule(() =>_provider._pause(), "pause provider"); 106 schedule(() => _provider._pause(), "pause provider");
100 } 107 }
101 108
102 /// Schedules an unpause of the provider after a call to [pauseProvider] and 109 /// Schedules an unpause of the provider after a call to [pauseProvider] and
103 /// allows all pending asset loads to finish. 110 /// allows all pending asset loads to finish.
104 void resumeProvider() { 111 void resumeProvider() {
105 schedule(() => _provider._resume(), "resume provider"); 112 schedule(() => _provider._resume(), "resume provider");
106 } 113 }
107 114
115 /// Asserts that the build should not terminate at this point in the schedule.
Bob Nystrom 2013/07/11 23:03:40 "should not terminate at this point" is unclear. D
nweiz 2013/07/15 22:11:44 Clarified.
116 ///
117 /// This uses the same build counter as [buildShouldSucceed] and
118 /// [buildShouldFail], so those can be used to validate build results before and
119 /// after this.
120 void buildShouldNotBeDone() {
121 var resultAllowed = false;
122 var trace = new Trace.current();
123 _graphManager.results.elementAt(_nextBuildResult).then((result) {
124 if (resultAllowed) return;
125
126 currentSchedule.signalError(
127 new Exception("Expected build not to terminate "
128 "here, but it terminated with result: $result"), trace);
129 }).catchError((error) {
130 if (resultAllowed) return;
131 currentSchedule.signalError(error);
132 });
133
134 schedule(() {
135 return pumpEventQueue().then((_) {
Bob Nystrom 2013/07/11 23:03:40 Explain/document this.
nweiz 2013/07/15 22:11:44 Done.
136 resultAllowed = true;
137 });
138 }, "ensuring build doesn't terminate");
139 }
140
108 /// Expects that the next [BuildResult] is a build success. 141 /// Expects that the next [BuildResult] is a build success.
109 void buildShouldSucceed() { 142 void buildShouldSucceed() {
110 expect(_graph.results.elementAt(_nextBuildResult++).then((result) { 143 expect(_getNextBuildResult().then((result) {
111 expect(result.succeeded, isTrue); 144 expect(result.succeeded, isTrue);
112 }), completes); 145 }), completes);
113 } 146 }
114 147
115 /// Expects that the next [BuildResult] emitted is a failure. 148 /// Expects that the next [BuildResult] emitted is a failure.
116 /// 149 ///
117 /// [matchers] is a list of matchers to match against the errors that caused the 150 /// [matchers] is a list of matchers to match against the errors that caused the
118 /// build to fail. Every matcher is expected to match an error, but the order of 151 /// build to fail. Every matcher is expected to match an error, but the order of
119 /// matchers is unimportant. 152 /// matchers is unimportant.
120 void buildShouldFail(List matchers) { 153 void buildShouldFail(List matchers) {
121 expect(_graph.results.elementAt(_nextBuildResult++).then((result) { 154 expect(_getNextBuildResult().then((result) {
122 expect(result.succeeded, isFalse); 155 expect(result.succeeded, isFalse);
123 expect(result.errors.length, equals(matchers.length)); 156 expect(result.errors.length, equals(matchers.length));
124 for (var matcher in matchers) { 157 for (var matcher in matchers) {
125 expect(result.errors, contains(matcher)); 158 expect(result.errors, contains(matcher));
126 } 159 }
127 }), completes); 160 }), completes);
128 } 161 }
129 162
163 Future<BuildResult> _getNextBuildResult() =>
164 _graphManager.results.elementAt(_nextBuildResult++);
165
130 /// Pauses the schedule until the currently running build completes. 166 /// Pauses the schedule until the currently running build completes.
131 /// 167 ///
132 /// Validates that the build completed successfully. 168 /// Validates that the build completed successfully.
133 void waitForBuild() { 169 void waitForBuild() {
134 schedule(() { 170 schedule(() {
135 return _graph.results.first.then((result) { 171 return _graphManager.results.first.then((result) {
136 expect(result.succeeded, isTrue); 172 expect(result.succeeded, isTrue);
137 }); 173 });
138 }, "wait for build"); 174 }, "wait for build");
139 } 175 }
140 176
141 /// Schedules an expectation that the graph will deliver an asset matching 177 /// Schedules an expectation that the graph will deliver an asset matching
142 /// [name] and [contents]. 178 /// [name] and [contents].
143 /// 179 ///
144 /// If [contents] is omitted, defaults to the asset's filename without an 180 /// If [contents] is omitted, defaults to the asset's filename without an
145 /// extension (which is the same default that [initGraph] uses). 181 /// extension (which is the same default that [initGraph] uses).
146 void expectAsset(String name, [String contents]) { 182 void expectAsset(String name, [String contents]) {
147 var id = new AssetId.parse(name); 183 var id = new AssetId.parse(name);
148 184
149 if (contents == null) { 185 if (contents == null) {
150 contents = pathos.basenameWithoutExtension(id.path); 186 contents = pathos.basenameWithoutExtension(id.path);
151 } 187 }
152 188
153 schedule(() { 189 schedule(() {
154 return _graph.getAssetById(id).then((asset) { 190 return _graphManager.getAssetById(id).then((asset) {
155 // TODO(rnystrom): Make an actual Matcher class for this. 191 // TODO(rnystrom): Make an actual Matcher class for this.
156 expect(asset, new isInstanceOf<MockAsset>()); 192 expect(asset, new isInstanceOf<MockAsset>());
157 expect(asset.id, equals(id)); 193 expect(asset.id, equals(id));
158 expect(asset.contents, equals(contents)); 194 expect(asset.contents, equals(contents));
159 }); 195 });
160 }, "get asset $name"); 196 }, "get asset $name");
161 } 197 }
162 198
163 /// Schedules an expectation that the graph will not find an asset matching 199 /// Schedules an expectation that the graph will not find an asset matching
164 /// [name]. 200 /// [name].
165 void expectNoAsset(String name) { 201 void expectNoAsset(String name) {
166 var id = new AssetId.parse(name); 202 var id = new AssetId.parse(name);
167 203
168 // Make sure the future gets the error. 204 // Make sure the future gets the error.
169 schedule(() { 205 schedule(() {
170 return _graph.getAssetById(id).then((asset) { 206 return _graphManager.getAssetById(id).then((asset) {
171 fail("Should have thrown error but got $asset."); 207 fail("Should have thrown error but got $asset.");
172 }).catchError((error) { 208 }).catchError((error) {
173 expect(error, new isInstanceOf<AssetNotFoundException>()); 209 expect(error, new isInstanceOf<AssetNotFoundException>());
174 expect(error.id, equals(id)); 210 expect(error.id, equals(id));
175 }); 211 });
176 }, "get asset $name"); 212 }, "get asset $name");
177 } 213 }
178 214
179 /// Schedules an expectation that [graph] will have an error on an asset
180 /// matching [name] for missing [input].
181 Future expectMissingInput(AssetGraph graph, String name, String input) {
182 var missing = new AssetId.parse(input);
183
184 // Make sure the future gets the error.
185 schedule(() {
186 return graph.getAssetById(new AssetId.parse(name)).then((asset) {
187 fail("Should have thrown error but got $asset.");
188 }).catchError((error) {
189 expect(error, new isInstanceOf<MissingInputException>());
190 expect(error.id, equals(missing));
191 });
192 }, "get missing input on $name");
193 }
194
195 /// Returns a matcher for an [AssetNotFoundException] with the given [id]. 215 /// Returns a matcher for an [AssetNotFoundException] with the given [id].
196 Matcher isAssetNotFoundException(String name) { 216 Matcher isAssetNotFoundException(String name) {
197 var id = new AssetId.parse(name); 217 var id = new AssetId.parse(name);
198 return allOf( 218 return allOf(
199 new isInstanceOf<AssetNotFoundException>(), 219 new isInstanceOf<AssetNotFoundException>(),
200 predicate((error) => error.id == id, 'id is $name')); 220 predicate((error) => error.id == id, 'id is $name'));
201 } 221 }
202 222
203 /// Returns a matcher for an [AssetCollisionException] with the given [id]. 223 /// Returns a matcher for an [AssetCollisionException] with the given [id].
204 Matcher isAssetCollisionException(String name) { 224 Matcher isAssetCollisionException(String name) {
205 var id = new AssetId.parse(name); 225 var id = new AssetId.parse(name);
206 return allOf( 226 return allOf(
207 new isInstanceOf<AssetCollisionException>(), 227 new isInstanceOf<AssetCollisionException>(),
208 predicate((error) => error.id == id, 'id is $name')); 228 predicate((error) => error.id == id, 'id is $name'));
209 } 229 }
210 230
211 /// Returns a matcher for a [MissingInputException] with the given [id]. 231 /// Returns a matcher for a [MissingInputException] with the given [id].
212 Matcher isMissingInputException(String name) { 232 Matcher isMissingInputException(String name) {
213 var id = new AssetId.parse(name); 233 var id = new AssetId.parse(name);
214 return allOf( 234 return allOf(
215 new isInstanceOf<MissingInputException>(), 235 new isInstanceOf<MissingInputException>(),
216 predicate((error) => error.id == id, 'id is $name')); 236 predicate((error) => error.id == id, 'id is $name'));
217 } 237 }
218 238
219 /// An [AssetProvider] that provides the given set of assets. 239 /// An [AssetProvider] that provides the given set of assets.
220 class MockProvider implements AssetProvider { 240 class MockProvider implements AssetProvider {
221 Iterable<String> get packages => _packages.keys; 241 Iterable<String> get packages => _packages.keys;
222 242
223 final _packages = new Map<String, List<MockAsset>>(); 243 Map<String, _MockPackage> _packages;
224 244
225 /// The completer that [getAsset()] is waiting on to complete when paused. 245 /// The completer that [getAsset()] is waiting on to complete when paused.
226 /// 246 ///
227 /// If `null` it will return the asset immediately. 247 /// If `null` it will return the asset immediately.
228 Completer _pauseCompleter; 248 Completer _pauseCompleter;
229 249
230 /// Tells the provider to wait during [getAsset] until [complete()] 250 /// Tells the provider to wait during [getAsset] until [complete()]
231 /// is called. 251 /// is called.
232 /// 252 ///
233 /// Lets you test the asynchronous behavior of loading. 253 /// Lets you test the asynchronous behavior of loading.
234 void _pause() { 254 void _pause() {
235 _pauseCompleter = new Completer(); 255 _pauseCompleter = new Completer();
236 } 256 }
237 257
238 void _resume() { 258 void _resume() {
239 _pauseCompleter.complete(); 259 _pauseCompleter.complete();
240 _pauseCompleter = null; 260 _pauseCompleter = null;
241 } 261 }
242 262
243 MockProvider(assets) { 263 MockProvider(assets,
264 Map<String, Iterable<Iterable<Transformer>>> transformers) {
Bob Nystrom 2013/07/11 23:03:40 How about also allowing this to just be a nested l
nweiz 2013/07/15 22:11:44 I thought about that, but it seemed weird to assum
Bob Nystrom 2013/07/16 17:36:01 Yeah, I was actually thinking about doing the form
nweiz 2013/07/16 19:39:39 I'll leave it for now, but let's keep the idea in
265 var assetList;
244 if (assets is Map) { 266 if (assets is Map) {
245 assets.forEach((asset, contents) { 267 assetList = assets.keys.map((asset) {
246 var id = new AssetId.parse(asset); 268 var id = new AssetId.parse(asset);
247 var package = _packages.putIfAbsent(id.package, () => []); 269 return new MockAsset(id, assets[asset]);
248 package.add(new MockAsset(id, contents));
249 }); 270 });
250 } else if (assets is Iterable) { 271 } else if (assets is Iterable) {
251 for (var asset in assets) { 272 assetList = assets.map((asset) {
252 var id = new AssetId.parse(asset); 273 var id = new AssetId.parse(asset);
253 var package = _packages.putIfAbsent(id.package, () => []);
254 var contents = pathos.basenameWithoutExtension(id.path); 274 var contents = pathos.basenameWithoutExtension(id.path);
255 package.add(new MockAsset(id, contents)); 275 return new MockAsset(id, contents);
256 } 276 });
257 } 277 }
278
279 _packages = mapMapValues(groupBy(assetList, (asset) => asset.id.package),
280 (package, assets) {
281 var packageTransformers = transformers[package];
282 if (packageTransformers == null) packageTransformers = [];
283 return new _MockPackage(assets, packageTransformers.toList());
284 });
285
286 // If there are no assets or transformers, add a dummy package. This better
287 // simulates the real world, where there'll always be at least the
288 // entrypoint package.
289 if (_packages.isEmpty) _packages = {"app": new _MockPackage([], [])};
258 } 290 }
259 291
260 void _modifyAsset(String name, String contents) { 292 void _modifyAsset(String name, String contents) {
261 var id = new AssetId.parse(name); 293 var id = new AssetId.parse(name);
262 var asset = _packages[id.package].firstWhere((a) => a.id == id); 294 var asset = _packages[id.package].assets.firstWhere((a) => a.id == id);
263 asset.contents = contents; 295 asset.contents = contents;
264 } 296 }
265 297
266 List<AssetId> listAssets(String package, {String within}) { 298 List<AssetId> listAssets(String package, {String within}) {
267 if (within != null) { 299 if (within != null) {
268 throw new UnimplementedError("Doesn't handle 'within' yet."); 300 throw new UnimplementedError("Doesn't handle 'within' yet.");
269 } 301 }
270 302
271 return _packages[package].map((asset) => asset.id); 303 return _packages[package].assets.map((asset) => asset.id);
304 }
305
306 Iterable<Iterable<Transformer>> getTransformers(String package) {
307 var mockPackage = _packages[package];
308 if (mockPackage == null) {
309 throw new ArgumentError("No package named $package.");
310 }
311 return mockPackage.transformers;
272 } 312 }
273 313
274 Future<Asset> getAsset(AssetId id) { 314 Future<Asset> getAsset(AssetId id) {
275 var future; 315 var future;
276 if (_pauseCompleter != null) { 316 if (_pauseCompleter != null) {
277 future = _pauseCompleter.future; 317 future = _pauseCompleter.future;
278 } else { 318 } else {
279 future = new Future.value(); 319 future = new Future.value();
280 } 320 }
281 321
282 return future.then((_) { 322 return future.then((_) {
283 var package = _packages[id.package]; 323 var package = _packages[id.package];
284 if (package == null) throw new AssetNotFoundException(id); 324 if (package == null) throw new AssetNotFoundException(id);
285 325
286 return package.firstWhere((asset) => asset.id == id, 326 return package.assets.firstWhere((asset) => asset.id == id,
287 orElse: () => throw new AssetNotFoundException(id)); 327 orElse: () => throw new AssetNotFoundException(id));
288 }); 328 });
289 } 329 }
290 } 330 }
291 331
332 class _MockPackage {
Bob Nystrom 2013/07/11 23:03:40 Document.
nweiz 2013/07/15 22:11:44 Done.
333 final List<MockAsset> assets;
334 final List<List<Transformer>> transformers;
335
336 _MockPackage(this.assets, Iterable<Iterable<Transformer>> transformers)
337 : transformers = transformers.map((phase) => phase.toList()).toList();
338 }
339
292 /// A [Transformer] that takes assets ending with one extension and generates 340 /// A [Transformer] that takes assets ending with one extension and generates
293 /// assets with a given extension. 341 /// assets with a given extension.
294 /// 342 ///
295 /// Appends the output extension to the contents of the input file. 343 /// Appends the output extension to the contents of the input file.
296 class RewriteTransformer extends Transformer { 344 class RewriteTransformer extends Transformer {
297 final String from; 345 final String from;
298 final String to; 346 final String to;
299 347
300 /// The number of times the transformer has been applied. 348 /// The number of times the transformer has been applied.
301 int numRuns = 0; 349 int numRuns = 0;
(...skipping 173 matching lines...) Expand 10 before | Expand all | Expand 10 after
475 523
476 MockAsset(this.id, this.contents); 524 MockAsset(this.id, this.contents);
477 525
478 Future<String> readAsString({Encoding encoding}) => 526 Future<String> readAsString({Encoding encoding}) =>
479 new Future.value(contents); 527 new Future.value(contents);
480 528
481 Stream<List<int>> read() => throw new UnimplementedError(); 529 Stream<List<int>> read() => throw new UnimplementedError();
482 530
483 String toString() => "MockAsset $id $contents"; 531 String toString() => "MockAsset $id $contents";
484 } 532 }
OLDNEW
« pkg/barback/lib/src/utils.dart ('K') | « pkg/barback/test/asset_graph/transform_test.dart ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698