Skip to content

Commit 99f9419

Browse files
committed
Make completion proposal calculation test time-independent #906
The test cases for completion proposals in CompletionTest depend on specific timing behavior of a test proposal processor. On one hand, this is prone to errors if proper timing is not given on the test environment. On the other hand, the tests run unnecessarily long because of waiting long enough. This change replaces the fixed-time waiting of the LongRunningBarContentAssistProcessor with an explicit trigger that finalizes the content calculation as soon as some barrier is reached within the tests. In addition, the long-running processor is only enabled on demand and will be finalized whenever a test case was executed to avoid that the processor thread is leaking to the next test case. Contributes to #906
1 parent f243cf0 commit 99f9419

File tree

3 files changed

+76
-43
lines changed

3 files changed

+76
-43
lines changed

tests/org.eclipse.ui.genericeditor.tests/src/org/eclipse/ui/genericeditor/tests/CompletionTest.java

Lines changed: 56 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -82,8 +82,8 @@ public class CompletionTest extends AbstratGenericEditorTest {
8282
@Test
8383
public void testCompletion() throws Exception {
8484
editor.selectAndReveal(3, 0);
85-
this.completionShell= openConentAssist();
86-
final Table completionProposalList = findCompletionSelectionControl(completionShell);
85+
Shell shell = openContentAssistWithLongRunningProposalComputation();
86+
final Table completionProposalList = findCompletionSelectionControl(shell);
8787
checkCompletionContent(completionProposalList);
8888
// TODO find a way to actually trigger completion and verify result against Editor content
8989
// Assert.assertEquals("Completion didn't complete", "bars are good for a beer.", ((StyledText)editor.getAdapter(Control.class)).getText());
@@ -95,7 +95,7 @@ public void testDefaultContentAssistBug570488() throws Exception {
9595
TestLogListener listener= new TestLogListener();
9696
log.addLogListener(listener);
9797
createAndOpenFile("Bug570488.txt", "bar 'bar'");
98-
openConentAssist(false);
98+
assertNull("No shell is expected to open", openContentAssist());
9999
DisplayHelper.driveEventQueue(Display.getCurrent());
100100
assertFalse("There are errors in the log", listener.messages.stream().anyMatch(s -> s.matches(IStatus.ERROR)));
101101
log.removeLogListener(listener);
@@ -112,8 +112,8 @@ public void testCompletionService() throws Exception {
112112
new Hashtable<>(Collections.singletonMap("contentType", "org.eclipse.ui.genericeditor.tests.content-type")));
113113
DisplayHelper.driveEventQueue(Display.getCurrent());
114114
editor.selectAndReveal(3, 0);
115-
this.completionShell= openConentAssist();
116-
final Table completionProposalList= findCompletionSelectionControl(completionShell);
115+
Shell shell = openContentAssistWithLongRunningProposalComputation();
116+
final Table completionProposalList = findCompletionSelectionControl(shell);
117117
checkCompletionContent(completionProposalList);
118118
assertTrue("Service was not called!", service.called);
119119
registration.unregister();
@@ -123,20 +123,22 @@ public void testCompletionService() throws Exception {
123123
public void testCompletionUsingViewerSelection() throws Exception {
124124
editor.getDocumentProvider().getDocument(editor.getEditorInput()).set("abc");
125125
editor.selectAndReveal(0, 3);
126-
this.completionShell= openConentAssist();
127-
final Table completionProposalList = findCompletionSelectionControl(completionShell);
128-
waitForProposalRelatedCondition("Proposal list did not contain expected item: ABC", completionProposalList,
126+
final Shell shell = openContentAssist();
127+
assertNotNull("Shell is expected to open for completion proposals", shell);
128+
final Table completionProposalList = findCompletionSelectionControl(shell);
129+
waitForProposalRelatedCondition("Proposal list did not contain expected item 'ABC'", completionProposalList,
129130
() -> Arrays.stream(completionProposalList.getItems()).map(TableItem::getText).anyMatch("ABC"::equals), 5_000);
130131
}
131132

132-
private static void waitForProposalRelatedCondition(String errorMessage, Table completionProposalList, BooleanSupplier condition, int timeoutInMsec) {
133-
assertTrue(errorMessage, new DisplayHelper() {
133+
private static void waitForProposalRelatedCondition(String expectedListContentDescription, Table completionProposalList, BooleanSupplier condition, int timeoutInMsec) {
134+
boolean result = new DisplayHelper() {
134135
@Override
135136
protected boolean condition() {
136137
assertFalse("Completion proposal list was unexpectedly disposed", completionProposalList.isDisposed());
137138
return condition.getAsBoolean();
138139
}
139-
}.waitForCondition(completionProposalList.getDisplay(), timeoutInMsec));
140+
}.waitForCondition(completionProposalList.getDisplay(), timeoutInMsec);
141+
assertTrue(expectedListContentDescription + " but contained: " + Arrays.toString(completionProposalList.getItems()), result);
140142
}
141143

142144
@Test
@@ -145,26 +147,33 @@ public void testEnabledWhenCompletion() throws Exception {
145147
EnabledPropertyTester.setEnabled(false);
146148
createAndOpenFile("enabledWhen.txt", "bar 'bar'");
147149
editor.selectAndReveal(3, 0);
148-
assertNull("A new shell was found", openConentAssist(false));
150+
assertNull("No shell is expected to open", openContentAssist());
149151
cleanFileAndEditor();
150152

151153
// Confirm that when enabled, a completion shell is present
152154
EnabledPropertyTester.setEnabled(true);
153155
createAndOpenFile("enabledWhen.txt", "bar 'bar'");
154-
editor.selectAndReveal(3, 0);
155-
assertNotNull(openConentAssist());
156+
editor.selectAndReveal(3, 0);
157+
assertNotNull("Shell is expected to open for completion proposals", openContentAssist());
156158
}
157159

158-
private Shell openConentAssist() {
159-
return openConentAssist(true);
160+
private Shell openContentAssistWithLongRunningProposalComputation() {
161+
LongRunningBarContentAssistProcessor.enable();
162+
Shell shell = openContentAssist();
163+
assertNotNull("Shell is expected to open for completion proposals", shell);
164+
return shell;
160165
}
161-
private Shell openConentAssist(boolean expectShell) {
166+
167+
private Shell openContentAssist() {
162168
ContentAssistAction action = (ContentAssistAction) editor.getAction(ITextEditorActionConstants.CONTENT_ASSIST);
163169
action.update();
164170
final Set<Shell> beforeShells = Arrays.stream(editor.getSite().getShell().getDisplay().getShells()).filter(Shell::isVisible).collect(Collectors.toSet());
165171
action.run(); //opens shell
166-
Shell shell= findNewShell(beforeShells, editor.getSite().getShell().getDisplay(),expectShell);
172+
Shell shell = findNewShell(beforeShells, editor.getSite().getShell().getDisplay());
167173
waitAndDispatch(100); // can dispose shell when focus lost during debugging
174+
if (shell != null) {
175+
this.completionShell = shell;
176+
}
168177
return shell;
169178
}
170179

@@ -177,25 +186,25 @@ private Shell openConentAssist(boolean expectShell) {
177186
*/
178187
private void checkCompletionContent(final Table completionProposalList) {
179188
// should be instantaneous, but happens to go asynchronous on CI so let's allow a wait
180-
waitForProposalRelatedCondition("Proposal list did not show two initial items", completionProposalList,
189+
waitForProposalRelatedCondition("Proposal list should show two initial items", completionProposalList,
181190
() -> completionProposalList.getItemCount() == 2, 200);
182191
assertTrue("Missing computing info entry", isComputingInfoEntry(completionProposalList.getItem(0)));
183-
assertTrue("Missing computing info entry in proposal list", isComputingInfoEntry(completionProposalList.getItem(0)));
184192
final TableItem initialProposalItem = completionProposalList.getItem(1);
185193
final String initialProposalString = ((ICompletionProposal)initialProposalItem.getData()).getDisplayString();
186194
assertThat("Unexpected initial proposal item",
187195
BarContentAssistProcessor.PROPOSAL, endsWith(initialProposalString));
188196
completionProposalList.setSelection(initialProposalItem);
197+
198+
LongRunningBarContentAssistProcessor.finish();
189199
// asynchronous
190-
waitForProposalRelatedCondition("Proposal list did not show two items after finishing computing", completionProposalList,
191-
() -> !isComputingInfoEntry(completionProposalList.getItem(0)) && completionProposalList.getItemCount() == 2,
192-
LongRunningBarContentAssistProcessor.DELAY + 200);
200+
waitForProposalRelatedCondition("Proposal list should contain two items", completionProposalList,
201+
() -> !isComputingInfoEntry(completionProposalList.getItem(0)) && completionProposalList.getItemCount() == 2, 5000);
193202
final TableItem firstCompletionProposalItem = completionProposalList.getItem(0);
194203
final TableItem secondCompletionProposalItem = completionProposalList.getItem(1);
195204
String firstCompletionProposalText = ((ICompletionProposal)firstCompletionProposalItem.getData()).getDisplayString();
196-
String secondCOmpletionProposalText = ((ICompletionProposal)secondCompletionProposalItem.getData()).getDisplayString();
205+
String secondCompletionProposalText = ((ICompletionProposal)secondCompletionProposalItem.getData()).getDisplayString();
197206
assertThat("Unexpected first proposal item", BarContentAssistProcessor.PROPOSAL, endsWith(firstCompletionProposalText));
198-
assertThat("Unexpected second proposal item", LongRunningBarContentAssistProcessor.PROPOSAL, endsWith(secondCOmpletionProposalText));
207+
assertThat("Unexpected second proposal item", LongRunningBarContentAssistProcessor.PROPOSAL, endsWith(secondCompletionProposalText));
199208
String selectedProposalString = ((ICompletionProposal)completionProposalList.getSelection()[0].getData()).getDisplayString();
200209
assertEquals("Addition of completion proposal should keep selection", initialProposalString, selectedProposalString);
201210
}
@@ -204,24 +213,23 @@ private static boolean isComputingInfoEntry(TableItem item) {
204213
return item.getText().contains("Computing");
205214
}
206215

207-
public static Shell findNewShell(Set<Shell> beforeShells, Display display, boolean expectShell) {
216+
public static Shell findNewShell(Set<Shell> beforeShells, Display display) {
208217
Shell[] afterShells = Arrays.stream(display.getShells())
209218
.filter(Shell::isVisible)
210219
.filter(shell -> !beforeShells.contains(shell))
211220
.toArray(Shell[]::new);
212-
if (expectShell) {
213-
assertEquals("No new shell found", 1, afterShells.length);
214-
}
221+
assertTrue("More than one new shell was found", afterShells.length <= 1);
215222
return afterShells.length > 0 ? afterShells[0] : null;
216223
}
217224

218225
@Test
219226
public void testCompletionFreeze_bug521484() throws Exception {
220227
editor.selectAndReveal(3, 0);
221-
this.completionShell=openConentAssist();
222-
final Table completionProposalList = findCompletionSelectionControl(this.completionShell);
228+
final Shell shell = openContentAssistWithLongRunningProposalComputation();
229+
assertNotNull("Shell is expected to open for completion proposals", shell);
230+
final Table completionProposalList = findCompletionSelectionControl(shell);
223231
// should be instantaneous, but happens to go asynchronous on CI so let's allow a wait
224-
waitForProposalRelatedCondition("Proposal list did not show two items", completionProposalList,
232+
waitForProposalRelatedCondition("Proposal list should show two items", completionProposalList,
225233
() -> completionProposalList.getItemCount() == 2, 200);
226234
assertTrue("Missing computing info entry", isComputingInfoEntry(completionProposalList.getItem(0)));
227235
// Some processors are long running, moving cursor can cause freeze (bug 521484)
@@ -230,18 +238,24 @@ public void testCompletionFreeze_bug521484() throws Exception {
230238
emulatePressLeftArrowKey();
231239
DisplayHelper.sleep(editor.getSite().getShell().getDisplay(), 200); //give time to process events
232240
long processingDuration = System.currentTimeMillis() - timestamp;
233-
assertTrue("UI Thread frozen for " + processingDuration + "ms", processingDuration < LongRunningBarContentAssistProcessor.DELAY);
241+
assertTrue("UI Thread frozen for " + processingDuration + "ms", processingDuration < LongRunningBarContentAssistProcessor.TIMEOUT_MSEC);
234242
}
235243

236244
@Test
237245
public void testMoveCaretBackUsesAllProcessors_bug522255() throws Exception {
238-
testCompletion();
246+
editor.selectAndReveal(3, 0);
247+
Shell shell = openContentAssistWithLongRunningProposalComputation();
248+
final Table completionProposalList = findCompletionSelectionControl(shell);
249+
checkCompletionContent(completionProposalList);
250+
LongRunningBarContentAssistProcessor.enable();
239251
emulatePressLeftArrowKey();
240252
final Set<Shell> beforeShells = Arrays.stream(editor.getSite().getShell().getDisplay().getShells()).filter(Shell::isVisible).collect(Collectors.toSet());
241253
DisplayHelper.sleep(editor.getSite().getShell().getDisplay(), 200);
242-
this.completionShell= findNewShell(beforeShells, editor.getSite().getShell().getDisplay(), true);
243-
final Table completionProposalList = findCompletionSelectionControl(this.completionShell);
244-
checkCompletionContent(completionProposalList);
254+
assertTrue("Completion proposal shell should be disposed after moving the cusor", shell.isDisposed());
255+
this.completionShell = findNewShell(beforeShells, editor.getSite().getShell().getDisplay());
256+
assertNotNull("Shell is expected to open for completion proposals", completionShell);
257+
final Table newCompletionProposalList = findCompletionSelectionControl(completionShell);
258+
checkCompletionContent(newCompletionProposalList);
245259
}
246260

247261
private void emulatePressLeftArrowKey() {
@@ -276,6 +290,11 @@ public void closeShell() {
276290
completionShell.close();
277291
}
278292
}
293+
294+
@After
295+
public void stopLongRunningCompletionProposalProcessor() {
296+
LongRunningBarContentAssistProcessor.finish();
297+
}
279298

280299
private static final class TestLogListener implements ILogListener {
281300

tests/org.eclipse.ui.genericeditor.tests/src/org/eclipse/ui/genericeditor/tests/TestQuickAssist.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
package org.eclipse.ui.genericeditor.tests;
1616

1717
import static org.junit.Assert.assertEquals;
18+
import static org.junit.Assert.assertNotNull;
1819
import static org.junit.Assert.assertTrue;
1920

2021
import java.util.Arrays;
@@ -103,7 +104,8 @@ private Shell openQuickAssist() {
103104
action.update();
104105
final Set<Shell> beforeShells = Arrays.stream(editor.getSite().getShell().getDisplay().getShells()).filter(Shell::isVisible).collect(Collectors.toSet());
105106
action.run();
106-
Shell shell= CompletionTest.findNewShell(beforeShells, editor.getSite().getShell().getDisplay(), true);
107+
Shell shell= CompletionTest.findNewShell(beforeShells, editor.getSite().getShell().getDisplay());
108+
assertNotNull("Shell is expected to open for quick assist", shell);
107109
waitAndDispatch(100);
108110
return shell;
109111
}

tests/org.eclipse.ui.genericeditor.tests/src/org/eclipse/ui/genericeditor/tests/contributions/LongRunningBarContentAssistProcessor.java

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,20 +19,32 @@
1919
public class LongRunningBarContentAssistProcessor extends BarContentAssistProcessor {
2020

2121
public static final String PROPOSAL = "bars are also good for soft drink cocktails.";
22-
public static final int DELAY = 2000;
23-
22+
public static final int TIMEOUT_MSEC = 10_000;
23+
private static boolean running = false;
24+
2425
public LongRunningBarContentAssistProcessor() {
2526
super(PROPOSAL);
2627
}
2728

2829
@Override
2930
public ICompletionProposal[] computeCompletionProposals(ITextViewer viewer, int offset) {
3031
try {
31-
Thread.sleep(DELAY);
32+
long startExecutionTime = System.currentTimeMillis();
33+
while (running && (System.currentTimeMillis() - startExecutionTime) < TIMEOUT_MSEC) {
34+
Thread.sleep(20);
35+
}
3236
} catch (InterruptedException e) {
33-
// TODO Auto-generated catch block
34-
e.printStackTrace();
37+
// Just finish on unexpected interrupt
3538
}
3639
return super.computeCompletionProposals(viewer, offset);
3740
}
41+
42+
public static void enable() {
43+
running = true;
44+
}
45+
46+
public static void finish() {
47+
running = false;
48+
}
49+
3850
}

0 commit comments

Comments
 (0)