Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(308)

Side by Side Diff: test/dependency_graph_test.dart

Issue 1788973002: Remove code that requires whole-program compile (Closed) Base URL: git@github.com:dart-lang/dev_compiler.git@master
Patch Set: merged Created 4 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « test/codegen_test.dart ('k') | test/report_test.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(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');
OLDNEW
« no previous file with comments | « test/codegen_test.dart ('k') | test/report_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698