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

Side by Side 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 unified diff | 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 »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file.
4
5 import 'dart:async';
6 import 'dart:io' as io;
7
8 import 'package:analysis_server/src/plugin/notification_manager.dart';
9 import 'package:analysis_server/src/plugin/plugin_manager.dart';
10 import 'package:analyzer/file_system/memory_file_system.dart';
11 import 'package:analyzer/file_system/physical_file_system.dart';
12 import 'package:analyzer/instrumentation/instrumentation.dart';
13 import 'package:analyzer_plugin/channel/channel.dart';
14 import 'package:analyzer_plugin/protocol/protocol.dart';
15 import 'package:analyzer_plugin/protocol/protocol_generated.dart';
16 import 'package:path/path.dart' as path;
17 import 'package:test/test.dart';
18 import 'package:test_reflective_loader/test_reflective_loader.dart';
19
20 main() {
21 defineReflectiveSuite(() {
22 defineReflectiveTests(PluginInfoTest);
23 defineReflectiveTests(PluginManagerTest);
24 defineReflectiveTests(PluginManagerFromDiskTest);
25 defineReflectiveTests(PluginSessionTest);
26 defineReflectiveTests(PluginSessionFromDiskTest);
27 });
28 }
29
30 @reflectiveTest
31 class PluginInfoTest {
32 MemoryResourceProvider resourceProvider;
33 TestNotificationManager notificationManager;
34 String pluginPath = '/pluginDir';
35 String executionPath = '/pluginDir/bin/plugin.dart';
36 String packagesPath = '/pluginDir/.packages';
37 PluginInfo plugin;
38
39 void setUp() {
40 resourceProvider = new MemoryResourceProvider();
41 notificationManager = new TestNotificationManager();
42 plugin = new PluginInfo(pluginPath, executionPath, packagesPath,
43 notificationManager, InstrumentationService.NULL_SERVICE);
44 }
45
46 test_addContextRoot() {
47 ContextRoot contextRoot1 = new ContextRoot('/pkg1', []);
48 plugin.addContextRoot(contextRoot1);
49 expect(plugin.contextRoots, [contextRoot1]);
50 plugin.addContextRoot(contextRoot1);
51 expect(plugin.contextRoots, [contextRoot1]);
52 }
53
54 test_creation() {
55 expect(plugin.path, pluginPath);
56 expect(plugin.executionPath, executionPath);
57 expect(plugin.notificationManager, notificationManager);
58 expect(plugin.contextRoots, isEmpty);
59 expect(plugin.currentSession, isNull);
60 }
61
62 test_removeContextRoot() {
63 ContextRoot contextRoot1 = new ContextRoot('/pkg1', []);
64 ContextRoot contextRoot2 = new ContextRoot('/pkg2', []);
65 plugin.addContextRoot(contextRoot1);
66 expect(plugin.contextRoots, unorderedEquals([contextRoot1]));
67 plugin.addContextRoot(contextRoot2);
68 expect(plugin.contextRoots, unorderedEquals([contextRoot1, contextRoot2]));
69 plugin.removeContextRoot(contextRoot1);
70 expect(plugin.contextRoots, unorderedEquals([contextRoot2]));
71 plugin.removeContextRoot(contextRoot2);
72 expect(plugin.contextRoots, isEmpty);
73 }
74
75 @failingTest
76 test_start_notRunning() {
77 fail('Not tested');
78 }
79
80 test_start_running() async {
81 plugin.currentSession = new PluginSession(plugin);
82 try {
83 await plugin.start('');
84 fail('Expected a StateError');
85 } on StateError {
86 // Expected.
87 }
88 }
89
90 test_stop_notRunning() {
91 expect(() => plugin.stop(), throwsA(new isInstanceOf<StateError>()));
92 }
93
94 test_stop_running() {
95 PluginSession session = new PluginSession(plugin);
96 TestServerCommunicationChannel channel =
97 new TestServerCommunicationChannel(session);
98 plugin.currentSession = session;
99 plugin.stop();
100 expect(plugin.currentSession, isNull);
101 expect(channel.sentRequests, hasLength(1));
102 expect(channel.sentRequests[0].method, 'plugin.shutdown');
103 }
104 }
105
106 @reflectiveTest
107 class PluginManagerFromDiskTest extends PluginTestSupport {
108 String byteStorePath = '/byteStore';
109 PluginManager manager;
110
111 void setUp() {
112 super.setUp();
113 manager = new PluginManager(resourceProvider, byteStorePath,
114 notificationManager, InstrumentationService.NULL_SERVICE);
115 }
116
117 test_addPluginToContextRoot() async {
118 io.Directory pkg1Dir = io.Directory.systemTemp.createTempSync('pkg1');
119 String pkgPath = pkg1Dir.resolveSymbolicLinksSync();
120 await withPlugin(test: (String pluginPath) async {
121 ContextRoot contextRoot = new ContextRoot(pkgPath, []);
122 await manager.addPluginToContextRoot(contextRoot, pluginPath);
123 await manager.stopAll();
124 });
125 pkg1Dir.deleteSync(recursive: true);
126 }
127
128 test_broadcast_many() async {
129 io.Directory pkg1Dir = io.Directory.systemTemp.createTempSync('pkg1');
130 String pkgPath = pkg1Dir.resolveSymbolicLinksSync();
131 await withPlugin(
132 pluginName: 'plugin1',
133 test: (String plugin1Path) async {
134 await withPlugin(
135 pluginName: 'plugin2',
136 test: (String plugin2Path) async {
137 ContextRoot contextRoot = new ContextRoot(pkgPath, []);
138 await manager.addPluginToContextRoot(contextRoot, plugin1Path);
139 await manager.addPluginToContextRoot(contextRoot, plugin2Path);
140
141 List<Future<Response>> responses = manager.broadcast(
142 contextRoot,
143 new CompletionGetSuggestionsParams(
144 '/pkg1/lib/pkg1.dart', 100));
145 expect(responses, hasLength(2));
146
147 await manager.stopAll();
148 });
149 });
150 pkg1Dir.deleteSync(recursive: true);
151 }
152
153 test_pluginsForContextRoot_multiple() async {
154 io.Directory pkg1Dir = io.Directory.systemTemp.createTempSync('pkg1');
155 String pkgPath = pkg1Dir.resolveSymbolicLinksSync();
156 await withPlugin(
157 pluginName: 'plugin1',
158 test: (String plugin1Path) async {
159 await withPlugin(
160 pluginName: 'plugin2',
161 test: (String plugin2Path) async {
162 ContextRoot contextRoot = new ContextRoot(pkgPath, []);
163 await manager.addPluginToContextRoot(contextRoot, plugin1Path);
164 await manager.addPluginToContextRoot(contextRoot, plugin2Path);
165
166 List<PluginInfo> plugins =
167 manager.pluginsForContextRoot(contextRoot);
168 expect(plugins, hasLength(2));
169 List<String> paths =
170 plugins.map((PluginInfo plugin) => plugin.path).toList();
171 expect(paths, unorderedEquals([plugin1Path, plugin2Path]));
172
173 await manager.stopAll();
174 });
175 });
176 pkg1Dir.deleteSync(recursive: true);
177 }
178
179 test_pluginsForContextRoot_one() async {
180 io.Directory pkg1Dir = io.Directory.systemTemp.createTempSync('pkg1');
181 String pkgPath = pkg1Dir.resolveSymbolicLinksSync();
182 await withPlugin(test: (String pluginPath) async {
183 ContextRoot contextRoot = new ContextRoot(pkgPath, []);
184 await manager.addPluginToContextRoot(contextRoot, pluginPath);
185
186 List<PluginInfo> plugins = manager.pluginsForContextRoot(contextRoot);
187 expect(plugins, hasLength(1));
188 expect(plugins[0].path, pluginPath);
189
190 await manager.stopAll();
191 });
192 pkg1Dir.deleteSync(recursive: true);
193 }
194
195 test_removedContextRoot() async {
196 io.Directory pkg1Dir = io.Directory.systemTemp.createTempSync('pkg1');
197 String pkgPath = pkg1Dir.resolveSymbolicLinksSync();
198 await withPlugin(test: (String pluginPath) async {
199 ContextRoot contextRoot = new ContextRoot(pkgPath, []);
200 await manager.addPluginToContextRoot(contextRoot, pluginPath);
201
202 manager.removedContextRoot(contextRoot);
203
204 await manager.stopAll();
205 });
206 pkg1Dir.deleteSync(recursive: true);
207 }
208 }
209
210 @reflectiveTest
211 class PluginManagerTest {
212 MemoryResourceProvider resourceProvider;
213 String byteStorePath;
214 TestNotificationManager notificationManager;
215 PluginManager manager;
216
217 void setUp() {
218 resourceProvider = new MemoryResourceProvider();
219 byteStorePath = '/byteStore';
220 notificationManager = new TestNotificationManager();
221 manager = new PluginManager(resourceProvider, byteStorePath,
222 notificationManager, InstrumentationService.NULL_SERVICE);
223 }
224
225 void test_broadcast_none() {
226 ContextRoot contextRoot = new ContextRoot('/pkg1', []);
227 List<Future<Response>> responses = manager.broadcast(contextRoot,
228 new CompletionGetSuggestionsParams('/pkg1/lib/pkg1.dart', 100));
229 expect(responses, hasLength(0));
230 }
231
232 void test_creation() {
233 expect(manager.resourceProvider, resourceProvider);
234 expect(manager.byteStorePath, byteStorePath);
235 expect(manager.notificationManager, notificationManager);
236 }
237
238 void test_pluginsForContextRoot_none() {
239 ContextRoot contextRoot = new ContextRoot('/pkg1', []);
240 expect(manager.pluginsForContextRoot(contextRoot), isEmpty);
241 }
242
243 void test_stopAll_none() {
244 manager.stopAll();
245 }
246 }
247
248 @reflectiveTest
249 class PluginSessionFromDiskTest extends PluginTestSupport {
250 test_start_notRunning() async {
251 await withPlugin(test: (String pluginPath) async {
252 String packagesPath = path.join(pluginPath, '.packages');
253 String mainPath = path.join(pluginPath, 'bin', 'plugin.dart');
254 String byteStorePath = path.join(pluginPath, 'byteStore');
255 new io.Directory(byteStorePath).createSync();
256 PluginInfo plugin = new PluginInfo(pluginPath, mainPath, packagesPath,
257 notificationManager, InstrumentationService.NULL_SERVICE);
258 PluginSession session = new PluginSession(plugin);
259 plugin.currentSession = session;
260 expect(await session.start(byteStorePath), isTrue);
261 await session.stop();
262 });
263 }
264 }
265
266 @reflectiveTest
267 class PluginSessionTest {
268 MemoryResourceProvider resourceProvider;
269 TestNotificationManager notificationManager;
270 String pluginPath = '/pluginDir';
271 String executionPath = '/pluginDir/bin/plugin.dart';
272 String packagesPath = '/pluginDir/.packages';
273 PluginInfo plugin;
274 PluginSession session;
275
276 void setUp() {
277 resourceProvider = new MemoryResourceProvider();
278 notificationManager = new TestNotificationManager();
279 plugin = new PluginInfo(pluginPath, executionPath, packagesPath,
280 notificationManager, InstrumentationService.NULL_SERVICE);
281 session = new PluginSession(plugin);
282 }
283
284 void test_handleNotification() {
285 Notification notification =
286 new AnalysisErrorsParams('/test.dart', <AnalysisError>[])
287 .toNotification();
288 expect(notificationManager.notifications, hasLength(0));
289 session.handleNotification(notification);
290 expect(notificationManager.notifications, hasLength(1));
291 expect(notificationManager.notifications[0], notification);
292 }
293
294 void test_handleOnDone() {
295 TestServerCommunicationChannel channel =
296 new TestServerCommunicationChannel(session);
297 session.handleOnDone();
298 expect(channel.closeCount, 1);
299 expect(session.pluginStoppedCompleter.isCompleted, isTrue);
300 }
301
302 @failingTest
303 void test_handleOnError() {
304 session.handleOnError(<String>['message', 'trace']);
305 fail('The method handleOnError is not implemented');
306 }
307
308 test_handleResponse() async {
309 new TestServerCommunicationChannel(session);
310 Response response = new PluginVersionCheckResult(
311 true, 'name', 'version', <String>[],
312 contactInfo: 'contactInfo')
313 .toResponse('0');
314 Future<Response> future =
315 session.sendRequest(new PluginVersionCheckParams('', ''));
316 expect(session.pendingRequests, hasLength(1));
317 session.handleResponse(response);
318 expect(session.pendingRequests, hasLength(0));
319 Response result = await future;
320 expect(result, same(response));
321 }
322
323 void test_nextRequestId() {
324 expect(session.requestId, 0);
325 expect(session.nextRequestId, '0');
326 expect(session.requestId, 1);
327 }
328
329 void test_sendRequest() {
330 TestServerCommunicationChannel channel =
331 new TestServerCommunicationChannel(session);
332 session.sendRequest(new PluginVersionCheckParams('', ''));
333 expect(channel.sentRequests, hasLength(1));
334 expect(channel.sentRequests[0].method, 'plugin.versionCheck');
335 }
336
337 test_start_notCompatible() async {
338 session.isCompatible = false;
339 expect(await session.start(path.join(pluginPath, 'byteStore')), isFalse);
340 }
341
342 test_start_running() async {
343 new TestServerCommunicationChannel(session);
344 try {
345 await session.start(null);
346 fail('Expected a StateError to be thrown');
347 } on StateError {
348 // Expected behavior
349 }
350 }
351
352 test_stop_notRunning() {
353 expect(() => session.stop(), throwsA(new isInstanceOf<StateError>()));
354 }
355
356 void test_stop_running() {
357 TestServerCommunicationChannel channel =
358 new TestServerCommunicationChannel(session);
359 session.stop();
360 expect(channel.sentRequests, hasLength(1));
361 expect(channel.sentRequests[0].method, 'plugin.shutdown');
362 }
363 }
364
365 /**
366 * A class designed to be used as a superclass for test classes that define
367 * tests that require plugins to be created on disk.
368 */
369 abstract class PluginTestSupport {
370 PhysicalResourceProvider resourceProvider;
371 TestNotificationManager notificationManager;
372
373 /**
374 * The content to be used for the '.packages' file, or `null` if the content
375 * has not yet been computed.
376 */
377 String _packagesFileContent;
378
379 void setUp() {
380 resourceProvider = PhysicalResourceProvider.INSTANCE;
381 notificationManager = new TestNotificationManager();
382 }
383
384 /**
385 * Create a directory structure representing a plugin on disk, run the given
386 * [test] function, and then remove the directory. The directory will have the
387 * following structure:
388 * ```
389 * pluginDirectory
390 * .packages
391 * bin
392 * plugin.dart
393 * ```
394 * The name of the plugin directory will be the [pluginName], if one is
395 * provided (in order to allow more than one plugin to be created by a single
396 * test). The 'plugin.dart' file will contain the given [content], or default
397 * content that implements a minimal plugin if the contents are not given. The
398 * [test] function will be passed the path of the directory that was created.
399 */
400 Future<Null> withPlugin(
401 {String content,
402 String pluginName,
403 Future<Null> test(String pluginPath)}) async {
404 io.Directory tempDirectory =
405 io.Directory.systemTemp.createTempSync(pluginName ?? 'test_plugin');
406 try {
407 String pluginPath = tempDirectory.resolveSymbolicLinksSync();
408 //
409 // Create a .packages file.
410 //
411 io.File packagesFile = new io.File(path.join(pluginPath, '.packages'));
412 packagesFile.writeAsStringSync(_getPackagesFileContent());
413 //
414 // Create the 'bin' directory.
415 //
416 String binPath = path.join(pluginPath, 'bin');
417 new io.Directory(binPath).createSync();
418 //
419 // Create the 'plugin.dart' file.
420 //
421 io.File pluginFile = new io.File(path.join(binPath, 'plugin.dart'));
422 pluginFile.writeAsStringSync(content ?? _defaultPluginContent());
423 //
424 // Run the actual test code.
425 //
426 await test(pluginPath);
427 } finally {
428 tempDirectory.deleteSync(recursive: true);
429 }
430 }
431
432 /**
433 * Convert the [sdkPackageMap] into a plugin-specific map by applying the
434 * given relative path [delta] to each line.
435 */
436 String _convertPackageMap(String sdkDirPath, List<String> sdkPackageMap) {
437 StringBuffer buffer = new StringBuffer();
438 for (String line in sdkPackageMap) {
439 if (!line.startsWith('#')) {
440 int index = line.indexOf(':');
441 String packageName = line.substring(0, index + 1);
442 String relativePath = line.substring(index + 1);
443 String absolutePath = path.join(sdkDirPath, relativePath);
444 buffer.write(packageName);
445 buffer.writeln(absolutePath);
446 }
447 }
448 return buffer.toString();
449 }
450
451 /**
452 * The default content of the plugin. This is a minimal plugin that will only
453 * respond correctly to version checks and to shutdown requests.
454 */
455 String _defaultPluginContent() {
456 return r'''
457 import 'dart:isolate';
458 import 'package:analyzer/file_system/file_system.dart';
459 import 'package:analyzer/file_system/physical_file_system.dart';
460 import 'package:analyzer_plugin/plugin/plugin.dart';
461 import 'package:analyzer_plugin/starter.dart';
462 import 'package:pub_semver/pub_semver.dart';
463
464 void main(List<String> args, SendPort sendPort) {
465 MinimalPlugin plugin = new MinimalPlugin(PhysicalResourceProvider.INSTANCE);
466 new ServerPluginStarter(plugin).start(sendPort);
467 }
468
469 class MinimalPlugin extends ServerPlugin {
470 MinimalPlugin(ResourceProvider provider) : super(provider);
471
472 @override
473 List<String> get fileGlobsToAnalyze => <String>[];
474
475 @override
476 String get name => 'minimal';
477
478 @override
479 String get version => '0.0.1';
480
481 @override
482 bool isCompatibleWith(Version serverVersion) => true;
483 }
484 ''';
485 }
486
487 /**
488 * Return the content to be used for the '.packages' file.
489 */
490 String _getPackagesFileContent() {
491 if (_packagesFileContent == null) {
492 io.File sdkPackagesFile = new io.File(_sdkPackagesPath());
493 List<String> sdkPackageMap = sdkPackagesFile.readAsLinesSync();
494 _packagesFileContent =
495 _convertPackageMap(path.dirname(sdkPackagesFile.path), sdkPackageMap);
496 }
497 return _packagesFileContent;
498 }
499
500 /**
501 * Return the path to the '.packages' file in the root of the SDK checkout.
502 */
503 String _sdkPackagesPath() {
504 String packagesPath =
505 io.Platform.script.toFilePath(windows: io.Platform.isWindows);
506 while (packagesPath.isNotEmpty &&
507 path.basename(packagesPath) != 'analysis_server') {
508 packagesPath = path.dirname(packagesPath);
509 }
510 packagesPath = path.dirname(packagesPath);
511 packagesPath = path.dirname(packagesPath);
512 return path.join(packagesPath, '.packages');
513 }
514 }
515
516 class TestNotificationManager implements NotificationManager {
517 List<Notification> notifications = <Notification>[];
518
519 @override
520 void handlePluginNotification(String pluginId, Notification notification) {
521 notifications.add(notification);
522 }
523
524 @override
525 noSuchMethod(Invocation invocation) {
526 fail('Unexpected invocation of ${invocation.memberName}');
527 }
528 }
529
530 class TestServerCommunicationChannel implements ServerCommunicationChannel {
531 int closeCount = 0;
532 List<Request> sentRequests = <Request>[];
533
534 TestServerCommunicationChannel(PluginSession session) {
535 session.channel = this;
536 }
537
538 @override
539 void close() {
540 closeCount++;
541 }
542
543 @override
544 void listen(void onResponse(Response response),
545 void onNotification(Notification notification),
546 {Function onError, void onDone()}) {
547 fail('Unexpected invocation of listen');
548 }
549
550 @override
551 void sendRequest(Request request) {
552 sentRequests.add(request);
553 }
554 }
OLDNEW
« 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