|
13 | 13 | *******************************************************************************/ |
14 | 14 | package org.eclipse.swt.tests.junit; |
15 | 15 |
|
| 16 | +import static java.lang.System.currentTimeMillis; |
16 | 17 | import static org.eclipse.swt.tests.junit.SwtTestUtil.JENKINS_DETECT_ENV_VAR; |
17 | 18 | import static org.eclipse.swt.tests.junit.SwtTestUtil.JENKINS_DETECT_REGEX; |
18 | 19 | import static org.junit.jupiter.api.Assertions.assertEquals; |
19 | 20 | import static org.junit.jupiter.api.Assertions.assertFalse; |
20 | 21 | import static org.junit.jupiter.api.Assertions.assertThrows; |
21 | 22 | import static org.junit.jupiter.api.Assertions.assertTrue; |
| 23 | +import static org.junit.jupiter.api.Assertions.fail; |
22 | 24 |
|
23 | 25 | import org.eclipse.swt.SWT; |
24 | 26 | import org.eclipse.swt.events.ModifyListener; |
|
30 | 32 | import org.eclipse.swt.graphics.Font; |
31 | 33 | import org.eclipse.swt.graphics.FontData; |
32 | 34 | import org.eclipse.swt.graphics.Point; |
| 35 | +import org.eclipse.swt.layout.FillLayout; |
33 | 36 | import org.eclipse.swt.widgets.Display; |
34 | 37 | import org.eclipse.swt.widgets.Event; |
35 | 38 | import org.eclipse.swt.widgets.Group; |
@@ -1376,6 +1379,58 @@ public void test_showSelection() { |
1376 | 1379 | text.showSelection(); |
1377 | 1380 | } |
1378 | 1381 |
|
| 1382 | +// Originally reported as https://github.com/eclipse-platform/eclipse.platform.ui/issues/3920 |
| 1383 | +@Test |
| 1384 | +public void test_finiteRedrawCancelButtonWithBackground() throws InterruptedException { |
| 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() throws InterruptedException { |
| 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() throws InterruptedException { |
| 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 | + |
1379 | 1434 | /* custom */ |
1380 | 1435 | Text text; |
1381 | 1436 | String delimiterString; |
@@ -1639,4 +1694,62 @@ private void pasteFromClipboard(Text text) throws InterruptedException { |
1639 | 1694 | SwtTestUtil.processEvents(1000, () -> !oldText.equals(text.getText())); |
1640 | 1695 | } |
1641 | 1696 |
|
| 1697 | + |
| 1698 | +private void processEvents() { |
| 1699 | + long hangTimeout = currentTimeMillis() + 1000; |
| 1700 | + while (currentTimeMillis() < hangTimeout) { |
| 1701 | + if (!shell.getDisplay().readAndDispatch()) { |
| 1702 | + return; |
| 1703 | + } |
| 1704 | + } |
| 1705 | + fail("UI scheduler should settle"); |
| 1706 | +} |
| 1707 | + |
| 1708 | +private void waitUntilIdle() { |
| 1709 | + long hangTimeout = currentTimeMillis() + 1000; |
| 1710 | + long lastActive = currentTimeMillis(); |
| 1711 | + while (currentTimeMillis() < hangTimeout) { |
| 1712 | + if (shell.getDisplay().readAndDispatch()) { |
| 1713 | + lastActive = currentTimeMillis(); |
| 1714 | + } else { |
| 1715 | + if (lastActive < currentTimeMillis() - 1) { |
| 1716 | + return; |
| 1717 | + } |
| 1718 | + Thread.yield(); |
| 1719 | + } |
| 1720 | + } |
| 1721 | + fail("Unexpected system events keep coming"); |
| 1722 | +} |
| 1723 | + |
| 1724 | +private void assertIdle() throws InterruptedException { |
| 1725 | + long start = currentTimeMillis(); |
| 1726 | + long stop = start + 1000; |
| 1727 | + int eventStreakCount = 0; |
| 1728 | + int idleIterations = 0; |
| 1729 | + while (currentTimeMillis() < stop) { |
| 1730 | + idleIterations++; |
| 1731 | + // If redraws are constantly scheduled, readAndDispatch() rarely return false. |
| 1732 | + // Side effects - high CPU usage, asyncExec() stops working in modal contexts |
| 1733 | + if (shell.getDisplay().readAndDispatch()) { |
| 1734 | + eventStreakCount++; |
| 1735 | + // Skip other events of the streak |
| 1736 | + // No need to count events individually, as many events immediately following each other are common and normal |
| 1737 | + // Currently, streaks are short, but we do not want any noise to cause test failures as UI evolves |
| 1738 | + processEvents(); |
| 1739 | + } else { |
| 1740 | + Thread.yield(); |
| 1741 | + } |
| 1742 | + } |
| 1743 | + double intensity = ((double)eventStreakCount/idleIterations); |
| 1744 | + String message = "Detected %d/%d UI event streaks/idle event loop iterations per second, intensity %.2g. Expected: 2/100_000."; |
| 1745 | + |
| 1746 | + message = message.formatted(eventStreakCount, idleIterations, intensity); |
| 1747 | + assertTrue(eventStreakCount < 10, message); |
| 1748 | + assertTrue(idleIterations > 1000, message); |
| 1749 | + assertTrue(intensity < 1e-4, message); |
| 1750 | + if (SwtTestUtil.verbose) { |
| 1751 | + System.out.println(message); |
| 1752 | + } |
| 1753 | +} |
| 1754 | + |
1642 | 1755 | } |
0 commit comments