Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 library ddc.test.dependency_graph_test; | |
|
Jennifer Messerly
2015/03/03 02:24:22
copyright header?
Siggi Cherem (dart-lang)
2015/03/04 04:44:24
Done.
| |
| 2 | |
| 3 import 'package:unittest/compact_vm_config.dart'; | |
| 4 import 'package:unittest/unittest.dart'; | |
| 5 | |
| 6 import 'package:dev_compiler/src/checker/dart_sdk.dart' | |
| 7 show mockSdkSources, dartSdkDirectory; | |
| 8 import 'package:dev_compiler/src/testing.dart'; | |
| 9 import 'package:dev_compiler/src/options.dart'; | |
| 10 import 'package:dev_compiler/src/checker/resolver.dart'; | |
| 11 import 'package:dev_compiler/src/dependency_graph.dart'; | |
| 12 import 'package:path/path.dart' as path; | |
| 13 | |
| 14 main() { | |
| 15 groupSep = " > "; | |
| 16 useCompactVMConfiguration(); | |
| 17 | |
| 18 var options = new CompilerOptions(); | |
| 19 var testUriResolver; | |
| 20 var context; | |
| 21 var graph; | |
| 22 | |
| 23 /// Initial values for test files | |
| 24 var testFiles = { | |
| 25 '/index1.html': ''' | |
| 26 <script src="foo.js"></script> | |
| 27 ''', | |
| 28 '/index2.html': ''' | |
| 29 <script type="application/dart" src="a1.dart"></script> | |
| 30 ''', | |
| 31 '/index3.html': ''' | |
| 32 <script type="application/dart" src="a2.dart"></script> | |
| 33 ''', | |
| 34 '/a1.dart': ''' | |
| 35 library a1; | |
| 36 ''', | |
| 37 '/a2.dart': ''' | |
| 38 library a2; | |
| 39 import 'a3.dart'; | |
| 40 import 'a4.dart'; | |
| 41 export 'a5.dart'; | |
| 42 part 'a6.dart'; | |
| 43 ''', | |
| 44 '/a3.dart': 'library a3;', | |
| 45 '/a4.dart': 'library a4; export "a10.dart";', | |
| 46 '/a5.dart': 'library a5;', | |
| 47 '/a6.dart': 'part of a2;', | |
| 48 '/a8.dart': 'library a8; import "a8.dart";', | |
| 49 '/a9.dart': 'library a9; import "a8.dart";', | |
| 50 '/a10.dart': 'library a10;', | |
| 51 }; | |
| 52 | |
| 53 nodeOf(String filepath, [bool isPart = false]) => | |
| 54 graph.nodeFromUri(new Uri.file(filepath), isPart); | |
| 55 | |
| 56 setUp(() { | |
| 57 /// We completely reset the TestUriResolver to avoid interference between | |
| 58 /// tests (since some tests modify the state of the files). | |
| 59 testUriResolver = new TestUriResolver(testFiles); | |
| 60 context = new TypeResolver.fromMock(mockSdkSources, options, | |
| 61 otherResolvers: [testUriResolver]).context; | |
| 62 graph = new SourceGraph(context, options); | |
| 63 }); | |
| 64 | |
| 65 group('HTML deps', () { | |
| 66 test('initial deps', () { | |
| 67 var i1 = nodeOf('/index1.html'); | |
| 68 var i2 = nodeOf('/index2.html'); | |
| 69 expect(i1.scripts.length, 0); | |
| 70 expect(i2.scripts.length, 0); | |
| 71 i1.update(graph); | |
| 72 i2.update(graph); | |
| 73 expect(i1.scripts.length, 0); | |
| 74 expect(i2.scripts.length, 1); | |
| 75 expect(i2.scripts.first, nodeOf('/a1.dart')); | |
| 76 }); | |
| 77 | |
| 78 test('add a dep', () { | |
| 79 // After initial load, dependencies are 0: | |
| 80 var node = nodeOf('/index1.html'); | |
| 81 node.update(graph); | |
| 82 expect(node.scripts.length, 0); | |
| 83 | |
| 84 // Adding the dependency is discovered on the next round of updates: | |
| 85 node.source.contents.modificationTime++; | |
| 86 node.source.contents.data = | |
| 87 '<script type="application/dart" src="a2.dart"></script>'; | |
| 88 expect(node.scripts.length, 0); | |
| 89 node.update(graph); | |
| 90 expect(node.scripts.length, 1); | |
| 91 expect(node.scripts.first, nodeOf('/a2.dart')); | |
| 92 }); | |
| 93 | |
| 94 test('add more deps', () { | |
| 95 // After initial load, dependencies are 1: | |
| 96 var node = nodeOf('/index2.html'); | |
| 97 node.update(graph); | |
| 98 expect(node.scripts.length, 1); | |
| 99 expect(node.scripts.first, nodeOf('/a1.dart')); | |
| 100 | |
| 101 node.source.contents.modificationTime++; | |
| 102 node.source.contents.data += | |
| 103 '<script type="application/dart" src="a2.dart"></script>'; | |
| 104 expect(node.scripts.length, 1); | |
| 105 node.update(graph); | |
| 106 expect(node.scripts.length, 2); | |
| 107 expect(node.scripts.first, nodeOf('/a1.dart')); | |
| 108 expect(node.scripts.last, nodeOf('/a2.dart')); | |
| 109 }); | |
| 110 | |
| 111 test('remove all deps', () { | |
| 112 // After initial load, dependencies are 1: | |
| 113 var node = nodeOf('/index2.html'); | |
| 114 node.update(graph); | |
| 115 expect(node.scripts.length, 1); | |
| 116 expect(node.scripts.first, nodeOf('/a1.dart')); | |
| 117 | |
| 118 // Removing the dependency is discovered on the next round of updates: | |
| 119 node.source.contents.modificationTime++; | |
| 120 node.source.contents.data = ''; | |
| 121 expect(node.scripts.length, 1); | |
| 122 node.update(graph); | |
| 123 expect(node.scripts.length, 0); | |
| 124 }); | |
| 125 }); | |
| 126 | |
| 127 group('Dart deps', () { | |
| 128 test('initial deps', () { | |
| 129 var a1 = nodeOf('/a1.dart'); | |
| 130 var a2 = nodeOf('/a2.dart'); | |
| 131 expect(a1.imports.length, 0); | |
| 132 expect(a1.exports.length, 0); | |
| 133 expect(a1.parts.length, 0); | |
| 134 expect(a2.imports.length, 0); | |
| 135 expect(a2.exports.length, 0); | |
| 136 expect(a2.parts.length, 0); | |
| 137 | |
| 138 a1.update(graph); | |
| 139 a2.update(graph); | |
| 140 | |
| 141 expect(a1.imports.length, 0); | |
| 142 expect(a1.exports.length, 0); | |
| 143 expect(a1.parts.length, 0); | |
| 144 expect(a2.imports.length, 2); | |
| 145 expect(a2.exports.length, 1); | |
| 146 expect(a2.parts.length, 1); | |
| 147 expect(a2.imports.contains(nodeOf('/a3.dart')), isTrue); | |
| 148 expect(a2.imports.contains(nodeOf('/a4.dart')), isTrue); | |
| 149 expect(a2.exports.contains(nodeOf('/a5.dart')), isTrue); | |
| 150 expect(a2.parts.contains(nodeOf('/a6.dart')), isTrue); | |
| 151 }); | |
| 152 | |
| 153 test('add deps', () { | |
| 154 var node = nodeOf('/a1.dart'); | |
| 155 node.update(graph); | |
| 156 expect(node.imports.length, 0); | |
| 157 expect(node.exports.length, 0); | |
| 158 expect(node.parts.length, 0); | |
| 159 | |
| 160 node.source.contents.modificationTime++; | |
| 161 node.source.contents.data = | |
| 162 'import "a3.dart"; export "a5.dart"; part "a8.dart";'; | |
| 163 node.update(graph); | |
| 164 | |
| 165 expect(node.imports.length, 1); | |
| 166 expect(node.exports.length, 1); | |
| 167 expect(node.parts.length, 1); | |
| 168 expect(node.imports.contains(nodeOf('/a3.dart')), isTrue); | |
| 169 expect(node.exports.contains(nodeOf('/a5.dart')), isTrue); | |
| 170 expect(node.parts.contains(nodeOf('/a8.dart')), isTrue); | |
| 171 }); | |
| 172 | |
| 173 test('remove deps', () { | |
| 174 var node = nodeOf('/a2.dart'); | |
| 175 node.update(graph); | |
| 176 expect(node.imports.length, 2); | |
| 177 expect(node.exports.length, 1); | |
| 178 expect(node.parts.length, 1); | |
| 179 expect(node.imports.contains(nodeOf('/a3.dart')), isTrue); | |
| 180 expect(node.imports.contains(nodeOf('/a4.dart')), isTrue); | |
| 181 expect(node.exports.contains(nodeOf('/a5.dart')), isTrue); | |
| 182 expect(node.parts.contains(nodeOf('/a6.dart')), isTrue); | |
| 183 | |
| 184 node.source.contents.modificationTime++; | |
| 185 node.source.contents.data = | |
| 186 'import "a3.dart"; export "a6.dart"; part "a8.dart";'; | |
| 187 node.update(graph); | |
| 188 | |
| 189 expect(node.imports.length, 1); | |
| 190 expect(node.exports.length, 1); | |
| 191 expect(node.parts.length, 1); | |
| 192 expect(node.imports.contains(nodeOf('/a3.dart')), isTrue); | |
| 193 expect(node.exports.contains(nodeOf('/a6.dart')), isTrue); | |
| 194 expect(node.parts.contains(nodeOf('/a8.dart')), isTrue); | |
| 195 }); | |
| 196 }); | |
| 197 | |
| 198 group('local changes', () { | |
| 199 group('needs rebuild', () { | |
| 200 test('in HTML', () { | |
| 201 var node = nodeOf('/index1.html'); | |
| 202 node.update(graph); | |
| 203 expect(node.needsRebuild, isTrue); | |
| 204 node.needsRebuild = false; | |
| 205 | |
| 206 node.update(graph); | |
| 207 expect(node.needsRebuild, isFalse); | |
| 208 | |
| 209 // For now, an empty modification is enough to trigger a rebuild | |
| 210 node.source.contents.modificationTime++; | |
| 211 expect(node.needsRebuild, isFalse); | |
| 212 node.update(graph); | |
| 213 expect(node.needsRebuild, isTrue); | |
| 214 }); | |
| 215 | |
| 216 test('main library in Dart', () { | |
| 217 var node = nodeOf('/a2.dart'); | |
| 218 var partNode = nodeOf('/a6.dart', true); | |
| 219 node.update(graph); | |
| 220 expect(node.needsRebuild, isTrue); | |
| 221 node.needsRebuild = false; | |
| 222 partNode.needsRebuild = false; | |
| 223 | |
| 224 node.update(graph); | |
| 225 expect(node.needsRebuild, isFalse); | |
| 226 | |
| 227 // For now, an empty modification is enough to trigger a rebuild | |
| 228 node.source.contents.modificationTime++; | |
| 229 expect(node.needsRebuild, isFalse); | |
| 230 node.update(graph); | |
| 231 expect(node.needsRebuild, isTrue); | |
| 232 }); | |
| 233 | |
| 234 test('part of library in Dart', () { | |
| 235 var node = nodeOf('/a2.dart'); | |
| 236 var importNode = nodeOf('/a3.dart'); | |
| 237 var exportNode = nodeOf('/a5.dart'); | |
| 238 var partNode = nodeOf('/a6.dart', true); | |
| 239 node.update(graph); | |
| 240 expect(node.needsRebuild, isTrue); | |
| 241 node.needsRebuild = false; | |
| 242 partNode.needsRebuild = false; | |
| 243 | |
| 244 node.update(graph); | |
| 245 expect(node.needsRebuild, isFalse); | |
| 246 | |
| 247 // Modification in imported/exported node makes no difference for local | |
| 248 // rebuild label (globally that's tested elsewhere) | |
| 249 importNode.source.contents.modificationTime++; | |
| 250 exportNode.source.contents.modificationTime++; | |
| 251 node.update(graph); | |
| 252 expect(node.needsRebuild, isFalse); | |
| 253 expect(partNode.needsRebuild, isFalse); | |
| 254 | |
| 255 // Modification in part triggers change in containing library: | |
| 256 partNode.source.contents.modificationTime++; | |
| 257 expect(node.needsRebuild, isFalse); | |
| 258 expect(partNode.needsRebuild, isFalse); | |
| 259 node.update(graph); | |
| 260 expect(node.needsRebuild, isTrue); | |
| 261 expect(partNode.needsRebuild, isTrue); | |
| 262 }); | |
| 263 }); | |
| 264 | |
| 265 group('structure change', () { | |
| 266 test('no mod in HTML', () { | |
| 267 var node = nodeOf('/index2.html'); | |
| 268 node.update(graph); | |
| 269 expect(node.structureChanged, isTrue); | |
| 270 node.structureChanged = false; | |
| 271 | |
| 272 node.update(graph); | |
| 273 expect(node.structureChanged, isFalse); | |
| 274 | |
| 275 // An empty modification will not trigger a structural change | |
| 276 node.source.contents.modificationTime++; | |
| 277 expect(node.structureChanged, isFalse); | |
| 278 node.update(graph); | |
| 279 expect(node.structureChanged, isFalse); | |
| 280 }); | |
| 281 | |
| 282 test('added scripts in HTML', () { | |
| 283 var node = nodeOf('/index2.html'); | |
| 284 node.update(graph); | |
| 285 expect(node.structureChanged, isTrue); | |
| 286 expect(node.scripts.length, 1); | |
| 287 | |
| 288 node.structureChanged = false; | |
| 289 node.update(graph); | |
| 290 expect(node.structureChanged, isFalse); | |
| 291 | |
| 292 // This change will not include new script tags: | |
| 293 node.source.contents.modificationTime++; | |
| 294 node.source.contents.data += '<div></div>'; | |
| 295 expect(node.structureChanged, isFalse); | |
| 296 node.update(graph); | |
| 297 expect(node.structureChanged, isFalse); | |
| 298 expect(node.scripts.length, 1); | |
| 299 | |
| 300 node.source.contents.modificationTime++; | |
| 301 node.source.contents.data += | |
| 302 '<script type="application/dart" src="a4.dart"></script>'; | |
| 303 expect(node.structureChanged, isFalse); | |
| 304 node.update(graph); | |
| 305 expect(node.structureChanged, isTrue); | |
| 306 expect(node.scripts.length, 2); | |
| 307 }); | |
| 308 | |
| 309 test('no mod in Dart', () { | |
| 310 var node = nodeOf('/a2.dart'); | |
| 311 var importNode = nodeOf('/a3.dart'); | |
| 312 var exportNode = nodeOf('/a5.dart'); | |
| 313 var partNode = nodeOf('/a6.dart', true); | |
| 314 node.update(graph); | |
| 315 expect(node.structureChanged, isTrue); | |
| 316 node.structureChanged = false; | |
| 317 | |
| 318 node.update(graph); | |
| 319 expect(node.structureChanged, isFalse); | |
| 320 | |
| 321 // These modifications make no difference at all. | |
| 322 importNode.source.contents.modificationTime++; | |
| 323 exportNode.source.contents.modificationTime++; | |
| 324 partNode.source.contents.modificationTime++; | |
| 325 node.source.contents.modificationTime++; | |
| 326 | |
| 327 expect(node.structureChanged, isFalse); | |
| 328 node.update(graph); | |
| 329 expect(node.structureChanged, isFalse); | |
| 330 }); | |
| 331 | |
| 332 test('same directives, different order', () { | |
| 333 var node = nodeOf('/a2.dart'); | |
| 334 node.update(graph); | |
| 335 expect(node.structureChanged, isTrue); | |
| 336 node.structureChanged = false; | |
| 337 | |
| 338 node.update(graph); | |
| 339 expect(node.structureChanged, isFalse); | |
| 340 | |
| 341 // modified order of imports, but structure stays the same: | |
| 342 node.source.contents.modificationTime++; | |
| 343 node.source.contents.data = 'import "a4.dart"; import "a3.dart"; ' | |
| 344 'export "a5.dart"; part "a6.dart";'; | |
| 345 node.update(graph); | |
| 346 | |
| 347 expect(node.structureChanged, isFalse); | |
| 348 node.update(graph); | |
| 349 expect(node.structureChanged, isFalse); | |
| 350 }); | |
| 351 | |
| 352 test('changed parts', () { | |
| 353 var node = nodeOf('/a2.dart'); | |
| 354 node.update(graph); | |
| 355 expect(node.structureChanged, isTrue); | |
| 356 node.structureChanged = false; | |
| 357 | |
| 358 node.update(graph); | |
| 359 expect(node.structureChanged, isFalse); | |
| 360 | |
| 361 // added one. | |
| 362 node.source.contents.modificationTime++; | |
| 363 node.source.contents.data = 'import "a4.dart"; import "a3.dart"; ' | |
| 364 'export "a5.dart"; part "a6.dart"; part "a7.dart";'; | |
| 365 expect(node.structureChanged, isFalse); | |
| 366 node.update(graph); | |
| 367 expect(node.structureChanged, isTrue); | |
| 368 | |
| 369 // no change | |
| 370 node.structureChanged = false; | |
| 371 node.source.contents.modificationTime++; | |
| 372 node.update(graph); | |
| 373 expect(node.structureChanged, isFalse); | |
| 374 | |
| 375 // removed one | |
| 376 node.source.contents.modificationTime++; | |
| 377 node.source.contents.data = 'import "a4.dart"; import "a3.dart"; ' | |
| 378 'export "a5.dart"; part "a7.dart";'; | |
| 379 expect(node.structureChanged, isFalse); | |
| 380 node.update(graph); | |
| 381 expect(node.structureChanged, isTrue); | |
| 382 }); | |
| 383 | |
| 384 test('changed import', () { | |
| 385 var node = nodeOf('/a2.dart'); | |
| 386 node.update(graph); | |
| 387 expect(node.structureChanged, isTrue); | |
| 388 node.structureChanged = false; | |
| 389 | |
| 390 node.update(graph); | |
| 391 expect(node.structureChanged, isFalse); | |
| 392 | |
| 393 // added one. | |
| 394 node.source.contents.modificationTime++; | |
| 395 node.source.contents.data = | |
| 396 'import "a4.dart"; import "a3.dart"; import "a7.dart";' | |
| 397 'export "a5.dart"; part "a6.dart";'; | |
| 398 expect(node.structureChanged, isFalse); | |
| 399 node.update(graph); | |
| 400 expect(node.structureChanged, isTrue); | |
| 401 | |
| 402 // no change | |
| 403 node.structureChanged = false; | |
| 404 node.source.contents.modificationTime++; | |
| 405 node.update(graph); | |
| 406 expect(node.structureChanged, isFalse); | |
| 407 | |
| 408 // removed one | |
| 409 node.source.contents.modificationTime++; | |
| 410 node.source.contents.data = 'import "a4.dart"; import "a7.dart"; ' | |
| 411 'export "a5.dart"; part "a6.dart";'; | |
| 412 expect(node.structureChanged, isFalse); | |
| 413 node.update(graph); | |
| 414 expect(node.structureChanged, isTrue); | |
| 415 }); | |
| 416 | |
| 417 test('changed exports', () { | |
| 418 var node = nodeOf('/a2.dart'); | |
| 419 node.update(graph); | |
| 420 expect(node.structureChanged, isTrue); | |
| 421 node.structureChanged = false; | |
| 422 | |
| 423 node.update(graph); | |
| 424 expect(node.structureChanged, isFalse); | |
| 425 | |
| 426 // added one. | |
| 427 node.source.contents.modificationTime++; | |
| 428 node.source.contents.data = 'import "a4.dart"; import "a3.dart";' | |
| 429 'export "a5.dart"; export "a9.dart"; part "a6.dart";'; | |
| 430 expect(node.structureChanged, isFalse); | |
| 431 node.update(graph); | |
| 432 expect(node.structureChanged, isTrue); | |
| 433 | |
| 434 // no change | |
| 435 node.structureChanged = false; | |
| 436 node.source.contents.modificationTime++; | |
| 437 node.update(graph); | |
| 438 expect(node.structureChanged, isFalse); | |
| 439 | |
| 440 // removed one | |
| 441 node.source.contents.modificationTime++; | |
| 442 node.source.contents.data = 'import "a4.dart"; import "a3.dart"; ' | |
| 443 'export "a5.dart"; part "a6.dart";'; | |
| 444 expect(node.structureChanged, isFalse); | |
| 445 node.update(graph); | |
| 446 expect(node.structureChanged, isTrue); | |
| 447 }); | |
| 448 }); | |
| 449 }); | |
| 450 | |
| 451 group('refresh structure and marks', () { | |
| 452 test('initial marks', () { | |
| 453 var node = nodeOf('/index3.html'); | |
| 454 expectGraph(node, 'index3.html'); | |
| 455 refreshStructureAndMarks(node, graph); | |
| 456 expectGraph(node, ''' | |
| 457 index3.html [needs-rebuild] [structure-changed] | |
| 458 |-- a2.dart [needs-rebuild] [structure-changed] | |
| 459 | |-- a3.dart [needs-rebuild] | |
| 460 | |-- a4.dart [needs-rebuild] [structure-changed] | |
| 461 | | |-- a10.dart [needs-rebuild] | |
| 462 | |-- a5.dart [needs-rebuild] | |
| 463 | |-- a6.dart [needs-rebuild] | |
| 464 '''); | |
| 465 }); | |
| 466 | |
| 467 test('cleared marks stay clear', () { | |
| 468 var node = nodeOf('/index3.html'); | |
| 469 refreshStructureAndMarks(node, graph); | |
| 470 expectGraph(node, ''' | |
| 471 index3.html [needs-rebuild] [structure-changed] | |
| 472 |-- a2.dart [needs-rebuild] [structure-changed] | |
| 473 | |-- a3.dart [needs-rebuild] | |
| 474 | |-- a4.dart [needs-rebuild] [structure-changed] | |
| 475 | | |-- a10.dart [needs-rebuild] | |
| 476 | |-- a5.dart [needs-rebuild] | |
| 477 | |-- a6.dart [needs-rebuild] | |
| 478 '''); | |
| 479 clearMarks(node); | |
| 480 expectGraph(node, ''' | |
| 481 index3.html | |
| 482 |-- a2.dart | |
| 483 | |-- a3.dart | |
| 484 | |-- a4.dart | |
| 485 | | |-- a10.dart | |
| 486 | |-- a5.dart | |
| 487 | |-- a6.dart | |
| 488 '''); | |
| 489 | |
| 490 refreshStructureAndMarks(node, graph); | |
| 491 expectGraph(node, ''' | |
| 492 index3.html | |
| 493 |-- a2.dart | |
| 494 | |-- a3.dart | |
| 495 | |-- a4.dart | |
| 496 | | |-- a10.dart | |
| 497 | |-- a5.dart | |
| 498 | |-- a6.dart | |
| 499 '''); | |
| 500 }); | |
| 501 | |
| 502 test('needsRebuild mark updated on local modifications', () { | |
| 503 var node = nodeOf('/index3.html'); | |
| 504 refreshStructureAndMarks(node, graph); | |
| 505 clearMarks(node); | |
| 506 var a3 = nodeOf('/a3.dart'); | |
| 507 a3.source.contents.modificationTime++; | |
| 508 | |
| 509 refreshStructureAndMarks(node, graph); | |
| 510 expectGraph(node, ''' | |
| 511 index3.html | |
| 512 |-- a2.dart | |
| 513 | |-- a3.dart [needs-rebuild] | |
| 514 | |-- a4.dart | |
| 515 | | |-- a10.dart | |
| 516 | |-- a5.dart | |
| 517 | |-- a6.dart | |
| 518 '''); | |
| 519 }); | |
| 520 | |
| 521 test('structuredChanged mark updated on structure modifications', () { | |
| 522 var node = nodeOf('/index3.html'); | |
| 523 refreshStructureAndMarks(node, graph); | |
| 524 clearMarks(node); | |
| 525 var a5 = nodeOf('/a5.dart'); | |
| 526 a5.source.contents.modificationTime++; | |
| 527 a5.source.contents.data = 'import "a8.dart";'; | |
| 528 | |
| 529 refreshStructureAndMarks(node, graph); | |
| 530 expectGraph(node, ''' | |
| 531 index3.html | |
| 532 |-- a2.dart | |
| 533 | |-- a3.dart | |
| 534 | |-- a4.dart | |
| 535 | | |-- a10.dart | |
| 536 | |-- a5.dart [needs-rebuild] [structure-changed] | |
| 537 | | |-- a8.dart [needs-rebuild] [structure-changed] | |
| 538 | | | |-- a8.dart... | |
| 539 | |-- a6.dart | |
| 540 '''); | |
| 541 }); | |
| 542 }); | |
| 543 | |
| 544 group('rebuild', () { | |
| 545 var results; | |
| 546 void addName(SourceNode n) => results.add(nameFor(n)); | |
| 547 | |
| 548 bool buildNoTransitiveChange(SourceNode n) { | |
| 549 addName(n); | |
| 550 return false; | |
| 551 } | |
| 552 | |
| 553 bool buildWithTransitiveChange(SourceNode n) { | |
| 554 addName(n); | |
| 555 return true; | |
| 556 } | |
| 557 | |
| 558 setUp(() { | |
| 559 results = []; | |
| 560 }); | |
| 561 | |
| 562 test('everything build on first run', () { | |
| 563 var node = nodeOf('/index3.html'); | |
| 564 rebuild(node, graph, buildNoTransitiveChange); | |
| 565 // Note: a6.dart is not included because it built as part of a2.dart | |
| 566 expect(results, [ | |
| 567 'a3.dart', | |
| 568 'a10.dart', | |
| 569 'a4.dart', | |
| 570 'a5.dart', | |
| 571 'a2.dart', | |
| 572 'index3.html' | |
| 573 ]); | |
| 574 | |
| 575 // Marks are removed automatically by rebuild | |
| 576 expectGraph(node, ''' | |
| 577 index3.html | |
| 578 |-- a2.dart | |
| 579 | |-- a3.dart | |
| 580 | |-- a4.dart | |
| 581 | | |-- a10.dart | |
| 582 | |-- a5.dart | |
| 583 | |-- a6.dart | |
| 584 '''); | |
| 585 }); | |
| 586 | |
| 587 test('nothing to do after build', () { | |
| 588 var node = nodeOf('/index3.html'); | |
| 589 rebuild(node, graph, buildNoTransitiveChange); | |
| 590 | |
| 591 results = []; | |
| 592 rebuild(node, graph, buildNoTransitiveChange); | |
| 593 expect(results, []); | |
| 594 }); | |
| 595 | |
| 596 test('modified part triggers building library', () { | |
| 597 var node = nodeOf('/index3.html'); | |
| 598 rebuild(node, graph, buildNoTransitiveChange); | |
| 599 results = []; | |
| 600 | |
| 601 var a6 = nodeOf('/a6.dart'); | |
| 602 a6.source.contents.modificationTime++; | |
| 603 rebuild(node, graph, buildNoTransitiveChange); | |
| 604 expect(results, ['a2.dart']); | |
| 605 | |
| 606 results = []; | |
| 607 rebuild(node, graph, buildNoTransitiveChange); | |
| 608 expect(results, []); | |
| 609 }); | |
| 610 | |
| 611 test('non-API change triggers build stays local', () { | |
| 612 var node = nodeOf('/index3.html'); | |
| 613 rebuild(node, graph, buildNoTransitiveChange); | |
| 614 results = []; | |
| 615 | |
| 616 var a3 = nodeOf('/a3.dart'); | |
| 617 a3.source.contents.modificationTime++; | |
| 618 rebuild(node, graph, buildNoTransitiveChange); | |
| 619 expect(results, ['a3.dart']); | |
| 620 | |
| 621 results = []; | |
| 622 rebuild(node, graph, buildNoTransitiveChange); | |
| 623 expect(results, []); | |
| 624 }); | |
| 625 | |
| 626 test('no-API change in exported file stays local', () { | |
| 627 var node = nodeOf('/index3.html'); | |
| 628 rebuild(node, graph, buildNoTransitiveChange); | |
| 629 results = []; | |
| 630 | |
| 631 // similar to the test above, but a10 is exported from a4. | |
| 632 var a3 = nodeOf('/a10.dart'); | |
| 633 a3.source.contents.modificationTime++; | |
| 634 rebuild(node, graph, buildNoTransitiveChange); | |
| 635 expect(results, ['a10.dart']); | |
| 636 | |
| 637 results = []; | |
| 638 rebuild(node, graph, buildNoTransitiveChange); | |
| 639 expect(results, []); | |
| 640 }); | |
| 641 | |
| 642 test('API change in lib, triggers build on imports', () { | |
| 643 var node = nodeOf('/index3.html'); | |
| 644 rebuild(node, graph, buildNoTransitiveChange); | |
| 645 results = []; | |
| 646 | |
| 647 var a3 = nodeOf('/a3.dart'); | |
| 648 a3.source.contents.modificationTime++; | |
| 649 rebuild(node, graph, buildWithTransitiveChange); | |
| 650 expect(results, ['a3.dart', 'a2.dart']); | |
| 651 | |
| 652 results = []; | |
| 653 rebuild(node, graph, buildNoTransitiveChange); | |
| 654 expect(results, []); | |
| 655 }); | |
| 656 | |
| 657 test('API change in export, triggers build on imports', () { | |
| 658 var node = nodeOf('/index3.html'); | |
| 659 rebuild(node, graph, buildNoTransitiveChange); | |
| 660 results = []; | |
| 661 | |
| 662 var a3 = nodeOf('/a10.dart'); | |
| 663 a3.source.contents.modificationTime++; | |
| 664 rebuild(node, graph, buildWithTransitiveChange); | |
| 665 | |
| 666 // Node: a4.dart reexports a10.dart, but it doesn't import it, so we don't | |
| 667 // need to rebuild it. | |
| 668 expect(results, ['a10.dart', 'a2.dart']); | |
| 669 | |
| 670 results = []; | |
| 671 rebuild(node, graph, buildNoTransitiveChange); | |
| 672 expect(results, []); | |
| 673 }); | |
| 674 | |
| 675 test('structural change rebuilds HTML, but skips unreachable code', () { | |
| 676 var node = nodeOf('/index3.html'); | |
| 677 rebuild(node, graph, buildNoTransitiveChange); | |
| 678 results = []; | |
| 679 | |
| 680 var a2 = nodeOf('/a2.dart'); | |
| 681 a2.source.contents.modificationTime++; | |
| 682 a2.source.contents.data = 'import "a4.dart";'; | |
| 683 | |
| 684 var a3 = nodeOf('/a3.dart'); | |
| 685 a3.source.contents.modificationTime++; | |
| 686 rebuild(node, graph, buildNoTransitiveChange); | |
| 687 | |
| 688 // a3 will become unreachable, index3 reflects structural changes. | |
| 689 expect(results, ['a2.dart', 'index3.html']); | |
| 690 | |
| 691 results = []; | |
| 692 rebuild(node, graph, buildNoTransitiveChange); | |
| 693 expect(results, []); | |
| 694 }); | |
| 695 | |
| 696 test('newly discovered files get built too', () { | |
| 697 var node = nodeOf('/index3.html'); | |
| 698 rebuild(node, graph, buildNoTransitiveChange); | |
| 699 results = []; | |
| 700 | |
| 701 var a2 = nodeOf('/a2.dart'); | |
| 702 a2.source.contents.modificationTime++; | |
| 703 a2.source.contents.data = 'import "a9.dart";'; | |
| 704 | |
| 705 rebuild(node, graph, buildNoTransitiveChange); | |
| 706 expect(results, ['a8.dart', 'a9.dart', 'a2.dart', 'index3.html']); | |
| 707 | |
| 708 results = []; | |
| 709 rebuild(node, graph, buildNoTransitiveChange); | |
| 710 expect(results, []); | |
| 711 }); | |
| 712 }); | |
| 713 } | |
| 714 | |
| 715 expectGraph(SourceNode node, String expectation) { | |
| 716 expect(printReachable(node), equalsIgnoringWhitespace(expectation)); | |
| 717 } | |
| 718 | |
| 719 nameFor(SourceNode node) => path.basename(node.uri.path); | |
| 720 printReachable(SourceNode node) { | |
| 721 var seen = new Set(); | |
| 722 var sb = new StringBuffer(); | |
| 723 helper(n, {indent: 0}) { | |
| 724 if (indent > 0) { | |
| 725 sb | |
| 726 ..write("| " * (indent - 1)) | |
| 727 ..write("|-- "); | |
| 728 } | |
| 729 sb.write(nameFor(n)); | |
| 730 if (seen.contains(n)) { | |
| 731 sb.write('...\n'); | |
| 732 return; | |
| 733 } | |
| 734 seen.add(n); | |
| 735 sb | |
| 736 ..write(' ') | |
| 737 ..write(n.needsRebuild ? '[needs-rebuild] ' : '') | |
| 738 ..write(n.structureChanged ? '[structure-changed] ' : ' ') | |
| 739 ..write('\n'); | |
| 740 n.directDeps.forEach((e) => helper(e, indent: indent + 1)); | |
| 741 } | |
| 742 helper(node); | |
| 743 return sb.toString(); | |
| 744 } | |
| 745 | |
| 746 bool _same(Set a, Set b) => a.length == b.length && a.containsAll(b); | |
| OLD | NEW |