| 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 |