| 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 dev_compiler.test.dependency_graph_test; | |
| 6 | |
| 7 import 'package:analyzer/file_system/file_system.dart'; | |
| 8 import 'package:analyzer/file_system/memory_file_system.dart'; | |
| 9 import 'package:analyzer/src/generated/source.dart'; | |
| 10 import 'package:path/path.dart' as path; | |
| 11 import 'package:test/test.dart'; | |
| 12 | |
| 13 import 'package:dev_compiler/src/analysis_context.dart'; | |
| 14 import 'package:dev_compiler/src/compiler.dart'; | |
| 15 import 'package:dev_compiler/src/options.dart'; | |
| 16 import 'package:dev_compiler/src/report.dart'; | |
| 17 import 'package:dev_compiler/src/server/dependency_graph.dart'; | |
| 18 | |
| 19 import 'testing.dart'; | |
| 20 | |
| 21 void main() { | |
| 22 var options = new CompilerOptions( | |
| 23 runtimeDir: '/dev_compiler_runtime/', | |
| 24 sourceOptions: new SourceResolverOptions(useMockSdk: true)); | |
| 25 MemoryResourceProvider testResourceProvider; | |
| 26 ResourceUriResolver testUriResolver; | |
| 27 var context; | |
| 28 var graph; | |
| 29 | |
| 30 /// Initial values for test files | |
| 31 var testFiles = { | |
| 32 '/index1.html': ''' | |
| 33 <script src="foo.js"></script> | |
| 34 ''', | |
| 35 '/index2.html': ''' | |
| 36 <script type="application/dart" src="a1.dart"></script> | |
| 37 ''', | |
| 38 '/index3.html': ''' | |
| 39 <script type="application/dart" src="a2.dart"></script> | |
| 40 ''', | |
| 41 '/a1.dart': ''' | |
| 42 library a1; | |
| 43 ''', | |
| 44 '/a2.dart': ''' | |
| 45 library a2; | |
| 46 import 'a3.dart'; | |
| 47 import 'a4.dart'; | |
| 48 export 'a5.dart'; | |
| 49 part 'a6.dart'; | |
| 50 ''', | |
| 51 '/a3.dart': 'library a3;', | |
| 52 '/a4.dart': 'library a4; export "a10.dart";', | |
| 53 '/a5.dart': 'library a5;', | |
| 54 '/a6.dart': 'part of a2;', | |
| 55 '/a7.dart': 'library a7;', | |
| 56 '/a8.dart': 'library a8; import "a8.dart";', | |
| 57 '/a9.dart': 'library a9; import "a8.dart";', | |
| 58 '/a10.dart': 'library a10;', | |
| 59 }; | |
| 60 | |
| 61 nodeOf(String filepath) => graph.nodeFromUri(new Uri.file(filepath)); | |
| 62 | |
| 63 setUp(() { | |
| 64 /// We completely reset the TestUriResolver to avoid interference between | |
| 65 /// tests (since some tests modify the state of the files). | |
| 66 testResourceProvider = createTestResourceProvider(testFiles); | |
| 67 testUriResolver = new ResourceUriResolver(testResourceProvider); | |
| 68 context = createAnalysisContextWithSources(options.sourceOptions, | |
| 69 fileResolvers: [testUriResolver]); | |
| 70 graph = new SourceGraph(context, new LogReporter(context), options); | |
| 71 }); | |
| 72 | |
| 73 updateFile(Source source, [String newContents]) { | |
| 74 var path = testResourceProvider.pathContext.fromUri(source.uri); | |
| 75 if (newContents == null) newContents = source.contents.data; | |
| 76 testResourceProvider.updateFile(path, newContents); | |
| 77 } | |
| 78 | |
| 79 group('HTML deps', () { | |
| 80 test('initial deps', () { | |
| 81 var i1 = nodeOf('/index1.html'); | |
| 82 var i2 = nodeOf('/index2.html'); | |
| 83 expect(i1.scripts.length, 0); | |
| 84 expect(i2.scripts.length, 0); | |
| 85 i1.update(); | |
| 86 i2.update(); | |
| 87 expect(i1.scripts.length, 0); | |
| 88 expect(i2.scripts.length, 1); | |
| 89 expect(i2.scripts.first, nodeOf('/a1.dart')); | |
| 90 }); | |
| 91 | |
| 92 test('add a dep', () { | |
| 93 // After initial load, dependencies are 0: | |
| 94 var node = nodeOf('/index1.html'); | |
| 95 node.update(); | |
| 96 expect(node.scripts.length, 0); | |
| 97 | |
| 98 // Adding the dependency is discovered on the next round of updates: | |
| 99 updateFile(node.source, | |
| 100 '<script type="application/dart" src="a2.dart"></script>'); | |
| 101 expect(node.scripts.length, 0); | |
| 102 node.update(); | |
| 103 expect(node.scripts.length, 1); | |
| 104 expect(node.scripts.first, nodeOf('/a2.dart')); | |
| 105 }); | |
| 106 | |
| 107 test('add more deps', () { | |
| 108 // After initial load, dependencies are 1: | |
| 109 var node = nodeOf('/index2.html'); | |
| 110 node.update(); | |
| 111 expect(node.scripts.length, 1); | |
| 112 expect(node.scripts.first, nodeOf('/a1.dart')); | |
| 113 | |
| 114 updateFile( | |
| 115 node.source, | |
| 116 node.source.contents.data + | |
| 117 '<script type="application/dart" src="a2.dart"></script>'); | |
| 118 expect(node.scripts.length, 1); | |
| 119 node.update(); | |
| 120 expect(node.scripts.length, 2); | |
| 121 expect(node.scripts.first, nodeOf('/a1.dart')); | |
| 122 expect(node.scripts.last, nodeOf('/a2.dart')); | |
| 123 }); | |
| 124 | |
| 125 test('remove all deps', () { | |
| 126 // After initial load, dependencies are 1: | |
| 127 var node = nodeOf('/index2.html'); | |
| 128 node.update(); | |
| 129 expect(node.scripts.length, 1); | |
| 130 expect(node.scripts.first, nodeOf('/a1.dart')); | |
| 131 | |
| 132 // Removing the dependency is discovered on the next round of updates: | |
| 133 updateFile(node.source, ''); | |
| 134 expect(node.scripts.length, 1); | |
| 135 node.update(); | |
| 136 expect(node.scripts.length, 0); | |
| 137 }); | |
| 138 }); | |
| 139 | |
| 140 group('Dart deps', () { | |
| 141 test('initial deps', () { | |
| 142 var a1 = nodeOf('/a1.dart'); | |
| 143 var a2 = nodeOf('/a2.dart'); | |
| 144 expect(a1.imports.length, 0); | |
| 145 expect(a1.exports.length, 0); | |
| 146 expect(a1.parts.length, 0); | |
| 147 expect(a2.imports.length, 0); | |
| 148 expect(a2.exports.length, 0); | |
| 149 expect(a2.parts.length, 0); | |
| 150 | |
| 151 a1.update(); | |
| 152 a2.update(); | |
| 153 | |
| 154 expect(a1.imports.length, 0); | |
| 155 expect(a1.exports.length, 0); | |
| 156 expect(a1.parts.length, 0); | |
| 157 expect(a2.imports.length, 2); | |
| 158 expect(a2.exports.length, 1); | |
| 159 expect(a2.parts.length, 1); | |
| 160 expect(a2.imports.contains(nodeOf('/a3.dart')), isTrue); | |
| 161 expect(a2.imports.contains(nodeOf('/a4.dart')), isTrue); | |
| 162 expect(a2.exports.contains(nodeOf('/a5.dart')), isTrue); | |
| 163 expect(a2.parts.contains(nodeOf('/a6.dart')), isTrue); | |
| 164 }); | |
| 165 | |
| 166 test('add deps', () { | |
| 167 var node = nodeOf('/a1.dart'); | |
| 168 node.update(); | |
| 169 expect(node.imports.length, 0); | |
| 170 expect(node.exports.length, 0); | |
| 171 expect(node.parts.length, 0); | |
| 172 | |
| 173 updateFile( | |
| 174 node.source, 'import "a3.dart"; export "a5.dart"; part "a8.dart";'); | |
| 175 node.update(); | |
| 176 | |
| 177 expect(node.imports.length, 1); | |
| 178 expect(node.exports.length, 1); | |
| 179 expect(node.parts.length, 1); | |
| 180 expect(node.imports.contains(nodeOf('/a3.dart')), isTrue); | |
| 181 expect(node.exports.contains(nodeOf('/a5.dart')), isTrue); | |
| 182 expect(node.parts.contains(nodeOf('/a8.dart')), isTrue); | |
| 183 }); | |
| 184 | |
| 185 test('remove deps', () { | |
| 186 var node = nodeOf('/a2.dart'); | |
| 187 node.update(); | |
| 188 expect(node.imports.length, 2); | |
| 189 expect(node.exports.length, 1); | |
| 190 expect(node.parts.length, 1); | |
| 191 expect(node.imports.contains(nodeOf('/a3.dart')), isTrue); | |
| 192 expect(node.imports.contains(nodeOf('/a4.dart')), isTrue); | |
| 193 expect(node.exports.contains(nodeOf('/a5.dart')), isTrue); | |
| 194 expect(node.parts.contains(nodeOf('/a6.dart')), isTrue); | |
| 195 | |
| 196 updateFile( | |
| 197 node.source, 'import "a3.dart"; export "a7.dart"; part "a8.dart";'); | |
| 198 node.update(); | |
| 199 | |
| 200 expect(node.imports.length, 1); | |
| 201 expect(node.exports.length, 1); | |
| 202 expect(node.parts.length, 1); | |
| 203 expect(node.imports.contains(nodeOf('/a3.dart')), isTrue); | |
| 204 expect(node.exports.contains(nodeOf('/a7.dart')), isTrue); | |
| 205 expect(node.parts.contains(nodeOf('/a8.dart')), isTrue); | |
| 206 }); | |
| 207 | |
| 208 test('change part to library', () { | |
| 209 var node = nodeOf('/a2.dart'); | |
| 210 node.update(); | |
| 211 expect(node.imports.length, 2); | |
| 212 expect(node.exports.length, 1); | |
| 213 expect(node.parts.length, 1); | |
| 214 expect(node.imports.contains(nodeOf('/a3.dart')), isTrue); | |
| 215 expect(node.imports.contains(nodeOf('/a4.dart')), isTrue); | |
| 216 expect(node.exports.contains(nodeOf('/a5.dart')), isTrue); | |
| 217 expect(node.parts.contains(nodeOf('/a6.dart')), isTrue); | |
| 218 | |
| 219 updateFile( | |
| 220 node.source, | |
| 221 ''' | |
| 222 library a2; | |
| 223 import 'a3.dart'; | |
| 224 import 'a4.dart'; | |
| 225 export 'a5.dart'; | |
| 226 import 'a6.dart'; // changed from part | |
| 227 '''); | |
| 228 var a6 = nodeOf('/a6.dart'); | |
| 229 updateFile(a6.source, ''); | |
| 230 node.update(); | |
| 231 | |
| 232 expect(node.imports.length, 3); | |
| 233 expect(node.exports.length, 1); | |
| 234 expect(node.parts.length, 0); | |
| 235 expect(node.imports.contains(nodeOf('/a3.dart')), isTrue); | |
| 236 expect(node.imports.contains(nodeOf('/a4.dart')), isTrue); | |
| 237 expect(node.imports.contains(nodeOf('/a6.dart')), isTrue); | |
| 238 expect(node.exports.contains(nodeOf('/a5.dart')), isTrue); | |
| 239 | |
| 240 expect(a6.imports.length, 0); | |
| 241 expect(a6.exports.length, 0); | |
| 242 expect(a6.parts.length, 0); | |
| 243 }); | |
| 244 | |
| 245 test('change library to part', () { | |
| 246 var node = nodeOf('/a2.dart'); | |
| 247 var a4 = nodeOf('/a4.dart'); | |
| 248 node.update(); | |
| 249 expect(node.imports.length, 2); | |
| 250 expect(node.exports.length, 1); | |
| 251 expect(node.parts.length, 1); | |
| 252 expect(node.imports.contains(nodeOf('/a3.dart')), isTrue); | |
| 253 expect(node.imports.contains(nodeOf('/a4.dart')), isTrue); | |
| 254 expect(node.exports.contains(nodeOf('/a5.dart')), isTrue); | |
| 255 expect(node.parts.contains(nodeOf('/a6.dart')), isTrue); | |
| 256 | |
| 257 a4.update(); | |
| 258 expect(a4.imports.length, 0); | |
| 259 expect(a4.exports.length, 1); | |
| 260 expect(a4.parts.length, 0); | |
| 261 | |
| 262 updateFile( | |
| 263 node.source, | |
| 264 ''' | |
| 265 library a2; | |
| 266 import 'a3.dart'; | |
| 267 part 'a4.dart'; // changed from export | |
| 268 export 'a5.dart'; | |
| 269 part 'a6.dart'; | |
| 270 '''); | |
| 271 node.update(); | |
| 272 | |
| 273 expect(node.imports.length, 1); | |
| 274 expect(node.exports.length, 1); | |
| 275 expect(node.parts.length, 2); | |
| 276 expect(node.imports.contains(nodeOf('/a3.dart')), isTrue); | |
| 277 expect(node.exports.contains(nodeOf('/a5.dart')), isTrue); | |
| 278 expect(node.parts.contains(nodeOf('/a4.dart')), isTrue); | |
| 279 expect(node.parts.contains(nodeOf('/a6.dart')), isTrue); | |
| 280 | |
| 281 // Note, technically we never modified the contents of a4 and it contains | |
| 282 // an export. This is invalid Dart, but we'll let the analyzer report that | |
| 283 // error instead of doing so ourselves. | |
| 284 expect(a4.imports.length, 0); | |
| 285 expect(a4.exports.length, 1); | |
| 286 expect(a4.parts.length, 0); | |
| 287 | |
| 288 // And change it back. | |
| 289 updateFile( | |
| 290 node.source, | |
| 291 ''' | |
| 292 library a2; | |
| 293 import 'a3.dart'; | |
| 294 import 'a4.dart'; // changed again | |
| 295 export 'a5.dart'; | |
| 296 part 'a6.dart'; | |
| 297 '''); | |
| 298 node.update(); | |
| 299 expect(node.imports.contains(a4), isTrue); | |
| 300 expect(a4.imports.length, 0); | |
| 301 expect(a4.exports.length, 1); | |
| 302 expect(a4.parts.length, 0); | |
| 303 }); | |
| 304 }); | |
| 305 | |
| 306 group('local changes', () { | |
| 307 group('needs rebuild', () { | |
| 308 test('in HTML', () { | |
| 309 var node = nodeOf('/index1.html'); | |
| 310 node.update(); | |
| 311 expect(node.needsRebuild, isTrue); | |
| 312 node.needsRebuild = false; | |
| 313 | |
| 314 node.update(); | |
| 315 expect(node.needsRebuild, isFalse); | |
| 316 | |
| 317 // For now, an empty modification is enough to trigger a rebuild | |
| 318 updateFile(node.source); | |
| 319 expect(node.needsRebuild, isFalse); | |
| 320 node.update(); | |
| 321 expect(node.needsRebuild, isTrue); | |
| 322 }); | |
| 323 | |
| 324 test('main library in Dart', () { | |
| 325 var node = nodeOf('/a2.dart'); | |
| 326 var partNode = nodeOf('/a6.dart'); | |
| 327 node.update(); | |
| 328 expect(node.needsRebuild, isTrue); | |
| 329 node.needsRebuild = false; | |
| 330 partNode.needsRebuild = false; | |
| 331 | |
| 332 node.update(); | |
| 333 expect(node.needsRebuild, isFalse); | |
| 334 | |
| 335 // For now, an empty modification is enough to trigger a rebuild | |
| 336 updateFile(node.source); | |
| 337 expect(node.needsRebuild, isFalse); | |
| 338 node.update(); | |
| 339 expect(node.needsRebuild, isTrue); | |
| 340 }); | |
| 341 | |
| 342 test('part of library in Dart', () { | |
| 343 var node = nodeOf('/a2.dart'); | |
| 344 var importNode = nodeOf('/a3.dart'); | |
| 345 var exportNode = nodeOf('/a5.dart'); | |
| 346 var partNode = nodeOf('/a6.dart'); | |
| 347 node.update(); | |
| 348 expect(node.needsRebuild, isTrue); | |
| 349 node.needsRebuild = false; | |
| 350 partNode.needsRebuild = false; | |
| 351 | |
| 352 node.update(); | |
| 353 expect(node.needsRebuild, isFalse); | |
| 354 | |
| 355 // Modification in imported/exported node makes no difference for local | |
| 356 // rebuild label (globally that's tested elsewhere) | |
| 357 updateFile(importNode.source); | |
| 358 updateFile(exportNode.source); | |
| 359 node.update(); | |
| 360 expect(node.needsRebuild, isFalse); | |
| 361 expect(partNode.needsRebuild, isFalse); | |
| 362 | |
| 363 // Modification in part triggers change in containing library: | |
| 364 updateFile(partNode.source); | |
| 365 expect(node.needsRebuild, isFalse); | |
| 366 expect(partNode.needsRebuild, isFalse); | |
| 367 node.update(); | |
| 368 expect(node.needsRebuild, isTrue); | |
| 369 expect(partNode.needsRebuild, isTrue); | |
| 370 }); | |
| 371 }); | |
| 372 | |
| 373 group('structure change', () { | |
| 374 test('no mod in HTML', () { | |
| 375 var node = nodeOf('/index2.html'); | |
| 376 node.update(); | |
| 377 expect(node.structureChanged, isTrue); | |
| 378 node.structureChanged = false; | |
| 379 | |
| 380 node.update(); | |
| 381 expect(node.structureChanged, isFalse); | |
| 382 | |
| 383 // An empty modification will not trigger a structural change | |
| 384 updateFile(node.source); | |
| 385 expect(node.structureChanged, isFalse); | |
| 386 node.update(); | |
| 387 expect(node.structureChanged, isFalse); | |
| 388 }); | |
| 389 | |
| 390 test('added scripts in HTML', () { | |
| 391 var node = nodeOf('/index2.html'); | |
| 392 node.update(); | |
| 393 expect(node.structureChanged, isTrue); | |
| 394 expect(node.scripts.length, 1); | |
| 395 | |
| 396 node.structureChanged = false; | |
| 397 node.update(); | |
| 398 expect(node.structureChanged, isFalse); | |
| 399 | |
| 400 // This change will not include new script tags: | |
| 401 updateFile(node.source, node.source.contents.data + '<div></div>'); | |
| 402 expect(node.structureChanged, isFalse); | |
| 403 node.update(); | |
| 404 expect(node.structureChanged, isFalse); | |
| 405 expect(node.scripts.length, 1); | |
| 406 | |
| 407 updateFile( | |
| 408 node.source, | |
| 409 node.source.contents.data + | |
| 410 '<script type="application/dart" src="a4.dart"></script>'); | |
| 411 expect(node.structureChanged, isFalse); | |
| 412 node.update(); | |
| 413 expect(node.structureChanged, isTrue); | |
| 414 expect(node.scripts.length, 2); | |
| 415 }); | |
| 416 | |
| 417 test('no mod in Dart', () { | |
| 418 var node = nodeOf('/a2.dart'); | |
| 419 var importNode = nodeOf('/a3.dart'); | |
| 420 var exportNode = nodeOf('/a5.dart'); | |
| 421 var partNode = nodeOf('/a6.dart'); | |
| 422 node.update(); | |
| 423 expect(node.structureChanged, isTrue); | |
| 424 node.structureChanged = false; | |
| 425 | |
| 426 node.update(); | |
| 427 expect(node.structureChanged, isFalse); | |
| 428 | |
| 429 // These modifications make no difference at all. | |
| 430 updateFile(importNode.source); | |
| 431 updateFile(exportNode.source); | |
| 432 updateFile(partNode.source); | |
| 433 updateFile(node.source); | |
| 434 | |
| 435 expect(node.structureChanged, isFalse); | |
| 436 node.update(); | |
| 437 expect(node.structureChanged, isFalse); | |
| 438 }); | |
| 439 | |
| 440 test('same directives, different order', () { | |
| 441 var node = nodeOf('/a2.dart'); | |
| 442 node.update(); | |
| 443 expect(node.structureChanged, isTrue); | |
| 444 node.structureChanged = false; | |
| 445 | |
| 446 node.update(); | |
| 447 expect(node.structureChanged, isFalse); | |
| 448 | |
| 449 // modified order of imports, but structure stays the same: | |
| 450 updateFile( | |
| 451 node.source, | |
| 452 'import "a4.dart"; import "a3.dart"; ' | |
| 453 'export "a5.dart"; part "a6.dart";'); | |
| 454 node.update(); | |
| 455 | |
| 456 expect(node.structureChanged, isFalse); | |
| 457 node.update(); | |
| 458 expect(node.structureChanged, isFalse); | |
| 459 }); | |
| 460 | |
| 461 test('changed parts', () { | |
| 462 var node = nodeOf('/a2.dart'); | |
| 463 node.update(); | |
| 464 expect(node.structureChanged, isTrue); | |
| 465 node.structureChanged = false; | |
| 466 | |
| 467 node.update(); | |
| 468 expect(node.structureChanged, isFalse); | |
| 469 | |
| 470 // added one. | |
| 471 updateFile( | |
| 472 node.source, | |
| 473 'import "a4.dart"; import "a3.dart"; ' | |
| 474 'export "a5.dart"; part "a6.dart"; part "a7.dart";'); | |
| 475 expect(node.structureChanged, isFalse); | |
| 476 node.update(); | |
| 477 expect(node.structureChanged, isTrue); | |
| 478 | |
| 479 // no change | |
| 480 node.structureChanged = false; | |
| 481 updateFile(node.source); | |
| 482 node.update(); | |
| 483 expect(node.structureChanged, isFalse); | |
| 484 | |
| 485 // removed one | |
| 486 updateFile(node.source); | |
| 487 updateFile( | |
| 488 node.source, | |
| 489 'import "a4.dart"; import "a3.dart"; ' | |
| 490 'export "a5.dart"; part "a7.dart";'); | |
| 491 expect(node.structureChanged, isFalse); | |
| 492 node.update(); | |
| 493 expect(node.structureChanged, isTrue); | |
| 494 }); | |
| 495 | |
| 496 test('changed import', () { | |
| 497 var node = nodeOf('/a2.dart'); | |
| 498 node.update(); | |
| 499 expect(node.structureChanged, isTrue); | |
| 500 node.structureChanged = false; | |
| 501 | |
| 502 node.update(); | |
| 503 expect(node.structureChanged, isFalse); | |
| 504 | |
| 505 // added one. | |
| 506 updateFile( | |
| 507 node.source, | |
| 508 'import "a4.dart"; import "a3.dart"; import "a7.dart";' | |
| 509 'export "a5.dart"; part "a6.dart";'); | |
| 510 expect(node.structureChanged, isFalse); | |
| 511 node.update(); | |
| 512 expect(node.structureChanged, isTrue); | |
| 513 | |
| 514 // no change | |
| 515 node.structureChanged = false; | |
| 516 updateFile(node.source); | |
| 517 node.update(); | |
| 518 expect(node.structureChanged, isFalse); | |
| 519 | |
| 520 // removed one | |
| 521 updateFile( | |
| 522 node.source, | |
| 523 'import "a4.dart"; import "a7.dart"; ' | |
| 524 'export "a5.dart"; part "a6.dart";'); | |
| 525 expect(node.structureChanged, isFalse); | |
| 526 node.update(); | |
| 527 expect(node.structureChanged, isTrue); | |
| 528 }); | |
| 529 | |
| 530 test('changed exports', () { | |
| 531 var node = nodeOf('/a2.dart'); | |
| 532 node.update(); | |
| 533 expect(node.structureChanged, isTrue); | |
| 534 node.structureChanged = false; | |
| 535 | |
| 536 node.update(); | |
| 537 expect(node.structureChanged, isFalse); | |
| 538 | |
| 539 // added one. | |
| 540 updateFile( | |
| 541 node.source, | |
| 542 'import "a4.dart"; import "a3.dart";' | |
| 543 'export "a5.dart"; export "a9.dart"; part "a6.dart";'); | |
| 544 expect(node.structureChanged, isFalse); | |
| 545 node.update(); | |
| 546 expect(node.structureChanged, isTrue); | |
| 547 | |
| 548 // no change | |
| 549 node.structureChanged = false; | |
| 550 updateFile(node.source); | |
| 551 node.update(); | |
| 552 expect(node.structureChanged, isFalse); | |
| 553 | |
| 554 // removed one | |
| 555 updateFile( | |
| 556 node.source, | |
| 557 'import "a4.dart"; import "a3.dart"; ' | |
| 558 'export "a5.dart"; part "a6.dart";'); | |
| 559 expect(node.structureChanged, isFalse); | |
| 560 node.update(); | |
| 561 expect(node.structureChanged, isTrue); | |
| 562 }); | |
| 563 }); | |
| 564 }); | |
| 565 | |
| 566 group('refresh structure and marks', () { | |
| 567 test('initial marks', () { | |
| 568 var node = nodeOf('/index3.html'); | |
| 569 expectGraph( | |
| 570 node, | |
| 571 ''' | |
| 572 index3.html | |
| 573 $_RUNTIME_GRAPH | |
| 574 '''); | |
| 575 refreshStructureAndMarks(node); | |
| 576 expectGraph( | |
| 577 node, | |
| 578 ''' | |
| 579 index3.html [needs-rebuild] [structure-changed] | |
| 580 |-- a2.dart [needs-rebuild] [structure-changed] | |
| 581 | |-- a3.dart [needs-rebuild] | |
| 582 | |-- a4.dart [needs-rebuild] [structure-changed] | |
| 583 | | |-- a10.dart [needs-rebuild] | |
| 584 | |-- a5.dart [needs-rebuild] | |
| 585 | |-- a6.dart (part) [needs-rebuild] | |
| 586 $_RUNTIME_GRAPH_REBUILD | |
| 587 '''); | |
| 588 }); | |
| 589 | |
| 590 test('cleared marks stay clear', () { | |
| 591 var node = nodeOf('/index3.html'); | |
| 592 refreshStructureAndMarks(node); | |
| 593 expectGraph( | |
| 594 node, | |
| 595 ''' | |
| 596 index3.html [needs-rebuild] [structure-changed] | |
| 597 |-- a2.dart [needs-rebuild] [structure-changed] | |
| 598 | |-- a3.dart [needs-rebuild] | |
| 599 | |-- a4.dart [needs-rebuild] [structure-changed] | |
| 600 | | |-- a10.dart [needs-rebuild] | |
| 601 | |-- a5.dart [needs-rebuild] | |
| 602 | |-- a6.dart (part) [needs-rebuild] | |
| 603 $_RUNTIME_GRAPH_REBUILD | |
| 604 '''); | |
| 605 clearMarks(node); | |
| 606 expectGraph( | |
| 607 node, | |
| 608 ''' | |
| 609 index3.html | |
| 610 |-- a2.dart | |
| 611 | |-- a3.dart | |
| 612 | |-- a4.dart | |
| 613 | | |-- a10.dart | |
| 614 | |-- a5.dart | |
| 615 | |-- a6.dart (part) | |
| 616 $_RUNTIME_GRAPH | |
| 617 '''); | |
| 618 | |
| 619 refreshStructureAndMarks(node); | |
| 620 expectGraph( | |
| 621 node, | |
| 622 ''' | |
| 623 index3.html | |
| 624 |-- a2.dart | |
| 625 | |-- a3.dart | |
| 626 | |-- a4.dart | |
| 627 | | |-- a10.dart | |
| 628 | |-- a5.dart | |
| 629 | |-- a6.dart (part) | |
| 630 $_RUNTIME_GRAPH | |
| 631 '''); | |
| 632 }); | |
| 633 | |
| 634 test('needsRebuild mark updated on local modifications', () { | |
| 635 var node = nodeOf('/index3.html'); | |
| 636 refreshStructureAndMarks(node); | |
| 637 clearMarks(node); | |
| 638 var a3 = nodeOf('/a3.dart'); | |
| 639 updateFile(a3.source); | |
| 640 | |
| 641 refreshStructureAndMarks(node); | |
| 642 expectGraph( | |
| 643 node, | |
| 644 ''' | |
| 645 index3.html | |
| 646 |-- a2.dart | |
| 647 | |-- a3.dart [needs-rebuild] | |
| 648 | |-- a4.dart | |
| 649 | | |-- a10.dart | |
| 650 | |-- a5.dart | |
| 651 | |-- a6.dart (part) | |
| 652 $_RUNTIME_GRAPH | |
| 653 '''); | |
| 654 }); | |
| 655 | |
| 656 test('structuredChanged mark updated on structure modifications', () { | |
| 657 var node = nodeOf('/index3.html'); | |
| 658 refreshStructureAndMarks(node); | |
| 659 clearMarks(node); | |
| 660 var a5 = nodeOf('/a5.dart'); | |
| 661 updateFile(a5.source, 'import "a8.dart";'); | |
| 662 | |
| 663 refreshStructureAndMarks(node); | |
| 664 expectGraph( | |
| 665 node, | |
| 666 ''' | |
| 667 index3.html | |
| 668 |-- a2.dart | |
| 669 | |-- a3.dart | |
| 670 | |-- a4.dart | |
| 671 | | |-- a10.dart | |
| 672 | |-- a5.dart [needs-rebuild] [structure-changed] | |
| 673 | | |-- a8.dart [needs-rebuild] [structure-changed] | |
| 674 | | | |-- a8.dart... | |
| 675 | |-- a6.dart (part) | |
| 676 $_RUNTIME_GRAPH | |
| 677 '''); | |
| 678 }); | |
| 679 }); | |
| 680 | |
| 681 group('server-mode', () { | |
| 682 setUp(() { | |
| 683 var opts = new CompilerOptions( | |
| 684 runtimeDir: '/dev_compiler_runtime/', | |
| 685 sourceOptions: new SourceResolverOptions(useMockSdk: true), | |
| 686 serverMode: true); | |
| 687 context = createAnalysisContextWithSources(opts.sourceOptions, | |
| 688 fileResolvers: [testUriResolver]); | |
| 689 graph = new SourceGraph(context, new LogReporter(context), opts); | |
| 690 }); | |
| 691 | |
| 692 test('messages widget is automatically included', () { | |
| 693 var node = nodeOf('/index3.html'); | |
| 694 expectGraph( | |
| 695 node, | |
| 696 ''' | |
| 697 index3.html | |
| 698 $_RUNTIME_GRAPH | |
| 699 |-- messages_widget.js | |
| 700 |-- messages.css | |
| 701 '''); | |
| 702 refreshStructureAndMarks(node); | |
| 703 expectGraph( | |
| 704 node, | |
| 705 ''' | |
| 706 index3.html [needs-rebuild] [structure-changed] | |
| 707 |-- a2.dart [needs-rebuild] [structure-changed] | |
| 708 | |-- a3.dart [needs-rebuild] | |
| 709 | |-- a4.dart [needs-rebuild] [structure-changed] | |
| 710 | | |-- a10.dart [needs-rebuild] | |
| 711 | |-- a5.dart [needs-rebuild] | |
| 712 | |-- a6.dart (part) [needs-rebuild] | |
| 713 $_RUNTIME_GRAPH_REBUILD | |
| 714 |-- messages_widget.js [needs-rebuild] | |
| 715 |-- messages.css [needs-rebuild] | |
| 716 '''); | |
| 717 }); | |
| 718 }); | |
| 719 | |
| 720 group('rebuild', () { | |
| 721 var results; | |
| 722 void addName(SourceNode n) => results.add(nameFor(n)); | |
| 723 | |
| 724 bool buildNoTransitiveChange(SourceNode n) { | |
| 725 addName(n); | |
| 726 return false; | |
| 727 } | |
| 728 | |
| 729 bool buildWithTransitiveChange(SourceNode n) { | |
| 730 addName(n); | |
| 731 return true; | |
| 732 } | |
| 733 | |
| 734 setUp(() { | |
| 735 results = []; | |
| 736 }); | |
| 737 | |
| 738 test('everything build on first run', () { | |
| 739 var node = nodeOf('/index3.html'); | |
| 740 rebuild(node, buildNoTransitiveChange); | |
| 741 // Note: a6.dart is not included because it built as part of a2.dart | |
| 742 expect( | |
| 743 results, | |
| 744 ['a3.dart', 'a10.dart', 'a4.dart', 'a5.dart', 'a2.dart'] | |
| 745 ..addAll(runtimeFilesWithoutPath) | |
| 746 ..add('index3.html')); | |
| 747 | |
| 748 // Marks are removed automatically by rebuild | |
| 749 expectGraph( | |
| 750 node, | |
| 751 ''' | |
| 752 index3.html | |
| 753 |-- a2.dart | |
| 754 | |-- a3.dart | |
| 755 | |-- a4.dart | |
| 756 | | |-- a10.dart | |
| 757 | |-- a5.dart | |
| 758 | |-- a6.dart (part) | |
| 759 $_RUNTIME_GRAPH | |
| 760 '''); | |
| 761 }); | |
| 762 | |
| 763 test('nothing to do after build', () { | |
| 764 var node = nodeOf('/index3.html'); | |
| 765 rebuild(node, buildNoTransitiveChange); | |
| 766 | |
| 767 results = []; | |
| 768 rebuild(node, buildNoTransitiveChange); | |
| 769 expect(results, []); | |
| 770 }); | |
| 771 | |
| 772 test('modified part triggers building library', () { | |
| 773 var node = nodeOf('/index3.html'); | |
| 774 rebuild(node, buildNoTransitiveChange); | |
| 775 results = []; | |
| 776 | |
| 777 var a6 = nodeOf('/a6.dart'); | |
| 778 updateFile(a6.source); | |
| 779 rebuild(node, buildNoTransitiveChange); | |
| 780 expect(results, ['a2.dart']); | |
| 781 | |
| 782 results = []; | |
| 783 rebuild(node, buildNoTransitiveChange); | |
| 784 expect(results, []); | |
| 785 }); | |
| 786 | |
| 787 test('non-API change triggers build stays local', () { | |
| 788 var node = nodeOf('/index3.html'); | |
| 789 rebuild(node, buildNoTransitiveChange); | |
| 790 results = []; | |
| 791 | |
| 792 var a3 = nodeOf('/a3.dart'); | |
| 793 updateFile(a3.source); | |
| 794 rebuild(node, buildNoTransitiveChange); | |
| 795 expect(results, ['a3.dart']); | |
| 796 | |
| 797 results = []; | |
| 798 rebuild(node, buildNoTransitiveChange); | |
| 799 expect(results, []); | |
| 800 }); | |
| 801 | |
| 802 test('no-API change in exported file stays local', () { | |
| 803 var node = nodeOf('/index3.html'); | |
| 804 rebuild(node, buildNoTransitiveChange); | |
| 805 results = []; | |
| 806 | |
| 807 // similar to the test above, but a10 is exported from a4. | |
| 808 var a3 = nodeOf('/a10.dart'); | |
| 809 updateFile(a3.source); | |
| 810 rebuild(node, buildNoTransitiveChange); | |
| 811 expect(results, ['a10.dart']); | |
| 812 | |
| 813 results = []; | |
| 814 rebuild(node, buildNoTransitiveChange); | |
| 815 expect(results, []); | |
| 816 }); | |
| 817 | |
| 818 test('API change in lib, triggers build on imports', () { | |
| 819 var node = nodeOf('/index3.html'); | |
| 820 rebuild(node, buildNoTransitiveChange); | |
| 821 results = []; | |
| 822 | |
| 823 var a3 = nodeOf('/a3.dart'); | |
| 824 updateFile(a3.source); | |
| 825 rebuild(node, buildWithTransitiveChange); | |
| 826 expect(results, ['a3.dart', 'a2.dart']); | |
| 827 | |
| 828 results = []; | |
| 829 rebuild(node, buildNoTransitiveChange); | |
| 830 expect(results, []); | |
| 831 }); | |
| 832 | |
| 833 test('API change in export, triggers build on imports', () { | |
| 834 var node = nodeOf('/index3.html'); | |
| 835 rebuild(node, buildNoTransitiveChange); | |
| 836 results = []; | |
| 837 | |
| 838 var a3 = nodeOf('/a10.dart'); | |
| 839 updateFile(a3.source); | |
| 840 rebuild(node, buildWithTransitiveChange); | |
| 841 | |
| 842 // Node: a4.dart reexports a10.dart, but it doesn't import it, so we don't | |
| 843 // need to rebuild it. | |
| 844 expect(results, ['a10.dart', 'a2.dart']); | |
| 845 | |
| 846 results = []; | |
| 847 rebuild(node, buildNoTransitiveChange); | |
| 848 expect(results, []); | |
| 849 }); | |
| 850 | |
| 851 test('structural change rebuilds HTML, but skips unreachable code', () { | |
| 852 var node = nodeOf('/index3.html'); | |
| 853 rebuild(node, buildNoTransitiveChange); | |
| 854 results = []; | |
| 855 | |
| 856 var a2 = nodeOf('/a2.dart'); | |
| 857 updateFile(a2.source, 'import "a4.dart";'); | |
| 858 | |
| 859 var a3 = nodeOf('/a3.dart'); | |
| 860 updateFile(a3.source); | |
| 861 rebuild(node, buildNoTransitiveChange); | |
| 862 | |
| 863 // a3 will become unreachable, index3 reflects structural changes. | |
| 864 expect(results, ['a2.dart', 'index3.html']); | |
| 865 | |
| 866 results = []; | |
| 867 rebuild(node, buildNoTransitiveChange); | |
| 868 expect(results, []); | |
| 869 }); | |
| 870 | |
| 871 test('newly discovered files get built too', () { | |
| 872 var node = nodeOf('/index3.html'); | |
| 873 rebuild(node, buildNoTransitiveChange); | |
| 874 results = []; | |
| 875 | |
| 876 var a2 = nodeOf('/a2.dart'); | |
| 877 updateFile(a2.source, 'import "a9.dart";'); | |
| 878 | |
| 879 rebuild(node, buildNoTransitiveChange); | |
| 880 expect(results, ['a8.dart', 'a9.dart', 'a2.dart', 'index3.html']); | |
| 881 | |
| 882 results = []; | |
| 883 rebuild(node, buildNoTransitiveChange); | |
| 884 expect(results, []); | |
| 885 }); | |
| 886 | |
| 887 group('file upgrades', () { | |
| 888 // Normally upgrading involves two changes: | |
| 889 // (a) change the affected file | |
| 890 // (b) change directive from part to import (or viceversa) | |
| 891 // These could happen in any order and we should reach a consistent state | |
| 892 // in the end. | |
| 893 | |
| 894 test('convert part to a library before updating the import', () { | |
| 895 var node = nodeOf('/index3.html'); | |
| 896 var a2 = nodeOf('/a2.dart'); | |
| 897 var a6 = nodeOf('/a6.dart'); | |
| 898 rebuild(node, buildNoTransitiveChange); | |
| 899 | |
| 900 expectGraph( | |
| 901 node, | |
| 902 ''' | |
| 903 index3.html | |
| 904 |-- a2.dart | |
| 905 | |-- a3.dart | |
| 906 | |-- a4.dart | |
| 907 | | |-- a10.dart | |
| 908 | |-- a5.dart | |
| 909 | |-- a6.dart (part) | |
| 910 $_RUNTIME_GRAPH | |
| 911 '''); | |
| 912 | |
| 913 // Modify the file first: | |
| 914 updateFile(a6.source, 'library a6; import "a5.dart";'); | |
| 915 results = []; | |
| 916 rebuild(node, buildNoTransitiveChange); | |
| 917 | |
| 918 // Looks to us like a change in a part, we'll report errors that the | |
| 919 // part is not really a part-file. Note that a6.dart is not included | |
| 920 // below, because we don't build it as a library. | |
| 921 expect(results, ['a2.dart']); | |
| 922 expectGraph( | |
| 923 node, | |
| 924 ''' | |
| 925 index3.html | |
| 926 |-- a2.dart | |
| 927 | |-- a3.dart | |
| 928 | |-- a4.dart | |
| 929 | | |-- a10.dart | |
| 930 | |-- a5.dart | |
| 931 | |-- a6.dart (part) | |
| 932 $_RUNTIME_GRAPH | |
| 933 '''); | |
| 934 | |
| 935 updateFile( | |
| 936 a2.source, | |
| 937 ''' | |
| 938 library a2; | |
| 939 import 'a3.dart'; | |
| 940 import 'a4.dart'; | |
| 941 import 'a6.dart'; // properly import it | |
| 942 export 'a5.dart'; | |
| 943 '''); | |
| 944 results = []; | |
| 945 rebuild(node, buildNoTransitiveChange); | |
| 946 // Note that a6 is now included, because we haven't built it as a | |
| 947 // library until now: | |
| 948 expect(results, ['a6.dart', 'a2.dart', 'index3.html']); | |
| 949 | |
| 950 updateFile(a6.source); | |
| 951 results = []; | |
| 952 rebuild(node, buildNoTransitiveChange); | |
| 953 expect(results, ['a6.dart']); | |
| 954 | |
| 955 expectGraph( | |
| 956 node, | |
| 957 ''' | |
| 958 index3.html | |
| 959 |-- a2.dart | |
| 960 | |-- a3.dart | |
| 961 | |-- a4.dart | |
| 962 | | |-- a10.dart | |
| 963 | |-- a6.dart | |
| 964 | | |-- a5.dart | |
| 965 | |-- a5.dart... | |
| 966 $_RUNTIME_GRAPH | |
| 967 '''); | |
| 968 }); | |
| 969 | |
| 970 test('convert part to a library after updating the import', () { | |
| 971 var node = nodeOf('/index3.html'); | |
| 972 var a2 = nodeOf('/a2.dart'); | |
| 973 var a6 = nodeOf('/a6.dart'); | |
| 974 rebuild(node, buildNoTransitiveChange); | |
| 975 | |
| 976 expectGraph( | |
| 977 node, | |
| 978 ''' | |
| 979 index3.html | |
| 980 |-- a2.dart | |
| 981 | |-- a3.dart | |
| 982 | |-- a4.dart | |
| 983 | | |-- a10.dart | |
| 984 | |-- a5.dart | |
| 985 | |-- a6.dart (part) | |
| 986 $_RUNTIME_GRAPH | |
| 987 '''); | |
| 988 | |
| 989 updateFile( | |
| 990 a2.source, | |
| 991 ''' | |
| 992 library a2; | |
| 993 import 'a3.dart'; | |
| 994 import 'a4.dart'; | |
| 995 import 'a6.dart'; // properly import it | |
| 996 export 'a5.dart'; | |
| 997 '''); | |
| 998 results = []; | |
| 999 rebuild(node, buildNoTransitiveChange); | |
| 1000 expect(results, ['a6.dart', 'a2.dart', 'index3.html']); | |
| 1001 expectGraph( | |
| 1002 node, | |
| 1003 ''' | |
| 1004 index3.html | |
| 1005 |-- a2.dart | |
| 1006 | |-- a3.dart | |
| 1007 | |-- a4.dart | |
| 1008 | | |-- a10.dart | |
| 1009 | |-- a6.dart | |
| 1010 | |-- a5.dart | |
| 1011 $_RUNTIME_GRAPH | |
| 1012 '''); | |
| 1013 | |
| 1014 updateFile(a6.source, 'library a6; import "a5.dart";'); | |
| 1015 results = []; | |
| 1016 rebuild(node, buildNoTransitiveChange); | |
| 1017 expect(results, ['a6.dart', 'index3.html']); | |
| 1018 expectGraph( | |
| 1019 node, | |
| 1020 ''' | |
| 1021 index3.html | |
| 1022 |-- a2.dart | |
| 1023 | |-- a3.dart | |
| 1024 | |-- a4.dart | |
| 1025 | | |-- a10.dart | |
| 1026 | |-- a6.dart | |
| 1027 | | |-- a5.dart | |
| 1028 | |-- a5.dart... | |
| 1029 $_RUNTIME_GRAPH | |
| 1030 '''); | |
| 1031 }); | |
| 1032 | |
| 1033 test('disconnect part making it a library', () { | |
| 1034 var node = nodeOf('/index3.html'); | |
| 1035 var a2 = nodeOf('/a2.dart'); | |
| 1036 var a6 = nodeOf('/a6.dart'); | |
| 1037 rebuild(node, buildNoTransitiveChange); | |
| 1038 | |
| 1039 expectGraph( | |
| 1040 node, | |
| 1041 ''' | |
| 1042 index3.html | |
| 1043 |-- a2.dart | |
| 1044 | |-- a3.dart | |
| 1045 | |-- a4.dart | |
| 1046 | | |-- a10.dart | |
| 1047 | |-- a5.dart | |
| 1048 | |-- a6.dart (part) | |
| 1049 $_RUNTIME_GRAPH | |
| 1050 '''); | |
| 1051 | |
| 1052 updateFile( | |
| 1053 a2.source, | |
| 1054 ''' | |
| 1055 library a2; | |
| 1056 import 'a3.dart'; | |
| 1057 import 'a4.dart'; | |
| 1058 export 'a5.dart'; | |
| 1059 '''); | |
| 1060 updateFile(a6.source, 'library a6; import "a5.dart";'); | |
| 1061 results = []; | |
| 1062 rebuild(node, buildNoTransitiveChange); | |
| 1063 // a6 is not here, it's not reachable so we don't build it. | |
| 1064 expect(results, ['a2.dart', 'index3.html']); | |
| 1065 expectGraph( | |
| 1066 node, | |
| 1067 ''' | |
| 1068 index3.html | |
| 1069 |-- a2.dart | |
| 1070 | |-- a3.dart | |
| 1071 | |-- a4.dart | |
| 1072 | | |-- a10.dart | |
| 1073 | |-- a5.dart | |
| 1074 $_RUNTIME_GRAPH | |
| 1075 '''); | |
| 1076 }); | |
| 1077 | |
| 1078 test('convert a library to a part', () { | |
| 1079 var node = nodeOf('/index3.html'); | |
| 1080 var a2 = nodeOf('/a2.dart'); | |
| 1081 var a5 = nodeOf('/a5.dart'); | |
| 1082 rebuild(node, buildNoTransitiveChange); | |
| 1083 | |
| 1084 expectGraph( | |
| 1085 node, | |
| 1086 ''' | |
| 1087 index3.html | |
| 1088 |-- a2.dart | |
| 1089 | |-- a3.dart | |
| 1090 | |-- a4.dart | |
| 1091 | | |-- a10.dart | |
| 1092 | |-- a5.dart | |
| 1093 | |-- a6.dart (part) | |
| 1094 $_RUNTIME_GRAPH | |
| 1095 '''); | |
| 1096 | |
| 1097 updateFile( | |
| 1098 a2.source, | |
| 1099 ''' | |
| 1100 library a2; | |
| 1101 import 'a3.dart'; | |
| 1102 import 'a4.dart'; | |
| 1103 part 'a5.dart'; // make it a part | |
| 1104 part 'a6.dart'; | |
| 1105 '''); | |
| 1106 results = []; | |
| 1107 rebuild(node, buildNoTransitiveChange); | |
| 1108 expect(results, ['a2.dart', 'index3.html']); | |
| 1109 expectGraph( | |
| 1110 node, | |
| 1111 ''' | |
| 1112 index3.html | |
| 1113 |-- a2.dart | |
| 1114 | |-- a3.dart | |
| 1115 | |-- a4.dart | |
| 1116 | | |-- a10.dart | |
| 1117 | |-- a5.dart (part) | |
| 1118 | |-- a6.dart (part) | |
| 1119 $_RUNTIME_GRAPH | |
| 1120 '''); | |
| 1121 | |
| 1122 updateFile(a5.source, 'part of a2;'); | |
| 1123 results = []; | |
| 1124 rebuild(node, buildNoTransitiveChange); | |
| 1125 expect(results, ['a2.dart']); | |
| 1126 expectGraph( | |
| 1127 node, | |
| 1128 ''' | |
| 1129 index3.html | |
| 1130 |-- a2.dart | |
| 1131 | |-- a3.dart | |
| 1132 | |-- a4.dart | |
| 1133 | | |-- a10.dart | |
| 1134 | |-- a5.dart (part) | |
| 1135 | |-- a6.dart (part) | |
| 1136 $_RUNTIME_GRAPH | |
| 1137 '''); | |
| 1138 }); | |
| 1139 }); | |
| 1140 | |
| 1141 group('represented non-existing files', () { | |
| 1142 test('recognize locally change between existing and not-existing', () { | |
| 1143 var n = nodeOf('/foo.dart'); | |
| 1144 expect(n.source, isNotNull); | |
| 1145 expect(n.source.exists(), isFalse); | |
| 1146 var source = testUriResolver.resolveAbsolute(new Uri.file('/foo.dart')); | |
| 1147 expect(n.source, source); | |
| 1148 updateFile(source, "hi"); | |
| 1149 expect(n.source.exists(), isTrue); | |
| 1150 }); | |
| 1151 | |
| 1152 test('non-existing files are tracked in dependencies', () { | |
| 1153 var node = nodeOf('/foo.dart'); | |
| 1154 updateFile(node.source, "import 'bar.dart';"); | |
| 1155 rebuild(node, buildNoTransitiveChange); | |
| 1156 expect(node.allDeps.contains(nodeOf('/bar.dart')), isTrue); | |
| 1157 | |
| 1158 var source = nodeOf('/bar.dart').source; | |
| 1159 updateFile(source, "hi"); | |
| 1160 results = []; | |
| 1161 rebuild(node, buildWithTransitiveChange); | |
| 1162 expect(results, ['bar.dart', 'foo.dart']); | |
| 1163 }); | |
| 1164 }); | |
| 1165 | |
| 1166 group('null for non-existing files', () { | |
| 1167 setUp(() { | |
| 1168 context = createAnalysisContextWithSources(options.sourceOptions, | |
| 1169 fileResolvers: [testUriResolver]); | |
| 1170 graph = new SourceGraph(context, new LogReporter(context), options); | |
| 1171 }); | |
| 1172 | |
| 1173 test('recognize locally change between existing and not-existing', () { | |
| 1174 var n = nodeOf('/foo.dart'); | |
| 1175 expect(n.source.exists(), isFalse); | |
| 1176 var source = | |
| 1177 testResourceProvider.newFile('/foo.dart', 'hi').createSource(); | |
| 1178 expect( | |
| 1179 testUriResolver.resolveAbsolute(new Uri.file('/foo.dart')), source); | |
| 1180 expect(n.source, source); | |
| 1181 expect(n.source.exists(), isTrue); | |
| 1182 n.update(); | |
| 1183 expect(n.needsRebuild, isTrue); | |
| 1184 }); | |
| 1185 | |
| 1186 test('non-existing files are tracked in dependencies', () { | |
| 1187 testResourceProvider | |
| 1188 .newFile('/foo.dart', "import 'bar.dart';") | |
| 1189 .createSource(); | |
| 1190 var node = nodeOf('/foo.dart'); | |
| 1191 rebuild(node, buildNoTransitiveChange); | |
| 1192 expect(node.allDeps.length, 1); | |
| 1193 expect(node.allDeps.contains(nodeOf('/bar.dart')), isTrue); | |
| 1194 expect(nodeOf('/bar.dart').source.exists(), isFalse); | |
| 1195 | |
| 1196 testResourceProvider.newFile('/bar.dart', 'hi').createSource(); | |
| 1197 results = []; | |
| 1198 rebuild(node, buildWithTransitiveChange); | |
| 1199 expect(results, ['bar.dart', 'foo.dart']); | |
| 1200 }); | |
| 1201 }); | |
| 1202 }); | |
| 1203 } | |
| 1204 | |
| 1205 expectGraph(SourceNode node, String expectation) { | |
| 1206 expect(printReachable(node), equalsIgnoringWhitespace(expectation)); | |
| 1207 } | |
| 1208 | |
| 1209 nameFor(SourceNode node) => path.basename(node.uri.path); | |
| 1210 printReachable(SourceNode node) { | |
| 1211 var seen = new Set(); | |
| 1212 var sb = new StringBuffer(); | |
| 1213 helper(n, {indent: 0}) { | |
| 1214 if (indent > 0) { | |
| 1215 sb..write("| " * (indent - 1))..write("|-- "); | |
| 1216 } | |
| 1217 sb.write(nameFor(n)); | |
| 1218 if (seen.contains(n)) { | |
| 1219 sb.write('...\n'); | |
| 1220 return; | |
| 1221 } | |
| 1222 seen.add(n); | |
| 1223 sb | |
| 1224 ..write(' ') | |
| 1225 ..write(n.needsRebuild ? '[needs-rebuild] ' : '') | |
| 1226 ..write(n.structureChanged ? '[structure-changed] ' : ' ') | |
| 1227 ..write('\n'); | |
| 1228 n.depsWithoutParts.forEach((e) => helper(e, indent: indent + 1)); | |
| 1229 if (n is DartSourceNode) { | |
| 1230 n.parts.forEach((e) { | |
| 1231 sb | |
| 1232 ..write("| " * indent) | |
| 1233 ..write("|-- ") | |
| 1234 ..write(nameFor(e)) | |
| 1235 ..write(" (part) ") | |
| 1236 ..write(e.needsRebuild ? '[needs-rebuild] ' : '') | |
| 1237 ..write(e.structureChanged ? '[structure-changed] ' : ' ') | |
| 1238 ..write('\n'); | |
| 1239 }); | |
| 1240 } | |
| 1241 } | |
| 1242 helper(node); | |
| 1243 return sb.toString(); | |
| 1244 } | |
| 1245 | |
| 1246 final runtimeFilesWithoutPath = defaultRuntimeFiles | |
| 1247 .map((f) => f.replaceAll('dart/', '')) | |
| 1248 .toList(growable: false); | |
| 1249 final _RUNTIME_GRAPH = runtimeFilesWithoutPath.map((s) => '|-- $s').join('\n'); | |
| 1250 final _RUNTIME_GRAPH_REBUILD = | |
| 1251 runtimeFilesWithoutPath.map((s) => '|-- $s [needs-rebuild]').join('\n'); | |
| OLD | NEW |