1f52372bfa
AnalysisDriverResolutionTest is partially updated, about 30 failing tests added. I will get back to it in a following CL, it is not directly CFE integration, but updated understanding how we want to resolve. For example we don't need types for non-expression identifiers. Change-Id: I3daddbb6c66ffad7a726f3313a1199fd7387aa04 Reviewed-on: https://dart-review.googlesource.com/71883 Reviewed-by: Brian Wilkerson <brianwilkerson@google.com> Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
266 lines
9.3 KiB
Dart
266 lines
9.3 KiB
Dart
// 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.
|
|
|
|
import 'package:analysis_server/protocol/protocol.dart';
|
|
import 'package:analysis_server/protocol/protocol_constants.dart';
|
|
import 'package:analysis_server/protocol/protocol_generated.dart';
|
|
import 'package:analyzer/file_system/file_system.dart';
|
|
import 'package:analyzer/src/dart/analysis/driver.dart';
|
|
import 'package:analyzer_plugin/protocol/protocol_common.dart' as plugin;
|
|
import 'package:analyzer_plugin/protocol/protocol_common.dart';
|
|
import 'package:analyzer_plugin/protocol/protocol_generated.dart' as plugin;
|
|
import 'package:test/test.dart';
|
|
import 'package:test_reflective_loader/test_reflective_loader.dart';
|
|
|
|
import '../analysis_abstract.dart';
|
|
|
|
main() {
|
|
defineReflectiveSuite(() {
|
|
defineReflectiveTests(UpdateContentTest);
|
|
});
|
|
}
|
|
|
|
@reflectiveTest
|
|
class UpdateContentTest extends AbstractAnalysisTest {
|
|
Map<String, List<String>> filesErrors = {};
|
|
int serverErrorCount = 0;
|
|
int navigationCount = 0;
|
|
|
|
@override
|
|
void processNotification(Notification notification) {
|
|
if (notification.event == ANALYSIS_NOTIFICATION_ERRORS) {
|
|
var decoded = new AnalysisErrorsParams.fromNotification(notification);
|
|
String _format(AnalysisError e) =>
|
|
"${e.location.startLine}: ${e.message}";
|
|
filesErrors[decoded.file] = decoded.errors.map(_format).toList();
|
|
}
|
|
if (notification.event == ANALYSIS_NOTIFICATION_NAVIGATION) {
|
|
navigationCount++;
|
|
}
|
|
if (notification.event == SERVER_NOTIFICATION_ERROR) {
|
|
serverErrorCount++;
|
|
}
|
|
}
|
|
|
|
test_illegal_ChangeContentOverlay() {
|
|
// It should be illegal to send a ChangeContentOverlay for a file that
|
|
// doesn't have an overlay yet.
|
|
createProject();
|
|
addTestFile('library foo;');
|
|
String id = 'myId';
|
|
try {
|
|
server.updateContent(id, {
|
|
testFile: new ChangeContentOverlay([new SourceEdit(8, 3, 'bar')])
|
|
});
|
|
fail('Expected an exception to be thrown');
|
|
} on RequestFailure catch (e) {
|
|
expect(e.response.id, id);
|
|
expect(e.response.error.code, RequestErrorCode.INVALID_OVERLAY_CHANGE);
|
|
}
|
|
}
|
|
|
|
test_multiple_contexts() async {
|
|
String project1path = convertPath('/project1');
|
|
String project2path = convertPath('/project2');
|
|
String fooPath = newFile('/project1/foo.dart', content: '''
|
|
library foo;
|
|
import '../project2/baz.dart';
|
|
main() { f(); }''').path;
|
|
String barPath = newFile('/project2/bar.dart', content: '''
|
|
library bar;
|
|
import 'baz.dart';
|
|
main() { f(); }''').path;
|
|
String bazPath = newFile('/project2/baz.dart', content: '''
|
|
library baz;
|
|
f(int i) {}
|
|
''').path;
|
|
Request request =
|
|
new AnalysisSetAnalysisRootsParams([project1path, project2path], [])
|
|
.toRequest('0');
|
|
handleSuccessfulRequest(request);
|
|
{
|
|
await server.onAnalysisComplete;
|
|
// Files foo.dart and bar.dart should both have errors, since they both
|
|
// call f() with the wrong number of arguments.
|
|
expect(filesErrors[fooPath], hasLength(1));
|
|
expect(filesErrors[barPath], hasLength(1));
|
|
// Overlay the content of baz.dart to eliminate the errors.
|
|
server.updateContent('1', {
|
|
bazPath: new AddContentOverlay('''
|
|
library baz;
|
|
f() {}
|
|
''')
|
|
});
|
|
}
|
|
{
|
|
await server.onAnalysisComplete;
|
|
// The overlay should have been propagated to both contexts, causing both
|
|
// foo.dart and bar.dart to be reanalyzed and found to be free of errors.
|
|
expect(filesErrors[fooPath], isEmpty);
|
|
expect(filesErrors[barPath], isEmpty);
|
|
}
|
|
}
|
|
|
|
@failingTest
|
|
test_overlay_addPreviouslyImported() async {
|
|
// The list of errors doesn't include errors for '/project/target.dart'.
|
|
Folder project = newFolder('/project');
|
|
handleSuccessfulRequest(
|
|
new AnalysisSetAnalysisRootsParams([project.path], []).toRequest('0'));
|
|
|
|
server.updateContent('1',
|
|
{'/project/main.dart': new AddContentOverlay('import "target.dart";')});
|
|
await server.onAnalysisComplete;
|
|
expect(filesErrors, {
|
|
'/project/main.dart': ["1: Target of URI doesn't exist: 'target.dart'."],
|
|
'/project/target.dart': []
|
|
});
|
|
|
|
server.updateContent('1',
|
|
{'/project/target.dart': new AddContentOverlay('import "none.dart";')});
|
|
await server.onAnalysisComplete;
|
|
expect(filesErrors, {
|
|
'/project/main.dart': ["1: Unused import."],
|
|
'/project/target.dart': ["1: Target of URI doesn't exist: 'none.dart'."],
|
|
'/project/none.dart': []
|
|
});
|
|
}
|
|
|
|
test_overlayOnly() async {
|
|
String filePath = convertPath('/User/project1/test.dart');
|
|
Folder folder1 = newFolder('/User/project1');
|
|
Folder folder2 = newFolder('/User/project2');
|
|
Request request =
|
|
new AnalysisSetAnalysisRootsParams([folder1.path, folder2.path], [])
|
|
.toRequest('0');
|
|
handleSuccessfulRequest(request);
|
|
// exactly 2 contexts
|
|
expect(server.driverMap, hasLength(2));
|
|
AnalysisDriver driver1 = server.driverMap[folder1];
|
|
AnalysisDriver driver2 = server.driverMap[folder2];
|
|
// no sources
|
|
expect(_getUserSources(driver1), isEmpty);
|
|
expect(_getUserSources(driver2), isEmpty);
|
|
// add an overlay - new Source in context1
|
|
server.updateContent('1', {filePath: new AddContentOverlay('')});
|
|
{
|
|
List<String> paths = _getUserSources(driver1);
|
|
expect(paths, hasLength(1));
|
|
expect(paths[0], filePath);
|
|
}
|
|
expect(_getUserSources(driver2), isEmpty);
|
|
// remove the overlay - no sources
|
|
server.updateContent('2', {filePath: new RemoveContentOverlay()});
|
|
// The file isn't removed from the list of added sources.
|
|
// expect(_getUserSources(driver1), isEmpty);
|
|
expect(_getUserSources(driver2), isEmpty);
|
|
}
|
|
|
|
@failingTest
|
|
test_sendNoticesAfterNopChange() async {
|
|
// The errors are empty on the last line.
|
|
createProject();
|
|
addTestFile('');
|
|
await server.onAnalysisComplete;
|
|
// add an overlay
|
|
server.updateContent(
|
|
'1', {testFile: new AddContentOverlay('main() {} main() {}')});
|
|
await server.onAnalysisComplete;
|
|
// clear errors and make a no-op change
|
|
filesErrors.clear();
|
|
server.updateContent('2', {
|
|
testFile: new ChangeContentOverlay([new SourceEdit(0, 4, 'main')])
|
|
});
|
|
await server.onAnalysisComplete;
|
|
// errors should have been resent
|
|
expect(filesErrors, isNotEmpty);
|
|
}
|
|
|
|
@failingTest
|
|
test_sendNoticesAfterNopChange_flushedUnit() async {
|
|
// The list of errors is empty on the last line.
|
|
createProject();
|
|
addTestFile('');
|
|
await server.onAnalysisComplete;
|
|
// add an overlay
|
|
server.updateContent(
|
|
'1', {testFile: new AddContentOverlay('main() {} main() {}')});
|
|
await server.onAnalysisComplete;
|
|
// clear errors and make a no-op change
|
|
filesErrors.clear();
|
|
server.updateContent('2', {
|
|
testFile: new ChangeContentOverlay([new SourceEdit(0, 4, 'main')])
|
|
});
|
|
await server.onAnalysisComplete;
|
|
// errors should have been resent
|
|
expect(filesErrors, isNotEmpty);
|
|
}
|
|
|
|
test_sentToPlugins() {
|
|
String filePath = '/project/target.dart';
|
|
String fileContent = 'import "none.dart";';
|
|
//
|
|
// Add
|
|
//
|
|
handleSuccessfulRequest(new AnalysisUpdateContentParams(
|
|
<String, dynamic>{filePath: new AddContentOverlay(fileContent)})
|
|
.toRequest('0'));
|
|
plugin.AnalysisUpdateContentParams params =
|
|
pluginManager.analysisUpdateContentParams;
|
|
expect(params, isNotNull);
|
|
Map<String, dynamic> files = params.files;
|
|
expect(files, hasLength(1));
|
|
Object overlay = files[filePath];
|
|
expect(overlay, const TypeMatcher<plugin.AddContentOverlay>());
|
|
plugin.AddContentOverlay addOverlay = overlay;
|
|
expect(addOverlay.content, fileContent);
|
|
//
|
|
// Change
|
|
//
|
|
pluginManager.analysisUpdateContentParams = null;
|
|
handleSuccessfulRequest(new AnalysisUpdateContentParams(<String, dynamic>{
|
|
filePath: new ChangeContentOverlay(
|
|
<SourceEdit>[new SourceEdit(8, 1, "'"), new SourceEdit(18, 1, "'")])
|
|
}).toRequest('1'));
|
|
params = pluginManager.analysisUpdateContentParams;
|
|
expect(params, isNotNull);
|
|
files = params.files;
|
|
expect(files, hasLength(1));
|
|
overlay = files[filePath];
|
|
expect(overlay, const TypeMatcher<plugin.ChangeContentOverlay>());
|
|
plugin.ChangeContentOverlay changeOverlay = overlay;
|
|
expect(changeOverlay.edits, hasLength(2));
|
|
//
|
|
// Remove
|
|
//
|
|
pluginManager.analysisUpdateContentParams = null;
|
|
handleSuccessfulRequest(new AnalysisUpdateContentParams(
|
|
<String, dynamic>{filePath: new RemoveContentOverlay()})
|
|
.toRequest('2'));
|
|
params = pluginManager.analysisUpdateContentParams;
|
|
expect(params, isNotNull);
|
|
files = params.files;
|
|
expect(files, hasLength(1));
|
|
overlay = files[filePath];
|
|
expect(overlay, const TypeMatcher<plugin.RemoveContentOverlay>());
|
|
}
|
|
|
|
// CompilationUnit _getTestUnit() {
|
|
// ContextSourcePair pair = server.getContextSourcePair(testFile);
|
|
// AnalysisContext context = pair.context;
|
|
// Source source = pair.source;
|
|
// return context.getResolvedCompilationUnit2(source, source);
|
|
// }
|
|
|
|
List<String> _getUserSources(AnalysisDriver driver) {
|
|
List<String> sources = <String>[];
|
|
driver.addedFiles.forEach((path) {
|
|
if (path.startsWith(convertPath('/User/'))) {
|
|
sources.add(path);
|
|
}
|
|
});
|
|
return sources;
|
|
}
|
|
}
|