diff options
author | Kristóf Marussy <kristof@marussy.com> | 2021-10-20 01:49:14 +0200 |
---|---|---|
committer | Kristóf Marussy <kristof@marussy.com> | 2021-10-31 19:26:11 +0100 |
commit | 7732fedb5933bdc699cd1ef22a766397d5a701d2 (patch) | |
tree | 691b0461ee763c0060f901f306a7fd99d756fcce /language-web/src/test | |
parent | feat(web): batched xtext websocket prototype (diff) | |
download | refinery-7732fedb5933bdc699cd1ef22a766397d5a701d2.tar.gz refinery-7732fedb5933bdc699cd1ef22a766397d5a701d2.tar.zst refinery-7732fedb5933bdc699cd1ef22a766397d5a701d2.zip |
feat(web): push precomputed service results
Diffstat (limited to 'language-web/src/test')
4 files changed, 198 insertions, 87 deletions
diff --git a/language-web/src/test/java/tools/refinery/language/web/xtext/servlet/AwaitTerminationExecutorServiceProvider.java b/language-web/src/test/java/tools/refinery/language/web/xtext/servlet/AwaitTerminationExecutorServiceProvider.java index 08230335..25bcec37 100644 --- a/language-web/src/test/java/tools/refinery/language/web/xtext/servlet/AwaitTerminationExecutorServiceProvider.java +++ b/language-web/src/test/java/tools/refinery/language/web/xtext/servlet/AwaitTerminationExecutorServiceProvider.java | |||
@@ -3,7 +3,6 @@ package tools.refinery.language.web.xtext.servlet; | |||
3 | import java.util.ArrayList; | 3 | import java.util.ArrayList; |
4 | import java.util.List; | 4 | import java.util.List; |
5 | import java.util.concurrent.ExecutorService; | 5 | import java.util.concurrent.ExecutorService; |
6 | import java.util.concurrent.TimeUnit; | ||
7 | 6 | ||
8 | import org.eclipse.xtext.ide.ExecutorServiceProvider; | 7 | import org.eclipse.xtext.ide.ExecutorServiceProvider; |
9 | 8 | ||
@@ -11,24 +10,33 @@ import com.google.inject.Singleton; | |||
11 | 10 | ||
12 | @Singleton | 11 | @Singleton |
13 | public class AwaitTerminationExecutorServiceProvider extends ExecutorServiceProvider { | 12 | public class AwaitTerminationExecutorServiceProvider extends ExecutorServiceProvider { |
14 | private List<ExecutorService> servicesToShutDown = new ArrayList<>(); | 13 | private List<RestartableCachedThreadPool> servicesToShutDown = new ArrayList<>(); |
15 | 14 | ||
16 | @Override | 15 | @Override |
17 | protected ExecutorService createInstance(String key) { | 16 | protected ExecutorService createInstance(String key) { |
18 | var instance = super.createInstance(key); | 17 | var instance = new RestartableCachedThreadPool(); |
19 | servicesToShutDown.add(instance); | 18 | synchronized (servicesToShutDown) { |
19 | servicesToShutDown.add(instance); | ||
20 | } | ||
20 | return instance; | 21 | return instance; |
21 | } | 22 | } |
22 | 23 | ||
24 | public void waitForAllTasksToFinish() { | ||
25 | synchronized (servicesToShutDown) { | ||
26 | for (var executorService : servicesToShutDown) { | ||
27 | executorService.waitForAllTasksToFinish(); | ||
28 | } | ||
29 | } | ||
30 | } | ||
31 | |||
23 | @Override | 32 | @Override |
24 | public void dispose() { | 33 | public void dispose() { |
25 | super.dispose(); | 34 | super.dispose(); |
26 | for (var executorService : servicesToShutDown) { | 35 | synchronized (servicesToShutDown) { |
27 | try { | 36 | for (var executorService : servicesToShutDown) { |
28 | executorService.awaitTermination(1, TimeUnit.SECONDS); | 37 | executorService.waitForTermination(); |
29 | } catch (InterruptedException e) { | ||
30 | // Continue normally. | ||
31 | } | 38 | } |
39 | servicesToShutDown.clear(); | ||
32 | } | 40 | } |
33 | } | 41 | } |
34 | } | 42 | } |
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/xtext/servlet/ProblemWebInjectorProvider.java index 3493c9eb..a6d97c8b 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/xtext/servlet/ProblemWebInjectorProvider.java | |||
@@ -26,7 +26,7 @@ public class ProblemWebInjectorProvider extends ProblemInjectorProvider { | |||
26 | 26 | ||
27 | protected ProblemWebModule createWebModule() { | 27 | protected ProblemWebModule createWebModule() { |
28 | // Await termination of the executor service to avoid race conditions between | 28 | // Await termination of the executor service to avoid race conditions between |
29 | // between the tasks in the service and the {@link | 29 | // the tasks in the service and the {@link |
30 | // org.eclipse.xtext.testing.extensions.InjectionExtension}. | 30 | // org.eclipse.xtext.testing.extensions.InjectionExtension}. |
31 | return new ProblemWebModule() { | 31 | return new ProblemWebModule() { |
32 | @SuppressWarnings("unused") | 32 | @SuppressWarnings("unused") |
diff --git a/language-web/src/test/java/tools/refinery/language/web/xtext/servlet/RestartableCachedThreadPool.java b/language-web/src/test/java/tools/refinery/language/web/xtext/servlet/RestartableCachedThreadPool.java new file mode 100644 index 00000000..02ef38e2 --- /dev/null +++ b/language-web/src/test/java/tools/refinery/language/web/xtext/servlet/RestartableCachedThreadPool.java | |||
@@ -0,0 +1,109 @@ | |||
1 | package tools.refinery.language.web.xtext.servlet; | ||
2 | |||
3 | import java.util.Collection; | ||
4 | import java.util.List; | ||
5 | import java.util.concurrent.Callable; | ||
6 | import java.util.concurrent.ExecutionException; | ||
7 | import java.util.concurrent.ExecutorService; | ||
8 | import java.util.concurrent.Executors; | ||
9 | import java.util.concurrent.Future; | ||
10 | import java.util.concurrent.TimeUnit; | ||
11 | import java.util.concurrent.TimeoutException; | ||
12 | |||
13 | import org.slf4j.Logger; | ||
14 | import org.slf4j.LoggerFactory; | ||
15 | |||
16 | public class RestartableCachedThreadPool implements ExecutorService { | ||
17 | private static final Logger LOG = LoggerFactory.getLogger(RestartableCachedThreadPool.class); | ||
18 | |||
19 | private ExecutorService delegate; | ||
20 | |||
21 | public RestartableCachedThreadPool() { | ||
22 | delegate = createExecutorService(); | ||
23 | } | ||
24 | |||
25 | public void waitForAllTasksToFinish() { | ||
26 | delegate.shutdown(); | ||
27 | waitForTermination(); | ||
28 | delegate = createExecutorService(); | ||
29 | } | ||
30 | |||
31 | public void waitForTermination() { | ||
32 | try { | ||
33 | delegate.awaitTermination(1, TimeUnit.SECONDS); | ||
34 | } catch (InterruptedException e) { | ||
35 | LOG.warn("Interrupted while waiting for delegate executor to stop", e); | ||
36 | } | ||
37 | } | ||
38 | |||
39 | protected ExecutorService createExecutorService() { | ||
40 | return Executors.newCachedThreadPool(); | ||
41 | } | ||
42 | |||
43 | @Override | ||
44 | public boolean awaitTermination(long arg0, TimeUnit arg1) throws InterruptedException { | ||
45 | return delegate.awaitTermination(arg0, arg1); | ||
46 | } | ||
47 | |||
48 | @Override | ||
49 | public void execute(Runnable arg0) { | ||
50 | delegate.execute(arg0); | ||
51 | } | ||
52 | |||
53 | @Override | ||
54 | public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> arg0, long arg1, TimeUnit arg2) | ||
55 | throws InterruptedException { | ||
56 | return delegate.invokeAll(arg0, arg1, arg2); | ||
57 | } | ||
58 | |||
59 | @Override | ||
60 | public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> arg0) throws InterruptedException { | ||
61 | return delegate.invokeAll(arg0); | ||
62 | } | ||
63 | |||
64 | @Override | ||
65 | public <T> T invokeAny(Collection<? extends Callable<T>> arg0, long arg1, TimeUnit arg2) | ||
66 | throws InterruptedException, ExecutionException, TimeoutException { | ||
67 | return delegate.invokeAny(arg0, arg1, arg2); | ||
68 | } | ||
69 | |||
70 | @Override | ||
71 | public <T> T invokeAny(Collection<? extends Callable<T>> arg0) throws InterruptedException, ExecutionException { | ||
72 | return delegate.invokeAny(arg0); | ||
73 | } | ||
74 | |||
75 | @Override | ||
76 | public boolean isShutdown() { | ||
77 | return delegate.isShutdown(); | ||
78 | } | ||
79 | |||
80 | @Override | ||
81 | public boolean isTerminated() { | ||
82 | return delegate.isTerminated(); | ||
83 | } | ||
84 | |||
85 | @Override | ||
86 | public void shutdown() { | ||
87 | delegate.shutdown(); | ||
88 | } | ||
89 | |||
90 | @Override | ||
91 | public List<Runnable> shutdownNow() { | ||
92 | return delegate.shutdownNow(); | ||
93 | } | ||
94 | |||
95 | @Override | ||
96 | public <T> Future<T> submit(Callable<T> arg0) { | ||
97 | return delegate.submit(arg0); | ||
98 | } | ||
99 | |||
100 | @Override | ||
101 | public <T> Future<T> submit(Runnable arg0, T arg1) { | ||
102 | return delegate.submit(arg0, arg1); | ||
103 | } | ||
104 | |||
105 | @Override | ||
106 | public Future<?> submit(Runnable arg0) { | ||
107 | return delegate.submit(arg0); | ||
108 | } | ||
109 | } | ||
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 6ad82d7f..7f7c3e43 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 | |||
@@ -4,23 +4,19 @@ import static org.hamcrest.MatcherAssert.assertThat; | |||
4 | import static org.hamcrest.Matchers.equalTo; | 4 | import static org.hamcrest.Matchers.equalTo; |
5 | import static org.hamcrest.Matchers.hasProperty; | 5 | import static org.hamcrest.Matchers.hasProperty; |
6 | import static org.hamcrest.Matchers.instanceOf; | 6 | import static org.hamcrest.Matchers.instanceOf; |
7 | import static org.junit.jupiter.api.Assertions.fail; | ||
8 | import static org.mockito.Mockito.mock; | 7 | import static org.mockito.Mockito.mock; |
9 | import static org.mockito.Mockito.times; | 8 | import static org.mockito.Mockito.times; |
10 | import static org.mockito.Mockito.verify; | 9 | import static org.mockito.Mockito.verify; |
11 | 10 | ||
12 | import java.io.IOException; | ||
13 | import java.util.List; | 11 | import java.util.List; |
14 | import java.util.Map; | 12 | import java.util.Map; |
15 | import java.util.UUID; | ||
16 | 13 | ||
17 | import org.eclipse.xtext.resource.IResourceServiceProvider; | 14 | import org.eclipse.xtext.resource.IResourceServiceProvider; |
18 | import org.eclipse.xtext.testing.InjectWith; | 15 | import org.eclipse.xtext.testing.InjectWith; |
19 | import org.eclipse.xtext.testing.extensions.InjectionExtension; | 16 | import org.eclipse.xtext.testing.extensions.InjectionExtension; |
20 | import org.eclipse.xtext.web.server.ServiceConflictResult; | ||
21 | import org.eclipse.xtext.web.server.model.DocumentStateResult; | 17 | import org.eclipse.xtext.web.server.model.DocumentStateResult; |
18 | import org.eclipse.xtext.web.server.syntaxcoloring.HighlightingResult; | ||
22 | import org.eclipse.xtext.web.server.validation.ValidationResult; | 19 | import org.eclipse.xtext.web.server.validation.ValidationResult; |
23 | import org.hamcrest.Matcher; | ||
24 | import org.junit.jupiter.api.BeforeEach; | 20 | import org.junit.jupiter.api.BeforeEach; |
25 | import org.junit.jupiter.api.Test; | 21 | import org.junit.jupiter.api.Test; |
26 | import org.junit.jupiter.api.extension.ExtendWith; | 22 | import org.junit.jupiter.api.extension.ExtendWith; |
@@ -29,123 +25,121 @@ import org.mockito.junit.jupiter.MockitoExtension; | |||
29 | 25 | ||
30 | import com.google.inject.Inject; | 26 | import com.google.inject.Inject; |
31 | 27 | ||
28 | import tools.refinery.language.web.xtext.server.ResponseHandler; | ||
29 | import tools.refinery.language.web.xtext.server.ResponseHandlerException; | ||
30 | import tools.refinery.language.web.xtext.server.TransactionExecutor; | ||
31 | import tools.refinery.language.web.xtext.server.message.XtextWebOkResponse; | ||
32 | import tools.refinery.language.web.xtext.server.message.XtextWebRequest; | ||
33 | import tools.refinery.language.web.xtext.server.message.XtextWebResponse; | ||
34 | |||
32 | @ExtendWith(MockitoExtension.class) | 35 | @ExtendWith(MockitoExtension.class) |
33 | @ExtendWith(InjectionExtension.class) | 36 | @ExtendWith(InjectionExtension.class) |
34 | @InjectWith(ProblemWebInjectorProvider.class) | 37 | @InjectWith(ProblemWebInjectorProvider.class) |
35 | class TransactionExecutorTest { | 38 | class TransactionExecutorTest { |
36 | private static final String RESOURCE_NAME = "test.problem"; | 39 | private static final String RESOURCE_NAME = "test.problem"; |
37 | 40 | ||
38 | private static final String INVALID_STATE_ID = "<invalid_state>"; | ||
39 | |||
40 | private static final String TEST_PROBLEM = """ | 41 | private static final String TEST_PROBLEM = """ |
41 | class Person { | 42 | class Person { |
42 | Person friend[0..*] opposite friend | 43 | Person[0..*] friend opposite friend |
43 | } | 44 | } |
44 | 45 | ||
45 | friend(a, b). | 46 | friend(a, b). |
46 | """; | 47 | """; |
47 | 48 | ||
48 | private static final Map<String, String> UPDATE_FULL_TEXT_PARAMS = Map.of("serviceType", "update", "fullText", | 49 | private static final Map<String, String> UPDATE_FULL_TEXT_PARAMS = Map.of("resource", RESOURCE_NAME, "serviceType", |
49 | TEST_PROBLEM); | 50 | "update", "fullText", TEST_PROBLEM); |
50 | |||
51 | private static final Map<String, String> VALIDATE_PARAMS = Map.of("serviceType", "validate"); | ||
52 | 51 | ||
53 | @Inject | 52 | @Inject |
54 | private IResourceServiceProvider.Registry resourceServiceProviderRegistry; | 53 | private IResourceServiceProvider.Registry resourceServiceProviderRegistry; |
55 | 54 | ||
55 | @Inject | ||
56 | private AwaitTerminationExecutorServiceProvider executorServices; | ||
57 | |||
56 | private TransactionExecutor transactionExecutor; | 58 | private TransactionExecutor transactionExecutor; |
57 | 59 | ||
58 | @BeforeEach | 60 | @BeforeEach |
59 | void beforeEach() { | 61 | void beforeEach() { |
60 | transactionExecutor = new TransactionExecutor(new SimpleSession(), resourceServiceProviderRegistry); | 62 | transactionExecutor = new TransactionExecutor(new SimpleSession(), resourceServiceProviderRegistry); |
61 | } | 63 | } |
62 | |||
63 | @Test | ||
64 | void emptyBatchTest() { | ||
65 | performBatchRequest(null); | ||
66 | } | ||
67 | 64 | ||
68 | @Test | 65 | @Test |
69 | void fullTextUpdateTest() { | 66 | void updateFullTextTest() throws ResponseHandlerException { |
70 | var response = performSingleRequest(null, UPDATE_FULL_TEXT_PARAMS); | 67 | var captor = newCaptor(); |
71 | assertThat(response, hasResponseData(instanceOf(DocumentStateResult.class))); | 68 | var stateId = updateFullText(captor); |
69 | assertThatPrecomputedMessagesAreReceived(stateId, captor.getAllValues()); | ||
72 | } | 70 | } |
73 | 71 | ||
74 | @Test | 72 | @Test |
75 | void validationAfterFullTextUpdateInSameBatchTest() { | 73 | void updateDeltaTextHighlightAndValidationChange() throws ResponseHandlerException { |
76 | var response = performBatchRequest(null, UPDATE_FULL_TEXT_PARAMS, VALIDATE_PARAMS).get(1); | 74 | var stateId = updateFullText(); |
77 | assertThat(response, hasResponseData(instanceOf(ValidationResult.class))); | 75 | var responseHandler = sendRequestAndWaitForAllResponses( |
76 | new XtextWebRequest("bar", Map.of("resource", RESOURCE_NAME, "serviceType", "update", "requiredStateId", | ||
77 | stateId, "deltaText", "<invalid text>\n", "deltaOffset", "0", "deltaReplaceLength", "0"))); | ||
78 | |||
79 | var captor = newCaptor(); | ||
80 | verify(responseHandler, times(3)).onResponse(captor.capture()); | ||
81 | var newStateId = getStateId("bar", captor.getAllValues().get(0)); | ||
82 | assertThatPrecomputedMessagesAreReceived(newStateId, captor.getAllValues()); | ||
78 | } | 83 | } |
79 | 84 | ||
80 | @Test | 85 | @Test |
81 | void validationAfterFullTextUpdateInDifferentBatchTest() { | 86 | void updateDeltaTextHighlightChangeOnly() throws ResponseHandlerException { |
82 | var stateId = updateFullText(); | 87 | var stateId = updateFullText(); |
83 | var validateResponse = performSingleRequest(stateId, VALIDATE_PARAMS); | 88 | var responseHandler = sendRequestAndWaitForAllResponses( |
84 | assertThat(validateResponse, hasResponseData(instanceOf(ValidationResult.class))); | 89 | new XtextWebRequest("bar", Map.of("resource", RESOURCE_NAME, "serviceType", "update", "requiredStateId", |
90 | stateId, "deltaText", "class Vehicle.\n", "deltaOffset", "0", "deltaReplaceLength", "0"))); | ||
91 | |||
92 | var captor = newCaptor(); | ||
93 | verify(responseHandler, times(2)).onResponse(captor.capture()); | ||
94 | var newStateId = getStateId("bar", captor.getAllValues().get(0)); | ||
95 | assertHighlightingResponse(newStateId, captor.getAllValues().get(1)); | ||
85 | } | 96 | } |
86 | 97 | ||
87 | @Test | 98 | private ArgumentCaptor<XtextWebResponse> newCaptor() { |
88 | void conflictTest() { | 99 | return ArgumentCaptor.forClass(XtextWebResponse.class); |
89 | updateFullText(); | ||
90 | var response = performSingleRequest(INVALID_STATE_ID, VALIDATE_PARAMS); | ||
91 | assertThat(response, hasResponseData(instanceOf(ServiceConflictResult.class))); | ||
92 | } | 100 | } |
93 | 101 | ||
94 | @Test | 102 | private String updateFullText() throws ResponseHandlerException { |
95 | void transactionCancelledDueToConflictTest() { | 103 | return updateFullText(newCaptor()); |
96 | updateFullText(); | 104 | } |
97 | var response = performBatchRequest(INVALID_STATE_ID, VALIDATE_PARAMS, VALIDATE_PARAMS).get(1); | 105 | |
98 | assertThat(response, hasErrorKind(equalTo(XtextWebSocketErrorKind.TRANSACTION_CANCELLED))); | 106 | private String updateFullText(ArgumentCaptor<XtextWebResponse> captor) throws ResponseHandlerException { |
107 | var responseHandler = sendRequestAndWaitForAllResponses(new XtextWebRequest("foo", UPDATE_FULL_TEXT_PARAMS)); | ||
108 | |||
109 | verify(responseHandler, times(3)).onResponse(captor.capture()); | ||
110 | return getStateId("foo", captor.getAllValues().get(0)); | ||
99 | } | 111 | } |
100 | 112 | ||
101 | @SafeVarargs | 113 | private ResponseHandler sendRequestAndWaitForAllResponses(XtextWebRequest request) throws ResponseHandlerException { |
102 | private List<XtextWebSocketResponse> performBatchRequest(String requiredStateId, Map<String, String>... params) { | ||
103 | var id = UUID.randomUUID().toString(); | ||
104 | var request = new XtextWebSocketRequest(id, RESOURCE_NAME, null, requiredStateId, List.of(params)); | ||
105 | |||
106 | var responseHandler = mock(ResponseHandler.class); | 114 | var responseHandler = mock(ResponseHandler.class); |
107 | try { | 115 | transactionExecutor.setResponseHandler(responseHandler); |
108 | transactionExecutor.handleRequest(request, responseHandler); | 116 | transactionExecutor.handleRequest(request); |
109 | } catch (IOException e) { | 117 | executorServices.waitForAllTasksToFinish(); |
110 | fail("Unexpected IOException", e); | 118 | return responseHandler; |
111 | } | ||
112 | |||
113 | var captor = ArgumentCaptor.forClass(XtextWebSocketResponse.class); | ||
114 | int nParams = params.length; | ||
115 | try { | ||
116 | verify(responseHandler, times(nParams)).onResponse(captor.capture()); | ||
117 | } catch (IOException e) { | ||
118 | throw new RuntimeException("Mockito threw unexcepted exception", e); | ||
119 | } | ||
120 | var allResponses = captor.getAllValues(); | ||
121 | for (int i = 0; i < nParams; i++) { | ||
122 | var response = allResponses.get(i); | ||
123 | assertThat(response, hasProperty("id", equalTo(id))); | ||
124 | assertThat(response, hasProperty("index", equalTo(i))); | ||
125 | } | ||
126 | return allResponses; | ||
127 | } | 119 | } |
128 | 120 | ||
129 | private XtextWebSocketResponse performSingleRequest(String requiredStateId, Map<String, String> param) { | 121 | private String getStateId(String requestId, XtextWebResponse okResponse) { |
130 | return performBatchRequest(requiredStateId, param).get(0); | 122 | assertThat(okResponse, hasProperty("id", equalTo(requestId))); |
123 | assertThat(okResponse, hasProperty("responseData", instanceOf(DocumentStateResult.class))); | ||
124 | return ((DocumentStateResult) ((XtextWebOkResponse) okResponse).getResponseData()).getStateId(); | ||
131 | } | 125 | } |
132 | 126 | ||
133 | private String updateFullText() { | 127 | private void assertThatPrecomputedMessagesAreReceived(String stateId, List<XtextWebResponse> responses) { |
134 | var updateResponse = (XtextWebSocketOkResponse) performSingleRequest(null, UPDATE_FULL_TEXT_PARAMS); | 128 | assertHighlightingResponse(stateId, responses.get(1)); |
135 | var documentStateResult = (DocumentStateResult) updateResponse.getResponseData(); | 129 | assertValidationResponse(stateId, responses.get(2)); |
136 | var stateId = documentStateResult.getStateId(); | ||
137 | if (INVALID_STATE_ID.equals(stateId)) { | ||
138 | throw new RuntimeException("Service returned unexpected stateId: " + stateId); | ||
139 | } | ||
140 | return stateId; | ||
141 | } | 130 | } |
142 | 131 | ||
143 | private static Matcher<XtextWebSocketResponse> hasResponseData(Matcher<?> responseDataMatcher) { | 132 | private void assertHighlightingResponse(String stateId, XtextWebResponse highlightingResponse) { |
144 | return hasProperty("responseData", responseDataMatcher); | 133 | assertThat(highlightingResponse, hasProperty("resourceId", equalTo(RESOURCE_NAME))); |
134 | assertThat(highlightingResponse, hasProperty("stateId", equalTo(stateId))); | ||
135 | assertThat(highlightingResponse, hasProperty("service", equalTo("highlighting"))); | ||
136 | assertThat(highlightingResponse, hasProperty("pushData", instanceOf(HighlightingResult.class))); | ||
145 | } | 137 | } |
146 | 138 | ||
147 | private static Matcher<XtextWebSocketResponse> hasErrorKind( | 139 | private void assertValidationResponse(String stateId, XtextWebResponse validationResponse) { |
148 | Matcher<? extends XtextWebSocketErrorKind> errorKindMatcher) { | 140 | assertThat(validationResponse, hasProperty("resourceId", equalTo(RESOURCE_NAME))); |
149 | return hasProperty("errorKind", errorKindMatcher); | 141 | assertThat(validationResponse, hasProperty("stateId", equalTo(stateId))); |
142 | assertThat(validationResponse, hasProperty("service", equalTo("validation"))); | ||
143 | assertThat(validationResponse, hasProperty("pushData", instanceOf(ValidationResult.class))); | ||
150 | } | 144 | } |
151 | } | 145 | } |