3030import static org .junit .Assert .assertNotNull ;
3131import static org .junit .Assert .assertNull ;
3232import static org .junit .Assert .assertTrue ;
33+ import static org .junit .Assert .fail ;
3334import static org .mockito .ArgumentMatchers .any ;
35+ import static org .mockito .Mockito .atLeastOnce ;
36+ import static org .mockito .Mockito .atMostOnce ;
3437import static org .mockito .Mockito .inOrder ;
3538import static org .mockito .Mockito .never ;
3639import static org .mockito .Mockito .only ;
3942
4043import java .util .ArrayList ;
4144import java .util .HashSet ;
42- import java .util .LinkedList ;
4345import java .util .List ;
4446import java .util .Map ;
4547import java .util .Set ;
6062import org .eclipse .lsp4j .RegistrationParams ;
6163import org .eclipse .lsp4j .ServerCapabilities ;
6264import org .eclipse .lsp4j .TextDocumentClientCapabilities ;
63- import org .eclipse .lsp4j .Unregistration ;
6465import org .eclipse .lsp4j .UnregistrationParams ;
6566import org .eclipse .lsp4j .services .LanguageClient ;
6667import org .junit .Before ;
7172import org .mockito .InOrder ;
7273import org .mockito .Mockito ;
7374import org .mockito .Spy ;
75+ import org .mockito .exceptions .verification .VerificationInOrderFailure ;
7476import org .mockito .junit .MockitoJUnitRunner ;
7577import org .rascalmpl .values .IRascalValueFactory ;
7678import org .rascalmpl .vscode .lsp .parametric .ILanguageContributions ;
@@ -173,21 +175,15 @@ public CompletableFuture<SummaryConfig> getOndemandSummaryConfig() {
173175 return params .getRegistrations ().stream ().collect (Collectors .toMap (Registration ::getMethod , Registration ::getRegisterOptions ));
174176 }
175177
176- private List <String > unregistrationOptions (UnregistrationParams params ) {
177- return params .getUnregisterations ().stream ().map (Unregistration ::getMethod ).collect (Collectors .toList ());
178- }
179-
180178 @ SafeVarargs
181- private CompletableFuture < Void > registerIncrementally (List <String >... options ) {
182- return registerIncrementally (Stream .of (options ).map (SomeContribs ::new ).map (ILanguageContributions .class ::cast ).collect (Collectors .toList ()));
179+ private void registerSequentially (List <String >... options ) throws InterruptedException , ExecutionException {
180+ registerSequentially (Stream .of (options ).map (SomeContribs ::new ).map (ILanguageContributions .class ::cast ).collect (Collectors .toList ()));
183181 }
184182
185- private CompletableFuture <Void > registerIncrementally (List <ILanguageContributions > contribs ) {
186- List <CompletableFuture <Void >> jobs = new LinkedList <>();
183+ private void registerSequentially (List <ILanguageContributions > contribs ) throws InterruptedException , ExecutionException {
187184 for (int i = 0 ; i < contribs .size (); i ++) {
188- jobs . add ( dynCap .updateCapabilities (contribs .subList (0 , i + 1 )));
185+ dynCap .updateCapabilities (contribs .subList (0 , i + 1 )). get ( );
189186 }
190- return CompletableFutureUtils .reduce (jobs ).thenAccept (_v -> {});
191187 }
192188
193189 //// TESTS
@@ -219,22 +215,17 @@ public void registerSingleContribution() throws InterruptedException, ExecutionE
219215
220216 @ Test
221217 public void registerIncrementalContribution () throws InterruptedException , ExecutionException {
222- registerIncrementally (List .of ("." , "::" ), List .of ("+" , "*" , "-" , "/" , "%" )). get ( );
218+ registerSequentially (List .of ("." , "::" ), List .of ("+" , "*" , "-" , "/" , "%" ));
223219
224- InOrder inOrder = inOrder (client );
225- inOrder .verify (client ).registerCapability (registrationCaptor .capture ());
226- inOrder .verify (client ).unregisterCapability (unregistrationCaptor .capture ());
227- inOrder .verify (client ).registerCapability (registrationCaptor .capture ());
228- inOrder .verifyNoMoreInteractions ();
220+ verify (client , atLeastOnce ()).registerCapability (registrationCaptor .capture ());
221+ verify (client , atMostOnce ()).unregisterCapability (unregistrationCaptor .capture ());
229222
230- assertEquals (Map .of ("textDocument/completion" , new CompletionRegistrationOptions (List .of ("." , "::" ), false )), registrationOptions (registrationCaptor .getAllValues ().get (0 )));
231- assertEquals (List .of ("textDocument/completion" ), unregistrationOptions (unregistrationCaptor .getValue ()));
232- assertEquals (Map .of ("textDocument/completion" , new CompletionRegistrationOptions (List .of ("." , "::" , "+" , "*" , "-" , "/" , "%" ), false )), registrationOptions (registrationCaptor .getAllValues ().get (1 )));
223+ assertEquals (Map .of ("textDocument/completion" , new CompletionRegistrationOptions (List .of ("." , "::" , "+" , "*" , "-" , "/" , "%" ), false )), registrationOptions (registrationCaptor .getValue ()));
233224 }
234225
235226 @ Test
236227 public void registerIdenticalContribution () throws InterruptedException , ExecutionException {
237- registerIncrementally (List .of ("." , "::" ), List .of ("." , "::" )). get ( );
228+ registerSequentially (List .of ("." , "::" ), List .of ("." , "::" ));
238229
239230 InOrder inOrder = inOrder (client );
240231 inOrder .verify (client ).registerCapability (registrationCaptor .capture ());
@@ -250,7 +241,7 @@ public void registerOverlappingContributions() throws InterruptedException, Exec
250241 .map (ILanguageContributions .class ::cast )
251242 .collect (Collectors .toList ());
252243
253- registerIncrementally (contribs ). get ( );
244+ registerSequentially (contribs );
254245
255246 // unregister one of both
256247 dynCap .updateCapabilities (contribs .subList (1 , 2 )).get ();
@@ -340,7 +331,7 @@ public void preferStaticRegistration() throws InterruptedException, ExecutionExc
340331 }
341332
342333 @ Test
343- public void multiThreadingConisistency () throws InterruptedException , ExecutionException {
334+ public void multiThreadingAtomicity () throws InterruptedException , ExecutionException {
344335 int N = 50 ;
345336 List <CompletableFuture <Void >> jobs = new ArrayList <>(N );
346337 var exec = Executors .newFixedThreadPool (N / 2 ); // less threads than jobs; causes some overlap
@@ -359,9 +350,21 @@ public void multiThreadingConisistency() throws InterruptedException, ExecutionE
359350 InOrder inOrder = inOrder (client );
360351
361352 inOrder .verify (client ).registerCapability (any ());
362- for (int i = 1 ; i < N ; i ++) {
363- inOrder .verify (client ).unregisterCapability (any ());
364- inOrder .verify (client ).registerCapability (registrationCaptor .capture ());
353+ boolean atomic = true ;
354+ int i = 1 ;
355+ while (atomic && i < N ) {
356+ try {
357+ inOrder .verify (client ).unregisterCapability (any ());
358+ atomic = false ;
359+ inOrder .verify (client ).registerCapability (registrationCaptor .capture ());
360+ atomic = true ;
361+ } catch (VerificationInOrderFailure e ) {
362+ if (atomic ) {
363+ break ;
364+ }
365+ fail (e .toString ());
366+ }
367+ i ++;
365368 }
366369 inOrder .verifyNoMoreInteractions ();
367370
0 commit comments