Index: test/dependency_graph_test.dart |
diff --git a/test/dependency_graph_test.dart b/test/dependency_graph_test.dart |
deleted file mode 100644 |
index d7f8b0a60ed05ccdd3bffd01e7348253a33a55f2..0000000000000000000000000000000000000000 |
--- a/test/dependency_graph_test.dart |
+++ /dev/null |
@@ -1,1251 +0,0 @@ |
-// 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 dev_compiler.test.dependency_graph_test; |
- |
-import 'package:analyzer/file_system/file_system.dart'; |
-import 'package:analyzer/file_system/memory_file_system.dart'; |
-import 'package:analyzer/src/generated/source.dart'; |
-import 'package:path/path.dart' as path; |
-import 'package:test/test.dart'; |
- |
-import 'package:dev_compiler/src/analysis_context.dart'; |
-import 'package:dev_compiler/src/compiler.dart'; |
-import 'package:dev_compiler/src/options.dart'; |
-import 'package:dev_compiler/src/report.dart'; |
-import 'package:dev_compiler/src/server/dependency_graph.dart'; |
- |
-import 'testing.dart'; |
- |
-void main() { |
- var options = new CompilerOptions( |
- runtimeDir: '/dev_compiler_runtime/', |
- sourceOptions: new SourceResolverOptions(useMockSdk: true)); |
- MemoryResourceProvider testResourceProvider; |
- ResourceUriResolver 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;', |
- '/a7.dart': 'library a7;', |
- '/a8.dart': 'library a8; import "a8.dart";', |
- '/a9.dart': 'library a9; import "a8.dart";', |
- '/a10.dart': 'library a10;', |
- }; |
- |
- nodeOf(String filepath) => graph.nodeFromUri(new Uri.file(filepath)); |
- |
- setUp(() { |
- /// We completely reset the TestUriResolver to avoid interference between |
- /// tests (since some tests modify the state of the files). |
- testResourceProvider = createTestResourceProvider(testFiles); |
- testUriResolver = new ResourceUriResolver(testResourceProvider); |
- context = createAnalysisContextWithSources(options.sourceOptions, |
- fileResolvers: [testUriResolver]); |
- graph = new SourceGraph(context, new LogReporter(context), options); |
- }); |
- |
- updateFile(Source source, [String newContents]) { |
- var path = testResourceProvider.pathContext.fromUri(source.uri); |
- if (newContents == null) newContents = source.contents.data; |
- testResourceProvider.updateFile(path, newContents); |
- } |
- |
- 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(); |
- i2.update(); |
- 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(); |
- expect(node.scripts.length, 0); |
- |
- // Adding the dependency is discovered on the next round of updates: |
- updateFile(node.source, |
- '<script type="application/dart" src="a2.dart"></script>'); |
- expect(node.scripts.length, 0); |
- node.update(); |
- 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(); |
- expect(node.scripts.length, 1); |
- expect(node.scripts.first, nodeOf('/a1.dart')); |
- |
- updateFile( |
- node.source, |
- node.source.contents.data + |
- '<script type="application/dart" src="a2.dart"></script>'); |
- expect(node.scripts.length, 1); |
- node.update(); |
- 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(); |
- expect(node.scripts.length, 1); |
- expect(node.scripts.first, nodeOf('/a1.dart')); |
- |
- // Removing the dependency is discovered on the next round of updates: |
- updateFile(node.source, ''); |
- expect(node.scripts.length, 1); |
- node.update(); |
- 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(); |
- a2.update(); |
- |
- 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(); |
- expect(node.imports.length, 0); |
- expect(node.exports.length, 0); |
- expect(node.parts.length, 0); |
- |
- updateFile( |
- node.source, 'import "a3.dart"; export "a5.dart"; part "a8.dart";'); |
- node.update(); |
- |
- 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(); |
- 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); |
- |
- updateFile( |
- node.source, 'import "a3.dart"; export "a7.dart"; part "a8.dart";'); |
- node.update(); |
- |
- 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('/a7.dart')), isTrue); |
- expect(node.parts.contains(nodeOf('/a8.dart')), isTrue); |
- }); |
- |
- test('change part to library', () { |
- var node = nodeOf('/a2.dart'); |
- node.update(); |
- 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); |
- |
- updateFile( |
- node.source, |
- ''' |
- library a2; |
- import 'a3.dart'; |
- import 'a4.dart'; |
- export 'a5.dart'; |
- import 'a6.dart'; // changed from part |
- '''); |
- var a6 = nodeOf('/a6.dart'); |
- updateFile(a6.source, ''); |
- node.update(); |
- |
- expect(node.imports.length, 3); |
- expect(node.exports.length, 1); |
- expect(node.parts.length, 0); |
- expect(node.imports.contains(nodeOf('/a3.dart')), isTrue); |
- expect(node.imports.contains(nodeOf('/a4.dart')), isTrue); |
- expect(node.imports.contains(nodeOf('/a6.dart')), isTrue); |
- expect(node.exports.contains(nodeOf('/a5.dart')), isTrue); |
- |
- expect(a6.imports.length, 0); |
- expect(a6.exports.length, 0); |
- expect(a6.parts.length, 0); |
- }); |
- |
- test('change library to part', () { |
- var node = nodeOf('/a2.dart'); |
- var a4 = nodeOf('/a4.dart'); |
- node.update(); |
- 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); |
- |
- a4.update(); |
- expect(a4.imports.length, 0); |
- expect(a4.exports.length, 1); |
- expect(a4.parts.length, 0); |
- |
- updateFile( |
- node.source, |
- ''' |
- library a2; |
- import 'a3.dart'; |
- part 'a4.dart'; // changed from export |
- export 'a5.dart'; |
- part 'a6.dart'; |
- '''); |
- node.update(); |
- |
- expect(node.imports.length, 1); |
- expect(node.exports.length, 1); |
- expect(node.parts.length, 2); |
- expect(node.imports.contains(nodeOf('/a3.dart')), isTrue); |
- expect(node.exports.contains(nodeOf('/a5.dart')), isTrue); |
- expect(node.parts.contains(nodeOf('/a4.dart')), isTrue); |
- expect(node.parts.contains(nodeOf('/a6.dart')), isTrue); |
- |
- // Note, technically we never modified the contents of a4 and it contains |
- // an export. This is invalid Dart, but we'll let the analyzer report that |
- // error instead of doing so ourselves. |
- expect(a4.imports.length, 0); |
- expect(a4.exports.length, 1); |
- expect(a4.parts.length, 0); |
- |
- // And change it back. |
- updateFile( |
- node.source, |
- ''' |
- library a2; |
- import 'a3.dart'; |
- import 'a4.dart'; // changed again |
- export 'a5.dart'; |
- part 'a6.dart'; |
- '''); |
- node.update(); |
- expect(node.imports.contains(a4), isTrue); |
- expect(a4.imports.length, 0); |
- expect(a4.exports.length, 1); |
- expect(a4.parts.length, 0); |
- }); |
- }); |
- |
- group('local changes', () { |
- group('needs rebuild', () { |
- test('in HTML', () { |
- var node = nodeOf('/index1.html'); |
- node.update(); |
- expect(node.needsRebuild, isTrue); |
- node.needsRebuild = false; |
- |
- node.update(); |
- expect(node.needsRebuild, isFalse); |
- |
- // For now, an empty modification is enough to trigger a rebuild |
- updateFile(node.source); |
- expect(node.needsRebuild, isFalse); |
- node.update(); |
- expect(node.needsRebuild, isTrue); |
- }); |
- |
- test('main library in Dart', () { |
- var node = nodeOf('/a2.dart'); |
- var partNode = nodeOf('/a6.dart'); |
- node.update(); |
- expect(node.needsRebuild, isTrue); |
- node.needsRebuild = false; |
- partNode.needsRebuild = false; |
- |
- node.update(); |
- expect(node.needsRebuild, isFalse); |
- |
- // For now, an empty modification is enough to trigger a rebuild |
- updateFile(node.source); |
- expect(node.needsRebuild, isFalse); |
- node.update(); |
- 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'); |
- node.update(); |
- expect(node.needsRebuild, isTrue); |
- node.needsRebuild = false; |
- partNode.needsRebuild = false; |
- |
- node.update(); |
- expect(node.needsRebuild, isFalse); |
- |
- // Modification in imported/exported node makes no difference for local |
- // rebuild label (globally that's tested elsewhere) |
- updateFile(importNode.source); |
- updateFile(exportNode.source); |
- node.update(); |
- expect(node.needsRebuild, isFalse); |
- expect(partNode.needsRebuild, isFalse); |
- |
- // Modification in part triggers change in containing library: |
- updateFile(partNode.source); |
- expect(node.needsRebuild, isFalse); |
- expect(partNode.needsRebuild, isFalse); |
- node.update(); |
- expect(node.needsRebuild, isTrue); |
- expect(partNode.needsRebuild, isTrue); |
- }); |
- }); |
- |
- group('structure change', () { |
- test('no mod in HTML', () { |
- var node = nodeOf('/index2.html'); |
- node.update(); |
- expect(node.structureChanged, isTrue); |
- node.structureChanged = false; |
- |
- node.update(); |
- expect(node.structureChanged, isFalse); |
- |
- // An empty modification will not trigger a structural change |
- updateFile(node.source); |
- expect(node.structureChanged, isFalse); |
- node.update(); |
- expect(node.structureChanged, isFalse); |
- }); |
- |
- test('added scripts in HTML', () { |
- var node = nodeOf('/index2.html'); |
- node.update(); |
- expect(node.structureChanged, isTrue); |
- expect(node.scripts.length, 1); |
- |
- node.structureChanged = false; |
- node.update(); |
- expect(node.structureChanged, isFalse); |
- |
- // This change will not include new script tags: |
- updateFile(node.source, node.source.contents.data + '<div></div>'); |
- expect(node.structureChanged, isFalse); |
- node.update(); |
- expect(node.structureChanged, isFalse); |
- expect(node.scripts.length, 1); |
- |
- updateFile( |
- node.source, |
- node.source.contents.data + |
- '<script type="application/dart" src="a4.dart"></script>'); |
- expect(node.structureChanged, isFalse); |
- node.update(); |
- 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'); |
- node.update(); |
- expect(node.structureChanged, isTrue); |
- node.structureChanged = false; |
- |
- node.update(); |
- expect(node.structureChanged, isFalse); |
- |
- // These modifications make no difference at all. |
- updateFile(importNode.source); |
- updateFile(exportNode.source); |
- updateFile(partNode.source); |
- updateFile(node.source); |
- |
- expect(node.structureChanged, isFalse); |
- node.update(); |
- expect(node.structureChanged, isFalse); |
- }); |
- |
- test('same directives, different order', () { |
- var node = nodeOf('/a2.dart'); |
- node.update(); |
- expect(node.structureChanged, isTrue); |
- node.structureChanged = false; |
- |
- node.update(); |
- expect(node.structureChanged, isFalse); |
- |
- // modified order of imports, but structure stays the same: |
- updateFile( |
- node.source, |
- 'import "a4.dart"; import "a3.dart"; ' |
- 'export "a5.dart"; part "a6.dart";'); |
- node.update(); |
- |
- expect(node.structureChanged, isFalse); |
- node.update(); |
- expect(node.structureChanged, isFalse); |
- }); |
- |
- test('changed parts', () { |
- var node = nodeOf('/a2.dart'); |
- node.update(); |
- expect(node.structureChanged, isTrue); |
- node.structureChanged = false; |
- |
- node.update(); |
- expect(node.structureChanged, isFalse); |
- |
- // added one. |
- updateFile( |
- node.source, |
- 'import "a4.dart"; import "a3.dart"; ' |
- 'export "a5.dart"; part "a6.dart"; part "a7.dart";'); |
- expect(node.structureChanged, isFalse); |
- node.update(); |
- expect(node.structureChanged, isTrue); |
- |
- // no change |
- node.structureChanged = false; |
- updateFile(node.source); |
- node.update(); |
- expect(node.structureChanged, isFalse); |
- |
- // removed one |
- updateFile(node.source); |
- updateFile( |
- node.source, |
- 'import "a4.dart"; import "a3.dart"; ' |
- 'export "a5.dart"; part "a7.dart";'); |
- expect(node.structureChanged, isFalse); |
- node.update(); |
- expect(node.structureChanged, isTrue); |
- }); |
- |
- test('changed import', () { |
- var node = nodeOf('/a2.dart'); |
- node.update(); |
- expect(node.structureChanged, isTrue); |
- node.structureChanged = false; |
- |
- node.update(); |
- expect(node.structureChanged, isFalse); |
- |
- // added one. |
- updateFile( |
- node.source, |
- 'import "a4.dart"; import "a3.dart"; import "a7.dart";' |
- 'export "a5.dart"; part "a6.dart";'); |
- expect(node.structureChanged, isFalse); |
- node.update(); |
- expect(node.structureChanged, isTrue); |
- |
- // no change |
- node.structureChanged = false; |
- updateFile(node.source); |
- node.update(); |
- expect(node.structureChanged, isFalse); |
- |
- // removed one |
- updateFile( |
- node.source, |
- 'import "a4.dart"; import "a7.dart"; ' |
- 'export "a5.dart"; part "a6.dart";'); |
- expect(node.structureChanged, isFalse); |
- node.update(); |
- expect(node.structureChanged, isTrue); |
- }); |
- |
- test('changed exports', () { |
- var node = nodeOf('/a2.dart'); |
- node.update(); |
- expect(node.structureChanged, isTrue); |
- node.structureChanged = false; |
- |
- node.update(); |
- expect(node.structureChanged, isFalse); |
- |
- // added one. |
- updateFile( |
- node.source, |
- 'import "a4.dart"; import "a3.dart";' |
- 'export "a5.dart"; export "a9.dart"; part "a6.dart";'); |
- expect(node.structureChanged, isFalse); |
- node.update(); |
- expect(node.structureChanged, isTrue); |
- |
- // no change |
- node.structureChanged = false; |
- updateFile(node.source); |
- node.update(); |
- expect(node.structureChanged, isFalse); |
- |
- // removed one |
- updateFile( |
- node.source, |
- 'import "a4.dart"; import "a3.dart"; ' |
- 'export "a5.dart"; part "a6.dart";'); |
- expect(node.structureChanged, isFalse); |
- node.update(); |
- expect(node.structureChanged, isTrue); |
- }); |
- }); |
- }); |
- |
- group('refresh structure and marks', () { |
- test('initial marks', () { |
- var node = nodeOf('/index3.html'); |
- expectGraph( |
- node, |
- ''' |
- index3.html |
- $_RUNTIME_GRAPH |
- '''); |
- refreshStructureAndMarks(node); |
- 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 (part) [needs-rebuild] |
- $_RUNTIME_GRAPH_REBUILD |
- '''); |
- }); |
- |
- test('cleared marks stay clear', () { |
- var node = nodeOf('/index3.html'); |
- refreshStructureAndMarks(node); |
- 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 (part) [needs-rebuild] |
- $_RUNTIME_GRAPH_REBUILD |
- '''); |
- clearMarks(node); |
- expectGraph( |
- node, |
- ''' |
- index3.html |
- |-- a2.dart |
- | |-- a3.dart |
- | |-- a4.dart |
- | | |-- a10.dart |
- | |-- a5.dart |
- | |-- a6.dart (part) |
- $_RUNTIME_GRAPH |
- '''); |
- |
- refreshStructureAndMarks(node); |
- expectGraph( |
- node, |
- ''' |
- index3.html |
- |-- a2.dart |
- | |-- a3.dart |
- | |-- a4.dart |
- | | |-- a10.dart |
- | |-- a5.dart |
- | |-- a6.dart (part) |
- $_RUNTIME_GRAPH |
- '''); |
- }); |
- |
- test('needsRebuild mark updated on local modifications', () { |
- var node = nodeOf('/index3.html'); |
- refreshStructureAndMarks(node); |
- clearMarks(node); |
- var a3 = nodeOf('/a3.dart'); |
- updateFile(a3.source); |
- |
- refreshStructureAndMarks(node); |
- expectGraph( |
- node, |
- ''' |
- index3.html |
- |-- a2.dart |
- | |-- a3.dart [needs-rebuild] |
- | |-- a4.dart |
- | | |-- a10.dart |
- | |-- a5.dart |
- | |-- a6.dart (part) |
- $_RUNTIME_GRAPH |
- '''); |
- }); |
- |
- test('structuredChanged mark updated on structure modifications', () { |
- var node = nodeOf('/index3.html'); |
- refreshStructureAndMarks(node); |
- clearMarks(node); |
- var a5 = nodeOf('/a5.dart'); |
- updateFile(a5.source, 'import "a8.dart";'); |
- |
- refreshStructureAndMarks(node); |
- 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 (part) |
- $_RUNTIME_GRAPH |
- '''); |
- }); |
- }); |
- |
- group('server-mode', () { |
- setUp(() { |
- var opts = new CompilerOptions( |
- runtimeDir: '/dev_compiler_runtime/', |
- sourceOptions: new SourceResolverOptions(useMockSdk: true), |
- serverMode: true); |
- context = createAnalysisContextWithSources(opts.sourceOptions, |
- fileResolvers: [testUriResolver]); |
- graph = new SourceGraph(context, new LogReporter(context), opts); |
- }); |
- |
- test('messages widget is automatically included', () { |
- var node = nodeOf('/index3.html'); |
- expectGraph( |
- node, |
- ''' |
- index3.html |
- $_RUNTIME_GRAPH |
- |-- messages_widget.js |
- |-- messages.css |
- '''); |
- refreshStructureAndMarks(node); |
- 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 (part) [needs-rebuild] |
- $_RUNTIME_GRAPH_REBUILD |
- |-- messages_widget.js [needs-rebuild] |
- |-- messages.css [needs-rebuild] |
- '''); |
- }); |
- }); |
- |
- 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, 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'] |
- ..addAll(runtimeFilesWithoutPath) |
- ..add('index3.html')); |
- |
- // Marks are removed automatically by rebuild |
- expectGraph( |
- node, |
- ''' |
- index3.html |
- |-- a2.dart |
- | |-- a3.dart |
- | |-- a4.dart |
- | | |-- a10.dart |
- | |-- a5.dart |
- | |-- a6.dart (part) |
- $_RUNTIME_GRAPH |
- '''); |
- }); |
- |
- test('nothing to do after build', () { |
- var node = nodeOf('/index3.html'); |
- rebuild(node, buildNoTransitiveChange); |
- |
- results = []; |
- rebuild(node, buildNoTransitiveChange); |
- expect(results, []); |
- }); |
- |
- test('modified part triggers building library', () { |
- var node = nodeOf('/index3.html'); |
- rebuild(node, buildNoTransitiveChange); |
- results = []; |
- |
- var a6 = nodeOf('/a6.dart'); |
- updateFile(a6.source); |
- rebuild(node, buildNoTransitiveChange); |
- expect(results, ['a2.dart']); |
- |
- results = []; |
- rebuild(node, buildNoTransitiveChange); |
- expect(results, []); |
- }); |
- |
- test('non-API change triggers build stays local', () { |
- var node = nodeOf('/index3.html'); |
- rebuild(node, buildNoTransitiveChange); |
- results = []; |
- |
- var a3 = nodeOf('/a3.dart'); |
- updateFile(a3.source); |
- rebuild(node, buildNoTransitiveChange); |
- expect(results, ['a3.dart']); |
- |
- results = []; |
- rebuild(node, buildNoTransitiveChange); |
- expect(results, []); |
- }); |
- |
- test('no-API change in exported file stays local', () { |
- var node = nodeOf('/index3.html'); |
- rebuild(node, buildNoTransitiveChange); |
- results = []; |
- |
- // similar to the test above, but a10 is exported from a4. |
- var a3 = nodeOf('/a10.dart'); |
- updateFile(a3.source); |
- rebuild(node, buildNoTransitiveChange); |
- expect(results, ['a10.dart']); |
- |
- results = []; |
- rebuild(node, buildNoTransitiveChange); |
- expect(results, []); |
- }); |
- |
- test('API change in lib, triggers build on imports', () { |
- var node = nodeOf('/index3.html'); |
- rebuild(node, buildNoTransitiveChange); |
- results = []; |
- |
- var a3 = nodeOf('/a3.dart'); |
- updateFile(a3.source); |
- rebuild(node, buildWithTransitiveChange); |
- expect(results, ['a3.dart', 'a2.dart']); |
- |
- results = []; |
- rebuild(node, buildNoTransitiveChange); |
- expect(results, []); |
- }); |
- |
- test('API change in export, triggers build on imports', () { |
- var node = nodeOf('/index3.html'); |
- rebuild(node, buildNoTransitiveChange); |
- results = []; |
- |
- var a3 = nodeOf('/a10.dart'); |
- updateFile(a3.source); |
- rebuild(node, 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, buildNoTransitiveChange); |
- expect(results, []); |
- }); |
- |
- test('structural change rebuilds HTML, but skips unreachable code', () { |
- var node = nodeOf('/index3.html'); |
- rebuild(node, buildNoTransitiveChange); |
- results = []; |
- |
- var a2 = nodeOf('/a2.dart'); |
- updateFile(a2.source, 'import "a4.dart";'); |
- |
- var a3 = nodeOf('/a3.dart'); |
- updateFile(a3.source); |
- rebuild(node, buildNoTransitiveChange); |
- |
- // a3 will become unreachable, index3 reflects structural changes. |
- expect(results, ['a2.dart', 'index3.html']); |
- |
- results = []; |
- rebuild(node, buildNoTransitiveChange); |
- expect(results, []); |
- }); |
- |
- test('newly discovered files get built too', () { |
- var node = nodeOf('/index3.html'); |
- rebuild(node, buildNoTransitiveChange); |
- results = []; |
- |
- var a2 = nodeOf('/a2.dart'); |
- updateFile(a2.source, 'import "a9.dart";'); |
- |
- rebuild(node, buildNoTransitiveChange); |
- expect(results, ['a8.dart', 'a9.dart', 'a2.dart', 'index3.html']); |
- |
- results = []; |
- rebuild(node, buildNoTransitiveChange); |
- expect(results, []); |
- }); |
- |
- group('file upgrades', () { |
- // Normally upgrading involves two changes: |
- // (a) change the affected file |
- // (b) change directive from part to import (or viceversa) |
- // These could happen in any order and we should reach a consistent state |
- // in the end. |
- |
- test('convert part to a library before updating the import', () { |
- var node = nodeOf('/index3.html'); |
- var a2 = nodeOf('/a2.dart'); |
- var a6 = nodeOf('/a6.dart'); |
- rebuild(node, buildNoTransitiveChange); |
- |
- expectGraph( |
- node, |
- ''' |
- index3.html |
- |-- a2.dart |
- | |-- a3.dart |
- | |-- a4.dart |
- | | |-- a10.dart |
- | |-- a5.dart |
- | |-- a6.dart (part) |
- $_RUNTIME_GRAPH |
- '''); |
- |
- // Modify the file first: |
- updateFile(a6.source, 'library a6; import "a5.dart";'); |
- results = []; |
- rebuild(node, buildNoTransitiveChange); |
- |
- // Looks to us like a change in a part, we'll report errors that the |
- // part is not really a part-file. Note that a6.dart is not included |
- // below, because we don't build it as a library. |
- expect(results, ['a2.dart']); |
- expectGraph( |
- node, |
- ''' |
- index3.html |
- |-- a2.dart |
- | |-- a3.dart |
- | |-- a4.dart |
- | | |-- a10.dart |
- | |-- a5.dart |
- | |-- a6.dart (part) |
- $_RUNTIME_GRAPH |
- '''); |
- |
- updateFile( |
- a2.source, |
- ''' |
- library a2; |
- import 'a3.dart'; |
- import 'a4.dart'; |
- import 'a6.dart'; // properly import it |
- export 'a5.dart'; |
- '''); |
- results = []; |
- rebuild(node, buildNoTransitiveChange); |
- // Note that a6 is now included, because we haven't built it as a |
- // library until now: |
- expect(results, ['a6.dart', 'a2.dart', 'index3.html']); |
- |
- updateFile(a6.source); |
- results = []; |
- rebuild(node, buildNoTransitiveChange); |
- expect(results, ['a6.dart']); |
- |
- expectGraph( |
- node, |
- ''' |
- index3.html |
- |-- a2.dart |
- | |-- a3.dart |
- | |-- a4.dart |
- | | |-- a10.dart |
- | |-- a6.dart |
- | | |-- a5.dart |
- | |-- a5.dart... |
- $_RUNTIME_GRAPH |
- '''); |
- }); |
- |
- test('convert part to a library after updating the import', () { |
- var node = nodeOf('/index3.html'); |
- var a2 = nodeOf('/a2.dart'); |
- var a6 = nodeOf('/a6.dart'); |
- rebuild(node, buildNoTransitiveChange); |
- |
- expectGraph( |
- node, |
- ''' |
- index3.html |
- |-- a2.dart |
- | |-- a3.dart |
- | |-- a4.dart |
- | | |-- a10.dart |
- | |-- a5.dart |
- | |-- a6.dart (part) |
- $_RUNTIME_GRAPH |
- '''); |
- |
- updateFile( |
- a2.source, |
- ''' |
- library a2; |
- import 'a3.dart'; |
- import 'a4.dart'; |
- import 'a6.dart'; // properly import it |
- export 'a5.dart'; |
- '''); |
- results = []; |
- rebuild(node, buildNoTransitiveChange); |
- expect(results, ['a6.dart', 'a2.dart', 'index3.html']); |
- expectGraph( |
- node, |
- ''' |
- index3.html |
- |-- a2.dart |
- | |-- a3.dart |
- | |-- a4.dart |
- | | |-- a10.dart |
- | |-- a6.dart |
- | |-- a5.dart |
- $_RUNTIME_GRAPH |
- '''); |
- |
- updateFile(a6.source, 'library a6; import "a5.dart";'); |
- results = []; |
- rebuild(node, buildNoTransitiveChange); |
- expect(results, ['a6.dart', 'index3.html']); |
- expectGraph( |
- node, |
- ''' |
- index3.html |
- |-- a2.dart |
- | |-- a3.dart |
- | |-- a4.dart |
- | | |-- a10.dart |
- | |-- a6.dart |
- | | |-- a5.dart |
- | |-- a5.dart... |
- $_RUNTIME_GRAPH |
- '''); |
- }); |
- |
- test('disconnect part making it a library', () { |
- var node = nodeOf('/index3.html'); |
- var a2 = nodeOf('/a2.dart'); |
- var a6 = nodeOf('/a6.dart'); |
- rebuild(node, buildNoTransitiveChange); |
- |
- expectGraph( |
- node, |
- ''' |
- index3.html |
- |-- a2.dart |
- | |-- a3.dart |
- | |-- a4.dart |
- | | |-- a10.dart |
- | |-- a5.dart |
- | |-- a6.dart (part) |
- $_RUNTIME_GRAPH |
- '''); |
- |
- updateFile( |
- a2.source, |
- ''' |
- library a2; |
- import 'a3.dart'; |
- import 'a4.dart'; |
- export 'a5.dart'; |
- '''); |
- updateFile(a6.source, 'library a6; import "a5.dart";'); |
- results = []; |
- rebuild(node, buildNoTransitiveChange); |
- // a6 is not here, it's not reachable so we don't build it. |
- expect(results, ['a2.dart', 'index3.html']); |
- expectGraph( |
- node, |
- ''' |
- index3.html |
- |-- a2.dart |
- | |-- a3.dart |
- | |-- a4.dart |
- | | |-- a10.dart |
- | |-- a5.dart |
- $_RUNTIME_GRAPH |
- '''); |
- }); |
- |
- test('convert a library to a part', () { |
- var node = nodeOf('/index3.html'); |
- var a2 = nodeOf('/a2.dart'); |
- var a5 = nodeOf('/a5.dart'); |
- rebuild(node, buildNoTransitiveChange); |
- |
- expectGraph( |
- node, |
- ''' |
- index3.html |
- |-- a2.dart |
- | |-- a3.dart |
- | |-- a4.dart |
- | | |-- a10.dart |
- | |-- a5.dart |
- | |-- a6.dart (part) |
- $_RUNTIME_GRAPH |
- '''); |
- |
- updateFile( |
- a2.source, |
- ''' |
- library a2; |
- import 'a3.dart'; |
- import 'a4.dart'; |
- part 'a5.dart'; // make it a part |
- part 'a6.dart'; |
- '''); |
- results = []; |
- rebuild(node, buildNoTransitiveChange); |
- expect(results, ['a2.dart', 'index3.html']); |
- expectGraph( |
- node, |
- ''' |
- index3.html |
- |-- a2.dart |
- | |-- a3.dart |
- | |-- a4.dart |
- | | |-- a10.dart |
- | |-- a5.dart (part) |
- | |-- a6.dart (part) |
- $_RUNTIME_GRAPH |
- '''); |
- |
- updateFile(a5.source, 'part of a2;'); |
- results = []; |
- rebuild(node, buildNoTransitiveChange); |
- expect(results, ['a2.dart']); |
- expectGraph( |
- node, |
- ''' |
- index3.html |
- |-- a2.dart |
- | |-- a3.dart |
- | |-- a4.dart |
- | | |-- a10.dart |
- | |-- a5.dart (part) |
- | |-- a6.dart (part) |
- $_RUNTIME_GRAPH |
- '''); |
- }); |
- }); |
- |
- group('represented non-existing files', () { |
- test('recognize locally change between existing and not-existing', () { |
- var n = nodeOf('/foo.dart'); |
- expect(n.source, isNotNull); |
- expect(n.source.exists(), isFalse); |
- var source = testUriResolver.resolveAbsolute(new Uri.file('/foo.dart')); |
- expect(n.source, source); |
- updateFile(source, "hi"); |
- expect(n.source.exists(), isTrue); |
- }); |
- |
- test('non-existing files are tracked in dependencies', () { |
- var node = nodeOf('/foo.dart'); |
- updateFile(node.source, "import 'bar.dart';"); |
- rebuild(node, buildNoTransitiveChange); |
- expect(node.allDeps.contains(nodeOf('/bar.dart')), isTrue); |
- |
- var source = nodeOf('/bar.dart').source; |
- updateFile(source, "hi"); |
- results = []; |
- rebuild(node, buildWithTransitiveChange); |
- expect(results, ['bar.dart', 'foo.dart']); |
- }); |
- }); |
- |
- group('null for non-existing files', () { |
- setUp(() { |
- context = createAnalysisContextWithSources(options.sourceOptions, |
- fileResolvers: [testUriResolver]); |
- graph = new SourceGraph(context, new LogReporter(context), options); |
- }); |
- |
- test('recognize locally change between existing and not-existing', () { |
- var n = nodeOf('/foo.dart'); |
- expect(n.source.exists(), isFalse); |
- var source = |
- testResourceProvider.newFile('/foo.dart', 'hi').createSource(); |
- expect( |
- testUriResolver.resolveAbsolute(new Uri.file('/foo.dart')), source); |
- expect(n.source, source); |
- expect(n.source.exists(), isTrue); |
- n.update(); |
- expect(n.needsRebuild, isTrue); |
- }); |
- |
- test('non-existing files are tracked in dependencies', () { |
- testResourceProvider |
- .newFile('/foo.dart', "import 'bar.dart';") |
- .createSource(); |
- var node = nodeOf('/foo.dart'); |
- rebuild(node, buildNoTransitiveChange); |
- expect(node.allDeps.length, 1); |
- expect(node.allDeps.contains(nodeOf('/bar.dart')), isTrue); |
- expect(nodeOf('/bar.dart').source.exists(), isFalse); |
- |
- testResourceProvider.newFile('/bar.dart', 'hi').createSource(); |
- results = []; |
- rebuild(node, buildWithTransitiveChange); |
- expect(results, ['bar.dart', 'foo.dart']); |
- }); |
- }); |
- }); |
-} |
- |
-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.depsWithoutParts.forEach((e) => helper(e, indent: indent + 1)); |
- if (n is DartSourceNode) { |
- n.parts.forEach((e) { |
- sb |
- ..write("| " * indent) |
- ..write("|-- ") |
- ..write(nameFor(e)) |
- ..write(" (part) ") |
- ..write(e.needsRebuild ? '[needs-rebuild] ' : '') |
- ..write(e.structureChanged ? '[structure-changed] ' : ' ') |
- ..write('\n'); |
- }); |
- } |
- } |
- helper(node); |
- return sb.toString(); |
-} |
- |
-final runtimeFilesWithoutPath = defaultRuntimeFiles |
- .map((f) => f.replaceAll('dart/', '')) |
- .toList(growable: false); |
-final _RUNTIME_GRAPH = runtimeFilesWithoutPath.map((s) => '|-- $s').join('\n'); |
-final _RUNTIME_GRAPH_REBUILD = |
- runtimeFilesWithoutPath.map((s) => '|-- $s [needs-rebuild]').join('\n'); |