aboutsummaryrefslogtreecommitdiffstats
path: root/language-web/src
diff options
context:
space:
mode:
authorLibravatar Kristóf Marussy <kristof@marussy.com>2021-10-24 01:19:02 +0200
committerLibravatar Kristóf Marussy <kristof@marussy.com>2021-10-31 19:26:11 +0100
commitaa205509820b526bf21dfedebfd798ded0d763e7 (patch)
tree6ec0178cc7bb245e2790e9807ad3184fc0033805 /language-web/src
parentfeat(web): push precomputed service results (diff)
downloadrefinery-aa205509820b526bf21dfedebfd798ded0d763e7.tar.gz
refinery-aa205509820b526bf21dfedebfd798ded0d763e7.tar.zst
refinery-aa205509820b526bf21dfedebfd798ded0d763e7.zip
test(web): websockets fixes and tests
Diffstat (limited to 'language-web/src')
-rw-r--r--language-web/src/main/java/tools/refinery/language/web/ServerLauncher.java2
-rw-r--r--language-web/src/main/java/tools/refinery/language/web/xtext/server/push/PushWebDocumentAccess.java4
-rw-r--r--language-web/src/test/java/tools/refinery/language/web/ProblemWebInjectorProvider.java (renamed from language-web/src/test/java/tools/refinery/language/web/xtext/servlet/ProblemWebInjectorProvider.java)5
-rw-r--r--language-web/src/test/java/tools/refinery/language/web/ProblemWebSocketServletIntegrationTest.java172
-rw-r--r--language-web/src/test/java/tools/refinery/language/web/xtext/servlet/TransactionExecutorTest.java23
5 files changed, 199 insertions, 7 deletions
diff --git a/language-web/src/main/java/tools/refinery/language/web/ServerLauncher.java b/language-web/src/main/java/tools/refinery/language/web/ServerLauncher.java
index a71d8e93..cde7278f 100644
--- a/language-web/src/main/java/tools/refinery/language/web/ServerLauncher.java
+++ b/language-web/src/main/java/tools/refinery/language/web/ServerLauncher.java
@@ -18,6 +18,7 @@ import org.eclipse.jetty.servlet.DefaultServlet;
18import org.eclipse.jetty.servlet.ServletContextHandler; 18import org.eclipse.jetty.servlet.ServletContextHandler;
19import org.eclipse.jetty.servlet.ServletHolder; 19import org.eclipse.jetty.servlet.ServletHolder;
20import org.eclipse.jetty.util.resource.Resource; 20import org.eclipse.jetty.util.resource.Resource;
21import org.eclipse.jetty.websocket.server.config.JettyWebSocketServletContainerInitializer;
21import org.slf4j.Logger; 22import org.slf4j.Logger;
22import org.slf4j.LoggerFactory; 23import org.slf4j.LoggerFactory;
23 24
@@ -73,6 +74,7 @@ public class ServerLauncher {
73 allowedOriginsString); 74 allowedOriginsString);
74 } 75 }
75 handler.addServlet(problemServletHolder, "/xtext-service/*"); 76 handler.addServlet(problemServletHolder, "/xtext-service/*");
77 JettyWebSocketServletContainerInitializer.configure(handler, null);
76 } 78 }
77 79
78 private void addDefaultServlet(ServletContextHandler handler) { 80 private void addDefaultServlet(ServletContextHandler handler) {
diff --git a/language-web/src/main/java/tools/refinery/language/web/xtext/server/push/PushWebDocumentAccess.java b/language-web/src/main/java/tools/refinery/language/web/xtext/server/push/PushWebDocumentAccess.java
index ff4bb035..b3666a86 100644
--- a/language-web/src/main/java/tools/refinery/language/web/xtext/server/push/PushWebDocumentAccess.java
+++ b/language-web/src/main/java/tools/refinery/language/web/xtext/server/push/PushWebDocumentAccess.java
@@ -58,10 +58,10 @@ public class PushWebDocumentAccess extends XtextWebDocumentAccess {
58 58
59 protected String getPrecomputedServiceName(AbstractCachedService<? extends IServiceResult> service) { 59 protected String getPrecomputedServiceName(AbstractCachedService<? extends IServiceResult> service) {
60 if (service instanceof ValidationService) { 60 if (service instanceof ValidationService) {
61 return "validation"; 61 return "validate";
62 } 62 }
63 if (service instanceof HighlightingService) { 63 if (service instanceof HighlightingService) {
64 return "highlighting"; 64 return "highlight";
65 } 65 }
66 throw new IllegalArgumentException("Unknown precomputed service: " + service); 66 throw new IllegalArgumentException("Unknown precomputed service: " + service);
67 } 67 }
diff --git a/language-web/src/test/java/tools/refinery/language/web/xtext/servlet/ProblemWebInjectorProvider.java b/language-web/src/test/java/tools/refinery/language/web/ProblemWebInjectorProvider.java
index a6d97c8b..2db4590e 100644
--- a/language-web/src/test/java/tools/refinery/language/web/xtext/servlet/ProblemWebInjectorProvider.java
+++ b/language-web/src/test/java/tools/refinery/language/web/ProblemWebInjectorProvider.java
@@ -1,4 +1,4 @@
1package tools.refinery.language.web.xtext.servlet; 1package tools.refinery.language.web;
2 2
3import org.eclipse.xtext.ide.ExecutorServiceProvider; 3import org.eclipse.xtext.ide.ExecutorServiceProvider;
4import org.eclipse.xtext.util.DisposableRegistry; 4import org.eclipse.xtext.util.DisposableRegistry;
@@ -9,8 +9,7 @@ import com.google.inject.Injector;
9 9
10import tools.refinery.language.ide.ProblemIdeModule; 10import tools.refinery.language.ide.ProblemIdeModule;
11import tools.refinery.language.tests.ProblemInjectorProvider; 11import tools.refinery.language.tests.ProblemInjectorProvider;
12import tools.refinery.language.web.ProblemWebModule; 12import tools.refinery.language.web.xtext.servlet.AwaitTerminationExecutorServiceProvider;
13import tools.refinery.language.web.ProblemWebSetup;
14 13
15public class ProblemWebInjectorProvider extends ProblemInjectorProvider { 14public class ProblemWebInjectorProvider extends ProblemInjectorProvider {
16 15
diff --git a/language-web/src/test/java/tools/refinery/language/web/ProblemWebSocketServletIntegrationTest.java b/language-web/src/test/java/tools/refinery/language/web/ProblemWebSocketServletIntegrationTest.java
new file mode 100644
index 00000000..60581b5c
--- /dev/null
+++ b/language-web/src/test/java/tools/refinery/language/web/ProblemWebSocketServletIntegrationTest.java
@@ -0,0 +1,172 @@
1package tools.refinery.language.web;
2
3import static org.hamcrest.MatcherAssert.assertThat;
4import static org.hamcrest.Matchers.equalTo;
5import static org.hamcrest.Matchers.hasSize;
6import static org.hamcrest.Matchers.startsWith;
7import static org.junit.jupiter.api.Assertions.fail;
8
9import java.io.IOException;
10import java.net.InetSocketAddress;
11import java.net.URI;
12import java.time.Duration;
13import java.util.ArrayList;
14import java.util.List;
15
16import org.eclipse.jetty.server.Server;
17import org.eclipse.jetty.servlet.ServletContextHandler;
18import org.eclipse.jetty.websocket.api.Session;
19import org.eclipse.jetty.websocket.api.StatusCode;
20import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose;
21import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect;
22import org.eclipse.jetty.websocket.api.annotations.OnWebSocketError;
23import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage;
24import org.eclipse.jetty.websocket.api.annotations.WebSocket;
25import org.eclipse.jetty.websocket.client.ClientUpgradeRequest;
26import org.eclipse.jetty.websocket.client.WebSocketClient;
27import org.eclipse.jetty.websocket.server.config.JettyWebSocketServletContainerInitializer;
28import org.eclipse.xtext.testing.GlobalRegistries;
29import org.eclipse.xtext.testing.GlobalRegistries.GlobalStateMemento;
30import org.junit.jupiter.api.AfterEach;
31import org.junit.jupiter.api.BeforeEach;
32import org.junit.jupiter.api.Test;
33
34import tools.refinery.language.web.xtext.servlet.XtextWebSocketServlet;
35
36class ProblemWebSocketServletIntegrationTest {
37 private static int SERVER_PORT = 28080;
38
39 private static long TIMEOUT_MILLIS = Duration.ofSeconds(1).toMillis();
40
41 private GlobalStateMemento stateBeforeInjectorCreation;
42
43 private Server server;
44
45 private WebSocketClient client;
46
47 @BeforeEach
48 void startServer() throws Exception {
49 stateBeforeInjectorCreation = GlobalRegistries.makeCopyOfGlobalState();
50 server = new Server(new InetSocketAddress(SERVER_PORT));
51 var handler = new ServletContextHandler();
52 handler.addServlet(ProblemWebSocketServlet.class, "/xtext-service/*");
53 JettyWebSocketServletContainerInitializer.configure(handler, null);
54 server.setHandler(handler);
55 server.start();
56 client = new WebSocketClient();
57 client.start();
58 }
59
60 @AfterEach
61 void stopServer() throws Exception {
62 client.stop();
63 server.stop();
64 stateBeforeInjectorCreation.restoreGlobalState();
65 }
66
67 @Test
68 void updateTest() throws IOException {
69 var clientSocket = new UpdateTestClient();
70 var upgradeRequest = new ClientUpgradeRequest();
71 upgradeRequest.setSubProtocols(XtextWebSocketServlet.XTEXT_SUBPROTOCOL_V1);
72 var sessionFuture = client.connect(clientSocket, URI.create("ws://localhost:" + SERVER_PORT + "/xtext-service"),
73 upgradeRequest);
74 var session = sessionFuture.join();
75 assertThat(session.getUpgradeResponse().getAcceptedSubProtocol(),
76 equalTo(XtextWebSocketServlet.XTEXT_SUBPROTOCOL_V1));
77 clientSocket.waitForTestResult();
78 }
79
80 @WebSocket
81 public static class UpdateTestClient {
82 private boolean finished = false;
83
84 private Object lock = new Object();
85
86 private Throwable error;
87
88 private int closeStatusCode;
89
90 private String closeReason;
91
92 private List<String> responses = new ArrayList<>();
93
94 @OnWebSocketConnect
95 public void onConnect(Session session) {
96 try {
97 session.getRemote().sendString(
98 "{\"id\":\"foo\",\"request\":{\"resource\":\"test.problem\",\"serviceType\":\"update\",\"fullText\":\"class Person.\n\"}}");
99 } catch (IOException e) {
100 finishedWithError(e);
101 }
102 }
103
104 @OnWebSocketClose
105 public void onClose(int statusCode, String reason) {
106 closeStatusCode = statusCode;
107 closeReason = reason;
108 testFinished();
109 }
110
111 @OnWebSocketError
112 public void onError(Throwable error) {
113 finishedWithError(error);
114 }
115
116 @OnWebSocketMessage
117 public void onMessage(Session session, String message) {
118 try {
119 responses.add(message);
120 switch (responses.size()) {
121 case 3 -> session.getRemote().sendString(
122 "{\"id\":\"bar\",\"request\":{\"resource\":\"test.problem\",\"serviceType\":\"update\",\"requiredStateId\":\"-80000000\",\"deltaText\":\"class Car.\n\",\"deltaOffset\":\"0\",\"deltaReplaceLength\":\"0\"}}");
123 case 5 -> session.close();
124 }
125 } catch (IOException e) {
126 finishedWithError(e);
127 }
128 }
129
130 private void finishedWithError(Throwable t) {
131 error = t;
132 testFinished();
133 }
134
135 private void testFinished() {
136 synchronized (lock) {
137 finished = true;
138 lock.notify();
139 }
140 }
141
142 public void waitForTestResult() {
143 synchronized (lock) {
144 if (!finished) {
145 try {
146 lock.wait(TIMEOUT_MILLIS);
147 } catch (InterruptedException e) {
148 fail("Unexpected InterruptedException", e);
149 }
150 }
151 }
152 if (!finished) {
153 fail("Test still not finished after timeout");
154 }
155 if (error != null) {
156 fail("Unexpected exception in websocket thread", error);
157 }
158 if (closeStatusCode != StatusCode.NORMAL) {
159 fail("Abnormal close status " + closeStatusCode + ": " + closeReason);
160 }
161 assertThat(responses, hasSize(5));
162 assertThat(responses.get(0), equalTo("{\"id\":\"foo\",\"response\":{\"stateId\":\"-80000000\"}}"));
163 assertThat(responses.get(1), startsWith(
164 "{\"resource\":\"test.problem\",\"stateId\":\"-80000000\",\"service\":\"highlight\",\"push\":{\"regions\":["));
165 assertThat(responses.get(2), equalTo(
166 "{\"resource\":\"test.problem\",\"stateId\":\"-80000000\",\"service\":\"validate\",\"push\":{\"issues\":[]}}"));
167 assertThat(responses.get(3), equalTo("{\"id\":\"bar\",\"response\":{\"stateId\":\"-7fffffff\"}}"));
168 assertThat(responses.get(4), startsWith(
169 "{\"resource\":\"test.problem\",\"stateId\":\"-7fffffff\",\"service\":\"highlight\",\"push\":{\"regions\":["));
170 }
171 }
172}
diff --git a/language-web/src/test/java/tools/refinery/language/web/xtext/servlet/TransactionExecutorTest.java b/language-web/src/test/java/tools/refinery/language/web/xtext/servlet/TransactionExecutorTest.java
index 7f7c3e43..2d3f45d6 100644
--- a/language-web/src/test/java/tools/refinery/language/web/xtext/servlet/TransactionExecutorTest.java
+++ b/language-web/src/test/java/tools/refinery/language/web/xtext/servlet/TransactionExecutorTest.java
@@ -11,6 +11,7 @@ import static org.mockito.Mockito.verify;
11import java.util.List; 11import java.util.List;
12import java.util.Map; 12import java.util.Map;
13 13
14import org.eclipse.emf.common.util.URI;
14import org.eclipse.xtext.resource.IResourceServiceProvider; 15import org.eclipse.xtext.resource.IResourceServiceProvider;
15import org.eclipse.xtext.testing.InjectWith; 16import org.eclipse.xtext.testing.InjectWith;
16import org.eclipse.xtext.testing.extensions.InjectionExtension; 17import org.eclipse.xtext.testing.extensions.InjectionExtension;
@@ -25,6 +26,7 @@ import org.mockito.junit.jupiter.MockitoExtension;
25 26
26import com.google.inject.Inject; 27import com.google.inject.Inject;
27 28
29import tools.refinery.language.web.ProblemWebInjectorProvider;
28import tools.refinery.language.web.xtext.server.ResponseHandler; 30import tools.refinery.language.web.xtext.server.ResponseHandler;
29import tools.refinery.language.web.xtext.server.ResponseHandlerException; 31import tools.refinery.language.web.xtext.server.ResponseHandlerException;
30import tools.refinery.language.web.xtext.server.TransactionExecutor; 32import tools.refinery.language.web.xtext.server.TransactionExecutor;
@@ -38,6 +40,8 @@ import tools.refinery.language.web.xtext.server.message.XtextWebResponse;
38class TransactionExecutorTest { 40class TransactionExecutorTest {
39 private static final String RESOURCE_NAME = "test.problem"; 41 private static final String RESOURCE_NAME = "test.problem";
40 42
43 private static final String PROBLEM_CONTENT_TYPE = "application/x-tools.refinery.problem";
44
41 private static final String TEST_PROBLEM = """ 45 private static final String TEST_PROBLEM = """
42 class Person { 46 class Person {
43 Person[0..*] friend opposite friend 47 Person[0..*] friend opposite friend
@@ -95,6 +99,21 @@ class TransactionExecutorTest {
95 assertHighlightingResponse(newStateId, captor.getAllValues().get(1)); 99 assertHighlightingResponse(newStateId, captor.getAllValues().get(1));
96 } 100 }
97 101
102 @Test
103 void fullTextWithoutResourceTest() throws ResponseHandlerException {
104 var resourceServiceProvider = resourceServiceProviderRegistry
105 .getResourceServiceProvider(URI.createFileURI(RESOURCE_NAME));
106 resourceServiceProviderRegistry.getContentTypeToFactoryMap().put(PROBLEM_CONTENT_TYPE, resourceServiceProvider);
107 var responseHandler = sendRequestAndWaitForAllResponses(new XtextWebRequest("foo",
108 Map.of("contentType", PROBLEM_CONTENT_TYPE, "fullText", TEST_PROBLEM, "serviceType", "validate")));
109
110 var captor = newCaptor();
111 verify(responseHandler).onResponse(captor.capture());
112 var response = captor.getValue();
113 assertThat(response, hasProperty("id", equalTo("foo")));
114 assertThat(response, hasProperty("responseData", instanceOf(ValidationResult.class)));
115 }
116
98 private ArgumentCaptor<XtextWebResponse> newCaptor() { 117 private ArgumentCaptor<XtextWebResponse> newCaptor() {
99 return ArgumentCaptor.forClass(XtextWebResponse.class); 118 return ArgumentCaptor.forClass(XtextWebResponse.class);
100 } 119 }
@@ -132,14 +151,14 @@ class TransactionExecutorTest {
132 private void assertHighlightingResponse(String stateId, XtextWebResponse highlightingResponse) { 151 private void assertHighlightingResponse(String stateId, XtextWebResponse highlightingResponse) {
133 assertThat(highlightingResponse, hasProperty("resourceId", equalTo(RESOURCE_NAME))); 152 assertThat(highlightingResponse, hasProperty("resourceId", equalTo(RESOURCE_NAME)));
134 assertThat(highlightingResponse, hasProperty("stateId", equalTo(stateId))); 153 assertThat(highlightingResponse, hasProperty("stateId", equalTo(stateId)));
135 assertThat(highlightingResponse, hasProperty("service", equalTo("highlighting"))); 154 assertThat(highlightingResponse, hasProperty("service", equalTo("highlight")));
136 assertThat(highlightingResponse, hasProperty("pushData", instanceOf(HighlightingResult.class))); 155 assertThat(highlightingResponse, hasProperty("pushData", instanceOf(HighlightingResult.class)));
137 } 156 }
138 157
139 private void assertValidationResponse(String stateId, XtextWebResponse validationResponse) { 158 private void assertValidationResponse(String stateId, XtextWebResponse validationResponse) {
140 assertThat(validationResponse, hasProperty("resourceId", equalTo(RESOURCE_NAME))); 159 assertThat(validationResponse, hasProperty("resourceId", equalTo(RESOURCE_NAME)));
141 assertThat(validationResponse, hasProperty("stateId", equalTo(stateId))); 160 assertThat(validationResponse, hasProperty("stateId", equalTo(stateId)));
142 assertThat(validationResponse, hasProperty("service", equalTo("validation"))); 161 assertThat(validationResponse, hasProperty("service", equalTo("validate")));
143 assertThat(validationResponse, hasProperty("pushData", instanceOf(ValidationResult.class))); 162 assertThat(validationResponse, hasProperty("pushData", instanceOf(ValidationResult.class)));
144 } 163 }
145} 164}