| Index: test/dependency_graph_test.dart
|
| diff --git a/test/dependency_graph_test.dart b/test/dependency_graph_test.dart
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..2e5f1bfd802ade8c743af78eb9f55aea0d8c5980
|
| --- /dev/null
|
| +++ b/test/dependency_graph_test.dart
|
| @@ -0,0 +1,750 @@
|
| +// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
|
| +// for details. All rights reserved. Use of this source code is governed by a
|
| +// BSD-style license that can be found in the LICENSE file.
|
| +
|
| +library ddc.test.dependency_graph_test;
|
| +
|
| +import 'package:unittest/compact_vm_config.dart';
|
| +import 'package:unittest/unittest.dart';
|
| +
|
| +import 'package:dev_compiler/src/checker/dart_sdk.dart'
|
| + show mockSdkSources, dartSdkDirectory;
|
| +import 'package:dev_compiler/src/testing.dart';
|
| +import 'package:dev_compiler/src/options.dart';
|
| +import 'package:dev_compiler/src/checker/resolver.dart';
|
| +import 'package:dev_compiler/src/dependency_graph.dart';
|
| +import 'package:path/path.dart' as path;
|
| +
|
| +main() {
|
| + groupSep = " > ";
|
| + useCompactVMConfiguration();
|
| +
|
| + var options = new CompilerOptions();
|
| + var testUriResolver;
|
| + var context;
|
| + var graph;
|
| +
|
| + /// Initial values for test files
|
| + var testFiles = {
|
| + '/index1.html': '''
|
| + <script src="foo.js"></script>
|
| + ''',
|
| + '/index2.html': '''
|
| + <script type="application/dart" src="a1.dart"></script>
|
| + ''',
|
| + '/index3.html': '''
|
| + <script type="application/dart" src="a2.dart"></script>
|
| + ''',
|
| + '/a1.dart': '''
|
| + library a1;
|
| + ''',
|
| + '/a2.dart': '''
|
| + library a2;
|
| + import 'a3.dart';
|
| + import 'a4.dart';
|
| + export 'a5.dart';
|
| + part 'a6.dart';
|
| + ''',
|
| + '/a3.dart': 'library a3;',
|
| + '/a4.dart': 'library a4; export "a10.dart";',
|
| + '/a5.dart': 'library a5;',
|
| + '/a6.dart': 'part of a2;',
|
| + '/a8.dart': 'library a8; import "a8.dart";',
|
| + '/a9.dart': 'library a9; import "a8.dart";',
|
| + '/a10.dart': 'library a10;',
|
| + };
|
| +
|
| + nodeOf(String filepath, [bool isPart = false]) =>
|
| + graph.nodeFromUri(new Uri.file(filepath), isPart);
|
| +
|
| + setUp(() {
|
| + /// We completely reset the TestUriResolver to avoid interference between
|
| + /// tests (since some tests modify the state of the files).
|
| + testUriResolver = new TestUriResolver(testFiles);
|
| + context = new TypeResolver.fromMock(mockSdkSources, options,
|
| + otherResolvers: [testUriResolver]).context;
|
| + graph = new SourceGraph(context, options);
|
| + });
|
| +
|
| + group('HTML deps', () {
|
| + test('initial deps', () {
|
| + var i1 = nodeOf('/index1.html');
|
| + var i2 = nodeOf('/index2.html');
|
| + expect(i1.scripts.length, 0);
|
| + expect(i2.scripts.length, 0);
|
| + i1.update(graph);
|
| + i2.update(graph);
|
| + expect(i1.scripts.length, 0);
|
| + expect(i2.scripts.length, 1);
|
| + expect(i2.scripts.first, nodeOf('/a1.dart'));
|
| + });
|
| +
|
| + test('add a dep', () {
|
| + // After initial load, dependencies are 0:
|
| + var node = nodeOf('/index1.html');
|
| + node.update(graph);
|
| + expect(node.scripts.length, 0);
|
| +
|
| + // Adding the dependency is discovered on the next round of updates:
|
| + node.source.contents.modificationTime++;
|
| + node.source.contents.data =
|
| + '<script type="application/dart" src="a2.dart"></script>';
|
| + expect(node.scripts.length, 0);
|
| + node.update(graph);
|
| + expect(node.scripts.length, 1);
|
| + expect(node.scripts.first, nodeOf('/a2.dart'));
|
| + });
|
| +
|
| + test('add more deps', () {
|
| + // After initial load, dependencies are 1:
|
| + var node = nodeOf('/index2.html');
|
| + node.update(graph);
|
| + expect(node.scripts.length, 1);
|
| + expect(node.scripts.first, nodeOf('/a1.dart'));
|
| +
|
| + node.source.contents.modificationTime++;
|
| + node.source.contents.data +=
|
| + '<script type="application/dart" src="a2.dart"></script>';
|
| + expect(node.scripts.length, 1);
|
| + node.update(graph);
|
| + expect(node.scripts.length, 2);
|
| + expect(node.scripts.first, nodeOf('/a1.dart'));
|
| + expect(node.scripts.last, nodeOf('/a2.dart'));
|
| + });
|
| +
|
| + test('remove all deps', () {
|
| + // After initial load, dependencies are 1:
|
| + var node = nodeOf('/index2.html');
|
| + node.update(graph);
|
| + expect(node.scripts.length, 1);
|
| + expect(node.scripts.first, nodeOf('/a1.dart'));
|
| +
|
| + // Removing the dependency is discovered on the next round of updates:
|
| + node.source.contents.modificationTime++;
|
| + node.source.contents.data = '';
|
| + expect(node.scripts.length, 1);
|
| + node.update(graph);
|
| + expect(node.scripts.length, 0);
|
| + });
|
| + });
|
| +
|
| + group('Dart deps', () {
|
| + test('initial deps', () {
|
| + var a1 = nodeOf('/a1.dart');
|
| + var a2 = nodeOf('/a2.dart');
|
| + expect(a1.imports.length, 0);
|
| + expect(a1.exports.length, 0);
|
| + expect(a1.parts.length, 0);
|
| + expect(a2.imports.length, 0);
|
| + expect(a2.exports.length, 0);
|
| + expect(a2.parts.length, 0);
|
| +
|
| + a1.update(graph);
|
| + a2.update(graph);
|
| +
|
| + expect(a1.imports.length, 0);
|
| + expect(a1.exports.length, 0);
|
| + expect(a1.parts.length, 0);
|
| + expect(a2.imports.length, 2);
|
| + expect(a2.exports.length, 1);
|
| + expect(a2.parts.length, 1);
|
| + expect(a2.imports.contains(nodeOf('/a3.dart')), isTrue);
|
| + expect(a2.imports.contains(nodeOf('/a4.dart')), isTrue);
|
| + expect(a2.exports.contains(nodeOf('/a5.dart')), isTrue);
|
| + expect(a2.parts.contains(nodeOf('/a6.dart')), isTrue);
|
| + });
|
| +
|
| + test('add deps', () {
|
| + var node = nodeOf('/a1.dart');
|
| + node.update(graph);
|
| + expect(node.imports.length, 0);
|
| + expect(node.exports.length, 0);
|
| + expect(node.parts.length, 0);
|
| +
|
| + node.source.contents.modificationTime++;
|
| + node.source.contents.data =
|
| + 'import "a3.dart"; export "a5.dart"; part "a8.dart";';
|
| + node.update(graph);
|
| +
|
| + expect(node.imports.length, 1);
|
| + expect(node.exports.length, 1);
|
| + expect(node.parts.length, 1);
|
| + expect(node.imports.contains(nodeOf('/a3.dart')), isTrue);
|
| + expect(node.exports.contains(nodeOf('/a5.dart')), isTrue);
|
| + expect(node.parts.contains(nodeOf('/a8.dart')), isTrue);
|
| + });
|
| +
|
| + test('remove deps', () {
|
| + var node = nodeOf('/a2.dart');
|
| + node.update(graph);
|
| + expect(node.imports.length, 2);
|
| + expect(node.exports.length, 1);
|
| + expect(node.parts.length, 1);
|
| + expect(node.imports.contains(nodeOf('/a3.dart')), isTrue);
|
| + expect(node.imports.contains(nodeOf('/a4.dart')), isTrue);
|
| + expect(node.exports.contains(nodeOf('/a5.dart')), isTrue);
|
| + expect(node.parts.contains(nodeOf('/a6.dart')), isTrue);
|
| +
|
| + node.source.contents.modificationTime++;
|
| + node.source.contents.data =
|
| + 'import "a3.dart"; export "a6.dart"; part "a8.dart";';
|
| + node.update(graph);
|
| +
|
| + expect(node.imports.length, 1);
|
| + expect(node.exports.length, 1);
|
| + expect(node.parts.length, 1);
|
| + expect(node.imports.contains(nodeOf('/a3.dart')), isTrue);
|
| + expect(node.exports.contains(nodeOf('/a6.dart')), isTrue);
|
| + expect(node.parts.contains(nodeOf('/a8.dart')), isTrue);
|
| + });
|
| + });
|
| +
|
| + group('local changes', () {
|
| + group('needs rebuild', () {
|
| + test('in HTML', () {
|
| + var node = nodeOf('/index1.html');
|
| + node.update(graph);
|
| + expect(node.needsRebuild, isTrue);
|
| + node.needsRebuild = false;
|
| +
|
| + node.update(graph);
|
| + expect(node.needsRebuild, isFalse);
|
| +
|
| + // For now, an empty modification is enough to trigger a rebuild
|
| + node.source.contents.modificationTime++;
|
| + expect(node.needsRebuild, isFalse);
|
| + node.update(graph);
|
| + expect(node.needsRebuild, isTrue);
|
| + });
|
| +
|
| + test('main library in Dart', () {
|
| + var node = nodeOf('/a2.dart');
|
| + var partNode = nodeOf('/a6.dart', true);
|
| + node.update(graph);
|
| + expect(node.needsRebuild, isTrue);
|
| + node.needsRebuild = false;
|
| + partNode.needsRebuild = false;
|
| +
|
| + node.update(graph);
|
| + expect(node.needsRebuild, isFalse);
|
| +
|
| + // For now, an empty modification is enough to trigger a rebuild
|
| + node.source.contents.modificationTime++;
|
| + expect(node.needsRebuild, isFalse);
|
| + node.update(graph);
|
| + expect(node.needsRebuild, isTrue);
|
| + });
|
| +
|
| + test('part of library in Dart', () {
|
| + var node = nodeOf('/a2.dart');
|
| + var importNode = nodeOf('/a3.dart');
|
| + var exportNode = nodeOf('/a5.dart');
|
| + var partNode = nodeOf('/a6.dart', true);
|
| + node.update(graph);
|
| + expect(node.needsRebuild, isTrue);
|
| + node.needsRebuild = false;
|
| + partNode.needsRebuild = false;
|
| +
|
| + node.update(graph);
|
| + expect(node.needsRebuild, isFalse);
|
| +
|
| + // Modification in imported/exported node makes no difference for local
|
| + // rebuild label (globally that's tested elsewhere)
|
| + importNode.source.contents.modificationTime++;
|
| + exportNode.source.contents.modificationTime++;
|
| + node.update(graph);
|
| + expect(node.needsRebuild, isFalse);
|
| + expect(partNode.needsRebuild, isFalse);
|
| +
|
| + // Modification in part triggers change in containing library:
|
| + partNode.source.contents.modificationTime++;
|
| + expect(node.needsRebuild, isFalse);
|
| + expect(partNode.needsRebuild, isFalse);
|
| + node.update(graph);
|
| + expect(node.needsRebuild, isTrue);
|
| + expect(partNode.needsRebuild, isTrue);
|
| + });
|
| + });
|
| +
|
| + group('structure change', () {
|
| + test('no mod in HTML', () {
|
| + var node = nodeOf('/index2.html');
|
| + node.update(graph);
|
| + expect(node.structureChanged, isTrue);
|
| + node.structureChanged = false;
|
| +
|
| + node.update(graph);
|
| + expect(node.structureChanged, isFalse);
|
| +
|
| + // An empty modification will not trigger a structural change
|
| + node.source.contents.modificationTime++;
|
| + expect(node.structureChanged, isFalse);
|
| + node.update(graph);
|
| + expect(node.structureChanged, isFalse);
|
| + });
|
| +
|
| + test('added scripts in HTML', () {
|
| + var node = nodeOf('/index2.html');
|
| + node.update(graph);
|
| + expect(node.structureChanged, isTrue);
|
| + expect(node.scripts.length, 1);
|
| +
|
| + node.structureChanged = false;
|
| + node.update(graph);
|
| + expect(node.structureChanged, isFalse);
|
| +
|
| + // This change will not include new script tags:
|
| + node.source.contents.modificationTime++;
|
| + node.source.contents.data += '<div></div>';
|
| + expect(node.structureChanged, isFalse);
|
| + node.update(graph);
|
| + expect(node.structureChanged, isFalse);
|
| + expect(node.scripts.length, 1);
|
| +
|
| + node.source.contents.modificationTime++;
|
| + node.source.contents.data +=
|
| + '<script type="application/dart" src="a4.dart"></script>';
|
| + expect(node.structureChanged, isFalse);
|
| + node.update(graph);
|
| + expect(node.structureChanged, isTrue);
|
| + expect(node.scripts.length, 2);
|
| + });
|
| +
|
| + test('no mod in Dart', () {
|
| + var node = nodeOf('/a2.dart');
|
| + var importNode = nodeOf('/a3.dart');
|
| + var exportNode = nodeOf('/a5.dart');
|
| + var partNode = nodeOf('/a6.dart', true);
|
| + node.update(graph);
|
| + expect(node.structureChanged, isTrue);
|
| + node.structureChanged = false;
|
| +
|
| + node.update(graph);
|
| + expect(node.structureChanged, isFalse);
|
| +
|
| + // These modifications make no difference at all.
|
| + importNode.source.contents.modificationTime++;
|
| + exportNode.source.contents.modificationTime++;
|
| + partNode.source.contents.modificationTime++;
|
| + node.source.contents.modificationTime++;
|
| +
|
| + expect(node.structureChanged, isFalse);
|
| + node.update(graph);
|
| + expect(node.structureChanged, isFalse);
|
| + });
|
| +
|
| + test('same directives, different order', () {
|
| + var node = nodeOf('/a2.dart');
|
| + node.update(graph);
|
| + expect(node.structureChanged, isTrue);
|
| + node.structureChanged = false;
|
| +
|
| + node.update(graph);
|
| + expect(node.structureChanged, isFalse);
|
| +
|
| + // modified order of imports, but structure stays the same:
|
| + node.source.contents.modificationTime++;
|
| + node.source.contents.data = 'import "a4.dart"; import "a3.dart"; '
|
| + 'export "a5.dart"; part "a6.dart";';
|
| + node.update(graph);
|
| +
|
| + expect(node.structureChanged, isFalse);
|
| + node.update(graph);
|
| + expect(node.structureChanged, isFalse);
|
| + });
|
| +
|
| + test('changed parts', () {
|
| + var node = nodeOf('/a2.dart');
|
| + node.update(graph);
|
| + expect(node.structureChanged, isTrue);
|
| + node.structureChanged = false;
|
| +
|
| + node.update(graph);
|
| + expect(node.structureChanged, isFalse);
|
| +
|
| + // added one.
|
| + node.source.contents.modificationTime++;
|
| + node.source.contents.data = 'import "a4.dart"; import "a3.dart"; '
|
| + 'export "a5.dart"; part "a6.dart"; part "a7.dart";';
|
| + expect(node.structureChanged, isFalse);
|
| + node.update(graph);
|
| + expect(node.structureChanged, isTrue);
|
| +
|
| + // no change
|
| + node.structureChanged = false;
|
| + node.source.contents.modificationTime++;
|
| + node.update(graph);
|
| + expect(node.structureChanged, isFalse);
|
| +
|
| + // removed one
|
| + node.source.contents.modificationTime++;
|
| + node.source.contents.data = 'import "a4.dart"; import "a3.dart"; '
|
| + 'export "a5.dart"; part "a7.dart";';
|
| + expect(node.structureChanged, isFalse);
|
| + node.update(graph);
|
| + expect(node.structureChanged, isTrue);
|
| + });
|
| +
|
| + test('changed import', () {
|
| + var node = nodeOf('/a2.dart');
|
| + node.update(graph);
|
| + expect(node.structureChanged, isTrue);
|
| + node.structureChanged = false;
|
| +
|
| + node.update(graph);
|
| + expect(node.structureChanged, isFalse);
|
| +
|
| + // added one.
|
| + node.source.contents.modificationTime++;
|
| + node.source.contents.data =
|
| + 'import "a4.dart"; import "a3.dart"; import "a7.dart";'
|
| + 'export "a5.dart"; part "a6.dart";';
|
| + expect(node.structureChanged, isFalse);
|
| + node.update(graph);
|
| + expect(node.structureChanged, isTrue);
|
| +
|
| + // no change
|
| + node.structureChanged = false;
|
| + node.source.contents.modificationTime++;
|
| + node.update(graph);
|
| + expect(node.structureChanged, isFalse);
|
| +
|
| + // removed one
|
| + node.source.contents.modificationTime++;
|
| + node.source.contents.data = 'import "a4.dart"; import "a7.dart"; '
|
| + 'export "a5.dart"; part "a6.dart";';
|
| + expect(node.structureChanged, isFalse);
|
| + node.update(graph);
|
| + expect(node.structureChanged, isTrue);
|
| + });
|
| +
|
| + test('changed exports', () {
|
| + var node = nodeOf('/a2.dart');
|
| + node.update(graph);
|
| + expect(node.structureChanged, isTrue);
|
| + node.structureChanged = false;
|
| +
|
| + node.update(graph);
|
| + expect(node.structureChanged, isFalse);
|
| +
|
| + // added one.
|
| + node.source.contents.modificationTime++;
|
| + node.source.contents.data = 'import "a4.dart"; import "a3.dart";'
|
| + 'export "a5.dart"; export "a9.dart"; part "a6.dart";';
|
| + expect(node.structureChanged, isFalse);
|
| + node.update(graph);
|
| + expect(node.structureChanged, isTrue);
|
| +
|
| + // no change
|
| + node.structureChanged = false;
|
| + node.source.contents.modificationTime++;
|
| + node.update(graph);
|
| + expect(node.structureChanged, isFalse);
|
| +
|
| + // removed one
|
| + node.source.contents.modificationTime++;
|
| + node.source.contents.data = 'import "a4.dart"; import "a3.dart"; '
|
| + 'export "a5.dart"; part "a6.dart";';
|
| + expect(node.structureChanged, isFalse);
|
| + node.update(graph);
|
| + expect(node.structureChanged, isTrue);
|
| + });
|
| + });
|
| + });
|
| +
|
| + group('refresh structure and marks', () {
|
| + test('initial marks', () {
|
| + var node = nodeOf('/index3.html');
|
| + expectGraph(node, 'index3.html');
|
| + refreshStructureAndMarks(node, graph);
|
| + expectGraph(node, '''
|
| + index3.html [needs-rebuild] [structure-changed]
|
| + |-- a2.dart [needs-rebuild] [structure-changed]
|
| + | |-- a3.dart [needs-rebuild]
|
| + | |-- a4.dart [needs-rebuild] [structure-changed]
|
| + | | |-- a10.dart [needs-rebuild]
|
| + | |-- a5.dart [needs-rebuild]
|
| + | |-- a6.dart [needs-rebuild]
|
| + ''');
|
| + });
|
| +
|
| + test('cleared marks stay clear', () {
|
| + var node = nodeOf('/index3.html');
|
| + refreshStructureAndMarks(node, graph);
|
| + expectGraph(node, '''
|
| + index3.html [needs-rebuild] [structure-changed]
|
| + |-- a2.dart [needs-rebuild] [structure-changed]
|
| + | |-- a3.dart [needs-rebuild]
|
| + | |-- a4.dart [needs-rebuild] [structure-changed]
|
| + | | |-- a10.dart [needs-rebuild]
|
| + | |-- a5.dart [needs-rebuild]
|
| + | |-- a6.dart [needs-rebuild]
|
| + ''');
|
| + clearMarks(node);
|
| + expectGraph(node, '''
|
| + index3.html
|
| + |-- a2.dart
|
| + | |-- a3.dart
|
| + | |-- a4.dart
|
| + | | |-- a10.dart
|
| + | |-- a5.dart
|
| + | |-- a6.dart
|
| + ''');
|
| +
|
| + refreshStructureAndMarks(node, graph);
|
| + expectGraph(node, '''
|
| + index3.html
|
| + |-- a2.dart
|
| + | |-- a3.dart
|
| + | |-- a4.dart
|
| + | | |-- a10.dart
|
| + | |-- a5.dart
|
| + | |-- a6.dart
|
| + ''');
|
| + });
|
| +
|
| + test('needsRebuild mark updated on local modifications', () {
|
| + var node = nodeOf('/index3.html');
|
| + refreshStructureAndMarks(node, graph);
|
| + clearMarks(node);
|
| + var a3 = nodeOf('/a3.dart');
|
| + a3.source.contents.modificationTime++;
|
| +
|
| + refreshStructureAndMarks(node, graph);
|
| + expectGraph(node, '''
|
| + index3.html
|
| + |-- a2.dart
|
| + | |-- a3.dart [needs-rebuild]
|
| + | |-- a4.dart
|
| + | | |-- a10.dart
|
| + | |-- a5.dart
|
| + | |-- a6.dart
|
| + ''');
|
| + });
|
| +
|
| + test('structuredChanged mark updated on structure modifications', () {
|
| + var node = nodeOf('/index3.html');
|
| + refreshStructureAndMarks(node, graph);
|
| + clearMarks(node);
|
| + var a5 = nodeOf('/a5.dart');
|
| + a5.source.contents.modificationTime++;
|
| + a5.source.contents.data = 'import "a8.dart";';
|
| +
|
| + refreshStructureAndMarks(node, graph);
|
| + expectGraph(node, '''
|
| + index3.html
|
| + |-- a2.dart
|
| + | |-- a3.dart
|
| + | |-- a4.dart
|
| + | | |-- a10.dart
|
| + | |-- a5.dart [needs-rebuild] [structure-changed]
|
| + | | |-- a8.dart [needs-rebuild] [structure-changed]
|
| + | | | |-- a8.dart...
|
| + | |-- a6.dart
|
| + ''');
|
| + });
|
| + });
|
| +
|
| + group('rebuild', () {
|
| + var results;
|
| + void addName(SourceNode n) => results.add(nameFor(n));
|
| +
|
| + bool buildNoTransitiveChange(SourceNode n) {
|
| + addName(n);
|
| + return false;
|
| + }
|
| +
|
| + bool buildWithTransitiveChange(SourceNode n) {
|
| + addName(n);
|
| + return true;
|
| + }
|
| +
|
| + setUp(() {
|
| + results = [];
|
| + });
|
| +
|
| + test('everything build on first run', () {
|
| + var node = nodeOf('/index3.html');
|
| + rebuild(node, graph, buildNoTransitiveChange);
|
| + // Note: a6.dart is not included because it built as part of a2.dart
|
| + expect(results, [
|
| + 'a3.dart',
|
| + 'a10.dart',
|
| + 'a4.dart',
|
| + 'a5.dart',
|
| + 'a2.dart',
|
| + 'index3.html'
|
| + ]);
|
| +
|
| + // Marks are removed automatically by rebuild
|
| + expectGraph(node, '''
|
| + index3.html
|
| + |-- a2.dart
|
| + | |-- a3.dart
|
| + | |-- a4.dart
|
| + | | |-- a10.dart
|
| + | |-- a5.dart
|
| + | |-- a6.dart
|
| + ''');
|
| + });
|
| +
|
| + test('nothing to do after build', () {
|
| + var node = nodeOf('/index3.html');
|
| + rebuild(node, graph, buildNoTransitiveChange);
|
| +
|
| + results = [];
|
| + rebuild(node, graph, buildNoTransitiveChange);
|
| + expect(results, []);
|
| + });
|
| +
|
| + test('modified part triggers building library', () {
|
| + var node = nodeOf('/index3.html');
|
| + rebuild(node, graph, buildNoTransitiveChange);
|
| + results = [];
|
| +
|
| + var a6 = nodeOf('/a6.dart');
|
| + a6.source.contents.modificationTime++;
|
| + rebuild(node, graph, buildNoTransitiveChange);
|
| + expect(results, ['a2.dart']);
|
| +
|
| + results = [];
|
| + rebuild(node, graph, buildNoTransitiveChange);
|
| + expect(results, []);
|
| + });
|
| +
|
| + test('non-API change triggers build stays local', () {
|
| + var node = nodeOf('/index3.html');
|
| + rebuild(node, graph, buildNoTransitiveChange);
|
| + results = [];
|
| +
|
| + var a3 = nodeOf('/a3.dart');
|
| + a3.source.contents.modificationTime++;
|
| + rebuild(node, graph, buildNoTransitiveChange);
|
| + expect(results, ['a3.dart']);
|
| +
|
| + results = [];
|
| + rebuild(node, graph, buildNoTransitiveChange);
|
| + expect(results, []);
|
| + });
|
| +
|
| + test('no-API change in exported file stays local', () {
|
| + var node = nodeOf('/index3.html');
|
| + rebuild(node, graph, buildNoTransitiveChange);
|
| + results = [];
|
| +
|
| + // similar to the test above, but a10 is exported from a4.
|
| + var a3 = nodeOf('/a10.dart');
|
| + a3.source.contents.modificationTime++;
|
| + rebuild(node, graph, buildNoTransitiveChange);
|
| + expect(results, ['a10.dart']);
|
| +
|
| + results = [];
|
| + rebuild(node, graph, buildNoTransitiveChange);
|
| + expect(results, []);
|
| + });
|
| +
|
| + test('API change in lib, triggers build on imports', () {
|
| + var node = nodeOf('/index3.html');
|
| + rebuild(node, graph, buildNoTransitiveChange);
|
| + results = [];
|
| +
|
| + var a3 = nodeOf('/a3.dart');
|
| + a3.source.contents.modificationTime++;
|
| + rebuild(node, graph, buildWithTransitiveChange);
|
| + expect(results, ['a3.dart', 'a2.dart']);
|
| +
|
| + results = [];
|
| + rebuild(node, graph, buildNoTransitiveChange);
|
| + expect(results, []);
|
| + });
|
| +
|
| + test('API change in export, triggers build on imports', () {
|
| + var node = nodeOf('/index3.html');
|
| + rebuild(node, graph, buildNoTransitiveChange);
|
| + results = [];
|
| +
|
| + var a3 = nodeOf('/a10.dart');
|
| + a3.source.contents.modificationTime++;
|
| + rebuild(node, graph, buildWithTransitiveChange);
|
| +
|
| + // Node: a4.dart reexports a10.dart, but it doesn't import it, so we don't
|
| + // need to rebuild it.
|
| + expect(results, ['a10.dart', 'a2.dart']);
|
| +
|
| + results = [];
|
| + rebuild(node, graph, buildNoTransitiveChange);
|
| + expect(results, []);
|
| + });
|
| +
|
| + test('structural change rebuilds HTML, but skips unreachable code', () {
|
| + var node = nodeOf('/index3.html');
|
| + rebuild(node, graph, buildNoTransitiveChange);
|
| + results = [];
|
| +
|
| + var a2 = nodeOf('/a2.dart');
|
| + a2.source.contents.modificationTime++;
|
| + a2.source.contents.data = 'import "a4.dart";';
|
| +
|
| + var a3 = nodeOf('/a3.dart');
|
| + a3.source.contents.modificationTime++;
|
| + rebuild(node, graph, buildNoTransitiveChange);
|
| +
|
| + // a3 will become unreachable, index3 reflects structural changes.
|
| + expect(results, ['a2.dart', 'index3.html']);
|
| +
|
| + results = [];
|
| + rebuild(node, graph, buildNoTransitiveChange);
|
| + expect(results, []);
|
| + });
|
| +
|
| + test('newly discovered files get built too', () {
|
| + var node = nodeOf('/index3.html');
|
| + rebuild(node, graph, buildNoTransitiveChange);
|
| + results = [];
|
| +
|
| + var a2 = nodeOf('/a2.dart');
|
| + a2.source.contents.modificationTime++;
|
| + a2.source.contents.data = 'import "a9.dart";';
|
| +
|
| + rebuild(node, graph, buildNoTransitiveChange);
|
| + expect(results, ['a8.dart', 'a9.dart', 'a2.dart', 'index3.html']);
|
| +
|
| + results = [];
|
| + rebuild(node, graph, buildNoTransitiveChange);
|
| + expect(results, []);
|
| + });
|
| + });
|
| +}
|
| +
|
| +expectGraph(SourceNode node, String expectation) {
|
| + expect(printReachable(node), equalsIgnoringWhitespace(expectation));
|
| +}
|
| +
|
| +nameFor(SourceNode node) => path.basename(node.uri.path);
|
| +printReachable(SourceNode node) {
|
| + var seen = new Set();
|
| + var sb = new StringBuffer();
|
| + helper(n, {indent: 0}) {
|
| + if (indent > 0) {
|
| + sb
|
| + ..write("| " * (indent - 1))
|
| + ..write("|-- ");
|
| + }
|
| + sb.write(nameFor(n));
|
| + if (seen.contains(n)) {
|
| + sb.write('...\n');
|
| + return;
|
| + }
|
| + seen.add(n);
|
| + sb
|
| + ..write(' ')
|
| + ..write(n.needsRebuild ? '[needs-rebuild] ' : '')
|
| + ..write(n.structureChanged ? '[structure-changed] ' : ' ')
|
| + ..write('\n');
|
| + n.directDeps.forEach((e) => helper(e, indent: indent + 1));
|
| + }
|
| + helper(node);
|
| + return sb.toString();
|
| +}
|
| +
|
| +bool _same(Set a, Set b) => a.length == b.length && a.containsAll(b);
|
|
|