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

Unified Diff: pkg/analyzer/test/src/dart/analysis/driver_test.dart

Issue 2450183004: More tests and a few fixed for the driver. (Closed)
Patch Set: Created 4 years, 2 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « pkg/analyzer/lib/src/dart/analysis/driver.dart ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: pkg/analyzer/test/src/dart/analysis/driver_test.dart
diff --git a/pkg/analyzer/test/src/dart/analysis/driver_test.dart b/pkg/analyzer/test/src/dart/analysis/driver_test.dart
index 9384ba2302f25339b864149490d419c45abece4f..34e20efdbb12f04d3580a5b54762526a16ef8807 100644
--- a/pkg/analyzer/test/src/dart/analysis/driver_test.dart
+++ b/pkg/analyzer/test/src/dart/analysis/driver_test.dart
@@ -17,7 +17,6 @@ import 'package:analyzer/src/dart/analysis/driver.dart';
import 'package:analyzer/src/error/codes.dart';
import 'package:analyzer/src/generated/engine.dart' show AnalysisOptionsImpl;
import 'package:analyzer/src/generated/source.dart';
-import 'package:async/async.dart';
import 'package:convert/convert.dart';
import 'package:crypto/crypto.dart';
import 'package:test/test.dart';
@@ -31,6 +30,20 @@ main() {
});
}
+/**
+ * Returns a [Future] that completes after pumping the event queue [times]
+ * times. By default, this should pump the event queue enough times to allow
+ * any code to run, as long as it's not waiting on some external event.
+ */
+Future pumpEventQueue([int times = 5000]) {
+ if (times == 0) return new Future.value();
+ // We use a delayed future to allow microtask events to finish. The
+ // Future.value or Future() constructors use scheduleMicrotask themselves and
+ // would therefore not wait for microtask callbacks that are scheduled after
+ // invoking this method.
+ return new Future.delayed(Duration.ZERO, () => pumpEventQueue(times - 1));
+}
+
@reflectiveTest
class DriverTest {
static final MockSdk sdk = new MockSdk();
@@ -41,7 +54,7 @@ class DriverTest {
final StringBuffer logBuffer = new StringBuffer();
AnalysisDriver driver;
- StreamSplitter<AnalysisStatus> statusSplitter;
+ final _Monitor idleStatusMonitor = new _Monitor();
final List<AnalysisStatus> allStatuses = <AnalysisStatus>[];
final List<AnalysisResult> allResults = <AnalysisResult>[];
@@ -64,11 +77,163 @@ class DriverTest {
})
], null, provider),
new AnalysisOptionsImpl()..strongMode = true);
- statusSplitter = new StreamSplitter(driver.status);
- statusSplitter.split().listen(allStatuses.add);
+ driver.status.lastWhere((status) {
+ allStatuses.add(status);
+ if (status.isIdle) {
+ idleStatusMonitor.notify();
+ }
+ });
driver.results.listen(allResults.add);
}
+ test_addFile_thenRemove() async {
+ var a = _p('/test/lib/a.dart');
+ var b = _p('/test/lib/b.dart');
+ provider.newFile(a, 'class A {}');
+ provider.newFile(b, 'class B {}');
+ driver.addFile(a);
+ driver.addFile(b);
+
+ // Now remove 'a'.
+ driver.removeFile(a);
+
+ await _waitForIdle();
+
+ // Only 'b' has been analyzed, because 'a' was removed before we started.
+ expect(allResults, hasLength(1));
+ expect(allResults[0].path, b);
+ }
+
+ test_changeFile_implicitlyAnalyzed() async {
+ var a = _p('/test/lib/a.dart');
+ var b = _p('/test/lib/b.dart');
+ provider.newFile(
+ a,
+ r'''
+import 'b.dart';
+var A = B;
+''');
+ provider.newFile(b, 'var B = 1;');
+
+ driver.priorityFiles = [a];
+ driver.addFile(a);
+
+ // We have a result only for "a".
+ await _waitForIdle();
+ expect(allResults, hasLength(1));
+ {
+ AnalysisResult ar = allResults.firstWhere((r) => r.path == a);
+ expect(_getTopLevelVarType(ar.unit, 'A'), 'int');
+ }
+ allResults.clear();
+
+ // Change "b" and notify.
+ provider.updateFile(b, 'var B = 1.2;');
+ driver.changeFile(b);
+
+ // While "b" is not analyzed explicitly, it is analyzed implicitly.
+ // The change causes "a" to be reanalyzed.
+ await _waitForIdle();
+ expect(allResults, hasLength(1));
+ {
+ AnalysisResult ar = allResults.firstWhere((r) => r.path == a);
+ expect(_getTopLevelVarType(ar.unit, 'A'), 'double');
+ }
+ }
+
+ test_changeFile_selfConsistent() async {
+ var a = _p('/test/lib/a.dart');
+ var b = _p('/test/lib/b.dart');
+ provider.newFile(
+ a,
+ r'''
+import 'b.dart';
+var A1 = 1;
+var A2 = B1;
+''');
+ provider.newFile(
+ b,
+ r'''
+import 'a.dart';
+var B1 = A1;
+''');
+
+ driver.priorityFiles = [a, b];
+ driver.addFile(a);
+ driver.addFile(b);
+ await _waitForIdle();
+
+ // We have results for both "a" and "b".
+ expect(allResults, hasLength(2));
+ {
+ AnalysisResult ar = allResults.firstWhere((r) => r.path == a);
+ expect(_getTopLevelVarType(ar.unit, 'A1'), 'int');
+ expect(_getTopLevelVarType(ar.unit, 'A2'), 'int');
+ }
+ {
+ AnalysisResult br = allResults.firstWhere((r) => r.path == b);
+ expect(_getTopLevelVarType(br.unit, 'B1'), 'int');
+ }
+
+ // Clear the results and update "a".
+ allResults.clear();
+ provider.updateFile(
+ a,
+ r'''
+import 'b.dart';
+var A1 = 1.2;
+var A2 = B1;
+''');
+ driver.changeFile(a);
+
+ // We again get results for both "a" and "b".
+ // The results are consistent.
+ await _waitForIdle();
+ expect(allResults, hasLength(2));
+ {
+ AnalysisResult ar = allResults.firstWhere((r) => r.path == a);
+ expect(_getTopLevelVarType(ar.unit, 'A1'), 'double');
+ expect(_getTopLevelVarType(ar.unit, 'A2'), 'double');
+ }
+ {
+ AnalysisResult br = allResults.firstWhere((r) => r.path == b);
+ expect(_getTopLevelVarType(br.unit, 'B1'), 'double');
+ }
+ }
+
+ test_changeFile_single() async {
+ _addTestFile('var V = 1;', priority: true);
+
+ // Initial analysis.
+ {
+ await _waitForIdle();
+ expect(allResults, hasLength(1));
+ AnalysisResult result = allResults[0];
+ expect(result.path, testFile);
+ expect(_getTopLevelVarType(result.unit, 'V'), 'int');
+ }
+
+ // Update the file, but don't notify the driver.
+ allResults.clear();
+ provider.updateFile(testFile, 'var V = 1.2');
+
+ // No new results.
+ await pumpEventQueue();
+ expect(allResults, isEmpty);
+
+ // Notify the driver about the change.
+ driver.changeFile(testFile);
+
+ // We get a new result.
+ {
+ await _waitForIdle();
+ expect(allResults, hasLength(1));
+ AnalysisResult result = allResults[0];
+ expect(result.path, testFile);
+ expect(_getTopLevelVarType(result.unit, 'V'), 'double');
+ }
+ }
+
test_getResult() async {
String content = 'int f() => 42;';
_addTestFile(content, priority: true);
@@ -107,6 +272,147 @@ class DriverTest {
}
}
+ test_getResult_selfConsistent() async {
+ var a = _p('/test/lib/a.dart');
+ var b = _p('/test/lib/b.dart');
+ provider.newFile(
+ a,
+ r'''
+import 'b.dart';
+var A1 = 1;
+var A2 = B1;
+''');
+ provider.newFile(
+ b,
+ r'''
+import 'a.dart';
+var B1 = A1;
+''');
+
+ driver.addFile(a);
+ driver.addFile(b);
+ await _waitForIdle();
+
+ {
+ AnalysisResult result = await driver.getResult(a);
+ expect(_getTopLevelVarType(result.unit, 'A1'), 'int');
+ expect(_getTopLevelVarType(result.unit, 'A2'), 'int');
+ }
+
+ // Update "a" that that "A1" is now "double".
Brian Wilkerson 2016/10/27 20:08:17 "that that"?
+ // Get result for "a".
+ //
+ // Even though we have not notified the driver about the change,
+ // we still get "double" for "A1", because getResult() re-read the content.
+ //
+ // We also get "double" for "A2", even though "A2" has the type from "b".
+ // That's because we check for "a" API signature consistency, and because
+ // it has changed, we invalidated the dependency cache, relinked libraries
+ // and recomputed types.
+ provider.updateFile(
+ a,
+ r'''
+import 'b.dart';
+var A1 = 1.2;
+var A2 = B1;
+''');
+ {
+ AnalysisResult result = await driver.getResult(a);
+ expect(_getTopLevelVarType(result.unit, 'A1'), 'double');
+ expect(_getTopLevelVarType(result.unit, 'A2'), 'double');
+ }
+ }
+
+ test_getResult_thenRemove() async {
+ _addTestFile('main() {}', priority: true);
+
+ Future<AnalysisResult> resultFuture = driver.getResult(testFile);
+ driver.removeFile(testFile);
+
+ AnalysisResult result = await resultFuture;
+ expect(result, isNotNull);
+ expect(result.path, testFile);
+ expect(result.unit, isNotNull);
+ }
+
+ test_getResult_twoPendingFutures() async {
+ String content = 'main() {}';
+ _addTestFile(content, priority: true);
+
+ Future<AnalysisResult> future1 = driver.getResult(testFile);
+ Future<AnalysisResult> future2 = driver.getResult(testFile);
+
+ // Both futures complete, with the same result.
+ AnalysisResult result1 = await future1;
+ AnalysisResult result2 = await future2;
+ expect(result2, same(result1));
+ expect(result1.path, testFile);
+ expect(result1.unit, isNotNull);
+ }
+
+ test_removeFile_changeFile_implicitlyAnalyzed() async {
+ var a = _p('/test/lib/a.dart');
+ var b = _p('/test/lib/b.dart');
+ provider.newFile(
+ a,
+ r'''
+import 'b.dart';
+var A = B;
+''');
+ provider.newFile(b, 'var B = 1;');
+
+ driver.priorityFiles = [a, b];
+ driver.addFile(a);
+ driver.addFile(b);
+
+ // We have results for both "a" and "b".
+ await _waitForIdle();
+ expect(allResults, hasLength(2));
+ {
+ AnalysisResult ar = allResults.firstWhere((r) => r.path == a);
+ expect(_getTopLevelVarType(ar.unit, 'A'), 'int');
+ }
+ {
+ AnalysisResult br = allResults.firstWhere((r) => r.path == b);
+ expect(_getTopLevelVarType(br.unit, 'B'), 'int');
+ }
+ allResults.clear();
+
+ // Remove "b" and send the change notification.
+ provider.updateFile(b, 'var B = 1.2;');
+ driver.removeFile(b);
+ driver.changeFile(b);
+
+ // While "b" is not analyzed explicitly, it is analyzed implicitly.
+ // We don't get a result for "b".
+ // But the change causes "a" to be reanalyzed.
+ await _waitForIdle();
+ expect(allResults, hasLength(1));
+ {
+ AnalysisResult ar = allResults.firstWhere((r) => r.path == a);
+ expect(_getTopLevelVarType(ar.unit, 'A'), 'double');
+ }
+ }
+
+ test_removeFile_changeFile_notAnalyzed() async {
+ _addTestFile('main() {}');
+
+ // We have a result.
+ await _waitForIdle();
+ expect(allResults, hasLength(1));
+ expect(allResults[0].path, testFile);
+ allResults.clear();
+
+ // Remove the file and send the change notification.
+ // The change notification does nothing, because the file is explicitly
+ // or implicitly analyzed.
+ driver.removeFile(testFile);
+ driver.changeFile(testFile);
+
+ await _waitForIdle();
+ expect(allResults, isEmpty);
+ }
+
test_results_priority() async {
String content = 'int f() => 42;';
_addTestFile(content, priority: true);
@@ -182,13 +488,31 @@ class DriverTest {
}
}
+ VariableDeclaration _getTopLevelVar(CompilationUnit unit, String name) {
+ for (CompilationUnitMember declaration in unit.declarations) {
+ if (declaration is TopLevelVariableDeclaration) {
+ for (VariableDeclaration variable in declaration.variables.variables) {
+ if (variable.name.name == name) {
+ return variable;
+ }
+ }
+ }
+ }
+ fail('Cannot find the top-level variable $name in\n$unit');
+ return null;
+ }
+
+ String _getTopLevelVarType(CompilationUnit unit, String name) {
+ return _getTopLevelVar(unit, name).element.type.toString();
+ }
+
/**
* Return the [provider] specific path for the given Posix [path].
*/
String _p(String path) => provider.convertPath(path);
Future<Null> _waitForIdle() async {
- await statusSplitter.split().firstWhere((status) => status.isIdle);
+ await idleStatusMonitor.signal;
}
static String _md5(String content) {
@@ -196,6 +520,21 @@ class DriverTest {
}
}
+class _Monitor {
+ Completer<Null> _completer = new Completer<Null>();
+
+ Future<Null> get signal async {
+ await _completer.future;
+ _completer = new Completer<Null>();
+ }
+
+ void notify() {
+ if (!_completer.isCompleted) {
+ _completer.complete(null);
+ }
+ }
+}
+
class _TestByteStore implements ByteStore {
final map = <String, List<int>>{};
« no previous file with comments | « pkg/analyzer/lib/src/dart/analysis/driver.dart ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698