Skip to content

Commit bcc5063

Browse files
committed
Text control should not redraw indefinitely
In some scenarios, `Text` control redraws on every event loop iteration. See eclipse-platform/eclipse.platform.ui#3920 This is a JUnit test reproducing the problem.
1 parent f03d65d commit bcc5063

2 files changed

Lines changed: 89 additions & 1 deletion

File tree

tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/SwtTestUtil.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ public class SwtTestUtil {
8383

8484
// used to specify verbose mode, if true unimplemented warning messages will
8585
// be written to System.out
86-
public static boolean verbose = false;
86+
public static boolean verbose = true;
8787

8888
// allow specific image formats to be tested
8989
public static String[] imageFormats = new String[] {"bmp", "jpg", "gif", "png"};

tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_widgets_Text.java

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,14 @@
1313
*******************************************************************************/
1414
package org.eclipse.swt.tests.junit;
1515

16+
import static java.lang.System.currentTimeMillis;
1617
import static org.eclipse.swt.tests.junit.SwtTestUtil.JENKINS_DETECT_ENV_VAR;
1718
import static org.eclipse.swt.tests.junit.SwtTestUtil.JENKINS_DETECT_REGEX;
1819
import static org.junit.jupiter.api.Assertions.assertEquals;
1920
import static org.junit.jupiter.api.Assertions.assertFalse;
2021
import static org.junit.jupiter.api.Assertions.assertThrows;
2122
import static org.junit.jupiter.api.Assertions.assertTrue;
23+
import static org.junit.jupiter.api.Assertions.fail;
2224

2325
import org.eclipse.swt.SWT;
2426
import org.eclipse.swt.events.ModifyListener;
@@ -30,6 +32,7 @@
3032
import org.eclipse.swt.graphics.Font;
3133
import org.eclipse.swt.graphics.FontData;
3234
import org.eclipse.swt.graphics.Point;
35+
import org.eclipse.swt.layout.FillLayout;
3336
import org.eclipse.swt.widgets.Display;
3437
import org.eclipse.swt.widgets.Event;
3538
import org.eclipse.swt.widgets.Group;
@@ -1376,6 +1379,58 @@ public void test_showSelection() {
13761379
text.showSelection();
13771380
}
13781381

1382+
// Originally reported as https://github.com/eclipse-platform/eclipse.platform.ui/issues/3920
1383+
@Test
1384+
public void test_finiteRedrawCancelButtonWithBackground() {
1385+
if ( text != null ) text.dispose();
1386+
// Style constants are causing
1387+
// org.eclipse.swt.widgets.Text.drawInteriorWithFrame_inView_searchfield(long, long, NSRect, long)
1388+
// to call
1389+
// org.eclipse.swt.internal.cocoa.NSControl.stringValue()
1390+
// which schedules redraw
1391+
text = new Text(shell, SWT.SEARCH | SWT.ICON_CANCEL);
1392+
// Background prevents early exit from drawInteriorWithFrame_inView_searchfield(long, long, NSRect, long)
1393+
text.setBackground(shell.getDisplay().getSystemColor(SWT.COLOR_RED));
1394+
setWidget(text);
1395+
shell.setLayout(new FillLayout());
1396+
text.requestLayout();
1397+
shell.open();
1398+
text.forceFocus();
1399+
waitUntilIdle();
1400+
assertIdle();
1401+
}
1402+
1403+
@Test
1404+
public void test_finiteRedrawCancelButton() {
1405+
if ( text != null ) text.dispose();
1406+
// Style constants are causing
1407+
// org.eclipse.swt.widgets.Text.drawInteriorWithFrame_inView_searchfield(long, long, NSRect, long)
1408+
// to call
1409+
// org.eclipse.swt.internal.cocoa.NSControl.stringValue()
1410+
// which schedules redraw
1411+
text = new Text(shell, SWT.SEARCH | SWT.ICON_CANCEL);
1412+
setWidget(text);
1413+
shell.setLayout(new FillLayout());
1414+
text.requestLayout();
1415+
shell.open();
1416+
text.forceFocus();
1417+
waitUntilIdle();
1418+
assertIdle();
1419+
}
1420+
1421+
@Test
1422+
public void test_finiteRedraw() {
1423+
if ( text != null ) text.dispose();
1424+
text = new Text(shell, SWT.NONE);
1425+
setWidget(text);
1426+
shell.setLayout(new FillLayout());
1427+
text.requestLayout();
1428+
shell.open();
1429+
text.forceFocus();
1430+
waitUntilIdle();
1431+
assertIdle();
1432+
}
1433+
13791434
/* custom */
13801435
Text text;
13811436
String delimiterString;
@@ -1639,4 +1694,37 @@ private void pasteFromClipboard(Text text) throws InterruptedException {
16391694
SwtTestUtil.processEvents(1000, () -> !oldText.equals(text.getText()));
16401695
}
16411696

1697+
private void waitUntilIdle() {
1698+
long hangTimeout = currentTimeMillis() + 1000;
1699+
while (shell.getDisplay().readAndDispatch()) {
1700+
if (currentTimeMillis() > hangTimeout) {
1701+
fail("UI scheduler should settle");
1702+
}
1703+
}
1704+
}
1705+
1706+
private void assertIdle() {
1707+
long start = currentTimeMillis();
1708+
long stop = start + 1000;
1709+
int eventStreakCount = 0;
1710+
while (currentTimeMillis() < stop) {
1711+
// If redraws are constantly scheduled, readAndDispatch() rarely return false.
1712+
// Side effects - high CPU usage, asyncExec() stops working in modal contexts
1713+
if (shell.getDisplay().readAndDispatch()) {
1714+
eventStreakCount++;
1715+
// Skip other events of the streak
1716+
// No need to count events individually, as many events immediately following each other are common and normal
1717+
// Currently, streaks are short, but we do not want any noise to cause test failures as UI evolves
1718+
waitUntilIdle();
1719+
} else {
1720+
Thread.yield();
1721+
}
1722+
}
1723+
String message = "Detected " + eventStreakCount + " UI event streaks per second. Expected rate: 5 event streaks per second or less.";
1724+
assertTrue(eventStreakCount < 50, message);
1725+
if (SwtTestUtil.verbose) {
1726+
System.out.println(message);
1727+
}
1728+
}
1729+
16421730
}

0 commit comments

Comments
 (0)