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

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

Issue 21226004: Refactor the barback tests somewhat. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: AssetSet additions Created 7 years, 4 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_cascade.dart'; 12 import 'package:barback/src/asset_cascade.dart';
13 import 'package:barback/src/asset_set.dart';
14 import 'package:barback/src/cancelable_future.dart';
13 import 'package:barback/src/package_graph.dart'; 15 import 'package:barback/src/package_graph.dart';
14 import 'package:barback/src/utils.dart'; 16 import 'package:barback/src/utils.dart';
15 import 'package:path/path.dart' as pathos; 17 import 'package:path/path.dart' as pathos;
16 import 'package:scheduled_test/scheduled_test.dart'; 18 import 'package:scheduled_test/scheduled_test.dart';
17 import 'package:stack_trace/stack_trace.dart'; 19 import 'package:stack_trace/stack_trace.dart';
18 import 'package:unittest/compact_vm_config.dart'; 20 import 'package:unittest/compact_vm_config.dart';
19 21
22 export 'transformer/bad.dart';
23 export 'transformer/check_content.dart';
24 export 'transformer/create_asset.dart';
25 export 'transformer/many_to_one.dart';
26 export 'transformer/mock.dart';
27 export 'transformer/one_to_many.dart';
28 export 'transformer/rewrite.dart';
29
20 var _configured = false; 30 var _configured = false;
21 31
22 MockProvider _provider; 32 MockProvider _provider;
23 PackageGraph _graph; 33 PackageGraph _graph;
24 34
25 /// Calls to [buildShouldSucceed] and [buildShouldFail] set expectations on 35 /// Calls to [buildShouldSucceed] and [buildShouldFail] set expectations on
26 /// successive [BuildResult]s from [_graph]. This keeps track of how many calls 36 /// successive [BuildResult]s from [_graph]. This keeps track of how many calls
27 /// have already been made so later calls know which result to look for. 37 /// have already been made so later calls know which result to look for.
28 int _nextBuildResult; 38 int _nextBuildResult;
29 39
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after
89 /// Schedules a change to the contents of an asset identified by [name] to 99 /// Schedules a change to the contents of an asset identified by [name] to
90 /// [contents]. 100 /// [contents].
91 /// 101 ///
92 /// Does not update it in the graph. 102 /// Does not update it in the graph.
93 void modifyAsset(String name, String contents) { 103 void modifyAsset(String name, String contents) {
94 schedule(() { 104 schedule(() {
95 _provider._modifyAsset(name, contents); 105 _provider._modifyAsset(name, contents);
96 }, "modify asset $name"); 106 }, "modify asset $name");
97 } 107 }
98 108
109 /// Schedules an error when loading the asset identified by [name].
Bob Nystrom 2013/07/30 22:25:02 "error" -> "error to be generated".
nweiz 2013/07/30 22:46:21 Done.
110 ///
111 /// Does not update the asset in the graph.
112 void setAssetError(String name) {
113 schedule(() {
114 _provider._setAssetError(name);
115 }, "set error for asset $name");
116 }
117
99 /// Schedules a pause of the internally created [PackageProvider]. 118 /// Schedules a pause of the internally created [PackageProvider].
100 /// 119 ///
101 /// All asset requests that the [PackageGraph] makes to the provider after this 120 /// All asset requests that the [PackageGraph] makes to the provider after this
102 /// will not complete until [resumeProvider] is called. 121 /// will not complete until [resumeProvider] is called.
103 void pauseProvider() { 122 void pauseProvider() {
104 schedule(() => _provider._pause(), "pause provider"); 123 schedule(() => _provider._pause(), "pause provider");
105 } 124 }
106 125
107 /// Schedules an unpause of the provider after a call to [pauseProvider] and 126 /// Schedules an unpause of the provider after a call to [pauseProvider] and
108 /// allows all pending asset loads to finish. 127 /// allows all pending asset loads to finish.
109 void resumeProvider() { 128 void resumeProvider() {
110 schedule(() => _provider._resume(), "resume provider"); 129 schedule(() => _provider._resume(), "resume provider");
111 } 130 }
112 131
113 /// Asserts that the current build step shouldn't have finished by this point in 132 /// Asserts that the current build step shouldn't have finished by this point in
114 /// the schedule. 133 /// the schedule.
115 /// 134 ///
116 /// This uses the same build counter as [buildShouldSucceed] and 135 /// This uses the same build counter as [buildShouldSucceed] and
117 /// [buildShouldFail], so those can be used to validate build results before and 136 /// [buildShouldFail], so those can be used to validate build results before and
118 /// after this. 137 /// after this.
119 void buildShouldNotBeDone() { 138 void buildShouldNotBeDone() {
120 var resultAllowed = false; 139 _futureShouldNotCompleteUntil(
121 var trace = new Trace.current(); 140 _graph.results.elementAt(_nextBuildResult),
122 _graph.results.elementAt(_nextBuildResult).then((result) { 141 schedule(() => pumpEventQueue(), "build should not terminate"),
123 if (resultAllowed) return; 142 "build");
124
125 currentSchedule.signalError(
126 new Exception("Expected build not to terminate "
127 "here, but it terminated with result: $result"), trace);
128 }).catchError((error) {
129 if (resultAllowed) return;
130 currentSchedule.signalError(error);
131 });
132
133 schedule(() {
134 // Pump the event queue in case the build completes out-of-band after we get
135 // here. If it does, we want to signal an error.
136 return pumpEventQueue().then((_) {
137 resultAllowed = true;
138 });
139 }, "ensuring build doesn't terminate");
140 } 143 }
141 144
142 /// Expects that the next [BuildResult] is a build success. 145 /// Expects that the next [BuildResult] is a build success.
143 void buildShouldSucceed() { 146 void buildShouldSucceed() {
144 expect(_getNextBuildResult().then((result) { 147 expect(_getNextBuildResult().then((result) {
148 result.errors.forEach(currentSchedule.signalError);
145 expect(result.succeeded, isTrue); 149 expect(result.succeeded, isTrue);
146 }), completes); 150 }), completes);
147 } 151 }
148 152
149 /// Expects that the next [BuildResult] emitted is a failure. 153 /// Expects that the next [BuildResult] emitted is a failure.
150 /// 154 ///
151 /// [matchers] is a list of matchers to match against the errors that caused the 155 /// [matchers] is a list of matchers to match against the errors that caused the
152 /// build to fail. Every matcher is expected to match an error, but the order of 156 /// build to fail. Every matcher is expected to match an error, but the order of
153 /// matchers is unimportant. 157 /// matchers is unimportant.
154 void buildShouldFail(List matchers) { 158 void buildShouldFail(List matchers) {
(...skipping 28 matching lines...) Expand all
183 void expectAsset(String name, [String contents]) { 187 void expectAsset(String name, [String contents]) {
184 var id = new AssetId.parse(name); 188 var id = new AssetId.parse(name);
185 189
186 if (contents == null) { 190 if (contents == null) {
187 contents = pathos.basenameWithoutExtension(id.path); 191 contents = pathos.basenameWithoutExtension(id.path);
188 } 192 }
189 193
190 schedule(() { 194 schedule(() {
191 return _graph.getAssetById(id).then((asset) { 195 return _graph.getAssetById(id).then((asset) {
192 // TODO(rnystrom): Make an actual Matcher class for this. 196 // TODO(rnystrom): Make an actual Matcher class for this.
193 expect(asset, new isInstanceOf<MockAsset>());
194 expect(asset.id, equals(id)); 197 expect(asset.id, equals(id));
195 expect(asset.contents, equals(contents)); 198 expect(asset.readAsString(), completion(equals(contents)));
196 }); 199 });
197 }, "get asset $name"); 200 }, "get asset $name");
198 } 201 }
199 202
200 /// Schedules an expectation that the graph will not find an asset matching 203 /// Schedules an expectation that the graph will not find an asset matching
201 /// [name]. 204 /// [name].
202 void expectNoAsset(String name) { 205 void expectNoAsset(String name) {
203 var id = new AssetId.parse(name); 206 var id = new AssetId.parse(name);
204 207
205 // Make sure the future gets the error. 208 // Make sure the future gets the error.
206 schedule(() { 209 schedule(() {
207 return _graph.getAssetById(id).then((asset) { 210 return _graph.getAssetById(id).then((asset) {
208 fail("Should have thrown error but got $asset."); 211 fail("Should have thrown error but got $asset.");
209 }).catchError((error) { 212 }).catchError((error) {
210 expect(error, new isInstanceOf<AssetNotFoundException>()); 213 expect(error, new isInstanceOf<AssetNotFoundException>());
211 expect(error.id, equals(id)); 214 expect(error.id, equals(id));
212 }); 215 });
213 }, "get asset $name"); 216 }, "get asset $name");
214 } 217 }
215 218
219 /// Schedules an expectation that a [getAssetById] call for the given asset
220 /// won't terminate at this point in the schedule.
221 void expectAssetDoesNotComplete(String name) {
222 var id = new AssetId.parse(name);
223
224 schedule(() {
225 return _futureShouldNotCompleteUntil(
226 _graph.getAssetById(id),
227 pumpEventQueue(),
228 "asset $id");
229 }, "asset $id should not complete");
230 }
231
216 /// Returns a matcher for an [AssetNotFoundException] with the given [id]. 232 /// Returns a matcher for an [AssetNotFoundException] with the given [id].
217 Matcher isAssetNotFoundException(String name) { 233 Matcher isAssetNotFoundException(String name) {
218 var id = new AssetId.parse(name); 234 var id = new AssetId.parse(name);
219 return allOf( 235 return allOf(
220 new isInstanceOf<AssetNotFoundException>(), 236 new isInstanceOf<AssetNotFoundException>(),
221 predicate((error) => error.id == id, 'id is $name')); 237 predicate((error) => error.id == id, 'id is $name'));
222 } 238 }
223 239
224 /// Returns a matcher for an [AssetCollisionException] with the given [id]. 240 /// Returns a matcher for an [AssetCollisionException] with the given [id].
225 Matcher isAssetCollisionException(String name) { 241 Matcher isAssetCollisionException(String name) {
(...skipping 14 matching lines...) Expand all
240 /// Returns a matcher for an [InvalidOutputException] with the given id and 256 /// Returns a matcher for an [InvalidOutputException] with the given id and
241 /// package name. 257 /// package name.
242 Matcher isInvalidOutputException(String package, String name) { 258 Matcher isInvalidOutputException(String package, String name) {
243 var id = new AssetId.parse(name); 259 var id = new AssetId.parse(name);
244 return allOf( 260 return allOf(
245 new isInstanceOf<InvalidOutputException>(), 261 new isInstanceOf<InvalidOutputException>(),
246 predicate((error) => error.package == package, 'package is $package'), 262 predicate((error) => error.package == package, 'package is $package'),
247 predicate((error) => error.id == id, 'id is $name')); 263 predicate((error) => error.id == id, 'id is $name'));
248 } 264 }
249 265
266 /// Returns a matcher for a [MockLoadException] with the given [id].
267 Matcher isMockLoadException(String name) {
268 var id = new AssetId.parse(name);
269 return allOf(
270 new isInstanceOf<MockLoadException>(),
271 predicate((error) => error.id == id, 'id is $name'));
Bob Nystrom 2013/07/30 22:25:02 "is" -> "=="
nweiz 2013/07/30 22:46:21 Done.
272 }
273
274 /// Asserts that [future] shouldn't complete until after [delay] completes.
275 ///
276 /// Once [delay] completes, the output of [future] is ignored, even if it's an
277 /// error.
278 ///
279 /// [description] should describe [future].
280 Future _futureShouldNotCompleteUntil(Future future, Future delay,
281 String description) {
282 var trace = new Trace.current();
283 var cancelable = new CancelableFuture(future);
284 cancelable.then((result) {
285 currentSchedule.signalError(
286 new Exception("Expected $description not to complete here, but it "
287 "completed with result: $result"),
288 trace);
289 }).catchError((error) {
290 currentSchedule.signalError(error);
291 });
292
293 return delay.then((_) => cancelable.cancel());
294 }
295
250 /// An [AssetProvider] that provides the given set of assets. 296 /// An [AssetProvider] that provides the given set of assets.
251 class MockProvider implements PackageProvider { 297 class MockProvider implements PackageProvider {
252 Iterable<String> get packages => _packages.keys; 298 Iterable<String> get packages => _packages.keys;
253 299
254 Map<String, _MockPackage> _packages; 300 Map<String, _MockPackage> _packages;
255 301
302 final _errors = new Set<AssetId>();
Bob Nystrom 2013/07/30 22:25:02 Document.
nweiz 2013/07/30 22:46:21 Done.
303
256 /// The completer that [getAsset()] is waiting on to complete when paused. 304 /// The completer that [getAsset()] is waiting on to complete when paused.
257 /// 305 ///
258 /// If `null` it will return the asset immediately. 306 /// If `null` it will return the asset immediately.
259 Completer _pauseCompleter; 307 Completer _pauseCompleter;
260 308
261 /// Tells the provider to wait during [getAsset] until [complete()] 309 /// Tells the provider to wait during [getAsset] until [complete()]
262 /// is called. 310 /// is called.
263 /// 311 ///
264 /// Lets you test the asynchronous behavior of loading. 312 /// Lets you test the asynchronous behavior of loading.
265 void _pause() { 313 void _pause() {
266 _pauseCompleter = new Completer(); 314 _pauseCompleter = new Completer();
267 } 315 }
268 316
269 void _resume() { 317 void _resume() {
270 _pauseCompleter.complete(); 318 _pauseCompleter.complete();
271 _pauseCompleter = null; 319 _pauseCompleter = null;
272 } 320 }
273 321
274 MockProvider(assets, 322 MockProvider(assets,
275 Map<String, Iterable<Iterable<Transformer>>> transformers) { 323 Map<String, Iterable<Iterable<Transformer>>> transformers) {
276 var assetList; 324 var assetList;
277 if (assets is Map) { 325 if (assets is Map) {
278 assetList = assets.keys.map((asset) { 326 assetList = assets.keys.map((asset) {
279 var id = new AssetId.parse(asset); 327 var id = new AssetId.parse(asset);
280 return new MockAsset(id, assets[asset]); 328 return new _MockAsset(id, assets[asset]);
281 }); 329 });
282 } else if (assets is Iterable) { 330 } else if (assets is Iterable) {
283 assetList = assets.map((asset) { 331 assetList = assets.map((asset) {
284 var id = new AssetId.parse(asset); 332 var id = new AssetId.parse(asset);
285 var contents = pathos.basenameWithoutExtension(id.path); 333 var contents = pathos.basenameWithoutExtension(id.path);
286 return new MockAsset(id, contents); 334 return new _MockAsset(id, contents);
287 }); 335 });
288 } 336 }
289 337
290 _packages = mapMapValues(groupBy(assetList, (asset) => asset.id.package), 338 _packages = mapMapValues(groupBy(assetList, (asset) => asset.id.package),
291 (package, assets) { 339 (package, assets) {
292 var packageTransformers = transformers[package]; 340 var packageTransformers = transformers[package];
293 if (packageTransformers == null) packageTransformers = []; 341 if (packageTransformers == null) packageTransformers = [];
294 return new _MockPackage(assets, packageTransformers.toList()); 342 return new _MockPackage(
343 new AssetSet.from(assets), packageTransformers.toList());
295 }); 344 });
296 345
297 // If there are no assets or transformers, add a dummy package. This better 346 // If there are no assets or transformers, add a dummy package. This better
298 // simulates the real world, where there'll always be at least the 347 // simulates the real world, where there'll always be at least the
299 // entrypoint package. 348 // entrypoint package.
300 if (_packages.isEmpty) _packages = {"app": new _MockPackage([], [])}; 349 if (_packages.isEmpty) {
350 _packages = {"app": new _MockPackage(new AssetSet(), [])};
351 }
301 } 352 }
302 353
303 void _modifyAsset(String name, String contents) { 354 void _modifyAsset(String name, String contents) {
304 var id = new AssetId.parse(name); 355 var id = new AssetId.parse(name);
305 var asset = _packages[id.package].assets.firstWhere((a) => a.id == id); 356 _errors.remove(id);
306 asset.contents = contents; 357 _packages[id.package].assets[id].contents = contents;
307 } 358 }
308 359
360 void _setAssetError(String name) => _errors.add(new AssetId.parse(name));
Bob Nystrom 2013/07/30 22:25:02 Document.
nweiz 2013/07/30 22:46:21 Like [_modifyAsset], I didn't document this becaus
361
309 List<AssetId> listAssets(String package, {String within}) { 362 List<AssetId> listAssets(String package, {String within}) {
310 if (within != null) { 363 if (within != null) {
311 throw new UnimplementedError("Doesn't handle 'within' yet."); 364 throw new UnimplementedError("Doesn't handle 'within' yet.");
312 } 365 }
313 366
314 return _packages[package].assets.map((asset) => asset.id); 367 return _packages[package].assets.map((asset) => asset.id);
315 } 368 }
316 369
317 Iterable<Iterable<Transformer>> getTransformers(String package) { 370 Iterable<Iterable<Transformer>> getTransformers(String package) {
318 var mockPackage = _packages[package]; 371 var mockPackage = _packages[package];
319 if (mockPackage == null) { 372 if (mockPackage == null) {
320 throw new ArgumentError("No package named $package."); 373 throw new ArgumentError("No package named $package.");
321 } 374 }
322 return mockPackage.transformers; 375 return mockPackage.transformers;
323 } 376 }
324 377
325 Future<Asset> getAsset(AssetId id) { 378 Future<Asset> getAsset(AssetId id) {
379 // Eagerly load the asset so we can test an asset's value changing between
380 // when a load starts and when it finishes.
381 var package = _packages[id.package];
382 var asset;
383 if (package != null) asset = package.assets[id];
384
385 var error = _errors.contains(id);
Bob Nystrom 2013/07/30 22:25:02 "hasError"
nweiz 2013/07/30 22:46:21 Done.
386
326 var future; 387 var future;
327 if (_pauseCompleter != null) { 388 if (_pauseCompleter != null) {
328 future = _pauseCompleter.future; 389 future = _pauseCompleter.future;
329 } else { 390 } else {
330 future = new Future.value(); 391 future = new Future.value();
331 } 392 }
332 393
333 return future.then((_) { 394 return future.then((_) {
334 var package = _packages[id.package]; 395 if (error) throw new MockLoadException(id);
335 if (package == null) throw new AssetNotFoundException(id); 396 if (asset == null) throw new AssetNotFoundException(id);
336 397 return asset;
337 return package.assets.firstWhere((asset) => asset.id == id,
338 orElse: () => throw new AssetNotFoundException(id));
339 }); 398 });
340 } 399 }
341 } 400 }
342 401
402 /// Error thrown for assets with [setAssetError] set.
403 class MockLoadException implements Exception {
404 final AssetId id;
405
406 MockLoadException(this.id);
407
408 String toString() => "Error loading $id.";
409 }
410
343 /// Used by [MockProvider] to keep track of which assets and transformers exist 411 /// Used by [MockProvider] to keep track of which assets and transformers exist
344 /// for each package. 412 /// for each package.
345 class _MockPackage { 413 class _MockPackage {
346 final List<MockAsset> assets; 414 final AssetSet assets;
347 final List<List<Transformer>> transformers; 415 final List<List<Transformer>> transformers;
348 416
349 _MockPackage(this.assets, Iterable<Iterable<Transformer>> transformers) 417 _MockPackage(this.assets, Iterable<Iterable<Transformer>> transformers)
350 : transformers = transformers.map((phase) => phase.toList()).toList(); 418 : transformers = transformers.map((phase) => phase.toList()).toList();
351 } 419 }
352 420
353 /// A [Transformer] that takes assets ending with one extension and generates
354 /// assets with a given extension.
355 ///
356 /// Appends the output extension to the contents of the input file.
357 class RewriteTransformer extends Transformer {
358 final String from;
359 final String to;
360
361 /// The number of times the transformer has been applied.
362 int numRuns = 0;
363
364 /// The number of currently running transforms.
365 int _runningTransforms = 0;
366
367 /// The completer that the transform is waiting on to complete.
368 ///
369 /// If `null` the transform will complete immediately.
370 Completer _wait;
371
372 /// A future that completes when the first apply of this transformer begins.
373 Future get started => _started.future;
374 final _started = new Completer();
375
376 /// Creates a transformer that rewrites assets whose extension is [from] to
377 /// one whose extension is [to].
378 ///
379 /// [to] may be a space-separated list in which case multiple outputs will be
380 /// created for each input.
381 RewriteTransformer(this.from, this.to);
382
383 /// `true` if any transforms are currently running.
384 bool get isRunning => _runningTransforms > 0;
385
386 /// Tells the transform to wait during its transformation until [complete()]
387 /// is called.
388 ///
389 /// Lets you test the asynchronous behavior of transformers.
390 void wait() {
391 _wait = new Completer();
392 }
393
394 void complete() {
395 _wait.complete();
396 _wait = null;
397 }
398
399 Future<bool> isPrimary(Asset asset) {
400 return new Future.value(asset.id.extension == ".$from");
401 }
402
403 Future apply(Transform transform) {
404 numRuns++;
405 if (!_started.isCompleted) _started.complete();
406 _runningTransforms++;
407 return transform.primaryInput.then((input) {
408 return Future.wait(to.split(" ").map((extension) {
409 var id = transform.primaryId.changeExtension(".$extension");
410 return input.readAsString().then((content) {
411 transform.addOutput(new MockAsset(id, "$content.$extension"));
412 });
413 })).then((_) {
414 if (_wait != null) return _wait.future;
415 });
416 }).whenComplete(() {
417 _runningTransforms--;
418 });
419 }
420
421 String toString() => "$from->$to";
422 }
423
424 /// A [Transformer] that takes an input asset that contains a comma-separated
425 /// list of paths and outputs a file for each path.
426 class OneToManyTransformer extends Transformer {
427 final String extension;
428
429 /// The number of times the transformer has been applied.
430 int numRuns = 0;
431
432 /// Creates a transformer that consumes assets with [extension].
433 ///
434 /// That file contains a comma-separated list of paths and it will output
435 /// files at each of those paths.
436 OneToManyTransformer(this.extension);
437
438 Future<bool> isPrimary(Asset asset) {
439 return new Future.value(asset.id.extension == ".$extension");
440 }
441
442 Future apply(Transform transform) {
443 numRuns++;
444 return transform.primaryInput.then((input) {
445 return input.readAsString().then((lines) {
446 for (var line in lines.split(",")) {
447 var id = new AssetId(transform.primaryId.package, line);
448 transform.addOutput(new MockAsset(id, "spread $extension"));
449 }
450 });
451 });
452 }
453
454 String toString() => "1->many $extension";
455 }
456
457 /// A transformer that uses the contents of a file to define the other inputs.
458 ///
459 /// Outputs a file with the same name as the primary but with an "out"
460 /// extension containing the concatenated contents of all non-primary inputs.
461 class ManyToOneTransformer extends Transformer {
462 final String extension;
463
464 /// The number of times the transformer has been applied.
465 int numRuns = 0;
466
467 /// Creates a transformer that consumes assets with [extension].
468 ///
469 /// That file contains a comma-separated list of paths and it will input
470 /// files at each of those paths.
471 ManyToOneTransformer(this.extension);
472
473 Future<bool> isPrimary(Asset asset) {
474 return new Future.value(asset.id.extension == ".$extension");
475 }
476
477 Future apply(Transform transform) {
478 numRuns++;
479 return transform.primaryInput.then((primary) {
480 return primary.readAsString().then((contents) {
481 // Get all of the included inputs.
482 var inputs = contents.split(",").map((path) {
483 var id = new AssetId(transform.primaryId.package, path);
484 return transform.getInput(id);
485 });
486
487 return Future.wait(inputs);
488 }).then((inputs) {
489 // Concatenate them to one output.
490 var output = "";
491 return Future.forEach(inputs, (input) {
492 return input.readAsString().then((contents) {
493 output += contents;
494 });
495 }).then((_) {
496 var id = transform.primaryId.changeExtension(".out");
497 transform.addOutput(new MockAsset(id, output));
498 });
499 });
500 });
501 }
502
503 String toString() => "many->1 $extension";
504 }
505
506 /// A transformer that throws an exception when run, after generating the
507 /// given outputs.
508 class BadTransformer extends Transformer {
509 /// The error it throws.
510 static const ERROR = "I am a bad transformer!";
511
512 /// The list of asset names that it should output.
513 final List<String> outputs;
514
515 BadTransformer(this.outputs);
516
517 Future<bool> isPrimary(Asset asset) => new Future.value(true);
518 Future apply(Transform transform) {
519 return newFuture(() {
520 // Create the outputs first.
521 for (var output in outputs) {
522 var id = new AssetId.parse(output);
523 transform.addOutput(new MockAsset(id, output));
524 }
525
526 // Then fail.
527 throw ERROR;
528 });
529 }
530 }
531
532 /// A transformer that outputs an asset with the given id.
533 class CreateAssetTransformer extends Transformer {
534 final String output;
535
536 CreateAssetTransformer(this.output);
537
538 Future<bool> isPrimary(Asset asset) => new Future.value(true);
539
540 Future apply(Transform transform) {
541 return newFuture(() {
542 transform.addOutput(new MockAsset(new AssetId.parse(output), output));
543 });
544 }
545 }
546
547 /// An implementation of [Asset] that never hits the file system. 421 /// An implementation of [Asset] that never hits the file system.
548 class MockAsset implements Asset { 422 class _MockAsset implements Asset {
Bob Nystrom 2013/07/30 22:25:02 Can you get rid of this entirely?
nweiz 2013/07/30 22:46:21 I considered that, but the fact that MockProvider
Bob Nystrom 2013/07/30 23:14:08 SGTM.
549 final AssetId id; 423 final AssetId id;
550 String contents; 424 String contents;
551 425
552 MockAsset(this.id, this.contents); 426 _MockAsset(this.id, this.contents);
553 427
554 Future<String> readAsString({Encoding encoding}) => 428 Future<String> readAsString({Encoding encoding}) =>
555 new Future.value(contents); 429 new Future.value(contents);
556 430
557 Stream<List<int>> read() => throw new UnimplementedError(); 431 Stream<List<int>> read() => throw new UnimplementedError();
558 432
559 String toString() => "MockAsset $id $contents"; 433 String toString() => "MockAsset $id $contents";
560 } 434 }
OLDNEW
« pkg/barback/test/transformer/mock.dart ('K') | « pkg/barback/test/transformer/rewrite.dart ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698