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