| /****************************************************************************** |
| * Copyright (c) 2016 TypeFox and others. |
| * |
| * This program and the accompanying materials are made available under the |
| * terms of the Eclipse Public License v. 2.0 which is available at |
| * http://www.eclipse.org/legal/epl-2.0, |
| * or the Eclipse Distribution License v. 1.0 which is available at |
| * http://www.eclipse.org/org/documents/edl-v10.php. |
| * |
| * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause |
| ******************************************************************************/ |
| package org.eclipse.lsp4j.test.launch; |
| |
| import java.io.IOException; |
| import java.io.PipedInputStream; |
| import java.io.PipedOutputStream; |
| import java.util.ArrayList; |
| import java.util.LinkedHashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.concurrent.CompletableFuture; |
| import java.util.concurrent.ExecutionException; |
| import java.util.concurrent.Future; |
| import java.util.concurrent.TimeUnit; |
| import java.util.logging.Level; |
| import java.util.logging.Logger; |
| |
| import org.eclipse.lsp4j.CompletionItem; |
| import org.eclipse.lsp4j.CompletionItemKind; |
| import org.eclipse.lsp4j.CompletionList; |
| import org.eclipse.lsp4j.CompletionParams; |
| import org.eclipse.lsp4j.MessageParams; |
| import org.eclipse.lsp4j.MessageType; |
| import org.eclipse.lsp4j.Position; |
| import org.eclipse.lsp4j.TextDocumentIdentifier; |
| import org.eclipse.lsp4j.jsonrpc.Endpoint; |
| import org.eclipse.lsp4j.jsonrpc.Launcher; |
| import org.eclipse.lsp4j.jsonrpc.json.StreamMessageProducer; |
| import org.eclipse.lsp4j.jsonrpc.messages.Either; |
| import org.eclipse.lsp4j.jsonrpc.services.ServiceEndpoints; |
| import org.eclipse.lsp4j.launch.LSPLauncher; |
| import org.eclipse.lsp4j.services.LanguageClient; |
| import org.eclipse.lsp4j.services.LanguageServer; |
| import org.eclipse.xtext.xbase.lib.Pair; |
| import org.eclipse.xtext.xbase.lib.util.ToStringBuilder; |
| import org.junit.After; |
| import org.junit.Assert; |
| import org.junit.Before; |
| import org.junit.Test; |
| |
| public class LauncherTest { |
| |
| private static final long TIMEOUT = 2000; |
| |
| @Test public void testNotification() throws IOException { |
| MessageParams p = new MessageParams(); |
| p.setMessage("Hello World"); |
| p.setType(MessageType.Info); |
| |
| client.expectedNotifications.put("window/logMessage", p); |
| serverLauncher.getRemoteProxy().logMessage(p); |
| client.joinOnEmpty(); |
| } |
| |
| @Test public void testRequest() throws Exception { |
| CompletionParams p = new CompletionParams(); |
| p.setPosition(new Position(1,1)); |
| p.setTextDocument(new TextDocumentIdentifier("test/foo.txt")); |
| |
| CompletionList result = new CompletionList(); |
| result.setIsIncomplete(true); |
| result.setItems(new ArrayList<>()); |
| |
| CompletionItem item = new CompletionItem(); |
| item.setDetail("test"); |
| item.setDocumentation("doc"); |
| item.setFilterText("filter"); |
| item.setInsertText("insert"); |
| item.setKind(CompletionItemKind.Field); |
| result.getItems().add(item); |
| |
| server.expectedRequests.put("textDocument/completion", new Pair<>(p, result)); |
| CompletableFuture<Either<List<CompletionItem>, CompletionList>> future = clientLauncher.getRemoteProxy().getTextDocumentService().completion(p); |
| Assert.assertEquals(Either.forRight(result).toString(), future.get(TIMEOUT, TimeUnit.MILLISECONDS).toString()); |
| client.joinOnEmpty(); |
| } |
| |
| |
| static class AssertingEndpoint implements Endpoint { |
| public Map<String, Pair<Object, Object>> expectedRequests = new LinkedHashMap<>(); |
| |
| @Override |
| public CompletableFuture<?> request(String method, Object parameter) { |
| Assert.assertTrue(expectedRequests.containsKey(method)); |
| Pair<Object, Object> result = expectedRequests.remove(method); |
| Assert.assertEquals(result.getKey().toString(), parameter.toString()); |
| return CompletableFuture.completedFuture(result.getValue()); |
| } |
| |
| public Map<String, Object> expectedNotifications = new LinkedHashMap<>(); |
| |
| @Override |
| public void notify(String method, Object parameter) { |
| Assert.assertTrue(expectedNotifications.containsKey(method)); |
| Object object = expectedNotifications.remove(method); |
| Assert.assertEquals(object.toString(), parameter.toString()); |
| } |
| |
| /** |
| * wait max 1 sec for all expectations to be removed |
| */ |
| public void joinOnEmpty() { |
| long before = System.currentTimeMillis(); |
| do { |
| if (expectedNotifications.isEmpty() && expectedNotifications.isEmpty()) { |
| return; |
| } |
| try { |
| Thread.sleep(100); |
| } catch (InterruptedException e) { |
| // TODO Auto-generated catch block |
| e.printStackTrace(); |
| } |
| } while ( System.currentTimeMillis() < before + 1000); |
| Assert.fail("expectations weren't empty "+toString()); |
| } |
| |
| @Override |
| public String toString() { |
| return new ToStringBuilder(this).addAllFields().toString(); |
| } |
| |
| } |
| |
| private AssertingEndpoint server; |
| private Launcher<LanguageClient> serverLauncher; |
| private Future<?> serverListening; |
| |
| private AssertingEndpoint client; |
| private Launcher<LanguageServer> clientLauncher; |
| private Future<?> clientListening; |
| |
| private Level logLevel; |
| |
| @Before public void setup() throws IOException { |
| PipedInputStream inClient = new PipedInputStream(); |
| PipedOutputStream outClient = new PipedOutputStream(); |
| PipedInputStream inServer = new PipedInputStream(); |
| PipedOutputStream outServer = new PipedOutputStream(); |
| |
| inClient.connect(outServer); |
| outClient.connect(inServer); |
| server = new AssertingEndpoint(); |
| serverLauncher = LSPLauncher.createServerLauncher(ServiceEndpoints.toServiceObject(server, LanguageServer.class), inServer, outServer); |
| serverListening = serverLauncher.startListening(); |
| |
| client = new AssertingEndpoint(); |
| clientLauncher = LSPLauncher.createClientLauncher(ServiceEndpoints.toServiceObject(client, LanguageClient.class), inClient, outClient); |
| clientListening = clientLauncher.startListening(); |
| |
| Logger logger = Logger.getLogger(StreamMessageProducer.class.getName()); |
| logLevel = logger.getLevel(); |
| logger.setLevel(Level.SEVERE); |
| } |
| |
| @After public void teardown() throws InterruptedException, ExecutionException { |
| clientListening.cancel(true); |
| serverListening.cancel(true); |
| Thread.sleep(10); |
| Logger logger = Logger.getLogger(StreamMessageProducer.class.getName()); |
| logger.setLevel(logLevel); |
| } |
| |
| } |