OLD | NEW |
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 |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
54 /// [AssetId] and the value should be a string defining the contents of that | 54 /// [AssetId] and the value should be a string defining the contents of that |
55 /// asset. | 55 /// asset. |
56 /// | 56 /// |
57 /// [transformers] is a map from package names to the transformers for each | 57 /// [transformers] is a map from package names to the transformers for each |
58 /// package. | 58 /// package. |
59 void initGraph([assets, | 59 void initGraph([assets, |
60 Map<String, Iterable<Iterable<Transformer>>> transformers]) { | 60 Map<String, Iterable<Iterable<Transformer>>> transformers]) { |
61 if (assets == null) assets = []; | 61 if (assets == null) assets = []; |
62 if (transformers == null) transformers = {}; | 62 if (transformers == null) transformers = {}; |
63 | 63 |
64 _provider = new MockProvider(assets, transformers); | 64 var assetList; |
| 65 if (assets is Map) { |
| 66 assetList = assets.keys.map((asset) { |
| 67 var id = new AssetId.parse(asset); |
| 68 return new _MockAsset(id, assets[asset]); |
| 69 }); |
| 70 } else if (assets is Iterable) { |
| 71 assetList = assets.map((asset) { |
| 72 var id = new AssetId.parse(asset); |
| 73 var contents = pathos.basenameWithoutExtension(id.path); |
| 74 return new _MockAsset(id, contents); |
| 75 }); |
| 76 } |
| 77 |
| 78 var assetMap = mapMapValues(groupBy(assetList, (asset) => asset.id.package), |
| 79 (package, assets) => new AssetSet.from(assets)); |
| 80 |
| 81 // Make sure that packages that have transformers but no assets are considered |
| 82 // by MockProvider to exist. |
| 83 for (var package in transformers.keys) { |
| 84 assetMap.putIfAbsent(package, () => new AssetSet()); |
| 85 } |
| 86 |
| 87 _provider = new MockProvider(assetMap); |
65 _barback = new Barback(_provider); | 88 _barback = new Barback(_provider); |
66 _nextBuildResult = 0; | 89 _nextBuildResult = 0; |
| 90 |
| 91 transformers.forEach(_barback.updateTransformers); |
| 92 |
| 93 // There should be one successful build after adding all the transformers but |
| 94 // before adding any sources. |
| 95 if (!transformers.isEmpty) buildShouldSucceed(); |
67 } | 96 } |
68 | 97 |
69 /// Updates [assets] in the current [PackageProvider]. | 98 /// Updates [assets] in the current [PackageProvider]. |
70 /// | 99 /// |
71 /// Each item in the list may either be an [AssetId] or a string that can be | 100 /// Each item in the list may either be an [AssetId] or a string that can be |
72 /// parsed as one. | 101 /// parsed as one. |
73 void updateSources(Iterable assets) { | 102 void updateSources(Iterable assets) { |
74 assets = _parseAssets(assets); | 103 assets = _parseAssets(assets); |
75 schedule(() => _barback.updateSources(assets), | 104 schedule(() => _barback.updateSources(assets), |
76 "updating ${assets.join(', ')}"); | 105 "updating ${assets.join(', ')}"); |
(...skipping 18 matching lines...) Expand all Loading... |
95 } | 124 } |
96 | 125 |
97 /// Removes [assets] from the current [PackageProvider]. | 126 /// Removes [assets] from the current [PackageProvider]. |
98 /// | 127 /// |
99 /// Each item in the list may either be an [AssetId] or a string that can be | 128 /// Each item in the list may either be an [AssetId] or a string that can be |
100 /// parsed as one. Unlike [removeSources], this is not automatically scheduled | 129 /// parsed as one. Unlike [removeSources], this is not automatically scheduled |
101 /// and will be run synchronously when called. | 130 /// and will be run synchronously when called. |
102 void removeSourcesSync(Iterable assets) => | 131 void removeSourcesSync(Iterable assets) => |
103 _barback.removeSources(_parseAssets(assets)); | 132 _barback.removeSources(_parseAssets(assets)); |
104 | 133 |
| 134 /// Sets the transformers for [package] to [transformers]. |
| 135 void updateTransformers(String package, |
| 136 Iterable<Iterable<Transformers>> transformers) { |
| 137 schedule(() => _barback.updateTransformers(package, transformers), |
| 138 "updating transformers for $package"); |
| 139 } |
| 140 |
105 /// Parse a list of strings or [AssetId]s into a list of [AssetId]s. | 141 /// Parse a list of strings or [AssetId]s into a list of [AssetId]s. |
106 List<AssetId> _parseAssets(Iterable assets) { | 142 List<AssetId> _parseAssets(Iterable assets) { |
107 return assets.map((asset) { | 143 return assets.map((asset) { |
108 if (asset is String) return new AssetId.parse(asset); | 144 if (asset is String) return new AssetId.parse(asset); |
109 return asset; | 145 return asset; |
110 }).toList(); | 146 }).toList(); |
111 } | 147 } |
112 | 148 |
113 /// Schedules a change to the contents of an asset identified by [name] to | 149 /// Schedules a change to the contents of an asset identified by [name] to |
114 /// [contents]. | 150 /// [contents]. |
(...skipping 218 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
333 trace); | 369 trace); |
334 }).catchError((error) { | 370 }).catchError((error) { |
335 currentSchedule.signalError(error); | 371 currentSchedule.signalError(error); |
336 }); | 372 }); |
337 | 373 |
338 return delay.then((_) => cancelable.cancel()); | 374 return delay.then((_) => cancelable.cancel()); |
339 } | 375 } |
340 | 376 |
341 /// An [AssetProvider] that provides the given set of assets. | 377 /// An [AssetProvider] that provides the given set of assets. |
342 class MockProvider implements PackageProvider { | 378 class MockProvider implements PackageProvider { |
343 Iterable<String> get packages => _packages.keys; | 379 Iterable<String> get packages => _assets.keys; |
344 | 380 |
345 Map<String, _MockPackage> _packages; | 381 Map<String, AssetSet> _assets; |
346 | 382 |
347 /// The set of assets for which [MockLoadException]s should be emitted if | 383 /// The set of assets for which [MockLoadException]s should be emitted if |
348 /// they're loaded. | 384 /// they're loaded. |
349 final _errors = new Set<AssetId>(); | 385 final _errors = new Set<AssetId>(); |
350 | 386 |
351 /// The completer that [getAsset()] is waiting on to complete when paused. | 387 /// The completer that [getAsset()] is waiting on to complete when paused. |
352 /// | 388 /// |
353 /// If `null` it will return the asset immediately. | 389 /// If `null` it will return the asset immediately. |
354 Completer _pauseCompleter; | 390 Completer _pauseCompleter; |
355 | 391 |
356 /// Tells the provider to wait during [getAsset] until [complete()] | 392 /// Tells the provider to wait during [getAsset] until [complete()] |
357 /// is called. | 393 /// is called. |
358 /// | 394 /// |
359 /// Lets you test the asynchronous behavior of loading. | 395 /// Lets you test the asynchronous behavior of loading. |
360 void _pause() { | 396 void _pause() { |
361 _pauseCompleter = new Completer(); | 397 _pauseCompleter = new Completer(); |
362 } | 398 } |
363 | 399 |
364 void _resume() { | 400 void _resume() { |
365 _pauseCompleter.complete(); | 401 _pauseCompleter.complete(); |
366 _pauseCompleter = null; | 402 _pauseCompleter = null; |
367 } | 403 } |
368 | 404 |
369 MockProvider(assets, | 405 MockProvider(this._assets) { |
370 Map<String, Iterable<Iterable<Transformer>>> transformers) { | |
371 var assetList; | |
372 if (assets is Map) { | |
373 assetList = assets.keys.map((asset) { | |
374 var id = new AssetId.parse(asset); | |
375 return new _MockAsset(id, assets[asset]); | |
376 }); | |
377 } else if (assets is Iterable) { | |
378 assetList = assets.map((asset) { | |
379 var id = new AssetId.parse(asset); | |
380 var contents = pathos.basenameWithoutExtension(id.path); | |
381 return new _MockAsset(id, contents); | |
382 }); | |
383 } | |
384 | |
385 _packages = mapMapValues(groupBy(assetList, (asset) => asset.id.package), | |
386 (package, assets) { | |
387 var packageTransformers = transformers[package]; | |
388 if (packageTransformers == null) packageTransformers = []; | |
389 return new _MockPackage( | |
390 new AssetSet.from(assets), packageTransformers.toList()); | |
391 }); | |
392 | |
393 // If there are no assets or transformers, add a dummy package. This better | 406 // If there are no assets or transformers, add a dummy package. This better |
394 // simulates the real world, where there'll always be at least the | 407 // simulates the real world, where there'll always be at least the |
395 // entrypoint package. | 408 // entrypoint package. |
396 if (_packages.isEmpty) { | 409 if (_assets.isEmpty) { |
397 _packages = {"app": new _MockPackage(new AssetSet(), [])}; | 410 _assets = {"app": new AssetSet()}; |
398 } | 411 } |
399 } | 412 } |
400 | 413 |
401 void _modifyAsset(String name, String contents) { | 414 void _modifyAsset(String name, String contents) { |
402 var id = new AssetId.parse(name); | 415 var id = new AssetId.parse(name); |
403 _errors.remove(id); | 416 _errors.remove(id); |
404 _packages[id.package].assets[id].contents = contents; | 417 _assets[id.package][id].contents = contents; |
405 } | 418 } |
406 | 419 |
407 void _setAssetError(String name) => _errors.add(new AssetId.parse(name)); | 420 void _setAssetError(String name) => _errors.add(new AssetId.parse(name)); |
408 | 421 |
409 List<AssetId> listAssets(String package, {String within}) { | 422 List<AssetId> listAssets(String package, {String within}) { |
410 if (within != null) { | 423 if (within != null) { |
411 throw new UnimplementedError("Doesn't handle 'within' yet."); | 424 throw new UnimplementedError("Doesn't handle 'within' yet."); |
412 } | 425 } |
413 | 426 |
414 return _packages[package].assets.map((asset) => asset.id); | 427 return _assets[package].map((asset) => asset.id); |
415 } | |
416 | |
417 Iterable<Iterable<Transformer>> getTransformers(String package) { | |
418 var mockPackage = _packages[package]; | |
419 if (mockPackage == null) { | |
420 throw new ArgumentError("No package named $package."); | |
421 } | |
422 return mockPackage.transformers; | |
423 } | 428 } |
424 | 429 |
425 Future<Asset> getAsset(AssetId id) { | 430 Future<Asset> getAsset(AssetId id) { |
426 // Eagerly load the asset so we can test an asset's value changing between | 431 // Eagerly load the asset so we can test an asset's value changing between |
427 // when a load starts and when it finishes. | 432 // when a load starts and when it finishes. |
428 var package = _packages[id.package]; | 433 var assets = _assets[id.package]; |
429 var asset; | 434 var asset; |
430 if (package != null) asset = package.assets[id]; | 435 if (assets != null) asset = assets[id]; |
431 | 436 |
432 var hasError = _errors.contains(id); | 437 var hasError = _errors.contains(id); |
433 | 438 |
434 var future; | 439 var future; |
435 if (_pauseCompleter != null) { | 440 if (_pauseCompleter != null) { |
436 future = _pauseCompleter.future; | 441 future = _pauseCompleter.future; |
437 } else { | 442 } else { |
438 future = new Future.value(); | 443 future = new Future.value(); |
439 } | 444 } |
440 | 445 |
441 return future.then((_) { | 446 return future.then((_) { |
442 if (hasError) throw new MockLoadException(id); | 447 if (hasError) throw new MockLoadException(id); |
443 if (asset == null) throw new AssetNotFoundException(id); | 448 if (asset == null) throw new AssetNotFoundException(id); |
444 return asset; | 449 return asset; |
445 }); | 450 }); |
446 } | 451 } |
447 } | 452 } |
448 | 453 |
449 /// Error thrown for assets with [setAssetError] set. | 454 /// Error thrown for assets with [setAssetError] set. |
450 class MockLoadException implements Exception { | 455 class MockLoadException implements Exception { |
451 final AssetId id; | 456 final AssetId id; |
452 | 457 |
453 MockLoadException(this.id); | 458 MockLoadException(this.id); |
454 | 459 |
455 String toString() => "Error loading $id."; | 460 String toString() => "Error loading $id."; |
456 } | 461 } |
457 | 462 |
458 /// Used by [MockProvider] to keep track of which assets and transformers exist | |
459 /// for each package. | |
460 class _MockPackage { | |
461 final AssetSet assets; | |
462 final List<List<Transformer>> transformers; | |
463 | |
464 _MockPackage(this.assets, Iterable<Iterable<Transformer>> transformers) | |
465 : transformers = transformers.map((phase) => phase.toList()).toList(); | |
466 } | |
467 | |
468 /// An implementation of [Asset] that never hits the file system. | 463 /// An implementation of [Asset] that never hits the file system. |
469 class _MockAsset implements Asset { | 464 class _MockAsset implements Asset { |
470 final AssetId id; | 465 final AssetId id; |
471 String contents; | 466 String contents; |
472 | 467 |
473 _MockAsset(this.id, this.contents); | 468 _MockAsset(this.id, this.contents); |
474 | 469 |
475 Future<String> readAsString({Encoding encoding}) => | 470 Future<String> readAsString({Encoding encoding}) => |
476 new Future.value(contents); | 471 new Future.value(contents); |
477 | 472 |
478 Stream<List<int>> read() => throw new UnimplementedError(); | 473 Stream<List<int>> read() => throw new UnimplementedError(); |
479 | 474 |
480 String toString() => "MockAsset $id $contents"; | 475 String toString() => "MockAsset $id $contents"; |
481 } | 476 } |
OLD | NEW |