From f2bf9960e8af386f246acb44ee958e70d9b47210 Mon Sep 17 00:00:00 2001 From: Jonah Graham Date: Tue, 2 May 2023 11:26:25 -0400 Subject: [PATCH] Void return types need to always be null value Void type in Java cannot be instantiated, therefore if a type is declared as Void the fromJson should always set that value to null. If you want to have a generic return type that allows non-null and null results, use a type other than Void as the return type. Fixes #721 --- .../debug/test/DebugIntegrationTest.java | 45 +++++++++++++++++++ .../json/DebugMessageJsonHandlerTest.java | 34 +++++--------- .../json/adapters/MessageTypeAdapter.java | 2 +- .../lsp4j/jsonrpc/test/IntegrationTest.java | 45 +++++++++++++++++++ 4 files changed, 101 insertions(+), 25 deletions(-) diff --git a/org.eclipse.lsp4j.jsonrpc.debug/src/test/java/org/eclipse/lsp4j/jsonrpc/debug/test/DebugIntegrationTest.java b/org.eclipse.lsp4j.jsonrpc.debug/src/test/java/org/eclipse/lsp4j/jsonrpc/debug/test/DebugIntegrationTest.java index 9e2ac51e..4d4fd190 100644 --- a/org.eclipse.lsp4j.jsonrpc.debug/src/test/java/org/eclipse/lsp4j/jsonrpc/debug/test/DebugIntegrationTest.java +++ b/org.eclipse.lsp4j.jsonrpc.debug/src/test/java/org/eclipse/lsp4j/jsonrpc/debug/test/DebugIntegrationTest.java @@ -56,6 +56,12 @@ public static interface MyServer { CompletableFuture askServer(MyParam param); } + + public static interface MyVoidServer { + @JsonRequest + CompletableFuture askServer(MyParam param); + } + public static interface MyClient { @JsonRequest CompletableFuture askClient(MyParam param); @@ -99,6 +105,45 @@ public CompletableFuture askServer(MyParam param) { Assert.assertEquals("BAR", barFuture.get(TIMEOUT, TimeUnit.MILLISECONDS).value); } + @Test + public void testVoidResponse() throws Exception { + // create client side + PipedInputStream in = new PipedInputStream(); + PipedOutputStream out = new PipedOutputStream(); + PipedInputStream in2 = new PipedInputStream(); + PipedOutputStream out2 = new PipedOutputStream(); + + in.connect(out2); + out.connect(in2); + + MyClient client = new MyClient() { + @Override + public CompletableFuture askClient(MyParam param) { + throw new UnsupportedOperationException("Unused by this test"); + } + }; + Launcher clientSideLauncher = DebugLauncher.createLauncher(client, MyVoidServer.class, in, out); + + // create server side + MyServer server = new MyServer() { + @Override + public CompletableFuture askServer(MyParam param) { + return CompletableFuture.completedFuture(param); + } + }; + Launcher serverSideLauncher = DebugLauncher.createLauncher(server, MyClient.class, in2, out2); + + clientSideLauncher.startListening(); + serverSideLauncher.startListening(); + + // We call a method that is declared as returning Void, but the other end returns a non-null value + // make sure that the json parsing discards that result + CompletableFuture fooFuture = clientSideLauncher.getRemoteProxy().askServer(new MyParam("FOO")); + + Void void1 = fooFuture.get(TIMEOUT, TimeUnit.MILLISECONDS); + Assert.assertNull(void1); + } + @Test public void testCancellation() throws Exception { // create client side diff --git a/org.eclipse.lsp4j.jsonrpc.debug/src/test/java/org/eclipse/lsp4j/jsonrpc/debug/test/json/DebugMessageJsonHandlerTest.java b/org.eclipse.lsp4j.jsonrpc.debug/src/test/java/org/eclipse/lsp4j/jsonrpc/debug/test/json/DebugMessageJsonHandlerTest.java index fc602002..a3b75650 100644 --- a/org.eclipse.lsp4j.jsonrpc.debug/src/test/java/org/eclipse/lsp4j/jsonrpc/debug/test/json/DebugMessageJsonHandlerTest.java +++ b/org.eclipse.lsp4j.jsonrpc.debug/src/test/java/org/eclipse/lsp4j/jsonrpc/debug/test/json/DebugMessageJsonHandlerTest.java @@ -850,7 +850,7 @@ public void testParseSyntaxErrorRequest() { } } - private Object voidResponse(String body) { + private void voidResponse(String body) { Map supportedMethods = new LinkedHashMap<>(); supportedMethods.put("foo", JsonRpcMethod.request("foo", new TypeToken() { }.getType(), new TypeToken() { @@ -870,56 +870,42 @@ private Object voidResponse(String body) { + "\"success\":true"// + bodyField // + "}\n"); - return ((ResponseMessage) message).getResult(); + Object result = ((ResponseMessage) message).getResult(); + Assert.assertNull(result); } @Test public void testVoidResponse_noBody() { - Assert.assertNull(voidResponse(null)); + voidResponse(null); } @Test public void testVoidResponse_null() { - Assert.assertNull(voidResponse("null")); - + voidResponse("null"); } @Test public void testVoidResponse_primitive() { - Object result = voidResponse("true"); - JsonPrimitive jsonPrimitive = (JsonPrimitive) result; - Assert.assertTrue(jsonPrimitive.getAsBoolean()); + voidResponse("true"); } @Test public void testVoidResponse_emptyArray() { - Object result = voidResponse("[]"); - JsonArray jsonArray = (JsonArray) result; - Assert.assertTrue(jsonArray.isEmpty()); + voidResponse("[]"); } @Test public void testVoidResponse_array() { - Object result = voidResponse("[1,2,3]"); - JsonArray jsonArray = (JsonArray) result; - Assert.assertEquals(1, jsonArray.get(0).getAsInt()); - Assert.assertEquals(2, jsonArray.get(1).getAsInt()); - Assert.assertEquals(3, jsonArray.get(2).getAsInt()); + voidResponse("[1,2,3]"); } @Test public void testVoidResponse_emptyObject() { - Object result = voidResponse("{}"); - Assert.assertTrue(result instanceof JsonObject); - JsonObject jsonObject = (JsonObject) result; - Assert.assertTrue(jsonObject.entrySet().isEmpty()); + voidResponse("{}"); } @Test public void testVoidResponse_object() { - Object result = voidResponse("{\"allThreadsContinued\":false}"); - Assert.assertTrue(result instanceof JsonObject); - JsonObject jsonObject = (JsonObject) result; - Assert.assertFalse(jsonObject.getAsJsonPrimitive("allThreadsContinued").getAsBoolean()); + voidResponse("{\"allThreadsContinued\":false}"); } } diff --git a/org.eclipse.lsp4j.jsonrpc/src/main/java/org/eclipse/lsp4j/jsonrpc/json/adapters/MessageTypeAdapter.java b/org.eclipse.lsp4j.jsonrpc/src/main/java/org/eclipse/lsp4j/jsonrpc/json/adapters/MessageTypeAdapter.java index 296e9259..00375aad 100644 --- a/org.eclipse.lsp4j.jsonrpc/src/main/java/org/eclipse/lsp4j/jsonrpc/json/adapters/MessageTypeAdapter.java +++ b/org.eclipse.lsp4j.jsonrpc/src/main/java/org/eclipse/lsp4j/jsonrpc/json/adapters/MessageTypeAdapter.java @@ -350,7 +350,7 @@ protected Object fromJson(JsonElement element, Type type) { return null; } if (isNullOrVoidType(type)) { - return element; + return null; } Object value = gson.fromJson(element, type); if (isNull(value)) { diff --git a/org.eclipse.lsp4j.jsonrpc/src/test/java/org/eclipse/lsp4j/jsonrpc/test/IntegrationTest.java b/org.eclipse.lsp4j.jsonrpc/src/test/java/org/eclipse/lsp4j/jsonrpc/test/IntegrationTest.java index 7a53ff4b..d7ba4db7 100644 --- a/org.eclipse.lsp4j.jsonrpc/src/test/java/org/eclipse/lsp4j/jsonrpc/test/IntegrationTest.java +++ b/org.eclipse.lsp4j.jsonrpc/src/test/java/org/eclipse/lsp4j/jsonrpc/test/IntegrationTest.java @@ -31,6 +31,7 @@ import org.eclipse.lsp4j.jsonrpc.Launcher; import org.eclipse.lsp4j.jsonrpc.RemoteEndpoint; import org.eclipse.lsp4j.jsonrpc.ResponseErrorException; + import org.eclipse.lsp4j.jsonrpc.json.StreamMessageProducer; import org.eclipse.lsp4j.jsonrpc.messages.Either; import org.eclipse.lsp4j.jsonrpc.services.GenericEndpoint; @@ -88,6 +89,11 @@ public static interface MyServer { @JsonRequest CompletableFuture askServer(MyParam param); } + + public static interface MyVoidServer { + @JsonRequest + CompletableFuture askServer(MyParam param); + } public static class MyServerImpl implements MyServer { @Override @@ -224,6 +230,45 @@ public void testEitherNull() throws Exception { out.toString()); } + @Test + public void testVoidResponse() throws Exception { + // create client side + PipedInputStream in = new PipedInputStream(); + PipedOutputStream out = new PipedOutputStream(); + PipedInputStream in2 = new PipedInputStream(); + PipedOutputStream out2 = new PipedOutputStream(); + + in.connect(out2); + out.connect(in2); + + MyClient client = new MyClient() { + @Override + public CompletableFuture askClient(MyParam param) { + throw new UnsupportedOperationException("Unused by this test"); + } + }; + Launcher clientSideLauncher = Launcher.createLauncher(client, MyVoidServer.class, in, out); + + // create server side + MyServer server = new MyServer() { + @Override + public CompletableFuture askServer(MyParam param) { + return CompletableFuture.completedFuture(param); + } + }; + Launcher serverSideLauncher = Launcher.createLauncher(server, MyClient.class, in2, out2); + + clientSideLauncher.startListening(); + serverSideLauncher.startListening(); + + // We call a method that is declared as returning Void, but the other end returns a non-null value + // make sure that the json parsing discards that result + CompletableFuture fooFuture = clientSideLauncher.getRemoteProxy().askServer(new MyParam("FOO")); + + Void void1 = fooFuture.get(TIMEOUT, TimeUnit.MILLISECONDS); + Assert.assertNull(void1); + } + @Test public void testCancellation() throws Exception {