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

Unified Diff: pkg/analysis_server/test/src/plugin/plugin_manager_test.dart

Issue 2746293004: Add an object that can manage which plugins are associated with each context / driver (Closed)
Patch Set: Created 3 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « pkg/analysis_server/lib/src/plugin/plugin_manager.dart ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: pkg/analysis_server/test/src/plugin/plugin_manager_test.dart
diff --git a/pkg/analysis_server/test/src/plugin/plugin_manager_test.dart b/pkg/analysis_server/test/src/plugin/plugin_manager_test.dart
new file mode 100644
index 0000000000000000000000000000000000000000..57b0c7dacef83ce8373bbacfced1346c626cc154
--- /dev/null
+++ b/pkg/analysis_server/test/src/plugin/plugin_manager_test.dart
@@ -0,0 +1,554 @@
+// Copyright (c) 2017, 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 'dart:async';
+import 'dart:io' as io;
+
+import 'package:analysis_server/src/plugin/notification_manager.dart';
+import 'package:analysis_server/src/plugin/plugin_manager.dart';
+import 'package:analyzer/file_system/memory_file_system.dart';
+import 'package:analyzer/file_system/physical_file_system.dart';
+import 'package:analyzer/instrumentation/instrumentation.dart';
+import 'package:analyzer_plugin/channel/channel.dart';
+import 'package:analyzer_plugin/protocol/protocol.dart';
+import 'package:analyzer_plugin/protocol/protocol_generated.dart';
+import 'package:path/path.dart' as path;
+import 'package:test/test.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+main() {
+ defineReflectiveSuite(() {
+ defineReflectiveTests(PluginInfoTest);
+ defineReflectiveTests(PluginManagerTest);
+ defineReflectiveTests(PluginManagerFromDiskTest);
+ defineReflectiveTests(PluginSessionTest);
+ defineReflectiveTests(PluginSessionFromDiskTest);
+ });
+}
+
+@reflectiveTest
+class PluginInfoTest {
+ MemoryResourceProvider resourceProvider;
+ TestNotificationManager notificationManager;
+ String pluginPath = '/pluginDir';
+ String executionPath = '/pluginDir/bin/plugin.dart';
+ String packagesPath = '/pluginDir/.packages';
+ PluginInfo plugin;
+
+ void setUp() {
+ resourceProvider = new MemoryResourceProvider();
+ notificationManager = new TestNotificationManager();
+ plugin = new PluginInfo(pluginPath, executionPath, packagesPath,
+ notificationManager, InstrumentationService.NULL_SERVICE);
+ }
+
+ test_addContextRoot() {
+ ContextRoot contextRoot1 = new ContextRoot('/pkg1', []);
+ plugin.addContextRoot(contextRoot1);
+ expect(plugin.contextRoots, [contextRoot1]);
+ plugin.addContextRoot(contextRoot1);
+ expect(plugin.contextRoots, [contextRoot1]);
+ }
+
+ test_creation() {
+ expect(plugin.path, pluginPath);
+ expect(plugin.executionPath, executionPath);
+ expect(plugin.notificationManager, notificationManager);
+ expect(plugin.contextRoots, isEmpty);
+ expect(plugin.currentSession, isNull);
+ }
+
+ test_removeContextRoot() {
+ ContextRoot contextRoot1 = new ContextRoot('/pkg1', []);
+ ContextRoot contextRoot2 = new ContextRoot('/pkg2', []);
+ plugin.addContextRoot(contextRoot1);
+ expect(plugin.contextRoots, unorderedEquals([contextRoot1]));
+ plugin.addContextRoot(contextRoot2);
+ expect(plugin.contextRoots, unorderedEquals([contextRoot1, contextRoot2]));
+ plugin.removeContextRoot(contextRoot1);
+ expect(plugin.contextRoots, unorderedEquals([contextRoot2]));
+ plugin.removeContextRoot(contextRoot2);
+ expect(plugin.contextRoots, isEmpty);
+ }
+
+ @failingTest
+ test_start_notRunning() {
+ fail('Not tested');
+ }
+
+ test_start_running() async {
+ plugin.currentSession = new PluginSession(plugin);
+ try {
+ await plugin.start('');
+ fail('Expected a StateError');
+ } on StateError {
+ // Expected.
+ }
+ }
+
+ test_stop_notRunning() {
+ expect(() => plugin.stop(), throwsA(new isInstanceOf<StateError>()));
+ }
+
+ test_stop_running() {
+ PluginSession session = new PluginSession(plugin);
+ TestServerCommunicationChannel channel =
+ new TestServerCommunicationChannel(session);
+ plugin.currentSession = session;
+ plugin.stop();
+ expect(plugin.currentSession, isNull);
+ expect(channel.sentRequests, hasLength(1));
+ expect(channel.sentRequests[0].method, 'plugin.shutdown');
+ }
+}
+
+@reflectiveTest
+class PluginManagerFromDiskTest extends PluginTestSupport {
+ String byteStorePath = '/byteStore';
+ PluginManager manager;
+
+ void setUp() {
+ super.setUp();
+ manager = new PluginManager(resourceProvider, byteStorePath,
+ notificationManager, InstrumentationService.NULL_SERVICE);
+ }
+
+ test_addPluginToContextRoot() async {
+ io.Directory pkg1Dir = io.Directory.systemTemp.createTempSync('pkg1');
+ String pkgPath = pkg1Dir.resolveSymbolicLinksSync();
+ await withPlugin(test: (String pluginPath) async {
+ ContextRoot contextRoot = new ContextRoot(pkgPath, []);
+ await manager.addPluginToContextRoot(contextRoot, pluginPath);
+ await manager.stopAll();
+ });
+ pkg1Dir.deleteSync(recursive: true);
+ }
+
+ test_broadcast_many() async {
+ io.Directory pkg1Dir = io.Directory.systemTemp.createTempSync('pkg1');
+ String pkgPath = pkg1Dir.resolveSymbolicLinksSync();
+ await withPlugin(
+ pluginName: 'plugin1',
+ test: (String plugin1Path) async {
+ await withPlugin(
+ pluginName: 'plugin2',
+ test: (String plugin2Path) async {
+ ContextRoot contextRoot = new ContextRoot(pkgPath, []);
+ await manager.addPluginToContextRoot(contextRoot, plugin1Path);
+ await manager.addPluginToContextRoot(contextRoot, plugin2Path);
+
+ List<Future<Response>> responses = manager.broadcast(
+ contextRoot,
+ new CompletionGetSuggestionsParams(
+ '/pkg1/lib/pkg1.dart', 100));
+ expect(responses, hasLength(2));
+
+ await manager.stopAll();
+ });
+ });
+ pkg1Dir.deleteSync(recursive: true);
+ }
+
+ test_pluginsForContextRoot_multiple() async {
+ io.Directory pkg1Dir = io.Directory.systemTemp.createTempSync('pkg1');
+ String pkgPath = pkg1Dir.resolveSymbolicLinksSync();
+ await withPlugin(
+ pluginName: 'plugin1',
+ test: (String plugin1Path) async {
+ await withPlugin(
+ pluginName: 'plugin2',
+ test: (String plugin2Path) async {
+ ContextRoot contextRoot = new ContextRoot(pkgPath, []);
+ await manager.addPluginToContextRoot(contextRoot, plugin1Path);
+ await manager.addPluginToContextRoot(contextRoot, plugin2Path);
+
+ List<PluginInfo> plugins =
+ manager.pluginsForContextRoot(contextRoot);
+ expect(plugins, hasLength(2));
+ List<String> paths =
+ plugins.map((PluginInfo plugin) => plugin.path).toList();
+ expect(paths, unorderedEquals([plugin1Path, plugin2Path]));
+
+ await manager.stopAll();
+ });
+ });
+ pkg1Dir.deleteSync(recursive: true);
+ }
+
+ test_pluginsForContextRoot_one() async {
+ io.Directory pkg1Dir = io.Directory.systemTemp.createTempSync('pkg1');
+ String pkgPath = pkg1Dir.resolveSymbolicLinksSync();
+ await withPlugin(test: (String pluginPath) async {
+ ContextRoot contextRoot = new ContextRoot(pkgPath, []);
+ await manager.addPluginToContextRoot(contextRoot, pluginPath);
+
+ List<PluginInfo> plugins = manager.pluginsForContextRoot(contextRoot);
+ expect(plugins, hasLength(1));
+ expect(plugins[0].path, pluginPath);
+
+ await manager.stopAll();
+ });
+ pkg1Dir.deleteSync(recursive: true);
+ }
+
+ test_removedContextRoot() async {
+ io.Directory pkg1Dir = io.Directory.systemTemp.createTempSync('pkg1');
+ String pkgPath = pkg1Dir.resolveSymbolicLinksSync();
+ await withPlugin(test: (String pluginPath) async {
+ ContextRoot contextRoot = new ContextRoot(pkgPath, []);
+ await manager.addPluginToContextRoot(contextRoot, pluginPath);
+
+ manager.removedContextRoot(contextRoot);
+
+ await manager.stopAll();
+ });
+ pkg1Dir.deleteSync(recursive: true);
+ }
+}
+
+@reflectiveTest
+class PluginManagerTest {
+ MemoryResourceProvider resourceProvider;
+ String byteStorePath;
+ TestNotificationManager notificationManager;
+ PluginManager manager;
+
+ void setUp() {
+ resourceProvider = new MemoryResourceProvider();
+ byteStorePath = '/byteStore';
+ notificationManager = new TestNotificationManager();
+ manager = new PluginManager(resourceProvider, byteStorePath,
+ notificationManager, InstrumentationService.NULL_SERVICE);
+ }
+
+ void test_broadcast_none() {
+ ContextRoot contextRoot = new ContextRoot('/pkg1', []);
+ List<Future<Response>> responses = manager.broadcast(contextRoot,
+ new CompletionGetSuggestionsParams('/pkg1/lib/pkg1.dart', 100));
+ expect(responses, hasLength(0));
+ }
+
+ void test_creation() {
+ expect(manager.resourceProvider, resourceProvider);
+ expect(manager.byteStorePath, byteStorePath);
+ expect(manager.notificationManager, notificationManager);
+ }
+
+ void test_pluginsForContextRoot_none() {
+ ContextRoot contextRoot = new ContextRoot('/pkg1', []);
+ expect(manager.pluginsForContextRoot(contextRoot), isEmpty);
+ }
+
+ void test_stopAll_none() {
+ manager.stopAll();
+ }
+}
+
+@reflectiveTest
+class PluginSessionFromDiskTest extends PluginTestSupport {
+ test_start_notRunning() async {
+ await withPlugin(test: (String pluginPath) async {
+ String packagesPath = path.join(pluginPath, '.packages');
+ String mainPath = path.join(pluginPath, 'bin', 'plugin.dart');
+ String byteStorePath = path.join(pluginPath, 'byteStore');
+ new io.Directory(byteStorePath).createSync();
+ PluginInfo plugin = new PluginInfo(pluginPath, mainPath, packagesPath,
+ notificationManager, InstrumentationService.NULL_SERVICE);
+ PluginSession session = new PluginSession(plugin);
+ plugin.currentSession = session;
+ expect(await session.start(byteStorePath), isTrue);
+ await session.stop();
+ });
+ }
+}
+
+@reflectiveTest
+class PluginSessionTest {
+ MemoryResourceProvider resourceProvider;
+ TestNotificationManager notificationManager;
+ String pluginPath = '/pluginDir';
+ String executionPath = '/pluginDir/bin/plugin.dart';
+ String packagesPath = '/pluginDir/.packages';
+ PluginInfo plugin;
+ PluginSession session;
+
+ void setUp() {
+ resourceProvider = new MemoryResourceProvider();
+ notificationManager = new TestNotificationManager();
+ plugin = new PluginInfo(pluginPath, executionPath, packagesPath,
+ notificationManager, InstrumentationService.NULL_SERVICE);
+ session = new PluginSession(plugin);
+ }
+
+ void test_handleNotification() {
+ Notification notification =
+ new AnalysisErrorsParams('/test.dart', <AnalysisError>[])
+ .toNotification();
+ expect(notificationManager.notifications, hasLength(0));
+ session.handleNotification(notification);
+ expect(notificationManager.notifications, hasLength(1));
+ expect(notificationManager.notifications[0], notification);
+ }
+
+ void test_handleOnDone() {
+ TestServerCommunicationChannel channel =
+ new TestServerCommunicationChannel(session);
+ session.handleOnDone();
+ expect(channel.closeCount, 1);
+ expect(session.pluginStoppedCompleter.isCompleted, isTrue);
+ }
+
+ @failingTest
+ void test_handleOnError() {
+ session.handleOnError(<String>['message', 'trace']);
+ fail('The method handleOnError is not implemented');
+ }
+
+ test_handleResponse() async {
+ new TestServerCommunicationChannel(session);
+ Response response = new PluginVersionCheckResult(
+ true, 'name', 'version', <String>[],
+ contactInfo: 'contactInfo')
+ .toResponse('0');
+ Future<Response> future =
+ session.sendRequest(new PluginVersionCheckParams('', ''));
+ expect(session.pendingRequests, hasLength(1));
+ session.handleResponse(response);
+ expect(session.pendingRequests, hasLength(0));
+ Response result = await future;
+ expect(result, same(response));
+ }
+
+ void test_nextRequestId() {
+ expect(session.requestId, 0);
+ expect(session.nextRequestId, '0');
+ expect(session.requestId, 1);
+ }
+
+ void test_sendRequest() {
+ TestServerCommunicationChannel channel =
+ new TestServerCommunicationChannel(session);
+ session.sendRequest(new PluginVersionCheckParams('', ''));
+ expect(channel.sentRequests, hasLength(1));
+ expect(channel.sentRequests[0].method, 'plugin.versionCheck');
+ }
+
+ test_start_notCompatible() async {
+ session.isCompatible = false;
+ expect(await session.start(path.join(pluginPath, 'byteStore')), isFalse);
+ }
+
+ test_start_running() async {
+ new TestServerCommunicationChannel(session);
+ try {
+ await session.start(null);
+ fail('Expected a StateError to be thrown');
+ } on StateError {
+ // Expected behavior
+ }
+ }
+
+ test_stop_notRunning() {
+ expect(() => session.stop(), throwsA(new isInstanceOf<StateError>()));
+ }
+
+ void test_stop_running() {
+ TestServerCommunicationChannel channel =
+ new TestServerCommunicationChannel(session);
+ session.stop();
+ expect(channel.sentRequests, hasLength(1));
+ expect(channel.sentRequests[0].method, 'plugin.shutdown');
+ }
+}
+
+/**
+ * A class designed to be used as a superclass for test classes that define
+ * tests that require plugins to be created on disk.
+ */
+abstract class PluginTestSupport {
+ PhysicalResourceProvider resourceProvider;
+ TestNotificationManager notificationManager;
+
+ /**
+ * The content to be used for the '.packages' file, or `null` if the content
+ * has not yet been computed.
+ */
+ String _packagesFileContent;
+
+ void setUp() {
+ resourceProvider = PhysicalResourceProvider.INSTANCE;
+ notificationManager = new TestNotificationManager();
+ }
+
+ /**
+ * Create a directory structure representing a plugin on disk, run the given
+ * [test] function, and then remove the directory. The directory will have the
+ * following structure:
+ * ```
+ * pluginDirectory
+ * .packages
+ * bin
+ * plugin.dart
+ * ```
+ * The name of the plugin directory will be the [pluginName], if one is
+ * provided (in order to allow more than one plugin to be created by a single
+ * test). The 'plugin.dart' file will contain the given [content], or default
+ * content that implements a minimal plugin if the contents are not given. The
+ * [test] function will be passed the path of the directory that was created.
+ */
+ Future<Null> withPlugin(
+ {String content,
+ String pluginName,
+ Future<Null> test(String pluginPath)}) async {
+ io.Directory tempDirectory =
+ io.Directory.systemTemp.createTempSync(pluginName ?? 'test_plugin');
+ try {
+ String pluginPath = tempDirectory.resolveSymbolicLinksSync();
+ //
+ // Create a .packages file.
+ //
+ io.File packagesFile = new io.File(path.join(pluginPath, '.packages'));
+ packagesFile.writeAsStringSync(_getPackagesFileContent());
+ //
+ // Create the 'bin' directory.
+ //
+ String binPath = path.join(pluginPath, 'bin');
+ new io.Directory(binPath).createSync();
+ //
+ // Create the 'plugin.dart' file.
+ //
+ io.File pluginFile = new io.File(path.join(binPath, 'plugin.dart'));
+ pluginFile.writeAsStringSync(content ?? _defaultPluginContent());
+ //
+ // Run the actual test code.
+ //
+ await test(pluginPath);
+ } finally {
+ tempDirectory.deleteSync(recursive: true);
+ }
+ }
+
+ /**
+ * Convert the [sdkPackageMap] into a plugin-specific map by applying the
+ * given relative path [delta] to each line.
+ */
+ String _convertPackageMap(String sdkDirPath, List<String> sdkPackageMap) {
+ StringBuffer buffer = new StringBuffer();
+ for (String line in sdkPackageMap) {
+ if (!line.startsWith('#')) {
+ int index = line.indexOf(':');
+ String packageName = line.substring(0, index + 1);
+ String relativePath = line.substring(index + 1);
+ String absolutePath = path.join(sdkDirPath, relativePath);
+ buffer.write(packageName);
+ buffer.writeln(absolutePath);
+ }
+ }
+ return buffer.toString();
+ }
+
+ /**
+ * The default content of the plugin. This is a minimal plugin that will only
+ * respond correctly to version checks and to shutdown requests.
+ */
+ String _defaultPluginContent() {
+ return r'''
+import 'dart:isolate';
+import 'package:analyzer/file_system/file_system.dart';
+import 'package:analyzer/file_system/physical_file_system.dart';
+import 'package:analyzer_plugin/plugin/plugin.dart';
+import 'package:analyzer_plugin/starter.dart';
+import 'package:pub_semver/pub_semver.dart';
+
+void main(List<String> args, SendPort sendPort) {
+ MinimalPlugin plugin = new MinimalPlugin(PhysicalResourceProvider.INSTANCE);
+ new ServerPluginStarter(plugin).start(sendPort);
+}
+
+class MinimalPlugin extends ServerPlugin {
+ MinimalPlugin(ResourceProvider provider) : super(provider);
+
+ @override
+ List<String> get fileGlobsToAnalyze => <String>[];
+
+ @override
+ String get name => 'minimal';
+
+ @override
+ String get version => '0.0.1';
+
+ @override
+ bool isCompatibleWith(Version serverVersion) => true;
+}
+''';
+ }
+
+ /**
+ * Return the content to be used for the '.packages' file.
+ */
+ String _getPackagesFileContent() {
+ if (_packagesFileContent == null) {
+ io.File sdkPackagesFile = new io.File(_sdkPackagesPath());
+ List<String> sdkPackageMap = sdkPackagesFile.readAsLinesSync();
+ _packagesFileContent =
+ _convertPackageMap(path.dirname(sdkPackagesFile.path), sdkPackageMap);
+ }
+ return _packagesFileContent;
+ }
+
+ /**
+ * Return the path to the '.packages' file in the root of the SDK checkout.
+ */
+ String _sdkPackagesPath() {
+ String packagesPath =
+ io.Platform.script.toFilePath(windows: io.Platform.isWindows);
+ while (packagesPath.isNotEmpty &&
+ path.basename(packagesPath) != 'analysis_server') {
+ packagesPath = path.dirname(packagesPath);
+ }
+ packagesPath = path.dirname(packagesPath);
+ packagesPath = path.dirname(packagesPath);
+ return path.join(packagesPath, '.packages');
+ }
+}
+
+class TestNotificationManager implements NotificationManager {
+ List<Notification> notifications = <Notification>[];
+
+ @override
+ void handlePluginNotification(String pluginId, Notification notification) {
+ notifications.add(notification);
+ }
+
+ @override
+ noSuchMethod(Invocation invocation) {
+ fail('Unexpected invocation of ${invocation.memberName}');
+ }
+}
+
+class TestServerCommunicationChannel implements ServerCommunicationChannel {
+ int closeCount = 0;
+ List<Request> sentRequests = <Request>[];
+
+ TestServerCommunicationChannel(PluginSession session) {
+ session.channel = this;
+ }
+
+ @override
+ void close() {
+ closeCount++;
+ }
+
+ @override
+ void listen(void onResponse(Response response),
+ void onNotification(Notification notification),
+ {Function onError, void onDone()}) {
+ fail('Unexpected invocation of listen');
+ }
+
+ @override
+ void sendRequest(Request request) {
+ sentRequests.add(request);
+ }
+}
« no previous file with comments | « pkg/analysis_server/lib/src/plugin/plugin_manager.dart ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698