OLD | NEW |
| (Empty) |
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 | |
3 // BSD-style license that can be found in the LICENSE file. | |
4 | |
5 /// This library contains tests for transformer behavior that relates to actions | |
6 /// happening concurrently or other complex asynchronous timing behavior. | |
7 library barback.test.package_graph.transform.concurrency_test; | |
8 | |
9 import 'package:barback/src/utils.dart'; | |
10 import 'package:scheduled_test/scheduled_test.dart'; | |
11 | |
12 import '../../utils.dart'; | |
13 | |
14 main() { | |
15 initConfig(); | |
16 test("runs transforms in the same phase in parallel", () { | |
17 var transformerA = new RewriteTransformer("txt", "a"); | |
18 var transformerB = new RewriteTransformer("txt", "b"); | |
19 initGraph(["app|foo.txt"], {"app": [[transformerA, transformerB]]}); | |
20 | |
21 transformerA.pauseApply(); | |
22 transformerB.pauseApply(); | |
23 | |
24 updateSources(["app|foo.txt"]); | |
25 | |
26 transformerA.waitUntilStarted(); | |
27 transformerB.waitUntilStarted(); | |
28 | |
29 // They should both still be running. | |
30 expect(transformerA.isRunning, completion(isTrue)); | |
31 expect(transformerB.isRunning, completion(isTrue)); | |
32 | |
33 transformerA.resumeApply(); | |
34 transformerB.resumeApply(); | |
35 | |
36 expectAsset("app|foo.a", "foo.a"); | |
37 expectAsset("app|foo.b", "foo.b"); | |
38 buildShouldSucceed(); | |
39 }); | |
40 | |
41 test("discards outputs from a transform whose primary input is removed " | |
42 "during processing", () { | |
43 var rewrite = new RewriteTransformer("txt", "out"); | |
44 initGraph(["app|foo.txt"], {"app": [[rewrite]]}); | |
45 | |
46 rewrite.pauseApply(); | |
47 updateSources(["app|foo.txt"]); | |
48 rewrite.waitUntilStarted(); | |
49 | |
50 removeSources(["app|foo.txt"]); | |
51 rewrite.resumeApply(); | |
52 expectNoAsset("app|foo.out"); | |
53 buildShouldSucceed(); | |
54 }); | |
55 | |
56 test("applies the correct transform if an asset is modified during isPrimary", | |
57 () { | |
58 var check1 = new CheckContentTransformer("first", "#1"); | |
59 var check2 = new CheckContentTransformer("second", "#2"); | |
60 initGraph({ | |
61 "app|foo.txt": "first", | |
62 }, {"app": [[check1, check2]]}); | |
63 | |
64 check1.pauseIsPrimary("app|foo.txt"); | |
65 updateSources(["app|foo.txt"]); | |
66 // Ensure that we're waiting on check1's isPrimary. | |
67 schedule(pumpEventQueue); | |
68 | |
69 modifyAsset("app|foo.txt", "second"); | |
70 updateSources(["app|foo.txt"]); | |
71 check1.resumeIsPrimary("app|foo.txt"); | |
72 | |
73 expectAsset("app|foo.txt", "second#2"); | |
74 buildShouldSucceed(); | |
75 }); | |
76 | |
77 test("applies the correct transform if an asset is removed and added during " | |
78 "isPrimary", () { | |
79 var check1 = new CheckContentTransformer("first", "#1"); | |
80 var check2 = new CheckContentTransformer("second", "#2"); | |
81 initGraph({ | |
82 "app|foo.txt": "first", | |
83 }, {"app": [[check1, check2]]}); | |
84 | |
85 check1.pauseIsPrimary("app|foo.txt"); | |
86 updateSources(["app|foo.txt"]); | |
87 // Ensure that we're waiting on check1's isPrimary. | |
88 schedule(pumpEventQueue); | |
89 | |
90 removeSources(["app|foo.txt"]); | |
91 modifyAsset("app|foo.txt", "second"); | |
92 updateSources(["app|foo.txt"]); | |
93 check1.resumeIsPrimary("app|foo.txt"); | |
94 | |
95 expectAsset("app|foo.txt", "second#2"); | |
96 buildShouldSucceed(); | |
97 }); | |
98 | |
99 test("restarts processing if a change occurs during processing", () { | |
100 var transformer = new RewriteTransformer("txt", "out"); | |
101 initGraph(["app|foo.txt"], {"app": [[transformer]]}); | |
102 | |
103 transformer.pauseApply(); | |
104 | |
105 updateSources(["app|foo.txt"]); | |
106 transformer.waitUntilStarted(); | |
107 | |
108 // Now update the graph during it. | |
109 updateSources(["app|foo.txt"]); | |
110 transformer.resumeApply(); | |
111 | |
112 expectAsset("app|foo.out", "foo.out"); | |
113 buildShouldSucceed(); | |
114 | |
115 expect(transformer.numRuns, completion(equals(2))); | |
116 }); | |
117 | |
118 test("aborts processing if the primary input is removed during processing", | |
119 () { | |
120 var transformer = new RewriteTransformer("txt", "out"); | |
121 initGraph(["app|foo.txt"], {"app": [[transformer]]}); | |
122 | |
123 transformer.pauseApply(); | |
124 | |
125 updateSources(["app|foo.txt"]); | |
126 transformer.waitUntilStarted(); | |
127 | |
128 // Now remove its primary input while it's running. | |
129 removeSources(["app|foo.txt"]); | |
130 transformer.resumeApply(); | |
131 | |
132 expectNoAsset("app|foo.out"); | |
133 buildShouldSucceed(); | |
134 | |
135 expect(transformer.numRuns, completion(equals(1))); | |
136 }); | |
137 | |
138 test("restarts processing if a change to a new secondary input occurs during " | |
139 "processing", () { | |
140 var transformer = new ManyToOneTransformer("txt"); | |
141 initGraph({ | |
142 "app|foo.txt": "bar.inc", | |
143 "app|bar.inc": "bar" | |
144 }, {"app": [[transformer]]}); | |
145 | |
146 transformer.pauseApply(); | |
147 | |
148 updateSources(["app|foo.txt", "app|bar.inc"]); | |
149 transformer.waitUntilStarted(); | |
150 | |
151 // Give the transform time to load bar.inc the first time. | |
152 schedule(pumpEventQueue); | |
153 | |
154 // Now update the secondary input before the transform finishes. | |
155 modifyAsset("app|bar.inc", "baz"); | |
156 updateSources(["app|bar.inc"]); | |
157 // Give bar.inc enough time to be loaded and marked available before the | |
158 // transformer completes. | |
159 schedule(pumpEventQueue); | |
160 | |
161 transformer.resumeApply(); | |
162 | |
163 expectAsset("app|foo.out", "baz"); | |
164 buildShouldSucceed(); | |
165 | |
166 expect(transformer.numRuns, completion(equals(2))); | |
167 }); | |
168 | |
169 test("doesn't restart processing if a change to an old secondary input " | |
170 "occurs during processing", () { | |
171 var transformer = new ManyToOneTransformer("txt"); | |
172 initGraph({ | |
173 "app|foo.txt": "bar.inc", | |
174 "app|bar.inc": "bar", | |
175 "app|baz.inc": "baz" | |
176 }, {"app": [[transformer]]}); | |
177 | |
178 updateSources(["app|foo.txt", "app|bar.inc", "app|baz.inc"]); | |
179 expectAsset("app|foo.out", "bar"); | |
180 buildShouldSucceed(); | |
181 | |
182 transformer.pauseApply(); | |
183 modifyAsset("app|foo.txt", "baz.inc"); | |
184 updateSources(["app|foo.txt"]); | |
185 transformer.waitUntilStarted(); | |
186 | |
187 // Now update the old secondary input before the transform finishes. | |
188 modifyAsset("app|bar.inc", "new bar"); | |
189 updateSources(["app|bar.inc"]); | |
190 // Give bar.inc enough time to be loaded and marked available before the | |
191 // transformer completes. | |
192 schedule(pumpEventQueue); | |
193 | |
194 transformer.resumeApply(); | |
195 expectAsset("app|foo.out", "baz"); | |
196 buildShouldSucceed(); | |
197 | |
198 // 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. | |
200 expect(transformer.numRuns, completion(equals(2))); | |
201 }); | |
202 | |
203 test("restarts before finishing later phases when a change occurs", () { | |
204 var txtToInt = new RewriteTransformer("txt", "int"); | |
205 var intToOut = new RewriteTransformer("int", "out"); | |
206 initGraph(["app|foo.txt", "app|bar.txt"], | |
207 {"app": [[txtToInt], [intToOut]]}); | |
208 | |
209 txtToInt.pauseApply(); | |
210 | |
211 updateSources(["app|foo.txt"]); | |
212 txtToInt.waitUntilStarted(); | |
213 | |
214 // Now update the graph during it. | |
215 updateSources(["app|bar.txt"]); | |
216 txtToInt.resumeApply(); | |
217 | |
218 expectAsset("app|foo.out", "foo.int.out"); | |
219 expectAsset("app|bar.out", "bar.int.out"); | |
220 buildShouldSucceed(); | |
221 | |
222 // Should only have run each transform once for each primary. | |
223 expect(txtToInt.numRuns, completion(equals(2))); | |
224 expect(intToOut.numRuns, completion(equals(2))); | |
225 }); | |
226 | |
227 test("doesn't return an asset until it's finished rebuilding", () { | |
228 initGraph(["app|foo.in"], {"app": [ | |
229 [new RewriteTransformer("in", "mid")], | |
230 [new RewriteTransformer("mid", "out")] | |
231 ]}); | |
232 | |
233 updateSources(["app|foo.in"]); | |
234 expectAsset("app|foo.out", "foo.mid.out"); | |
235 buildShouldSucceed(); | |
236 | |
237 pauseProvider(); | |
238 modifyAsset("app|foo.in", "new"); | |
239 updateSources(["app|foo.in"]); | |
240 expectAssetDoesNotComplete("app|foo.out"); | |
241 buildShouldNotBeDone(); | |
242 | |
243 resumeProvider(); | |
244 expectAsset("app|foo.out", "new.mid.out"); | |
245 buildShouldSucceed(); | |
246 }); | |
247 | |
248 test("doesn't return an asset until its in-place transform is done", () { | |
249 var rewrite = new RewriteTransformer("txt", "txt"); | |
250 initGraph(["app|foo.txt"], {"app": [[rewrite]]}); | |
251 | |
252 rewrite.pauseApply(); | |
253 updateSources(["app|foo.txt"]); | |
254 expectAssetDoesNotComplete("app|foo.txt"); | |
255 | |
256 rewrite.resumeApply(); | |
257 expectAsset("app|foo.txt", "foo.txt"); | |
258 buildShouldSucceed(); | |
259 }); | |
260 | |
261 test("doesn't return an asset that's removed during isPrimary", () { | |
262 var rewrite = new RewriteTransformer("txt", "txt"); | |
263 initGraph(["app|foo.txt"], {"app": [[rewrite]]}); | |
264 | |
265 rewrite.pauseIsPrimary("app|foo.txt"); | |
266 updateSources(["app|foo.txt"]); | |
267 // Make sure we're waiting on isPrimary. | |
268 schedule(pumpEventQueue); | |
269 | |
270 removeSources(["app|foo.txt"]); | |
271 rewrite.resumeIsPrimary("app|foo.txt"); | |
272 expectNoAsset("app|foo.txt"); | |
273 buildShouldSucceed(); | |
274 }); | |
275 | |
276 test("doesn't transform an asset that goes from primary to non-primary " | |
277 "during isPrimary", () { | |
278 var check = new CheckContentTransformer(new RegExp(r"^do$"), "ne"); | |
279 initGraph({ | |
280 "app|foo.txt": "do" | |
281 }, {"app": [[check]]}); | |
282 | |
283 check.pauseIsPrimary("app|foo.txt"); | |
284 updateSources(["app|foo.txt"]); | |
285 // Make sure we're waiting on isPrimary. | |
286 schedule(pumpEventQueue); | |
287 | |
288 modifyAsset("app|foo.txt", "don't"); | |
289 updateSources(["app|foo.txt"]); | |
290 check.resumeIsPrimary("app|foo.txt"); | |
291 | |
292 expectAsset("app|foo.txt", "don't"); | |
293 buildShouldSucceed(); | |
294 }); | |
295 | |
296 test("transforms an asset that goes from non-primary to primary " | |
297 "during isPrimary", () { | |
298 var check = new CheckContentTransformer("do", "ne"); | |
299 initGraph({ | |
300 "app|foo.txt": "don't" | |
301 }, {"app": [[check]]}); | |
302 | |
303 check.pauseIsPrimary("app|foo.txt"); | |
304 updateSources(["app|foo.txt"]); | |
305 // Make sure we're waiting on isPrimary. | |
306 schedule(pumpEventQueue); | |
307 | |
308 modifyAsset("app|foo.txt", "do"); | |
309 updateSources(["app|foo.txt"]); | |
310 check.resumeIsPrimary("app|foo.txt"); | |
311 | |
312 expectAsset("app|foo.txt", "done"); | |
313 buildShouldSucceed(); | |
314 }); | |
315 | |
316 test("doesn't return an asset that's removed during another transformer's " | |
317 "isPrimary", () { | |
318 var rewrite1 = new RewriteTransformer("txt", "txt"); | |
319 var rewrite2 = new RewriteTransformer("md", "md"); | |
320 initGraph(["app|foo.txt", "app|foo.md"], {"app": [[rewrite1, rewrite2]]}); | |
321 | |
322 rewrite2.pauseIsPrimary("app|foo.md"); | |
323 updateSources(["app|foo.txt", "app|foo.md"]); | |
324 // Make sure we're waiting on the correct isPrimary. | |
325 schedule(pumpEventQueue); | |
326 | |
327 removeSources(["app|foo.txt"]); | |
328 rewrite2.resumeIsPrimary("app|foo.md"); | |
329 expectNoAsset("app|foo.txt"); | |
330 expectAsset("app|foo.md", "foo.md"); | |
331 buildShouldSucceed(); | |
332 }); | |
333 | |
334 test("doesn't transform an asset that goes from primary to non-primary " | |
335 "during another transformer's isPrimary", () { | |
336 var rewrite = new RewriteTransformer("md", "md"); | |
337 var check = new CheckContentTransformer(new RegExp(r"^do$"), "ne"); | |
338 initGraph({ | |
339 "app|foo.txt": "do", | |
340 "app|foo.md": "foo" | |
341 }, {"app": [[rewrite, check]]}); | |
342 | |
343 rewrite.pauseIsPrimary("app|foo.md"); | |
344 updateSources(["app|foo.txt", "app|foo.md"]); | |
345 // Make sure we're waiting on the correct isPrimary. | |
346 schedule(pumpEventQueue); | |
347 | |
348 modifyAsset("app|foo.txt", "don't"); | |
349 updateSources(["app|foo.txt"]); | |
350 rewrite.resumeIsPrimary("app|foo.md"); | |
351 | |
352 expectAsset("app|foo.txt", "don't"); | |
353 expectAsset("app|foo.md", "foo.md"); | |
354 buildShouldSucceed(); | |
355 }); | |
356 | |
357 test("transforms an asset that goes from non-primary to primary " | |
358 "during another transformer's isPrimary", () { | |
359 var rewrite = new RewriteTransformer("md", "md"); | |
360 var check = new CheckContentTransformer("do", "ne"); | |
361 initGraph({ | |
362 "app|foo.txt": "don't", | |
363 "app|foo.md": "foo" | |
364 }, {"app": [[rewrite, check]]}); | |
365 | |
366 rewrite.pauseIsPrimary("app|foo.md"); | |
367 updateSources(["app|foo.txt", "app|foo.md"]); | |
368 // Make sure we're waiting on the correct isPrimary. | |
369 schedule(pumpEventQueue); | |
370 | |
371 modifyAsset("app|foo.txt", "do"); | |
372 updateSources(["app|foo.txt"]); | |
373 rewrite.resumeIsPrimary("app|foo.md"); | |
374 | |
375 expectAsset("app|foo.txt", "done"); | |
376 expectAsset("app|foo.md", "foo.md"); | |
377 buildShouldSucceed(); | |
378 }); | |
379 | |
380 test("returns an asset even if an unrelated build is running", () { | |
381 initGraph([ | |
382 "app|foo.in", | |
383 "app|bar.in", | |
384 ], {"app": [[new RewriteTransformer("in", "out")]]}); | |
385 | |
386 updateSources(["app|foo.in", "app|bar.in"]); | |
387 expectAsset("app|foo.out", "foo.out"); | |
388 expectAsset("app|bar.out", "bar.out"); | |
389 buildShouldSucceed(); | |
390 | |
391 pauseProvider(); | |
392 modifyAsset("app|foo.in", "new"); | |
393 updateSources(["app|foo.in"]); | |
394 expectAssetDoesNotComplete("app|foo.out"); | |
395 expectAsset("app|bar.out", "bar.out"); | |
396 buildShouldNotBeDone(); | |
397 | |
398 resumeProvider(); | |
399 expectAsset("app|foo.out", "new.out"); | |
400 buildShouldSucceed(); | |
401 }); | |
402 | |
403 test("doesn't report AssetNotFound until all builds are finished", () { | |
404 initGraph([ | |
405 "app|foo.in", | |
406 ], {"app": [[new RewriteTransformer("in", "out")]]}); | |
407 | |
408 updateSources(["app|foo.in"]); | |
409 expectAsset("app|foo.out", "foo.out"); | |
410 buildShouldSucceed(); | |
411 | |
412 pauseProvider(); | |
413 updateSources(["app|foo.in"]); | |
414 expectAssetDoesNotComplete("app|foo.out"); | |
415 expectAssetDoesNotComplete("app|non-existent.out"); | |
416 buildShouldNotBeDone(); | |
417 | |
418 resumeProvider(); | |
419 expectAsset("app|foo.out", "foo.out"); | |
420 expectNoAsset("app|non-existent.out"); | |
421 buildShouldSucceed(); | |
422 }); | |
423 | |
424 test("doesn't emit a result until all builds are finished", () { | |
425 var rewrite = new RewriteTransformer("txt", "out"); | |
426 initGraph([ | |
427 "pkg1|foo.txt", | |
428 "pkg2|foo.txt" | |
429 ], {"pkg1": [[rewrite]], "pkg2": [[rewrite]]}); | |
430 | |
431 // First, run both packages' transformers so both packages are successful. | |
432 updateSources(["pkg1|foo.txt", "pkg2|foo.txt"]); | |
433 expectAsset("pkg1|foo.out", "foo.out"); | |
434 expectAsset("pkg2|foo.out", "foo.out"); | |
435 buildShouldSucceed(); | |
436 | |
437 // pkg1 is still successful, but pkg2 is waiting on the provider, so the | |
438 // overall build shouldn't finish. | |
439 pauseProvider(); | |
440 updateSources(["pkg2|foo.txt"]); | |
441 expectAsset("pkg1|foo.out", "foo.out"); | |
442 buildShouldNotBeDone(); | |
443 | |
444 // Now that the provider is unpaused, pkg2's transforms finish and the | |
445 // overall build succeeds. | |
446 resumeProvider(); | |
447 buildShouldSucceed(); | |
448 }); | |
449 | |
450 test("one transformer takes a long time while the other finishes, then " | |
451 "the input is removed", () { | |
452 var rewrite1 = new RewriteTransformer("txt", "out1"); | |
453 var rewrite2 = new RewriteTransformer("txt", "out2"); | |
454 initGraph(["app|foo.txt"], {"app": [[rewrite1, rewrite2]]}); | |
455 | |
456 rewrite1.pauseApply(); | |
457 | |
458 updateSources(["app|foo.txt"]); | |
459 | |
460 // Wait for rewrite1 to pause and rewrite2 to finish. | |
461 schedule(pumpEventQueue); | |
462 | |
463 removeSources(["app|foo.txt"]); | |
464 | |
465 // Make sure the removal is processed completely before we restart rewrite2. | |
466 schedule(pumpEventQueue); | |
467 rewrite1.resumeApply(); | |
468 | |
469 buildShouldSucceed(); | |
470 expectNoAsset("app|foo.out1"); | |
471 expectNoAsset("app|foo.out2"); | |
472 }); | |
473 } | |
OLD | NEW |