OLD | NEW |
1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2014, 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 /// This library contains tests for transformer behavior that relates to actions | 5 /// This library contains tests for transformer behavior that relates to actions |
6 /// happening concurrently or other complex asynchronous timing behavior. | 6 /// happening concurrently or other complex asynchronous timing behavior. |
7 library barback.test.package_graph.transform.concurrency_test; | 7 library barback.test.package_graph.transform.concurrency_test; |
8 | 8 |
9 import 'package:barback/src/utils.dart'; | 9 import 'package:barback/src/utils.dart'; |
10 import 'package:scheduled_test/scheduled_test.dart'; | 10 import 'package:scheduled_test/scheduled_test.dart'; |
11 | 11 |
12 import '../../utils.dart'; | 12 import '../../utils.dart'; |
13 | 13 |
14 main() { | 14 main() { |
15 initConfig(); | 15 initConfig(); |
16 test("runs transforms in the same phase in parallel", () { | 16 test("runs transforms in the same phase in parallel", () { |
17 var transformerA = new RewriteTransformer("txt", "a"); | 17 var transformerA = new RewriteTransformer("txt", "a"); |
18 var transformerB = new RewriteTransformer("txt", "b"); | 18 var transformerB = new RewriteTransformer("txt", "b"); |
19 initGraph(["app|foo.txt"], {"app": [[transformerA, transformerB]]}); | 19 initGraph([ |
| 20 "app|foo.txt" |
| 21 ], { |
| 22 "app": [ |
| 23 [transformerA, transformerB] |
| 24 ] |
| 25 }); |
20 | 26 |
21 transformerA.pauseApply(); | 27 transformerA.pauseApply(); |
22 transformerB.pauseApply(); | 28 transformerB.pauseApply(); |
23 | 29 |
24 updateSources(["app|foo.txt"]); | 30 updateSources(["app|foo.txt"]); |
25 | 31 |
26 transformerA.waitUntilStarted(); | 32 transformerA.waitUntilStarted(); |
27 transformerB.waitUntilStarted(); | 33 transformerB.waitUntilStarted(); |
28 | 34 |
29 // They should both still be running. | 35 // They should both still be running. |
30 expect(transformerA.isRunning, completion(isTrue)); | 36 expect(transformerA.isRunning, completion(isTrue)); |
31 expect(transformerB.isRunning, completion(isTrue)); | 37 expect(transformerB.isRunning, completion(isTrue)); |
32 | 38 |
33 transformerA.resumeApply(); | 39 transformerA.resumeApply(); |
34 transformerB.resumeApply(); | 40 transformerB.resumeApply(); |
35 | 41 |
36 expectAsset("app|foo.a", "foo.a"); | 42 expectAsset("app|foo.a", "foo.a"); |
37 expectAsset("app|foo.b", "foo.b"); | 43 expectAsset("app|foo.b", "foo.b"); |
38 buildShouldSucceed(); | 44 buildShouldSucceed(); |
39 }); | 45 }); |
40 | 46 |
41 test("discards outputs from a transform whose primary input is removed " | 47 test( |
| 48 "discards outputs from a transform whose primary input is removed " |
42 "during processing", () { | 49 "during processing", () { |
43 var rewrite = new RewriteTransformer("txt", "out"); | 50 var rewrite = new RewriteTransformer("txt", "out"); |
44 initGraph(["app|foo.txt"], {"app": [[rewrite]]}); | 51 initGraph([ |
| 52 "app|foo.txt" |
| 53 ], { |
| 54 "app": [ |
| 55 [rewrite] |
| 56 ] |
| 57 }); |
45 | 58 |
46 rewrite.pauseApply(); | 59 rewrite.pauseApply(); |
47 updateSources(["app|foo.txt"]); | 60 updateSources(["app|foo.txt"]); |
48 rewrite.waitUntilStarted(); | 61 rewrite.waitUntilStarted(); |
49 | 62 |
50 removeSources(["app|foo.txt"]); | 63 removeSources(["app|foo.txt"]); |
51 rewrite.resumeApply(); | 64 rewrite.resumeApply(); |
52 expectNoAsset("app|foo.out"); | 65 expectNoAsset("app|foo.out"); |
53 buildShouldSucceed(); | 66 buildShouldSucceed(); |
54 }); | 67 }); |
55 | 68 |
56 test("applies the correct transform if an asset is modified during isPrimary", | 69 test("applies the correct transform if an asset is modified during isPrimary", |
57 () { | 70 () { |
58 var check1 = new CheckContentTransformer("first", "#1"); | 71 var check1 = new CheckContentTransformer("first", "#1"); |
59 var check2 = new CheckContentTransformer("second", "#2"); | 72 var check2 = new CheckContentTransformer("second", "#2"); |
60 initGraph({ | 73 initGraph({ |
61 "app|foo.txt": "first", | 74 "app|foo.txt": "first", |
62 }, {"app": [[check1, check2]]}); | 75 }, { |
| 76 "app": [ |
| 77 [check1, check2] |
| 78 ] |
| 79 }); |
63 | 80 |
64 check1.pauseIsPrimary("app|foo.txt"); | 81 check1.pauseIsPrimary("app|foo.txt"); |
65 updateSources(["app|foo.txt"]); | 82 updateSources(["app|foo.txt"]); |
66 // Ensure that we're waiting on check1's isPrimary. | 83 // Ensure that we're waiting on check1's isPrimary. |
67 schedule(pumpEventQueue); | 84 schedule(pumpEventQueue); |
68 | 85 |
69 modifyAsset("app|foo.txt", "second"); | 86 modifyAsset("app|foo.txt", "second"); |
70 updateSources(["app|foo.txt"]); | 87 updateSources(["app|foo.txt"]); |
71 check1.resumeIsPrimary("app|foo.txt"); | 88 check1.resumeIsPrimary("app|foo.txt"); |
72 | 89 |
73 expectAsset("app|foo.txt", "second#2"); | 90 expectAsset("app|foo.txt", "second#2"); |
74 buildShouldSucceed(); | 91 buildShouldSucceed(); |
75 }); | 92 }); |
76 | 93 |
77 test("applies the correct transform if an asset is removed and added during " | 94 test( |
| 95 "applies the correct transform if an asset is removed and added during " |
78 "isPrimary", () { | 96 "isPrimary", () { |
79 var check1 = new CheckContentTransformer("first", "#1"); | 97 var check1 = new CheckContentTransformer("first", "#1"); |
80 var check2 = new CheckContentTransformer("second", "#2"); | 98 var check2 = new CheckContentTransformer("second", "#2"); |
81 initGraph({ | 99 initGraph({ |
82 "app|foo.txt": "first", | 100 "app|foo.txt": "first", |
83 }, {"app": [[check1, check2]]}); | 101 }, { |
| 102 "app": [ |
| 103 [check1, check2] |
| 104 ] |
| 105 }); |
84 | 106 |
85 check1.pauseIsPrimary("app|foo.txt"); | 107 check1.pauseIsPrimary("app|foo.txt"); |
86 updateSources(["app|foo.txt"]); | 108 updateSources(["app|foo.txt"]); |
87 // Ensure that we're waiting on check1's isPrimary. | 109 // Ensure that we're waiting on check1's isPrimary. |
88 schedule(pumpEventQueue); | 110 schedule(pumpEventQueue); |
89 | 111 |
90 removeSources(["app|foo.txt"]); | 112 removeSources(["app|foo.txt"]); |
91 modifyAsset("app|foo.txt", "second"); | 113 modifyAsset("app|foo.txt", "second"); |
92 updateSources(["app|foo.txt"]); | 114 updateSources(["app|foo.txt"]); |
93 check1.resumeIsPrimary("app|foo.txt"); | 115 check1.resumeIsPrimary("app|foo.txt"); |
94 | 116 |
95 expectAsset("app|foo.txt", "second#2"); | 117 expectAsset("app|foo.txt", "second#2"); |
96 buildShouldSucceed(); | 118 buildShouldSucceed(); |
97 }); | 119 }); |
98 | 120 |
99 test("restarts processing if a change occurs during processing", () { | 121 test("restarts processing if a change occurs during processing", () { |
100 var transformer = new RewriteTransformer("txt", "out"); | 122 var transformer = new RewriteTransformer("txt", "out"); |
101 initGraph(["app|foo.txt"], {"app": [[transformer]]}); | 123 initGraph([ |
| 124 "app|foo.txt" |
| 125 ], { |
| 126 "app": [ |
| 127 [transformer] |
| 128 ] |
| 129 }); |
102 | 130 |
103 transformer.pauseApply(); | 131 transformer.pauseApply(); |
104 | 132 |
105 updateSources(["app|foo.txt"]); | 133 updateSources(["app|foo.txt"]); |
106 transformer.waitUntilStarted(); | 134 transformer.waitUntilStarted(); |
107 | 135 |
108 // Now update the graph during it. | 136 // Now update the graph during it. |
109 updateSources(["app|foo.txt"]); | 137 updateSources(["app|foo.txt"]); |
110 transformer.resumeApply(); | 138 transformer.resumeApply(); |
111 | 139 |
112 expectAsset("app|foo.out", "foo.out"); | 140 expectAsset("app|foo.out", "foo.out"); |
113 buildShouldSucceed(); | 141 buildShouldSucceed(); |
114 | 142 |
115 expect(transformer.numRuns, completion(equals(2))); | 143 expect(transformer.numRuns, completion(equals(2))); |
116 }); | 144 }); |
117 | 145 |
118 test("aborts processing if the primary input is removed during processing", | 146 test("aborts processing if the primary input is removed during processing", |
119 () { | 147 () { |
120 var transformer = new RewriteTransformer("txt", "out"); | 148 var transformer = new RewriteTransformer("txt", "out"); |
121 initGraph(["app|foo.txt"], {"app": [[transformer]]}); | 149 initGraph([ |
| 150 "app|foo.txt" |
| 151 ], { |
| 152 "app": [ |
| 153 [transformer] |
| 154 ] |
| 155 }); |
122 | 156 |
123 transformer.pauseApply(); | 157 transformer.pauseApply(); |
124 | 158 |
125 updateSources(["app|foo.txt"]); | 159 updateSources(["app|foo.txt"]); |
126 transformer.waitUntilStarted(); | 160 transformer.waitUntilStarted(); |
127 | 161 |
128 // Now remove its primary input while it's running. | 162 // Now remove its primary input while it's running. |
129 removeSources(["app|foo.txt"]); | 163 removeSources(["app|foo.txt"]); |
130 transformer.resumeApply(); | 164 transformer.resumeApply(); |
131 | 165 |
132 expectNoAsset("app|foo.out"); | 166 expectNoAsset("app|foo.out"); |
133 buildShouldSucceed(); | 167 buildShouldSucceed(); |
134 | 168 |
135 expect(transformer.numRuns, completion(equals(1))); | 169 expect(transformer.numRuns, completion(equals(1))); |
136 }); | 170 }); |
137 | 171 |
138 test("restarts processing if a change to a new secondary input occurs during " | 172 test( |
| 173 "restarts processing if a change to a new secondary input occurs during " |
139 "processing", () { | 174 "processing", () { |
140 var transformer = new ManyToOneTransformer("txt"); | 175 var transformer = new ManyToOneTransformer("txt"); |
141 initGraph({ | 176 initGraph({ |
142 "app|foo.txt": "bar.inc", | 177 "app|foo.txt": "bar.inc", |
143 "app|bar.inc": "bar" | 178 "app|bar.inc": "bar" |
144 }, {"app": [[transformer]]}); | 179 }, { |
| 180 "app": [ |
| 181 [transformer] |
| 182 ] |
| 183 }); |
145 | 184 |
146 transformer.pauseApply(); | 185 transformer.pauseApply(); |
147 | 186 |
148 updateSources(["app|foo.txt", "app|bar.inc"]); | 187 updateSources(["app|foo.txt", "app|bar.inc"]); |
149 transformer.waitUntilStarted(); | 188 transformer.waitUntilStarted(); |
150 | 189 |
151 // Give the transform time to load bar.inc the first time. | 190 // Give the transform time to load bar.inc the first time. |
152 schedule(pumpEventQueue); | 191 schedule(pumpEventQueue); |
153 | 192 |
154 // Now update the secondary input before the transform finishes. | 193 // Now update the secondary input before the transform finishes. |
155 modifyAsset("app|bar.inc", "baz"); | 194 modifyAsset("app|bar.inc", "baz"); |
156 updateSources(["app|bar.inc"]); | 195 updateSources(["app|bar.inc"]); |
157 // Give bar.inc enough time to be loaded and marked available before the | 196 // Give bar.inc enough time to be loaded and marked available before the |
158 // transformer completes. | 197 // transformer completes. |
159 schedule(pumpEventQueue); | 198 schedule(pumpEventQueue); |
160 | 199 |
161 transformer.resumeApply(); | 200 transformer.resumeApply(); |
162 | 201 |
163 expectAsset("app|foo.out", "baz"); | 202 expectAsset("app|foo.out", "baz"); |
164 buildShouldSucceed(); | 203 buildShouldSucceed(); |
165 | 204 |
166 expect(transformer.numRuns, completion(equals(2))); | 205 expect(transformer.numRuns, completion(equals(2))); |
167 }); | 206 }); |
168 | 207 |
169 test("doesn't restart processing if a change to an old secondary input " | 208 test( |
| 209 "doesn't restart processing if a change to an old secondary input " |
170 "occurs during processing", () { | 210 "occurs during processing", () { |
171 var transformer = new ManyToOneTransformer("txt"); | 211 var transformer = new ManyToOneTransformer("txt"); |
172 initGraph({ | 212 initGraph({ |
173 "app|foo.txt": "bar.inc", | 213 "app|foo.txt": "bar.inc", |
174 "app|bar.inc": "bar", | 214 "app|bar.inc": "bar", |
175 "app|baz.inc": "baz" | 215 "app|baz.inc": "baz" |
176 }, {"app": [[transformer]]}); | 216 }, { |
| 217 "app": [ |
| 218 [transformer] |
| 219 ] |
| 220 }); |
177 | 221 |
178 updateSources(["app|foo.txt", "app|bar.inc", "app|baz.inc"]); | 222 updateSources(["app|foo.txt", "app|bar.inc", "app|baz.inc"]); |
179 expectAsset("app|foo.out", "bar"); | 223 expectAsset("app|foo.out", "bar"); |
180 buildShouldSucceed(); | 224 buildShouldSucceed(); |
181 | 225 |
182 transformer.pauseApply(); | 226 transformer.pauseApply(); |
183 modifyAsset("app|foo.txt", "baz.inc"); | 227 modifyAsset("app|foo.txt", "baz.inc"); |
184 updateSources(["app|foo.txt"]); | 228 updateSources(["app|foo.txt"]); |
185 transformer.waitUntilStarted(); | 229 transformer.waitUntilStarted(); |
186 | 230 |
187 // Now update the old secondary input before the transform finishes. | 231 // Now update the old secondary input before the transform finishes. |
188 modifyAsset("app|bar.inc", "new bar"); | 232 modifyAsset("app|bar.inc", "new bar"); |
189 updateSources(["app|bar.inc"]); | 233 updateSources(["app|bar.inc"]); |
190 // Give bar.inc enough time to be loaded and marked available before the | 234 // Give bar.inc enough time to be loaded and marked available before the |
191 // transformer completes. | 235 // transformer completes. |
192 schedule(pumpEventQueue); | 236 schedule(pumpEventQueue); |
193 | 237 |
194 transformer.resumeApply(); | 238 transformer.resumeApply(); |
195 expectAsset("app|foo.out", "baz"); | 239 expectAsset("app|foo.out", "baz"); |
196 buildShouldSucceed(); | 240 buildShouldSucceed(); |
197 | 241 |
198 // Should have run once the first time, then again when switching to | 242 // Should have run once the first time, then again when switching to |
199 // baz.inc. Should not run a third time because of bar.inc being modified. | 243 // baz.inc. Should not run a third time because of bar.inc being modified. |
200 expect(transformer.numRuns, completion(equals(2))); | 244 expect(transformer.numRuns, completion(equals(2))); |
201 }); | 245 }); |
202 | 246 |
203 test("restarts before finishing later phases when a change occurs", () { | 247 test("restarts before finishing later phases when a change occurs", () { |
204 var txtToInt = new RewriteTransformer("txt", "int"); | 248 var txtToInt = new RewriteTransformer("txt", "int"); |
205 var intToOut = new RewriteTransformer("int", "out"); | 249 var intToOut = new RewriteTransformer("int", "out"); |
206 initGraph(["app|foo.txt", "app|bar.txt"], | 250 initGraph([ |
207 {"app": [[txtToInt], [intToOut]]}); | 251 "app|foo.txt", |
| 252 "app|bar.txt" |
| 253 ], { |
| 254 "app": [ |
| 255 [txtToInt], |
| 256 [intToOut] |
| 257 ] |
| 258 }); |
208 | 259 |
209 txtToInt.pauseApply(); | 260 txtToInt.pauseApply(); |
210 | 261 |
211 updateSources(["app|foo.txt"]); | 262 updateSources(["app|foo.txt"]); |
212 txtToInt.waitUntilStarted(); | 263 txtToInt.waitUntilStarted(); |
213 | 264 |
214 // Now update the graph during it. | 265 // Now update the graph during it. |
215 updateSources(["app|bar.txt"]); | 266 updateSources(["app|bar.txt"]); |
216 txtToInt.resumeApply(); | 267 txtToInt.resumeApply(); |
217 | 268 |
218 expectAsset("app|foo.out", "foo.int.out"); | 269 expectAsset("app|foo.out", "foo.int.out"); |
219 expectAsset("app|bar.out", "bar.int.out"); | 270 expectAsset("app|bar.out", "bar.int.out"); |
220 buildShouldSucceed(); | 271 buildShouldSucceed(); |
221 | 272 |
222 // Should only have run each transform once for each primary. | 273 // Should only have run each transform once for each primary. |
223 expect(txtToInt.numRuns, completion(equals(2))); | 274 expect(txtToInt.numRuns, completion(equals(2))); |
224 expect(intToOut.numRuns, completion(equals(2))); | 275 expect(intToOut.numRuns, completion(equals(2))); |
225 }); | 276 }); |
226 | 277 |
227 test("doesn't return an asset until it's finished rebuilding", () { | 278 test("doesn't return an asset until it's finished rebuilding", () { |
228 initGraph(["app|foo.in"], {"app": [ | 279 initGraph([ |
229 [new RewriteTransformer("in", "mid")], | 280 "app|foo.in" |
230 [new RewriteTransformer("mid", "out")] | 281 ], { |
231 ]}); | 282 "app": [ |
| 283 [new RewriteTransformer("in", "mid")], |
| 284 [new RewriteTransformer("mid", "out")] |
| 285 ] |
| 286 }); |
232 | 287 |
233 updateSources(["app|foo.in"]); | 288 updateSources(["app|foo.in"]); |
234 expectAsset("app|foo.out", "foo.mid.out"); | 289 expectAsset("app|foo.out", "foo.mid.out"); |
235 buildShouldSucceed(); | 290 buildShouldSucceed(); |
236 | 291 |
237 pauseProvider(); | 292 pauseProvider(); |
238 modifyAsset("app|foo.in", "new"); | 293 modifyAsset("app|foo.in", "new"); |
239 updateSources(["app|foo.in"]); | 294 updateSources(["app|foo.in"]); |
240 expectAssetDoesNotComplete("app|foo.out"); | 295 expectAssetDoesNotComplete("app|foo.out"); |
241 buildShouldNotBeDone(); | 296 buildShouldNotBeDone(); |
242 | 297 |
243 resumeProvider(); | 298 resumeProvider(); |
244 expectAsset("app|foo.out", "new.mid.out"); | 299 expectAsset("app|foo.out", "new.mid.out"); |
245 buildShouldSucceed(); | 300 buildShouldSucceed(); |
246 }); | 301 }); |
247 | 302 |
248 test("doesn't return an asset until its in-place transform is done", () { | 303 test("doesn't return an asset until its in-place transform is done", () { |
249 var rewrite = new RewriteTransformer("txt", "txt"); | 304 var rewrite = new RewriteTransformer("txt", "txt"); |
250 initGraph(["app|foo.txt"], {"app": [[rewrite]]}); | 305 initGraph([ |
| 306 "app|foo.txt" |
| 307 ], { |
| 308 "app": [ |
| 309 [rewrite] |
| 310 ] |
| 311 }); |
251 | 312 |
252 rewrite.pauseApply(); | 313 rewrite.pauseApply(); |
253 updateSources(["app|foo.txt"]); | 314 updateSources(["app|foo.txt"]); |
254 expectAssetDoesNotComplete("app|foo.txt"); | 315 expectAssetDoesNotComplete("app|foo.txt"); |
255 | 316 |
256 rewrite.resumeApply(); | 317 rewrite.resumeApply(); |
257 expectAsset("app|foo.txt", "foo.txt"); | 318 expectAsset("app|foo.txt", "foo.txt"); |
258 buildShouldSucceed(); | 319 buildShouldSucceed(); |
259 }); | 320 }); |
260 | 321 |
261 test("doesn't return an asset that's removed during isPrimary", () { | 322 test("doesn't return an asset that's removed during isPrimary", () { |
262 var rewrite = new RewriteTransformer("txt", "txt"); | 323 var rewrite = new RewriteTransformer("txt", "txt"); |
263 initGraph(["app|foo.txt"], {"app": [[rewrite]]}); | 324 initGraph([ |
| 325 "app|foo.txt" |
| 326 ], { |
| 327 "app": [ |
| 328 [rewrite] |
| 329 ] |
| 330 }); |
264 | 331 |
265 rewrite.pauseIsPrimary("app|foo.txt"); | 332 rewrite.pauseIsPrimary("app|foo.txt"); |
266 updateSources(["app|foo.txt"]); | 333 updateSources(["app|foo.txt"]); |
267 // Make sure we're waiting on isPrimary. | 334 // Make sure we're waiting on isPrimary. |
268 schedule(pumpEventQueue); | 335 schedule(pumpEventQueue); |
269 | 336 |
270 removeSources(["app|foo.txt"]); | 337 removeSources(["app|foo.txt"]); |
271 rewrite.resumeIsPrimary("app|foo.txt"); | 338 rewrite.resumeIsPrimary("app|foo.txt"); |
272 expectNoAsset("app|foo.txt"); | 339 expectNoAsset("app|foo.txt"); |
273 buildShouldSucceed(); | 340 buildShouldSucceed(); |
274 }); | 341 }); |
275 | 342 |
276 test("doesn't transform an asset that goes from primary to non-primary " | 343 test( |
| 344 "doesn't transform an asset that goes from primary to non-primary " |
277 "during isPrimary", () { | 345 "during isPrimary", () { |
278 var check = new CheckContentTransformer(new RegExp(r"^do$"), "ne"); | 346 var check = new CheckContentTransformer(new RegExp(r"^do$"), "ne"); |
279 initGraph({ | 347 initGraph({ |
280 "app|foo.txt": "do" | 348 "app|foo.txt": "do" |
281 }, {"app": [[check]]}); | 349 }, { |
| 350 "app": [ |
| 351 [check] |
| 352 ] |
| 353 }); |
282 | 354 |
283 check.pauseIsPrimary("app|foo.txt"); | 355 check.pauseIsPrimary("app|foo.txt"); |
284 updateSources(["app|foo.txt"]); | 356 updateSources(["app|foo.txt"]); |
285 // Make sure we're waiting on isPrimary. | 357 // Make sure we're waiting on isPrimary. |
286 schedule(pumpEventQueue); | 358 schedule(pumpEventQueue); |
287 | 359 |
288 modifyAsset("app|foo.txt", "don't"); | 360 modifyAsset("app|foo.txt", "don't"); |
289 updateSources(["app|foo.txt"]); | 361 updateSources(["app|foo.txt"]); |
290 check.resumeIsPrimary("app|foo.txt"); | 362 check.resumeIsPrimary("app|foo.txt"); |
291 | 363 |
292 expectAsset("app|foo.txt", "don't"); | 364 expectAsset("app|foo.txt", "don't"); |
293 buildShouldSucceed(); | 365 buildShouldSucceed(); |
294 }); | 366 }); |
295 | 367 |
296 test("transforms an asset that goes from non-primary to primary " | 368 test( |
| 369 "transforms an asset that goes from non-primary to primary " |
297 "during isPrimary", () { | 370 "during isPrimary", () { |
298 var check = new CheckContentTransformer("do", "ne"); | 371 var check = new CheckContentTransformer("do", "ne"); |
299 initGraph({ | 372 initGraph({ |
300 "app|foo.txt": "don't" | 373 "app|foo.txt": "don't" |
301 }, {"app": [[check]]}); | 374 }, { |
| 375 "app": [ |
| 376 [check] |
| 377 ] |
| 378 }); |
302 | 379 |
303 check.pauseIsPrimary("app|foo.txt"); | 380 check.pauseIsPrimary("app|foo.txt"); |
304 updateSources(["app|foo.txt"]); | 381 updateSources(["app|foo.txt"]); |
305 // Make sure we're waiting on isPrimary. | 382 // Make sure we're waiting on isPrimary. |
306 schedule(pumpEventQueue); | 383 schedule(pumpEventQueue); |
307 | 384 |
308 modifyAsset("app|foo.txt", "do"); | 385 modifyAsset("app|foo.txt", "do"); |
309 updateSources(["app|foo.txt"]); | 386 updateSources(["app|foo.txt"]); |
310 check.resumeIsPrimary("app|foo.txt"); | 387 check.resumeIsPrimary("app|foo.txt"); |
311 | 388 |
312 expectAsset("app|foo.txt", "done"); | 389 expectAsset("app|foo.txt", "done"); |
313 buildShouldSucceed(); | 390 buildShouldSucceed(); |
314 }); | 391 }); |
315 | 392 |
316 test("doesn't return an asset that's removed during another transformer's " | 393 test( |
| 394 "doesn't return an asset that's removed during another transformer's " |
317 "isPrimary", () { | 395 "isPrimary", () { |
318 var rewrite1 = new RewriteTransformer("txt", "txt"); | 396 var rewrite1 = new RewriteTransformer("txt", "txt"); |
319 var rewrite2 = new RewriteTransformer("md", "md"); | 397 var rewrite2 = new RewriteTransformer("md", "md"); |
320 initGraph(["app|foo.txt", "app|foo.md"], {"app": [[rewrite1, rewrite2]]}); | 398 initGraph([ |
| 399 "app|foo.txt", |
| 400 "app|foo.md" |
| 401 ], { |
| 402 "app": [ |
| 403 [rewrite1, rewrite2] |
| 404 ] |
| 405 }); |
321 | 406 |
322 rewrite2.pauseIsPrimary("app|foo.md"); | 407 rewrite2.pauseIsPrimary("app|foo.md"); |
323 updateSources(["app|foo.txt", "app|foo.md"]); | 408 updateSources(["app|foo.txt", "app|foo.md"]); |
324 // Make sure we're waiting on the correct isPrimary. | 409 // Make sure we're waiting on the correct isPrimary. |
325 schedule(pumpEventQueue); | 410 schedule(pumpEventQueue); |
326 | 411 |
327 removeSources(["app|foo.txt"]); | 412 removeSources(["app|foo.txt"]); |
328 rewrite2.resumeIsPrimary("app|foo.md"); | 413 rewrite2.resumeIsPrimary("app|foo.md"); |
329 expectNoAsset("app|foo.txt"); | 414 expectNoAsset("app|foo.txt"); |
330 expectAsset("app|foo.md", "foo.md"); | 415 expectAsset("app|foo.md", "foo.md"); |
331 buildShouldSucceed(); | 416 buildShouldSucceed(); |
332 }); | 417 }); |
333 | 418 |
334 test("doesn't transform an asset that goes from primary to non-primary " | 419 test( |
| 420 "doesn't transform an asset that goes from primary to non-primary " |
335 "during another transformer's isPrimary", () { | 421 "during another transformer's isPrimary", () { |
336 var rewrite = new RewriteTransformer("md", "md"); | 422 var rewrite = new RewriteTransformer("md", "md"); |
337 var check = new CheckContentTransformer(new RegExp(r"^do$"), "ne"); | 423 var check = new CheckContentTransformer(new RegExp(r"^do$"), "ne"); |
338 initGraph({ | 424 initGraph({ |
339 "app|foo.txt": "do", | 425 "app|foo.txt": "do", |
340 "app|foo.md": "foo" | 426 "app|foo.md": "foo" |
341 }, {"app": [[rewrite, check]]}); | 427 }, { |
| 428 "app": [ |
| 429 [rewrite, check] |
| 430 ] |
| 431 }); |
342 | 432 |
343 rewrite.pauseIsPrimary("app|foo.md"); | 433 rewrite.pauseIsPrimary("app|foo.md"); |
344 updateSources(["app|foo.txt", "app|foo.md"]); | 434 updateSources(["app|foo.txt", "app|foo.md"]); |
345 // Make sure we're waiting on the correct isPrimary. | 435 // Make sure we're waiting on the correct isPrimary. |
346 schedule(pumpEventQueue); | 436 schedule(pumpEventQueue); |
347 | 437 |
348 modifyAsset("app|foo.txt", "don't"); | 438 modifyAsset("app|foo.txt", "don't"); |
349 updateSources(["app|foo.txt"]); | 439 updateSources(["app|foo.txt"]); |
350 rewrite.resumeIsPrimary("app|foo.md"); | 440 rewrite.resumeIsPrimary("app|foo.md"); |
351 | 441 |
352 expectAsset("app|foo.txt", "don't"); | 442 expectAsset("app|foo.txt", "don't"); |
353 expectAsset("app|foo.md", "foo.md"); | 443 expectAsset("app|foo.md", "foo.md"); |
354 buildShouldSucceed(); | 444 buildShouldSucceed(); |
355 }); | 445 }); |
356 | 446 |
357 test("transforms an asset that goes from non-primary to primary " | 447 test( |
| 448 "transforms an asset that goes from non-primary to primary " |
358 "during another transformer's isPrimary", () { | 449 "during another transformer's isPrimary", () { |
359 var rewrite = new RewriteTransformer("md", "md"); | 450 var rewrite = new RewriteTransformer("md", "md"); |
360 var check = new CheckContentTransformer("do", "ne"); | 451 var check = new CheckContentTransformer("do", "ne"); |
361 initGraph({ | 452 initGraph({ |
362 "app|foo.txt": "don't", | 453 "app|foo.txt": "don't", |
363 "app|foo.md": "foo" | 454 "app|foo.md": "foo" |
364 }, {"app": [[rewrite, check]]}); | 455 }, { |
| 456 "app": [ |
| 457 [rewrite, check] |
| 458 ] |
| 459 }); |
365 | 460 |
366 rewrite.pauseIsPrimary("app|foo.md"); | 461 rewrite.pauseIsPrimary("app|foo.md"); |
367 updateSources(["app|foo.txt", "app|foo.md"]); | 462 updateSources(["app|foo.txt", "app|foo.md"]); |
368 // Make sure we're waiting on the correct isPrimary. | 463 // Make sure we're waiting on the correct isPrimary. |
369 schedule(pumpEventQueue); | 464 schedule(pumpEventQueue); |
370 | 465 |
371 modifyAsset("app|foo.txt", "do"); | 466 modifyAsset("app|foo.txt", "do"); |
372 updateSources(["app|foo.txt"]); | 467 updateSources(["app|foo.txt"]); |
373 rewrite.resumeIsPrimary("app|foo.md"); | 468 rewrite.resumeIsPrimary("app|foo.md"); |
374 | 469 |
375 expectAsset("app|foo.txt", "done"); | 470 expectAsset("app|foo.txt", "done"); |
376 expectAsset("app|foo.md", "foo.md"); | 471 expectAsset("app|foo.md", "foo.md"); |
377 buildShouldSucceed(); | 472 buildShouldSucceed(); |
378 }); | 473 }); |
379 | 474 |
380 test("returns an asset even if an unrelated build is running", () { | 475 test("returns an asset even if an unrelated build is running", () { |
381 initGraph([ | 476 initGraph([ |
382 "app|foo.in", | 477 "app|foo.in", |
383 "app|bar.in", | 478 "app|bar.in", |
384 ], {"app": [[new RewriteTransformer("in", "out")]]}); | 479 ], { |
| 480 "app": [ |
| 481 [new RewriteTransformer("in", "out")] |
| 482 ] |
| 483 }); |
385 | 484 |
386 updateSources(["app|foo.in", "app|bar.in"]); | 485 updateSources(["app|foo.in", "app|bar.in"]); |
387 expectAsset("app|foo.out", "foo.out"); | 486 expectAsset("app|foo.out", "foo.out"); |
388 expectAsset("app|bar.out", "bar.out"); | 487 expectAsset("app|bar.out", "bar.out"); |
389 buildShouldSucceed(); | 488 buildShouldSucceed(); |
390 | 489 |
391 pauseProvider(); | 490 pauseProvider(); |
392 modifyAsset("app|foo.in", "new"); | 491 modifyAsset("app|foo.in", "new"); |
393 updateSources(["app|foo.in"]); | 492 updateSources(["app|foo.in"]); |
394 expectAssetDoesNotComplete("app|foo.out"); | 493 expectAssetDoesNotComplete("app|foo.out"); |
395 expectAsset("app|bar.out", "bar.out"); | 494 expectAsset("app|bar.out", "bar.out"); |
396 buildShouldNotBeDone(); | 495 buildShouldNotBeDone(); |
397 | 496 |
398 resumeProvider(); | 497 resumeProvider(); |
399 expectAsset("app|foo.out", "new.out"); | 498 expectAsset("app|foo.out", "new.out"); |
400 buildShouldSucceed(); | 499 buildShouldSucceed(); |
401 }); | 500 }); |
402 | 501 |
403 test("doesn't report AssetNotFound until all builds are finished", () { | 502 test("doesn't report AssetNotFound until all builds are finished", () { |
404 initGraph([ | 503 initGraph([ |
405 "app|foo.in", | 504 "app|foo.in", |
406 ], {"app": [[new RewriteTransformer("in", "out")]]}); | 505 ], { |
| 506 "app": [ |
| 507 [new RewriteTransformer("in", "out")] |
| 508 ] |
| 509 }); |
407 | 510 |
408 updateSources(["app|foo.in"]); | 511 updateSources(["app|foo.in"]); |
409 expectAsset("app|foo.out", "foo.out"); | 512 expectAsset("app|foo.out", "foo.out"); |
410 buildShouldSucceed(); | 513 buildShouldSucceed(); |
411 | 514 |
412 pauseProvider(); | 515 pauseProvider(); |
413 updateSources(["app|foo.in"]); | 516 updateSources(["app|foo.in"]); |
414 expectAssetDoesNotComplete("app|foo.out"); | 517 expectAssetDoesNotComplete("app|foo.out"); |
415 expectAssetDoesNotComplete("app|non-existent.out"); | 518 expectAssetDoesNotComplete("app|non-existent.out"); |
416 buildShouldNotBeDone(); | 519 buildShouldNotBeDone(); |
417 | 520 |
418 resumeProvider(); | 521 resumeProvider(); |
419 expectAsset("app|foo.out", "foo.out"); | 522 expectAsset("app|foo.out", "foo.out"); |
420 expectNoAsset("app|non-existent.out"); | 523 expectNoAsset("app|non-existent.out"); |
421 buildShouldSucceed(); | 524 buildShouldSucceed(); |
422 }); | 525 }); |
423 | 526 |
424 test("doesn't emit a result until all builds are finished", () { | 527 test("doesn't emit a result until all builds are finished", () { |
425 var rewrite = new RewriteTransformer("txt", "out"); | 528 var rewrite = new RewriteTransformer("txt", "out"); |
426 initGraph([ | 529 initGraph([ |
427 "pkg1|foo.txt", | 530 "pkg1|foo.txt", |
428 "pkg2|foo.txt" | 531 "pkg2|foo.txt" |
429 ], {"pkg1": [[rewrite]], "pkg2": [[rewrite]]}); | 532 ], { |
| 533 "pkg1": [ |
| 534 [rewrite] |
| 535 ], |
| 536 "pkg2": [ |
| 537 [rewrite] |
| 538 ] |
| 539 }); |
430 | 540 |
431 // First, run both packages' transformers so both packages are successful. | 541 // First, run both packages' transformers so both packages are successful. |
432 updateSources(["pkg1|foo.txt", "pkg2|foo.txt"]); | 542 updateSources(["pkg1|foo.txt", "pkg2|foo.txt"]); |
433 expectAsset("pkg1|foo.out", "foo.out"); | 543 expectAsset("pkg1|foo.out", "foo.out"); |
434 expectAsset("pkg2|foo.out", "foo.out"); | 544 expectAsset("pkg2|foo.out", "foo.out"); |
435 buildShouldSucceed(); | 545 buildShouldSucceed(); |
436 | 546 |
437 // pkg1 is still successful, but pkg2 is waiting on the provider, so the | 547 // pkg1 is still successful, but pkg2 is waiting on the provider, so the |
438 // overall build shouldn't finish. | 548 // overall build shouldn't finish. |
439 pauseProvider(); | 549 pauseProvider(); |
440 updateSources(["pkg2|foo.txt"]); | 550 updateSources(["pkg2|foo.txt"]); |
441 expectAsset("pkg1|foo.out", "foo.out"); | 551 expectAsset("pkg1|foo.out", "foo.out"); |
442 buildShouldNotBeDone(); | 552 buildShouldNotBeDone(); |
443 | 553 |
444 // Now that the provider is unpaused, pkg2's transforms finish and the | 554 // Now that the provider is unpaused, pkg2's transforms finish and the |
445 // overall build succeeds. | 555 // overall build succeeds. |
446 resumeProvider(); | 556 resumeProvider(); |
447 buildShouldSucceed(); | 557 buildShouldSucceed(); |
448 }); | 558 }); |
449 | 559 |
450 test("one transformer takes a long time while the other finishes, then " | 560 test( |
| 561 "one transformer takes a long time while the other finishes, then " |
451 "the input is removed", () { | 562 "the input is removed", () { |
452 var rewrite1 = new RewriteTransformer("txt", "out1"); | 563 var rewrite1 = new RewriteTransformer("txt", "out1"); |
453 var rewrite2 = new RewriteTransformer("txt", "out2"); | 564 var rewrite2 = new RewriteTransformer("txt", "out2"); |
454 initGraph(["app|foo.txt"], {"app": [[rewrite1, rewrite2]]}); | 565 initGraph([ |
| 566 "app|foo.txt" |
| 567 ], { |
| 568 "app": [ |
| 569 [rewrite1, rewrite2] |
| 570 ] |
| 571 }); |
455 | 572 |
456 rewrite1.pauseApply(); | 573 rewrite1.pauseApply(); |
457 | 574 |
458 updateSources(["app|foo.txt"]); | 575 updateSources(["app|foo.txt"]); |
459 | 576 |
460 // Wait for rewrite1 to pause and rewrite2 to finish. | 577 // Wait for rewrite1 to pause and rewrite2 to finish. |
461 schedule(pumpEventQueue); | 578 schedule(pumpEventQueue); |
462 | 579 |
463 removeSources(["app|foo.txt"]); | 580 removeSources(["app|foo.txt"]); |
464 | 581 |
465 // Make sure the removal is processed completely before we restart rewrite2. | 582 // Make sure the removal is processed completely before we restart rewrite2. |
466 schedule(pumpEventQueue); | 583 schedule(pumpEventQueue); |
467 rewrite1.resumeApply(); | 584 rewrite1.resumeApply(); |
468 | 585 |
469 buildShouldSucceed(); | 586 buildShouldSucceed(); |
470 expectNoAsset("app|foo.out1"); | 587 expectNoAsset("app|foo.out1"); |
471 expectNoAsset("app|foo.out2"); | 588 expectNoAsset("app|foo.out2"); |
472 }); | 589 }); |
473 | 590 |
474 test("a transformer in a later phase gets a slow secondary input from an " | 591 test( |
| 592 "a transformer in a later phase gets a slow secondary input from an " |
475 "earlier phase", () { | 593 "earlier phase", () { |
476 var rewrite = new RewriteTransformer("in", "in"); | 594 var rewrite = new RewriteTransformer("in", "in"); |
477 initGraph({ | 595 initGraph({ |
478 "app|foo.in": "foo", | 596 "app|foo.in": "foo", |
479 "app|bar.txt": "foo.in" | 597 "app|bar.txt": "foo.in" |
480 }, {"app": [ | 598 }, { |
481 [rewrite], | 599 "app": [ |
482 [new ManyToOneTransformer("txt")] | 600 [rewrite], |
483 ]}); | 601 [new ManyToOneTransformer("txt")] |
| 602 ] |
| 603 }); |
484 | 604 |
485 rewrite.pauseApply(); | 605 rewrite.pauseApply(); |
486 updateSources(["app|foo.in", "app|bar.txt"]); | 606 updateSources(["app|foo.in", "app|bar.txt"]); |
487 expectAssetDoesNotComplete("app|bar.out"); | 607 expectAssetDoesNotComplete("app|bar.out"); |
488 | 608 |
489 rewrite.resumeApply(); | 609 rewrite.resumeApply(); |
490 expectAsset("app|bar.out", "foo.in"); | 610 expectAsset("app|bar.out", "foo.in"); |
491 buildShouldSucceed(); | 611 buildShouldSucceed(); |
492 }); | 612 }); |
493 | 613 |
494 test("materializes a passed-through asset that was emitted before it was " | 614 test( |
| 615 "materializes a passed-through asset that was emitted before it was " |
495 "available", () { | 616 "available", () { |
496 initGraph(["app|foo.in"], {"app": [ | 617 initGraph([ |
497 [new RewriteTransformer("txt", "txt")] | 618 "app|foo.in" |
498 ]}); | 619 ], { |
| 620 "app": [ |
| 621 [new RewriteTransformer("txt", "txt")] |
| 622 ] |
| 623 }); |
499 | 624 |
500 pauseProvider(); | 625 pauseProvider(); |
501 updateSources(["app|foo.in"]); | 626 updateSources(["app|foo.in"]); |
502 expectAssetDoesNotComplete("app|foo.in"); | 627 expectAssetDoesNotComplete("app|foo.in"); |
503 | 628 |
504 resumeProvider(); | 629 resumeProvider(); |
505 expectAsset("app|foo.in", "foo"); | 630 expectAsset("app|foo.in", "foo"); |
506 buildShouldSucceed(); | 631 buildShouldSucceed(); |
507 }); | 632 }); |
508 | 633 |
509 test("re-runs if the primary input is invalidated before accessing", () { | 634 test("re-runs if the primary input is invalidated before accessing", () { |
510 var transformer1 = new RewriteTransformer("txt", "mid"); | 635 var transformer1 = new RewriteTransformer("txt", "mid"); |
511 var transformer2 = new RewriteTransformer("mid", "out"); | 636 var transformer2 = new RewriteTransformer("mid", "out"); |
512 | 637 |
513 initGraph([ | 638 initGraph([ |
514 "app|foo.txt" | 639 "app|foo.txt" |
515 ], {"app": [ | 640 ], { |
516 [transformer1], | 641 "app": [ |
517 [transformer2] | 642 [transformer1], |
518 ]}); | 643 [transformer2] |
| 644 ] |
| 645 }); |
519 | 646 |
520 transformer2.pausePrimaryInput(); | 647 transformer2.pausePrimaryInput(); |
521 updateSources(["app|foo.txt"]); | 648 updateSources(["app|foo.txt"]); |
522 | 649 |
523 // Wait long enough to ensure that transformer1 has completed and | 650 // Wait long enough to ensure that transformer1 has completed and |
524 // transformer2 has started. | 651 // transformer2 has started. |
525 schedule(pumpEventQueue); | 652 schedule(pumpEventQueue); |
526 | 653 |
527 // Update the source again so that transformer1 invalidates the primary | 654 // Update the source again so that transformer1 invalidates the primary |
528 // input of transformer2. | 655 // input of transformer2. |
529 transformer1.pauseApply(); | 656 transformer1.pauseApply(); |
530 modifyAsset("app|foo.txt", "new foo"); | 657 modifyAsset("app|foo.txt", "new foo"); |
531 updateSources(["app|foo.txt"]); | 658 updateSources(["app|foo.txt"]); |
532 | 659 |
533 transformer2.resumePrimaryInput(); | 660 transformer2.resumePrimaryInput(); |
534 transformer1.resumeApply(); | 661 transformer1.resumeApply(); |
535 | 662 |
536 expectAsset("app|foo.out", "new foo.mid.out"); | 663 expectAsset("app|foo.out", "new foo.mid.out"); |
537 buildShouldSucceed(); | 664 buildShouldSucceed(); |
538 | 665 |
539 expect(transformer1.numRuns, completion(equals(2))); | 666 expect(transformer1.numRuns, completion(equals(2))); |
540 expect(transformer2.numRuns, completion(equals(2))); | 667 expect(transformer2.numRuns, completion(equals(2))); |
541 }); | 668 }); |
542 | 669 |
543 // Regression test for issue 19038. | 670 // Regression test for issue 19038. |
544 test("a secondary input that's marked dirty followed by the primary input " | 671 test( |
| 672 "a secondary input that's marked dirty followed by the primary input " |
545 "being synchronously marked dirty re-runs a transformer", () { | 673 "being synchronously marked dirty re-runs a transformer", () { |
546 // Issue 19038 was caused by the following sequence of events: | 674 // Issue 19038 was caused by the following sequence of events: |
547 // | 675 // |
548 // * Several inputs are marked dirty at once, causing dirty events to | 676 // * Several inputs are marked dirty at once, causing dirty events to |
549 // propagate synchronously throughout the transform graph. | 677 // propagate synchronously throughout the transform graph. |
550 // | 678 // |
551 // * A transform (ManyToOneTransformer in this test case) has a secondary | 679 // * A transform (ManyToOneTransformer in this test case) has a secondary |
552 // input ("one.in") and a primary input ("foo.txt") that will both be | 680 // input ("one.in") and a primary input ("foo.txt") that will both be |
553 // marked dirty. | 681 // marked dirty. |
554 // | 682 // |
555 // * The secondary input is marked dirty before the primary input. This | 683 // * The secondary input is marked dirty before the primary input. This |
556 // causes the transform to start running `apply`. Since as far as it knows | 684 // causes the transform to start running `apply`. Since as far as it knows |
557 // its primary input is still available, it passes that input to `apply`. | 685 // its primary input is still available, it passes that input to `apply`. |
558 // | 686 // |
559 // * Now the primary input is marked dirty. The transform node checks to see | 687 // * Now the primary input is marked dirty. The transform node checks to see |
560 // if this primary input has already been added to the transform | 688 // if this primary input has already been added to the transform |
561 // controller. This is where the bug existed: the answer to this was | 689 // controller. This is where the bug existed: the answer to this was |
562 // incorrectly "no" until after some asynchronous processing occurred. | 690 // incorrectly "no" until after some asynchronous processing occurred. |
563 // | 691 // |
564 // * Since the transform thought the primary input hadn't yet been passed to | 692 // * Since the transform thought the primary input hadn't yet been passed to |
565 // the transform controller, it didn't bother restarting the transform, | 693 // the transform controller, it didn't bother restarting the transform, |
566 // causing the old output to be preserved incorrectly. | 694 // causing the old output to be preserved incorrectly. |
567 initGraph({ | 695 initGraph({ |
568 "app|foo.txt": "one", | 696 "app|foo.txt": "one", |
569 "app|one.in": "1", | 697 "app|one.in": "1", |
570 "app|two.in": "2" | 698 "app|two.in": "2" |
571 }, {"app": [ | 699 }, { |
572 // We need to use CheckContentTransformer here so that | 700 "app": [ |
573 // ManyToOneTransformer reads its primary input from memory rather than | 701 // We need to use CheckContentTransformer here so that |
574 // from the filesystem. If it read from the filesystem, it might | 702 // ManyToOneTransformer reads its primary input from memory rather than |
575 // accidentally get the correct output despite accessing the incorrect | 703 // from the filesystem. If it read from the filesystem, it might |
576 // asset, which would cause false positives for the test. | 704 // accidentally get the correct output despite accessing the incorrect |
577 [new CheckContentTransformer(new RegExp("one|two"), ".in")], | 705 // asset, which would cause false positives for the test. |
578 [new ManyToOneTransformer("txt")] | 706 [new CheckContentTransformer(new RegExp("one|two"), ".in")], |
579 ]}); | 707 [new ManyToOneTransformer("txt")] |
| 708 ] |
| 709 }); |
580 | 710 |
581 updateSources(["app|foo.txt", "app|one.in", "app|two.in"]); | 711 updateSources(["app|foo.txt", "app|one.in", "app|two.in"]); |
582 expectAsset("app|foo.out", "1"); | 712 expectAsset("app|foo.out", "1"); |
583 buildShouldSucceed(); | 713 buildShouldSucceed(); |
584 | 714 |
585 modifyAsset("app|foo.txt", "two"); | 715 modifyAsset("app|foo.txt", "two"); |
586 | 716 |
587 // It's important that "one.in" come first in this list, since | 717 // It's important that "one.in" come first in this list, since |
588 // ManyToOneTransformer needs to see its secondary input change first. | 718 // ManyToOneTransformer needs to see its secondary input change first. |
589 updateSources(["app|one.in", "app|foo.txt"]); | 719 updateSources(["app|one.in", "app|foo.txt"]); |
590 | 720 |
591 expectAsset("app|foo.out", "2"); | 721 expectAsset("app|foo.out", "2"); |
592 buildShouldSucceed(); | 722 buildShouldSucceed(); |
593 }); | 723 }); |
594 } | 724 } |
OLD | NEW |