| OLD | NEW |
| (Empty) | |
| 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 |
| 3 // BSD-style license that can be found in the LICENSE file. |
| 4 |
| 5 library barback.test.utils; |
| 6 |
| 7 import 'dart:async'; |
| 8 |
| 9 import 'package:barback/barback.dart'; |
| 10 import 'package:barback/src/asset_graph.dart'; |
| 11 import 'package:pathos/path.dart' as pathos; |
| 12 import 'package:scheduled_test/scheduled_test.dart'; |
| 13 |
| 14 // TODO(rnystrom): Get rid of this or find a better path for it. |
| 15 import '../../../sdk/lib/_internal/pub/test/command_line_config.dart'; |
| 16 |
| 17 var _configured = false; |
| 18 |
| 19 void initConfig() { |
| 20 if (_configured) return; |
| 21 _configured = true; |
| 22 unittestConfiguration = new CommandLineConfiguration(); |
| 23 } |
| 24 |
| 25 /// Expects that [graph] will return an asset matching [name] and [contents]. |
| 26 void expectAsset(AssetGraph graph, String name, [String contents]) { |
| 27 var id = new AssetId.parse(name); |
| 28 |
| 29 if (contents == null) { |
| 30 contents = pathos.basenameWithoutExtension(id.path); |
| 31 } |
| 32 |
| 33 schedule(() { |
| 34 return graph.getAssetById(id).then((asset) { |
| 35 // TODO(rnystrom): Make an actual Matcher class for this. |
| 36 expect(asset, new isInstanceOf<MockAsset>()); |
| 37 expect(asset._id.package, equals(id.package)); |
| 38 expect(asset._id.path, equals(id.path)); |
| 39 expect(asset._contents, equals(contents)); |
| 40 }); |
| 41 }, "get asset $name"); |
| 42 } |
| 43 |
| 44 /// Expects that [graph] will not find an asset matching [name]. |
| 45 void expectNoAsset(AssetGraph graph, String name) { |
| 46 var id = new AssetId.parse(name); |
| 47 |
| 48 // Make sure the future gets the error. |
| 49 schedule(() { |
| 50 return graph.getAssetById(id).then((asset) { |
| 51 fail("Should have thrown error but got $asset."); |
| 52 }).catchError((error) { |
| 53 expect(error, new isInstanceOf<AssetNotFoundException>()); |
| 54 expect(error.id, equals(id)); |
| 55 }); |
| 56 }, "get asset $name"); |
| 57 } |
| 58 |
| 59 /// Expects that [graph] will have an output file collision error on an asset |
| 60 /// matching [name]. |
| 61 Future expectCollision(AssetGraph graph, String name) { |
| 62 var id = new AssetId.parse(name); |
| 63 return schedule(() { |
| 64 return graph.results.first.then((result) { |
| 65 expect(result.error, new isInstanceOf<AssetCollisionException>()); |
| 66 expect(result.error.id, equals(id)); |
| 67 }); |
| 68 }, "get collision on $name"); |
| 69 } |
| 70 |
| 71 /// Expects that [graph] will have an error on an asset matching [name] for |
| 72 /// missing [input]. |
| 73 Future expectMissingInput(AssetGraph graph, String name, String input) { |
| 74 var missing = new AssetId.parse(input); |
| 75 |
| 76 // Make sure the future gets the error. |
| 77 schedule(() { |
| 78 return graph.getAssetById(new AssetId.parse(name)).then((asset) { |
| 79 fail("Should have thrown error but got $asset."); |
| 80 }).catchError((error) { |
| 81 expect(error, new isInstanceOf<MissingInputException>()); |
| 82 expect(error.id, equals(missing)); |
| 83 }); |
| 84 }, "get missing input on $name"); |
| 85 } |
| 86 |
| 87 /// An [AssetProvider] that provides the given set of assets. |
| 88 class MockProvider implements AssetProvider { |
| 89 Iterable<String> get packages => _packages.keys; |
| 90 |
| 91 final _packages = new Map<String, List<MockAsset>>(); |
| 92 |
| 93 /// The completer that [getAsset()] is waiting on to complete. |
| 94 /// |
| 95 /// If `null` it will return the asset immediately. |
| 96 Completer _wait; |
| 97 |
| 98 /// Tells the provider to wait during [getAsset] until [complete()] |
| 99 /// is called. |
| 100 /// |
| 101 /// Lets you test the asynchronous behavior of loading. |
| 102 void wait() { |
| 103 _wait = new Completer(); |
| 104 } |
| 105 |
| 106 void complete() { |
| 107 _wait.complete(); |
| 108 _wait = null; |
| 109 } |
| 110 |
| 111 MockProvider(assets) { |
| 112 if (assets is Map) { |
| 113 assets.forEach((asset, contents) { |
| 114 var id = new AssetId.parse(asset); |
| 115 var package = _packages.putIfAbsent(id.package, () => []); |
| 116 package.add(new MockAsset(id, contents)); |
| 117 }); |
| 118 } else if (assets is Iterable) { |
| 119 for (var asset in assets) { |
| 120 var id = new AssetId.parse(asset); |
| 121 var package = _packages.putIfAbsent(id.package, () => []); |
| 122 var contents = pathos.basenameWithoutExtension(id.path); |
| 123 package.add(new MockAsset(id, contents)); |
| 124 } |
| 125 } |
| 126 } |
| 127 |
| 128 void modifyAsset(String name, String contents) { |
| 129 var id = new AssetId.parse(name); |
| 130 var asset = _packages[id.package].firstWhere((a) => a._id == id); |
| 131 asset._contents = contents; |
| 132 } |
| 133 |
| 134 List<AssetId> listAssets(String package, {String within}) { |
| 135 if (within != null) { |
| 136 throw new UnimplementedError("Doesn't handle 'within' yet."); |
| 137 } |
| 138 |
| 139 return _packages[package].map((asset) => asset.id); |
| 140 } |
| 141 |
| 142 Future<Asset> getAsset(AssetId id) { |
| 143 var future; |
| 144 if (_wait != null) { |
| 145 future = _wait.future; |
| 146 } else { |
| 147 future = new Future.value(); |
| 148 } |
| 149 |
| 150 return future.then((_) { |
| 151 var package = _packages[id.package]; |
| 152 if (package == null) throw new AssetNotFoundException(id); |
| 153 |
| 154 return package.firstWhere((asset) => asset._id == id, |
| 155 orElse: () => throw new AssetNotFoundException(id)); |
| 156 }); |
| 157 } |
| 158 } |
| 159 |
| 160 /// A [Transformer] that takes assets ending with one extension and generates |
| 161 /// assets with a given extension. |
| 162 /// |
| 163 /// Appends the output extension to the contents of the input file. |
| 164 class RewriteTransformer extends Transformer { |
| 165 final String from; |
| 166 final String to; |
| 167 |
| 168 /// The number of times the transformer has been applied. |
| 169 int numRuns = 0; |
| 170 |
| 171 /// The number of currently running transforms. |
| 172 int _runningTransforms = 0; |
| 173 |
| 174 /// The completer that the transform is waiting on to complete. |
| 175 /// |
| 176 /// If `null` the transform will complete immediately. |
| 177 Completer _wait; |
| 178 |
| 179 /// A future that completes when the first apply of this transformer begins. |
| 180 Future get started => _started.future; |
| 181 final _started = new Completer(); |
| 182 |
| 183 /// Creates a transformer that rewrites assets whose extension is [from] to |
| 184 /// one whose extension is [to]. |
| 185 /// |
| 186 /// [to] may be a space-separated list in which case multiple outputs will be |
| 187 /// created for each input. |
| 188 RewriteTransformer(this.from, this.to); |
| 189 |
| 190 /// `true` if any transforms are currently running. |
| 191 bool get isRunning => _runningTransforms > 0; |
| 192 |
| 193 /// Tells the transform to wait during its transformation until [complete()] |
| 194 /// is called. |
| 195 /// |
| 196 /// Lets you test the asynchronous behavior of transformers. |
| 197 void wait() { |
| 198 _wait = new Completer(); |
| 199 } |
| 200 |
| 201 void complete() { |
| 202 _wait.complete(); |
| 203 _wait = null; |
| 204 } |
| 205 |
| 206 Future<bool> isPrimary(AssetId asset) { |
| 207 return new Future.value(asset.extension == ".$from"); |
| 208 } |
| 209 |
| 210 Future apply(Transform transform) { |
| 211 numRuns++; |
| 212 if (!_started.isCompleted) _started.complete(); |
| 213 _runningTransforms++; |
| 214 return transform.primaryInput.then((input) { |
| 215 for (var extension in to.split(" ")) { |
| 216 var id = transform.primaryId.changeExtension(".$extension"); |
| 217 var content = input.readAsString() + ".$extension"; |
| 218 transform.addOutput(id, new MockAsset(id, content)); |
| 219 |
| 220 } |
| 221 |
| 222 if (_wait != null) return _wait.future; |
| 223 }).whenComplete(() { |
| 224 _runningTransforms--; |
| 225 }); |
| 226 } |
| 227 |
| 228 String toString() => "$from->$to"; |
| 229 } |
| 230 |
| 231 /// A [Transformer] that takes an input asset that contains a comma-separated |
| 232 /// list of paths and outputs a file for each path. |
| 233 class OneToManyTransformer extends Transformer { |
| 234 final String extension; |
| 235 |
| 236 /// The number of times the transformer has been applied. |
| 237 int numRuns = 0; |
| 238 |
| 239 /// Creates a transformer that consumes assets with [extension]. |
| 240 /// |
| 241 /// That file contains a comma-separated list of paths and it will output |
| 242 /// files at each of those paths. |
| 243 OneToManyTransformer(this.extension); |
| 244 |
| 245 Future<bool> isPrimary(AssetId asset) { |
| 246 return new Future.value(asset.extension == ".$extension"); |
| 247 } |
| 248 |
| 249 Future apply(Transform transform) { |
| 250 numRuns++; |
| 251 return transform.primaryInput.then((input) { |
| 252 for (var line in input.readAsString().split(",")) { |
| 253 var id = new AssetId(transform.primaryId.package, line); |
| 254 transform.addOutput(id, new MockAsset(id, "spread $extension")); |
| 255 } |
| 256 }); |
| 257 } |
| 258 |
| 259 String toString() => "1->many $extension"; |
| 260 } |
| 261 |
| 262 /// A transformer that uses the contents of a file to define the other inputs. |
| 263 /// |
| 264 /// Outputs a file with the same name as the primary but with an "out" |
| 265 /// extension containing the concatenated contents of all non-primary inputs. |
| 266 class ManyToOneTransformer extends Transformer { |
| 267 final String extension; |
| 268 |
| 269 /// The number of times the transformer has been applied. |
| 270 int numRuns = 0; |
| 271 |
| 272 /// Creates a transformer that consumes assets with [extension]. |
| 273 /// |
| 274 /// That file contains a comma-separated list of paths and it will input |
| 275 /// files at each of those paths. |
| 276 ManyToOneTransformer(this.extension); |
| 277 |
| 278 Future<bool> isPrimary(AssetId asset) { |
| 279 return new Future.value(asset.extension == ".$extension"); |
| 280 } |
| 281 |
| 282 Future apply(Transform transform) { |
| 283 numRuns++; |
| 284 return transform.primaryInput.then((primary) { |
| 285 // Get all of the included inputs. |
| 286 var inputs = primary.readAsString().split(",").map((path) { |
| 287 var id = new AssetId(transform.primaryId.package, path); |
| 288 return transform.getInput(id); |
| 289 }); |
| 290 |
| 291 // Concatenate them to one output. |
| 292 return Future.wait(inputs).then((inputs) { |
| 293 var id = transform.primaryId.changeExtension(".out"); |
| 294 var contents = inputs.map((input) => input.readAsString()).join(); |
| 295 transform.addOutput(id, new MockAsset(id, contents)); |
| 296 }); |
| 297 }); |
| 298 } |
| 299 |
| 300 String toString() => "many->1 $extension"; |
| 301 } |
| 302 |
| 303 /// A transformer that throws an exception when run, after generating the |
| 304 /// given outputs. |
| 305 class BadTransformer extends Transformer { |
| 306 /// The error it throws. |
| 307 static const ERROR = "I am a bad transformer!"; |
| 308 |
| 309 /// The list of asset names that it should output. |
| 310 final List<String> outputs; |
| 311 |
| 312 BadTransformer(this.outputs); |
| 313 |
| 314 Future<bool> isPrimary(AssetId asset) => new Future.value(true); |
| 315 Future apply(Transform transform) { |
| 316 return new Future(() { |
| 317 // Create the outputs first. |
| 318 for (var output in outputs) { |
| 319 var id = new AssetId.parse(output); |
| 320 transform.addOutput(id, new MockAsset(id, output)); |
| 321 } |
| 322 |
| 323 // Then fail. |
| 324 throw ERROR; |
| 325 }); |
| 326 } |
| 327 } |
| 328 |
| 329 /// An implementation of [Asset] that never hits the file system. |
| 330 class MockAsset implements Asset { |
| 331 final AssetId _id; |
| 332 String _contents; |
| 333 |
| 334 MockAsset(this._id, this._contents); |
| 335 |
| 336 String readAsString() => _contents; |
| 337 Stream<List<int>> read() => throw new UnimplementedError(); |
| 338 |
| 339 serialize() => throw new UnimplementedError(); |
| 340 |
| 341 String toString() => "MockAsset $_id $_contents"; |
| 342 } |
| OLD | NEW |