package org.graalvm.tools.lsp.test.server;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.Context.Builder;
import org.graalvm.tools.lsp.server.ContextAwareExecutor;
import org.graalvm.tools.lsp.exceptions.DiagnosticsNotification;
import org.graalvm.tools.lsp.instrument.EnvironmentProvider;
import org.graalvm.tools.lsp.server.LSPFileSystem;
import org.graalvm.tools.lsp.server.TruffleAdapter;
import org.graalvm.tools.lsp.server.types.Range;
import org.graalvm.polyglot.Engine;
import org.graalvm.polyglot.Instrument;
import org.junit.After;
import org.junit.Before;
public abstract class TruffleLSPTest {
protected static final String PROG_OBJ = "" +
"function main() {\n" +
" abc();\n" +
" x = abc();\n" +
"}\n" +
"\n" +
"function abc() {\n" +
" obj = new();\n" +
" obj.p = 1;\n" +
" return obj;\n" +
"}\n";
protected static final String PROG_OBJ_NOT_CALLED = "" +
"function main() {\n" +
" x = abc();\n" +
" return x;\n" +
"}\n" +
"\n" +
"function abc() {\n" +
" obj = new();\n" +
" obj.p = 1;\n" +
" return obj;\n" +
"}\n" +
"\n" +
"function notCalled() {\n" +
" abc();\n" +
" return abc();\n" +
"}\n";
protected Engine engine;
protected TruffleAdapter truffleAdapter;
protected Context context;
@Before
public void setup() {
engine = Engine.newBuilder().allowExperimentalOptions(true).build();
Instrument instrument = engine.getInstruments().get("lsp");
EnvironmentProvider envProvider = instrument.lookup(EnvironmentProvider.class);
truffleAdapter = new TruffleAdapter(envProvider.getEnvironment(), true);
Builder contextBuilder = Context.newBuilder();
contextBuilder.allowAllAccess(true);
contextBuilder.fileSystem(LSPFileSystem.newReadOnlyFileSystem(truffleAdapter));
contextBuilder.engine(engine);
context = contextBuilder.build();
context.enter();
ContextAwareExecutor executorWrapper = new ContextAwareExecutor() {
@Override
public <T> Future<T> executeWithDefaultContext(Callable<T> taskWithResult) {
try {
return CompletableFuture.completedFuture(taskWithResult.call());
} catch (Exception e) {
CompletableFuture<T> cf = new CompletableFuture<>();
cf.completeExceptionally(e);
return cf;
}
}
@Override
public <T> Future<T> executeWithNestedContext(Callable<T> taskWithResult, boolean cached) {
try (Context newContext = contextBuilder.build()) {
newContext.enter();
newContext.initialize("sl");
try {
return CompletableFuture.completedFuture(taskWithResult.call());
} catch (Exception e) {
CompletableFuture<T> cf = new CompletableFuture<>();
cf.completeExceptionally(e);
return cf;
} finally {
newContext.leave();
}
}
}
@Override
public <T> Future<T> executeWithNestedContext(Callable<T> taskWithResult, int timeoutMillis, Callable<T> onTimeoutTask) {
if (timeoutMillis <= 0) {
return executeWithNestedContext(taskWithResult, false);
} else {
CompletableFuture<Future<T>> future = CompletableFuture.supplyAsync(() -> executeWithNestedContext(taskWithResult, false));
try {
return future.get(timeoutMillis, TimeUnit.MILLISECONDS);
} catch (TimeoutException e) {
future.cancel(true);
try {
return CompletableFuture.completedFuture(onTimeoutTask.call());
} catch (Exception timeoutTaskException) {
CompletableFuture<T> cf = new CompletableFuture<>();
cf.completeExceptionally(timeoutTaskException);
return cf;
}
} catch (InterruptedException | ExecutionException e) {
CompletableFuture<T> cf = new CompletableFuture<>();
cf.completeExceptionally(e);
return cf;
}
}
}
@Override
public void shutdown() {
}
@Override
public void resetContextCache() {
}
};
truffleAdapter.register(envProvider.getEnvironment(), executorWrapper);
}
@After
public void tearDown() {
context.leave();
context.close();
}
public URI createDummyFileUriForSL() {
try {
File dummy = File.createTempFile("truffle-lsp-test-file-", ".sl");
dummy.deleteOnExit();
return dummy.getCanonicalFile().toPath().toUri();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
protected DiagnosticsNotification getDiagnosticsNotification(ExecutionException e) {
if (e.getCause() instanceof DiagnosticsNotification) {
return (DiagnosticsNotification) e.getCause();
} else {
throw new RuntimeException(e);
}
}
protected boolean rangeCheck(Range orig, Range range) {
return rangeCheck(orig.getStart().getLine(), orig.getStart().getCharacter(), orig.getEnd().getLine(), orig.getEnd().getCharacter(), range);
}
protected boolean rangeCheck(int startLine, int startColumn, int endLine, int endColumn, Range range) {
return startLine == range.getStart().getLine() && startColumn == range.getStart().getCharacter() && endLine == range.getEnd().getLine() && endColumn == range.getEnd().getCharacter();
}
}