From 2e1e66d7b783c4211ef5f0117106e61a20bb0b21 Mon Sep 17 00:00:00 2001 From: Lars Vogel Date: Mon, 10 Aug 2026 10:13:26 +0200 Subject: [PATCH] Report per operation timings in the multi editor and perspective tests Migrates OpenMultipleEditorTest and PerspectiveSwitchTest the same way as OpenCloseEditorTest and EditorSwitchTest: drop org.eclipse.test.performance, warm up before measuring, time the individual operations and print the distribution through UIPerformanceTestUtil.reportTimings. The performance database these reported to has not been configured for years, so commitMeasurements and assertPerformance were no-ops and the tests could neither fail nor report anything. OpenMultipleEditorTest additionally reports the first and the last ten editors separately. Cost per editor grows with the number already open, and that is only visible from the two ends of the same run, which also makes the comparison immune to machine load. It also opens 90 instead of 100 editors: the workbench recycles the oldest editor once REUSE_EDITORS are open, which defaults to 99, so the hundredth open silently reused the first editor and the test measured reuse rather than opening. An assertion now pins that down. PerspectiveSwitchTest reports each switch direction separately and skips through an assumption when a perspective is missing, instead of printing to stdout and returning, which reported a pass for something never measured. Three of its four cases reference JDT perspectives and are skipped in this target platform, which is now visible in the test results. --- .../performance/OpenMultipleEditorTest.java | 105 +++++++++++++++--- .../performance/PerspectiveSwitchTest.java | 91 +++++++++------ 2 files changed, 145 insertions(+), 51 deletions(-) diff --git a/tests/org.eclipse.ui.tests.performance/src/org/eclipse/ui/tests/performance/OpenMultipleEditorTest.java b/tests/org.eclipse.ui.tests.performance/src/org/eclipse/ui/tests/performance/OpenMultipleEditorTest.java index bccbb9356f2..d87177e98d1 100644 --- a/tests/org.eclipse.ui.tests.performance/src/org/eclipse/ui/tests/performance/OpenMultipleEditorTest.java +++ b/tests/org.eclipse.ui.tests.performance/src/org/eclipse/ui/tests/performance/OpenMultipleEditorTest.java @@ -17,12 +17,17 @@ import static org.eclipse.ui.tests.harness.util.UITestUtil.openTestWindow; import static org.eclipse.ui.tests.harness.util.UITestUtil.processEvents; import static org.eclipse.ui.tests.performance.UIPerformanceTestRule.getTestProject; +import static org.eclipse.ui.tests.performance.UIPerformanceTestUtil.reportTimings; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.List; import org.eclipse.core.resources.IFile; -import org.eclipse.test.performance.PerformanceTestCaseJunit4; +import org.eclipse.ui.IEditorPart; import org.eclipse.ui.IEditorReference; import org.eclipse.ui.IWorkbenchPage; import org.eclipse.ui.IWorkbenchWindow; @@ -36,10 +41,30 @@ import org.junit.runners.Parameterized.Parameters; /** + * Measures opening many editors and closing them again. Reports the cost of the + * first and the last editors separately, so that a cost that grows with the + * number of open editors is visible. + * * @since 3.1 */ @RunWith(Parameterized.class) -public class OpenMultipleEditorTest extends PerformanceTestCaseJunit4 { +public class OpenMultipleEditorTest { + + /** + * Stays below the REUSE_EDITORS threshold, which defaults to 99. Above it the + * workbench recycles the oldest editor, so further opens would measure reuse + * rather than opening. + */ + private static final int EDITOR_COUNT = 90; + + /** + * Enough editors to get the editor implementation loaded and compiled. Below + * roughly this many, the reported times are dominated by JIT warm-up. + */ + private static final int WARMUP_EDITORS = 20; + + /** How many editors at each end of the run are reported on their own. */ + private static final int SEGMENT = 10; @ClassRule public static final UIPerformanceTestRule uiPerformanceTestRule = new UIPerformanceTestRule(); @@ -50,7 +75,7 @@ public class OpenMultipleEditorTest extends PerformanceTestCaseJunit4 { private final String extension; private final boolean closeAll; - @Parameters(name = "{index}: closeAll: {0} - closeEach: {1}") + @Parameters(name = "{index}: {0} - closeAll: {1}") public static Collection data() { return Arrays.asList(new Object[][] { { "perf_basic", true }, { "perf_outline", true }, { "perf_text", true }, { "perf_basic", false }, { "perf_outline", false }, { "perf_text", false } }); @@ -64,27 +89,71 @@ public OpenMultipleEditorTest(String extension, boolean closeAll) { @Test public void test() throws Throwable { IWorkbenchWindow window = openTestWindow(UIPerformanceTestRule.PERSPECTIVE1); - IWorkbenchPage activePage = window.getActivePage(); + IWorkbenchPage page = window.getActivePage(); - startMeasuring(); + // Class loading and JIT warm-up would otherwise end up in the reported times. + openEditors(page, WARMUP_EDITORS, null); + page.closeAllEditors(false); + processEvents(); + EditorTestHelper.calmDown(500, 30000, 500); + + List openTimes = new ArrayList<>(); + openEditors(page, EDITOR_COUNT, openTimes); + assertEquals("Not all editors stayed open. The workbench recycles the oldest editor once REUSE_EDITORS" + + " (default 99) are open, so EDITOR_COUNT has to stay below that threshold.", EDITOR_COUNT, + page.getEditorReferences().length); + + String label = extension + (closeAll ? ", closeAll" : ", closeEach"); + reportTimings("openMultiple[" + label + "] open", openTimes); + reportTimings("openMultiple[" + label + "] open, first " + SEGMENT, openTimes.subList(0, SEGMENT)); + reportTimings("openMultiple[" + label + "] open, last " + SEGMENT, + openTimes.subList(EDITOR_COUNT - SEGMENT, EDITOR_COUNT)); - for (int i = 0; i < 100; i++) { - IFile file = getTestProject().getFile(i + "." + extension); - IDE.openEditor(activePage, file, true); - processEvents(); - } if (closeAll) { - activePage.closeAllEditors(false); + long before = System.nanoTime(); + page.closeAllEditors(false); + processEvents(); + reportTimings("openMultiple[" + label + "] closeAllEditors", List.of(System.nanoTime() - before)); + } else { + List closeTimes = closeEditorsIndividually(page); + reportTimings("openMultiple[" + label + "] close", closeTimes); + reportTimings("openMultiple[" + label + "] close, first " + SEGMENT, closeTimes.subList(0, SEGMENT)); + reportTimings("openMultiple[" + label + "] close, last " + SEGMENT, + closeTimes.subList(EDITOR_COUNT - SEGMENT, EDITOR_COUNT)); } - else { - IEditorReference[] parts = activePage.getEditorReferences(); - for (IEditorReference part : parts) { - activePage.closeEditor(part.getEditor(false), false); + assertEquals("Not all editors were closed", 0, page.getEditorReferences().length); + } + + /** + * Opens {@code count} editors, recording each open when the given list is not + * {@code null}. + */ + private void openEditors(IWorkbenchPage page, int count, List openTimes) throws Exception { + for (int i = 0; i < count; i++) { + IFile file = getTestProject().getFile(i + "." + extension); + long before = System.nanoTime(); + IEditorPart part = IDE.openEditor(page, file, true); + processEvents(); + long after = System.nanoTime(); + assertNotNull("No editor opened for " + file.getName(), part); + + if (openTimes != null) { + openTimes.add(after - before); } } - stopMeasuring(); - commitMeasurements(); - assertPerformance(); } + private static List closeEditorsIndividually(IWorkbenchPage page) { + List closeTimes = new ArrayList<>(); + for (IEditorReference reference : page.getEditorReferences()) { + IEditorPart part = reference.getEditor(false); + assertNotNull("Editor was never created: " + reference.getId(), part); + + long before = System.nanoTime(); + page.closeEditor(part, false); + processEvents(); + closeTimes.add(System.nanoTime() - before); + } + return closeTimes; + } } diff --git a/tests/org.eclipse.ui.tests.performance/src/org/eclipse/ui/tests/performance/PerspectiveSwitchTest.java b/tests/org.eclipse.ui.tests.performance/src/org/eclipse/ui/tests/performance/PerspectiveSwitchTest.java index c82149dd4f1..7bd3f6a7bb8 100644 --- a/tests/org.eclipse.ui.tests.performance/src/org/eclipse/ui/tests/performance/PerspectiveSwitchTest.java +++ b/tests/org.eclipse.ui.tests.performance/src/org/eclipse/ui/tests/performance/PerspectiveSwitchTest.java @@ -17,15 +17,19 @@ import static org.eclipse.ui.tests.harness.util.UITestUtil.processEvents; import static org.eclipse.ui.tests.performance.UIPerformanceTestRule.getTestProject; import static org.eclipse.ui.tests.performance.UIPerformanceTestUtil.exercise; +import static org.eclipse.ui.tests.performance.UIPerformanceTestUtil.reportTimings; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; +import static org.junit.Assume.assumeTrue; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.List; import org.eclipse.core.resources.IFile; import org.eclipse.core.runtime.CoreException; -import org.eclipse.test.performance.PerformanceTestCaseJunit4; import org.eclipse.ui.IPerspectiveDescriptor; import org.eclipse.ui.IPerspectiveRegistry; import org.eclipse.ui.IWorkbenchPage; @@ -42,10 +46,23 @@ import org.junit.runners.Parameterized.Parameters; /** - * Test perspective switching. + * Measures switching back and forth between two perspectives, and reports each + * switch direction separately. */ @RunWith(Parameterized.class) -public class PerspectiveSwitchTest extends PerformanceTestCaseJunit4 { +public class PerspectiveSwitchTest { + + /** + * Enough switches to get the participating parts loaded and compiled. Below + * roughly this many, the reported times are dominated by JIT warm-up. + */ + private static final int WARMUP_PAIRS = 5; + + private static final int MIN_PAIRS = 5; + + private static final int MAX_PAIRS = 50; + + private static final int MAX_MEASURE_TIME_MS = 12000; @ClassRule public static final UIPerformanceTestRule uiPerformanceTestRule = new UIPerformanceTestRule(); @@ -80,9 +97,6 @@ public PerspectiveSwitchTest(String id1, String id2, String activeEditor) { this.activeEditor = activeEditor; } - /** - * Test perspective switching performance. - */ @Test public void test() throws CoreException, WorkbenchException { // Get the two perspectives to switch between. @@ -90,18 +104,11 @@ public void test() throws CoreException, WorkbenchException { final IPerspectiveDescriptor perspective1 = registry.findPerspectiveWithId(id1); final IPerspectiveDescriptor perspective2 = registry.findPerspectiveWithId(id2); - // Don't fail if we reference an unknown perspective ID. This can be - // a normal occurrance since the test suites reference JDT perspectives, which - // might not exist. Just skip the test. - if (perspective1 == null) { - System.out.println("Unknown perspective ID: " + id1); - return; - } - - if (perspective2 == null) { - System.out.println("Unknown perspective ID: " + id2); - return; - } + // The parameters reference JDT perspectives, which are not part of every target + // platform. Skip visibly rather than reporting a pass for something that was + // never measured. + assumeTrue("Perspective not available: " + id1, perspective1 != null); + assumeTrue("Perspective not available: " + id2, perspective2 != null); // Open the two perspectives and the file, in a new window. // Do this outside the loop so as not to include @@ -111,25 +118,43 @@ public void test() throws CoreException, WorkbenchException { assertNotNull(page); page.setPerspective(perspective2); - // IFile aFile = getProject().getFile("1." + - // EditorPerformanceSuite.EDITOR_FILE_EXTENSIONS[0]); IFile aFile = getTestProject().getFile(activeEditor); - assertTrue(aFile.exists()); + assertTrue("Missing test file " + activeEditor + ", the fixture does not create it", aFile.exists()); IDE.openEditor(page, aFile, true); + // Class loading and JIT warm-up would otherwise end up in the reported times. + for (int i = 0; i < WARMUP_PAIRS; i++) { + switchTo(page, perspective1, null); + switchTo(page, perspective2, null); + } + EditorTestHelper.calmDown(500, 30000, 500); + + List toFirst = new ArrayList<>(); + List toSecond = new ArrayList<>(); + exercise(() -> { - processEvents(); - - startMeasuring(); - page.setPerspective(perspective1); - processEvents(); - page.setPerspective(perspective2); - processEvents(); - stopMeasuring(); - }); - - commitMeasurements(); - assertPerformance(); + switchTo(page, perspective1, toFirst); + switchTo(page, perspective2, toSecond); + }, MIN_PAIRS, MAX_PAIRS, MAX_MEASURE_TIME_MS); + + reportTimings("PerspectiveSwitch to [" + id1 + "]", toFirst); + reportTimings("PerspectiveSwitch to [" + id2 + "]", toSecond); + } + + /** + * Switches to the given perspective, recording the time when the given list is + * not {@code null}. + */ + private static void switchTo(IWorkbenchPage page, IPerspectiveDescriptor perspective, List times) { + long before = System.nanoTime(); + page.setPerspective(perspective); + processEvents(); + long after = System.nanoTime(); + assertEquals("Wrong perspective active", perspective, page.getPerspective()); + + if (times != null) { + times.add(after - before); + } } }