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 |