3535import java .util .List ;
3636import java .util .Queue ;
3737import java .util .Set ;
38+ import java .util .function .BooleanSupplier ;
3839import java .util .stream .Collectors ;
3940
4041import org .junit .jupiter .api .AfterEach ;
@@ -87,8 +88,8 @@ public class CompletionTest extends AbstratGenericEditorTest {
8788 @ DisabledOnOs (value = OS .MAC , disabledReason = "test fails on Mac, see https://github.com/eclipse-platform/eclipse.platform.ui/issues/906" )
8889 public void testCompletion () throws Exception {
8990 editor .selectAndReveal (3 , 0 );
90- this . completionShell = openContentAssist ( true );
91- final Table completionProposalList = findCompletionSelectionControl (completionShell );
91+ Shell shell = openContentAssistWithLongRunningProposalComputation ( );
92+ final Table completionProposalList = findCompletionSelectionControl (shell );
9293 checkCompletionContent (completionProposalList );
9394 // TODO find a way to actually trigger completion and verify result against
9495 // Editor content
@@ -102,7 +103,7 @@ public void testDefaultContentAssistBug570488() throws Exception {
102103 TestLogListener listener = new TestLogListener ();
103104 log .addLogListener (listener );
104105 createAndOpenFile ("Bug570488.txt" , "bar 'bar'" );
105- openContentAssist (false );
106+ assertNull ( openContentAssist (), "No shell is expected to open" );
106107 runEventLoop (Display .getCurrent (), 0 );
107108 assertFalse (listener .messages .stream ().anyMatch (s -> s .matches (IStatus .ERROR )), "There are errors in the log" );
108109 log .removeLogListener (listener );
@@ -120,8 +121,8 @@ public void testCompletionService() throws Exception {
120121 new Hashtable <>(Collections .singletonMap ("contentType" , "org.eclipse.ui.genericeditor.tests.content-type" )));
121122 runEventLoop (Display .getCurrent (), 0 );
122123 editor .selectAndReveal (3 , 0 );
123- this . completionShell = openContentAssist ( true );
124- final Table completionProposalList = findCompletionSelectionControl (completionShell );
124+ Shell shell = openContentAssistWithLongRunningProposalComputation ( );
125+ final Table completionProposalList = findCompletionSelectionControl (shell );
125126 assertTrue (service .called , "Service was not called!" );
126127 checkCompletionContent (completionProposalList );
127128 registration .unregister ();
@@ -132,127 +133,149 @@ public void testCompletionService() throws Exception {
132133 public void testCompletionUsingViewerSelection () throws Exception {
133134 editor .getDocumentProvider ().getDocument (editor .getEditorInput ()).set ("abc" );
134135 editor .selectAndReveal (0 , 3 );
135- this .completionShell = openContentAssist (true );
136- final Table completionProposalList = findCompletionSelectionControl (completionShell );
137- assertTrue (waitForCondition (completionProposalList .getDisplay (), 5000 , () -> {
136+ final Shell shell = openContentAssist ();
137+ assertNotNull (shell , "Shell is expected to open for completion proposals" );
138+ final Table completionProposalList = findCompletionSelectionControl (shell );
139+ waitForProposalRelatedCondition ("Proposal list did not contain expected item 'ABC'" , completionProposalList ,
140+ () -> Arrays .stream (completionProposalList .getItems ()).map (TableItem ::getText ).anyMatch ("ABC" ::equals ), 5_000 );
141+ }
142+
143+ private static void waitForProposalRelatedCondition (String expectedListContentDescription ,
144+ Table completionProposalList , BooleanSupplier condition , int timeoutInMsec ) {
145+ boolean result = waitForCondition (completionProposalList .getDisplay (), timeoutInMsec , () -> {
138146 assertFalse (completionProposalList .isDisposed (), "Completion proposal list was unexpectedly disposed" );
139- return Arrays .stream (completionProposalList .getItems ()).map (TableItem ::getText ).anyMatch ("ABC" ::equals );
140- }), "Proposal list did not contain expected item: ABC" );
147+ return condition .getAsBoolean ();
148+ });
149+ assertTrue (result , expectedListContentDescription + " but contained: "
150+ + Arrays .toString (completionProposalList .getItems ()));
141151 }
142-
152+
143153 @ Test
144154 public void testEnabledWhenCompletion () throws Exception {
145- // Confirm that when disabled, a completion shell is present
155+ // Confirm that when disabled, a completion shell is not present
146156 EnabledPropertyTester .setEnabled (false );
147157 createAndOpenFile ("enabledWhen.txt" , "bar 'bar'" );
148158 editor .selectAndReveal (3 , 0 );
149- assertNull (openContentAssist (false ), "A new shell was found " );
159+ assertNull (openContentAssist (), "No shell is expected to open " );
150160 cleanFileAndEditor ();
151161
152162 // Confirm that when enabled, a completion shell is present
153163 EnabledPropertyTester .setEnabled (true );
154164 createAndOpenFile ("enabledWhen.txt" , "bar 'bar'" );
155- editor .selectAndReveal (3 , 0 );
156- assertNotNull (openContentAssist (true ));
165+ editor .selectAndReveal (3 , 0 );
166+ assertNotNull (openContentAssist (), "Shell is expected to open for completion proposals" );
167+ }
168+
169+ private Shell openContentAssistWithLongRunningProposalComputation () {
170+ LongRunningBarContentAssistProcessor .enable ();
171+ Shell shell = openContentAssist ();
172+ assertNotNull (shell , "Shell is expected to open for completion proposals" );
173+ return shell ;
157174 }
158175
159- private Shell openContentAssist (boolean expectShell ) {
176+ private Shell openContentAssist () {
160177 ContentAssistAction action = (ContentAssistAction ) editor .getAction (ITextEditorActionConstants .CONTENT_ASSIST );
161178 action .update ();
162- final Set <Shell > beforeShells = Arrays .stream (editor .getSite ().getShell ().getDisplay ().getShells ()).filter (Shell ::isVisible ).collect (Collectors .toSet ());
163- action .run (); //opens shell
164- Shell shell = findNewShell (beforeShells , editor .getSite ().getShell ().getDisplay (),expectShell );
165- runEventLoop (PlatformUI .getWorkbench ().getDisplay (), 100 ); // can dispose shell when focus lost during debugging
179+ final Set <Shell > beforeShells = Arrays .stream (editor .getSite ().getShell ().getDisplay ().getShells ())
180+ .filter (Shell ::isVisible ).collect (Collectors .toSet ());
181+ action .run ();
182+ Shell shell = findNewShell (beforeShells , editor .getSite ().getShell ().getDisplay ());
183+ runEventLoop (PlatformUI .getWorkbench ().getDisplay (), 100 );
184+ if (shell != null ) {
185+ this .completionShell = shell ;
186+ }
166187 return shell ;
167188 }
168189
169190 /**
170191 * Checks that completion behaves as expected:
171192 * 1. Computing is shown instantaneously
172193 * 2. 1st proposal shown instantaneously
173- * 3. 2s later, 2nd proposal is shown
194+ * 3. Calculation finishes when the test explicitly releases it
174195 * @param completionProposalList the completion list
175196 */
176197 private void checkCompletionContent (final Table completionProposalList ) {
177- // should be instantaneous, but happens to go asynchronous on CI so let's allow a wait
178- assertTrue (waitForCondition (completionProposalList .getDisplay (), 200 , () -> {
179- assertFalse (completionProposalList .isDisposed (), "Completion proposal list was unexpectedly disposed" );
180- return completionProposalList .getItemCount () == 2 && completionProposalList .getItem (1 ).getData () != null ;
181- }), "Proposal list did not show two initial items" );
182- assertTrue (isComputingInfoEntry (completionProposalList .getItem (0 )), "Missing computing info entry in proposal list" );
198+ waitForProposalRelatedCondition ("Proposal list should show two initial items" , completionProposalList ,
199+ () -> completionProposalList .getItemCount () == 2
200+ && completionProposalList .getItem (1 ).getData () != null ,
201+ 200 );
202+ assertTrue (isComputingInfoEntry (completionProposalList .getItem (0 )), "Missing computing info entry" );
183203 final TableItem initialProposalItem = completionProposalList .getItem (1 );
184- final String initialProposalString = ((ICompletionProposal )initialProposalItem .getData ()).getDisplayString ();
185- assertThat ("Unexpected initial proposal item" ,
204+ final String initialProposalString = ((ICompletionProposal ) initialProposalItem .getData ()).getDisplayString ();
205+ assertThat ("Unexpected initial proposal item" ,
186206 BAR_CONTENT_ASSIST_PROPOSAL , endsWith (initialProposalString ));
187207 completionProposalList .setSelection (initialProposalItem );
188- // asynchronous
189- assertTrue (waitForCondition (completionProposalList .getDisplay (), LongRunningBarContentAssistProcessor .DELAY * 2 ,
190- () -> {
191- assertFalse (completionProposalList .isDisposed (),
192- "Completion proposal list was unexpectedly disposed" );
193- return !isComputingInfoEntry (completionProposalList .getItem (0 ))
194- && completionProposalList .getItemCount () == 2 ;
195- }), "Proposal list did not show two items after finishing computing" );
208+
209+ LongRunningBarContentAssistProcessor .finish ();
210+ waitForProposalRelatedCondition ("Proposal list should contain two items" , completionProposalList ,
211+ () -> !isComputingInfoEntry (completionProposalList .getItem (0 ))
212+ && completionProposalList .getItemCount () == 2 ,
213+ 5_000 );
196214 final TableItem firstCompletionProposalItem = completionProposalList .getItem (0 );
197215 final TableItem secondCompletionProposalItem = completionProposalList .getItem (1 );
198- String firstCompletionProposalText = ((ICompletionProposal )firstCompletionProposalItem .getData ()).getDisplayString ();
199- String secondCompletionProposalText = ((ICompletionProposal )secondCompletionProposalItem .getData ()).getDisplayString ();
216+ String firstCompletionProposalText = ((ICompletionProposal ) firstCompletionProposalItem .getData ()).getDisplayString ();
217+ String secondCompletionProposalText = ((ICompletionProposal ) secondCompletionProposalItem .getData ()).getDisplayString ();
200218 assertThat ("Unexpected first proposal item" , BAR_CONTENT_ASSIST_PROPOSAL , endsWith (firstCompletionProposalText ));
201- assertThat ("Unexpected second proposal item" , LONG_RUNNING_BAR_CONTENT_ASSIST_PROPOSAL , endsWith (secondCompletionProposalText ));
202- String selectedProposalString = ((ICompletionProposal )completionProposalList .getSelection ()[0 ].getData ()).getDisplayString ();
203- assertEquals (initialProposalString , selectedProposalString , "Addition of completion proposal should keep selection" );
219+ assertThat ("Unexpected second proposal item" , LONG_RUNNING_BAR_CONTENT_ASSIST_PROPOSAL ,
220+ endsWith (secondCompletionProposalText ));
221+ String selectedProposalString = ((ICompletionProposal ) completionProposalList .getSelection ()[0 ].getData ())
222+ .getDisplayString ();
223+ assertEquals (initialProposalString , selectedProposalString ,
224+ "Addition of completion proposal should keep selection" );
204225 }
205-
226+
206227 private static boolean isComputingInfoEntry (TableItem item ) {
207228 return item .getText ().contains ("Computing" );
208229 }
209230
210- public static Shell findNewShell (Set <Shell > beforeShells , Display display , boolean expectShell ) {
231+ public static Shell findNewShell (Set <Shell > beforeShells , Display display ) {
211232 List <Shell > afterShells = Arrays .stream (display .getShells ())
212233 .filter (Shell ::isVisible )
213234 .filter (shell -> !beforeShells .contains (shell ))
214235 .toList ();
215- if (expectShell ) {
216- assertEquals (1 , afterShells .size (), "No new shell found" );
217- }
236+ assertTrue (afterShells .size () <= 1 , "More than one new shell was found" );
218237 return afterShells .isEmpty () ? null : afterShells .get (0 );
219238 }
220239
221240 @ Test
222241 @ DisabledOnOs (value = OS .MAC , disabledReason = "test fails on Mac, see https://github.com/eclipse-platform/eclipse.platform.ui/issues/906" )
223242 public void testCompletionFreeze_bug521484 () throws Exception {
224243 editor .selectAndReveal (3 , 0 );
225- this .completionShell =openContentAssist (true );
226- final Table completionProposalList = findCompletionSelectionControl (this .completionShell );
227- // should be instantaneous, but happens to go asynchronous on CI so let's allow a wait
228- assertTrue (waitForCondition (completionProposalList .getDisplay (), 200 , () -> {
229- assertFalse (completionProposalList .isDisposed (), "Completion proposal list was unexpectedly disposed" );
230- return completionProposalList .getItemCount () == 2 ;
231- }), "Proposal list did not show two items" );
244+ final Shell shell = openContentAssistWithLongRunningProposalComputation ();
245+ assertNotNull (shell , "Shell is expected to open for completion proposals" );
246+ final Table completionProposalList = findCompletionSelectionControl (shell );
247+ waitForProposalRelatedCondition ("Proposal list should show two items" , completionProposalList ,
248+ () -> completionProposalList .getItemCount () == 2 , 200 );
232249 assertTrue (isComputingInfoEntry (completionProposalList .getItem (0 )), "Missing computing info entry" );
233- // Some processors are long running, moving cursor can cause freeze (bug 521484)
234- // asynchronous
235250 long timestamp = System .currentTimeMillis ();
236251 emulatePressLeftArrowKey ();
237- sleep (editor .getSite ().getShell ().getDisplay (), 200 ); //give time to process events
252+ sleep (editor .getSite ().getShell ().getDisplay (), 200 );
238253 long processingDuration = System .currentTimeMillis () - timestamp ;
239- assertTrue (processingDuration < LongRunningBarContentAssistProcessor .DELAY , "UI Thread frozen for " + processingDuration + "ms" );
254+ assertTrue (processingDuration < LongRunningBarContentAssistProcessor .TIMEOUT_MSEC ,
255+ "UI Thread frozen for " + processingDuration + "ms" );
240256 }
241257
242258 @ Test
243259 @ DisabledOnOs (value = OS .MAC , disabledReason = "test fails on Mac, see https://github.com/eclipse-platform/eclipse.platform.ui/issues/906" )
244260 public void testMoveCaretBackUsesAllProcessors_bug522255 () throws Exception {
245- testCompletion ();
261+ editor .selectAndReveal (3 , 0 );
262+ Shell shell = openContentAssistWithLongRunningProposalComputation ();
263+ final Table completionProposalList = findCompletionSelectionControl (shell );
264+ checkCompletionContent (completionProposalList );
265+ LongRunningBarContentAssistProcessor .enable ();
246266 emulatePressLeftArrowKey ();
247- final Set <Shell > beforeShells = Arrays .stream (editor .getSite ().getShell ().getDisplay ().getShells ()).filter (Shell ::isVisible ).collect (Collectors .toSet ());
267+ final Set <Shell > beforeShells = Arrays .stream (editor .getSite ().getShell ().getDisplay ().getShells ())
268+ .filter (Shell ::isVisible ).collect (Collectors .toSet ());
248269 sleep (editor .getSite ().getShell ().getDisplay (), 200 );
249- this .completionShell = findNewShell (beforeShells , editor .getSite ().getShell ().getDisplay (), true );
250- final Table completionProposalList = findCompletionSelectionControl (this .completionShell );
251- checkCompletionContent (completionProposalList );
270+ assertTrue (shell .isDisposed (), "Completion proposal shell should be disposed after moving the cursor" );
271+ this .completionShell = findNewShell (beforeShells , editor .getSite ().getShell ().getDisplay ());
272+ assertNotNull (completionShell , "Shell is expected to open for completion proposals" );
273+ final Table newCompletionProposalList = findCompletionSelectionControl (completionShell );
274+ checkCompletionContent (newCompletionProposalList );
252275 }
253276
254277 private void emulatePressLeftArrowKey () {
255- editor .selectAndReveal (((ITextSelection )editor .getSelectionProvider ().getSelection ()).getOffset () - 1 , 0 );
278+ editor .selectAndReveal (((ITextSelection ) editor .getSelectionProvider ().getSelection ()).getOffset () - 1 , 0 );
256279 Control styledText = editor .getAdapter (Control .class );
257280 Event e = new Event ();
258281 e .type = ST .VerifyKey ;
@@ -284,6 +307,11 @@ public void closeShell() {
284307 }
285308 }
286309
310+ @ AfterEach
311+ public void stopLongRunningCompletionProposalProcessor () {
312+ LongRunningBarContentAssistProcessor .finish ();
313+ }
314+
287315 private static final class TestLogListener implements ILogListener {
288316
289317 List <IStatus > messages = new ArrayList <>();
@@ -301,8 +329,8 @@ private static final class MockContentAssistProcessor implements IContentAssistP
301329
302330 @ Override
303331 public ICompletionProposal [] computeCompletionProposals (ITextViewer viewer , int offset ) {
304- called = true ;
305- return null ;
332+ this . called = true ;
333+ return new ICompletionProposal [ 0 ] ;
306334 }
307335
308336 @ Override
@@ -329,6 +357,5 @@ public String getErrorMessage() {
329357 public IContextInformationValidator getContextInformationValidator () {
330358 return null ;
331359 }
332-
333360 }
334361}
0 commit comments