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