From 6f6daeb69c8612f1b0494cc969368934624406d4 Mon Sep 17 00:00:00 2001 From: Adam Wisniewski Date: Mon, 10 Nov 2025 12:04:34 -0500 Subject: [PATCH 01/12] Update to 25.0.12 --- .../META-INF/MANIFEST.MF | 2 +- bundles/io.openliberty.tools.eclipse.lsp4e/pom.xml | 2 +- .../META-INF/MANIFEST.MF | 2 +- .../io.openliberty.tools.eclipse.ui/META-INF/MANIFEST.MF | 2 +- features/io.openliberty.tools.eclipse.feature/feature.xml | 8 ++++---- pom.xml | 4 ++-- releng/io.openliberty.tools.update/category.xml | 2 +- tests/META-INF/MANIFEST.MF | 2 +- tests/pom.xml | 2 +- 9 files changed, 13 insertions(+), 13 deletions(-) diff --git a/bundles/io.openliberty.tools.eclipse.lsp4e/META-INF/MANIFEST.MF b/bundles/io.openliberty.tools.eclipse.lsp4e/META-INF/MANIFEST.MF index b49798f3..feba70e0 100644 --- a/bundles/io.openliberty.tools.eclipse.lsp4e/META-INF/MANIFEST.MF +++ b/bundles/io.openliberty.tools.eclipse.lsp4e/META-INF/MANIFEST.MF @@ -4,7 +4,7 @@ Bundle-ManifestVersion: 2 Bundle-Name: Liberty Tools Support for Language Servers Bundle-Vendor: Open Liberty Bundle-SymbolicName: io.openliberty.tools.eclipse.lsp4e;singleton:=true -Bundle-Version: 25.0.8.qualifier +Bundle-Version: 25.0.12.qualifier Bundle-Activator: io.openliberty.tools.eclipse.ls.plugin.LibertyToolsLSPlugin Bundle-ActivationPolicy: lazy Automatic-Module-Name: io.openliberty.tools.eclipse.lsp4e diff --git a/bundles/io.openliberty.tools.eclipse.lsp4e/pom.xml b/bundles/io.openliberty.tools.eclipse.lsp4e/pom.xml index 0e381f5a..f7eff829 100644 --- a/bundles/io.openliberty.tools.eclipse.lsp4e/pom.xml +++ b/bundles/io.openliberty.tools.eclipse.lsp4e/pom.xml @@ -20,7 +20,7 @@ io.openliberty.tools.eclipse parent - 25.0.8-SNAPSHOT + 25.0.12-SNAPSHOT ../../pom.xml diff --git a/bundles/io.openliberty.tools.eclipse.product/META-INF/MANIFEST.MF b/bundles/io.openliberty.tools.eclipse.product/META-INF/MANIFEST.MF index accbaf63..8849879f 100644 --- a/bundles/io.openliberty.tools.eclipse.product/META-INF/MANIFEST.MF +++ b/bundles/io.openliberty.tools.eclipse.product/META-INF/MANIFEST.MF @@ -4,7 +4,7 @@ Bundle-ManifestVersion: 2 Bundle-Name: Liberty Tools Bundle-Vendor: Open Liberty Bundle-SymbolicName: io.openliberty.tools.eclipse.product;singleton:=true -Bundle-Version: 25.0.8.qualifier +Bundle-Version: 25.0.12.qualifier Bundle-RequiredExecutionEnvironment: JavaSE-21 Automatic-Module-Name: io.openliberty.tools.eclipse Bundle-ActivationPolicy: lazy diff --git a/bundles/io.openliberty.tools.eclipse.ui/META-INF/MANIFEST.MF b/bundles/io.openliberty.tools.eclipse.ui/META-INF/MANIFEST.MF index 2c2a989a..2df81a4d 100644 --- a/bundles/io.openliberty.tools.eclipse.ui/META-INF/MANIFEST.MF +++ b/bundles/io.openliberty.tools.eclipse.ui/META-INF/MANIFEST.MF @@ -4,7 +4,7 @@ Bundle-ManifestVersion: 2 Bundle-Name: Liberty Tools UI Bundle-Vendor: Open Liberty Bundle-SymbolicName: io.openliberty.tools.eclipse.ui;singleton:=true -Bundle-Version: 25.0.8.qualifier +Bundle-Version: 25.0.12.qualifier Bundle-Activator: io.openliberty.tools.eclipse.LibertyDevPlugin Export-Package: io.openliberty.tools.eclipse;x-friends:="io.openliberty.tools.eclipse.tests", io.openliberty.tools.eclipse.debug;x-friends:="io.openliberty.tools.eclipse.tests", diff --git a/features/io.openliberty.tools.eclipse.feature/feature.xml b/features/io.openliberty.tools.eclipse.feature/feature.xml index b9e939f0..c79341a8 100644 --- a/features/io.openliberty.tools.eclipse.feature/feature.xml +++ b/features/io.openliberty.tools.eclipse.feature/feature.xml @@ -14,7 +14,7 @@ @@ -28,21 +28,21 @@ id="io.openliberty.tools.eclipse.ui" download-size="0" install-size="0" - version="25.0.8.qualifier" + version="25.0.12.qualifier" unpack="false"/> diff --git a/pom.xml b/pom.xml index e3e9afa5..b4017f92 100644 --- a/pom.xml +++ b/pom.xml @@ -15,7 +15,7 @@ 4.0.0 io.openliberty.tools.eclipse parent - 25.0.8-SNAPSHOT + 25.0.12-SNAPSHOT pom @@ -196,7 +196,7 @@ io.openliberty.tools.eclipse target-platform-${eclipse.target} - 25.0.8-SNAPSHOT + 25.0.12-SNAPSHOT target-platform-${eclipse.target} diff --git a/releng/io.openliberty.tools.update/category.xml b/releng/io.openliberty.tools.update/category.xml index 1d32e25f..e01c58c0 100644 --- a/releng/io.openliberty.tools.update/category.xml +++ b/releng/io.openliberty.tools.update/category.xml @@ -12,7 +12,7 @@ IBM Corporation - initial implementation --> - + diff --git a/tests/META-INF/MANIFEST.MF b/tests/META-INF/MANIFEST.MF index 5cdc4d09..2ae3bd85 100644 --- a/tests/META-INF/MANIFEST.MF +++ b/tests/META-INF/MANIFEST.MF @@ -3,7 +3,7 @@ Bundle-Copyright: Copyright (c) 2022, 2025 IBM Corporation and others. Bundle-ManifestVersion: 2 Bundle-Name: Tests Plug-in Bundle-SymbolicName: io.openliberty.tools.eclipse.tests -Bundle-Version: 25.0.8.qualifier +Bundle-Version: 25.0.12.qualifier Bundle-RequiredExecutionEnvironment: JavaSE-21 Require-Bundle: junit-jupiter-api, org.eclipse.ui, diff --git a/tests/pom.xml b/tests/pom.xml index dbb3a409..53a4fad5 100755 --- a/tests/pom.xml +++ b/tests/pom.xml @@ -17,7 +17,7 @@ io.openliberty.tools.eclipse parent - 25.0.8-SNAPSHOT + 25.0.12-SNAPSHOT io.openliberty.tools.eclipse.tests From 6655dc391ce9cd11a2e987c59638b48ff3e8a955 Mon Sep 17 00:00:00 2001 From: Adam Wisniewski Date: Mon, 10 Nov 2025 14:30:34 -0500 Subject: [PATCH 02/12] Use eclipse preference store to avoid UI failures in github actions runs Signed-off-by: Adam Wisniewski --- .../test/it/utils/SWTBotPluginOperations.java | 93 ++++++++++--------- 1 file changed, 49 insertions(+), 44 deletions(-) diff --git a/tests/src/main/java/io/openliberty/tools/eclipse/test/it/utils/SWTBotPluginOperations.java b/tests/src/main/java/io/openliberty/tools/eclipse/test/it/utils/SWTBotPluginOperations.java index 7dbc1689..aae52564 100644 --- a/tests/src/main/java/io/openliberty/tools/eclipse/test/it/utils/SWTBotPluginOperations.java +++ b/tests/src/main/java/io/openliberty/tools/eclipse/test/it/utils/SWTBotPluginOperations.java @@ -389,56 +389,61 @@ public static SWTBotMenu getAppDebugAsMenu(SWTWorkbenchBot bot, String item) { * @param buildTool the build tool to be used (Maven or Gradle) */ public static void setBuildCmdPathInPreferences(SWTWorkbenchBot bot, String buildTool) { - - /* Preferences are accessed from a different menu on macOS than on Windows and Linux */ - /* Currently not possible to access the Preferences dialog panel on macOS so we */ - /* will return and just use an app configured with a wrapper */ - if (Platform.getOS().equals(Platform.OS_MACOSX)) { - return; + // Use Eclipse preference store API directly instead of UI navigation + // This avoids issues with menu accessibility in headless CI environments + + String finalMvnExecutableLoc = AbstractLibertyPluginSWTBotTest.getMvnCmdPath(); + String finalGradleExecutableLoc = AbstractLibertyPluginSWTBotTest.getGradleCmdPath(); + + // Get the preference store for the Liberty Tools plugin + org.eclipse.jface.preference.IPreferenceStore prefStore = + new org.eclipse.ui.preferences.ScopedPreferenceStore( + org.eclipse.core.runtime.preferences.InstanceScope.INSTANCE, + "io.openliberty.tools.eclipse.ui"); + + if ("Maven".equals(buildTool)) { + prefStore.setValue("MVNPATH", finalMvnExecutableLoc); + } else if ("Gradle".equals(buildTool)) { + prefStore.setValue("GRADLEPATH", finalGradleExecutableLoc); } - - String finalMvnExecutableLoc = null; - String finalGradleExecutableLoc = null; - Object locationLabel = null; - Object locationText = null; - - finalMvnExecutableLoc = AbstractLibertyPluginSWTBotTest.getMvnCmdPath(); - finalGradleExecutableLoc = AbstractLibertyPluginSWTBotTest.getGradleCmdPath(); - - Object windowMenu = findGlobal("Window", Option.factory().widgetClass(MenuItem.class).build()); - goMenuItem(windowMenu, "Preferences"); - - TreeItem liberty = (TreeItem) findGlobal("Liberty", Option.factory().widgetClass(TreeItem.class).build()); - go(liberty); - if (buildTool == "Maven") { - locationLabel = findGlobal("Maven Install Location:", Option.factory().widgetClass(Label.class).build()); - locationText = ControlFinder.findControlInRange(locationLabel, Text.class, Direction.EAST); - set(locationText, finalMvnExecutableLoc); - } else if (buildTool == "Gradle") { - locationLabel = findGlobal("Gradle Install Location:", Option.factory().widgetClass(Label.class).build()); - locationText = ControlFinder.findControlInRange(locationLabel, Text.class, Direction.EAST); - set(locationText, finalGradleExecutableLoc); + + // Save the preference store + if (prefStore instanceof org.eclipse.ui.preferences.ScopedPreferenceStore) { + try { + ((org.eclipse.ui.preferences.ScopedPreferenceStore) prefStore).save(); + } catch (java.io.IOException e) { + System.err.println("Failed to save preferences: " + e.getMessage()); + e.printStackTrace(); + } } - - goGlobal("Apply and Close"); } public static void unsetBuildCmdPathInPreferences(SWTWorkbenchBot bot, String buildTool) { - - /* Preferences are accessed from a different menu on macOS than on Windows and Linux */ - /* Currently not possible to access the Preferences dialog panel on macOS so we */ - /* will return and just use an app configured with a wrapper */ - if (Platform.getOS().equals(Platform.OS_MACOSX)) { - return; + // Use Eclipse preference store API directly instead of UI navigation + // This avoids issues with menu accessibility in headless CI environments + + // Get the preference store for the Liberty Tools plugin + org.eclipse.jface.preference.IPreferenceStore prefStore = + new org.eclipse.ui.preferences.ScopedPreferenceStore( + org.eclipse.core.runtime.preferences.InstanceScope.INSTANCE, + "io.openliberty.tools.eclipse.ui"); + + // Reset to default values (empty strings) + if ("Maven".equals(buildTool)) { + prefStore.setToDefault("MVNPATH"); + } else if ("Gradle".equals(buildTool)) { + prefStore.setToDefault("GRADLEPATH"); + } + + // Save the preference store + if (prefStore instanceof org.eclipse.ui.preferences.ScopedPreferenceStore) { + try { + ((org.eclipse.ui.preferences.ScopedPreferenceStore) prefStore).save(); + } catch (java.io.IOException e) { + System.err.println("Failed to save preferences: " + e.getMessage()); + e.printStackTrace(); + } } - - Object windowMenu = findGlobal("Window", Option.factory().widgetClass(MenuItem.class).build()); - goMenuItem(windowMenu, "Preferences"); - - findGlobal("Liberty", Option.factory().widgetClass(TreeItem.class).build()); - - goGlobal("Restore Defaults"); - goGlobal("Apply and Close"); } /** From 3ae4b73a8e68233a43aadd6d4a2281d8831afd63 Mon Sep 17 00:00:00 2001 From: Adam Wisniewski Date: Tue, 11 Nov 2025 16:21:31 -0500 Subject: [PATCH 03/12] Add test timeout Signed-off-by: Adam Wisniewski --- tests/pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/pom.xml b/tests/pom.xml index 53a4fad5..6398c258 100755 --- a/tests/pom.xml +++ b/tests/pom.xml @@ -39,6 +39,7 @@ junit5 false alphabetical + 3600 20000 ${mvnLogFile} From 8fccb28ca8e2467eec358df387249cb03a76bf0c Mon Sep 17 00:00:00 2001 From: Adam Wisniewski Date: Wed, 12 Nov 2025 09:24:16 -0500 Subject: [PATCH 04/12] Add cleanup after each test to avoid cascading failures Signed-off-by: Adam Wisniewski --- .../test/it/LibertyPluginSWTBotGradleTest.java | 16 ++++++++++++++++ .../test/it/LibertyPluginSWTBotMavenTest.java | 16 ++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/tests/src/main/java/io/openliberty/tools/eclipse/test/it/LibertyPluginSWTBotGradleTest.java b/tests/src/main/java/io/openliberty/tools/eclipse/test/it/LibertyPluginSWTBotGradleTest.java index 4a22a085..5803ab9a 100644 --- a/tests/src/main/java/io/openliberty/tools/eclipse/test/it/LibertyPluginSWTBotGradleTest.java +++ b/tests/src/main/java/io/openliberty/tools/eclipse/test/it/LibertyPluginSWTBotGradleTest.java @@ -72,10 +72,12 @@ import org.eclipse.swtbot.swt.finder.widgets.SWTBotMenu; import org.eclipse.swtbot.swt.finder.widgets.SWTBotTreeItem; import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInfo; import io.openliberty.tools.eclipse.CommandBuilder; import io.openliberty.tools.eclipse.CommandBuilder.CommandNotFoundException; @@ -87,6 +89,9 @@ import io.openliberty.tools.eclipse.ui.dashboard.DashboardView; import io.openliberty.tools.eclipse.ui.launch.LaunchConfigurationDelegateLauncher; +import static io.openliberty.tools.eclipse.test.it.utils.SWTBotPluginOperations.getObjectInDebugView; +import static io.openliberty.tools.eclipse.test.it.utils.SWTBotPluginOperations.terminateLaunch; + /** * Tests Open Liberty Eclipse plugin functions. */ @@ -199,6 +204,17 @@ public static void setup() throws Exception { } + @AfterEach + public void afterEach(TestInfo info) { + terminateLaunch(); + + // Validate that launch has been removed + Object launch = getObjectInDebugView("[Liberty]"); + Assertions.assertNull(launch); + + super.afterEach(info); + } + @AfterAll public static void cleanup() { for (File p : projectsToInstall) { diff --git a/tests/src/main/java/io/openliberty/tools/eclipse/test/it/LibertyPluginSWTBotMavenTest.java b/tests/src/main/java/io/openliberty/tools/eclipse/test/it/LibertyPluginSWTBotMavenTest.java index 855a9d8a..5980a99c 100644 --- a/tests/src/main/java/io/openliberty/tools/eclipse/test/it/LibertyPluginSWTBotMavenTest.java +++ b/tests/src/main/java/io/openliberty/tools/eclipse/test/it/LibertyPluginSWTBotMavenTest.java @@ -71,10 +71,12 @@ import org.eclipse.swtbot.swt.finder.widgets.SWTBotMenu; import org.eclipse.swtbot.swt.finder.widgets.SWTBotTreeItem; import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInfo; import io.openliberty.tools.eclipse.CommandBuilder; import io.openliberty.tools.eclipse.CommandBuilder.CommandNotFoundException; @@ -84,6 +86,9 @@ import io.openliberty.tools.eclipse.ui.dashboard.DashboardView; import io.openliberty.tools.eclipse.ui.launch.LaunchConfigurationDelegateLauncher; +import static io.openliberty.tools.eclipse.test.it.utils.SWTBotPluginOperations.getObjectInDebugView; +import static io.openliberty.tools.eclipse.test.it.utils.SWTBotPluginOperations.terminateLaunch; + /** * Tests Open Liberty Eclipse plugin functions. */ @@ -201,6 +206,17 @@ public static void setup() throws Exception { validateBeforeTestRun(); } + @AfterEach + public void afterEach(TestInfo info) { + terminateLaunch(); + + // Validate that launch has been removed + Object launch = getObjectInDebugView("[Liberty]"); + Assertions.assertNull(launch); + + super.afterEach(info); + } + @AfterAll public static void cleanup() { for (String p : projectPaths) { From 43920c220f26ef5cf089a438955698de5acc96f7 Mon Sep 17 00:00:00 2001 From: Adam Wisniewski Date: Wed, 12 Nov 2025 10:58:59 -0500 Subject: [PATCH 05/12] Make sure dashboard is in focus before using Signed-off-by: Adam Wisniewski --- .../test/it/utils/SWTBotPluginOperations.java | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/tests/src/main/java/io/openliberty/tools/eclipse/test/it/utils/SWTBotPluginOperations.java b/tests/src/main/java/io/openliberty/tools/eclipse/test/it/utils/SWTBotPluginOperations.java index aae52564..b08766b6 100644 --- a/tests/src/main/java/io/openliberty/tools/eclipse/test/it/utils/SWTBotPluginOperations.java +++ b/tests/src/main/java/io/openliberty/tools/eclipse/test/it/utils/SWTBotPluginOperations.java @@ -239,7 +239,33 @@ public static void openJavaPerspectiveViaMenu() { public static SWTBotTable getDashboardTable() { openDashboardUsingToolbar(); + + // Ensure the dashboard view is actually shown and has focus + // This prevents finding the wrong view (like ConsoleView) when the console takes focus after server start Object dashboardView = findGlobal(DASHBOARD_VIEW_TITLE, Option.factory().widgetClass(ViewPart.class).build()); + + // Explicitly show and activate the dashboard view to ensure it has focus + if (dashboardView instanceof ViewPart) { + final ViewPart vp = (ViewPart) dashboardView; + Display.getDefault().syncExec(new Runnable() { + @Override + public void run() { + try { + IWorkbench wb = PlatformUI.getWorkbench(); + IWorkbenchWindow window = wb.getActiveWorkbenchWindow(); + if (window != null && window.getActivePage() != null) { + window.getActivePage().activate(vp); + } + } catch (Exception e) { + System.err.println("Failed to activate dashboard view: " + e.getMessage()); + } + } + }); + + // Give the UI a moment to update after activation + pause(500); + } + Table table = ((DashboardView) dashboardView).getTable(); return new SWTBotTable(table); } From ab4e062c3623d3b903644008dd94a7613d633978 Mon Sep 17 00:00:00 2001 From: Adam Wisniewski Date: Wed, 12 Nov 2025 11:00:01 -0500 Subject: [PATCH 06/12] Add github actions caching to speed up test runs Signed-off-by: Adam Wisniewski --- .github/workflows/run-regression-tests.yml | 33 ++++++++++++++++++- tests/pom.xml | 2 +- .../test/it/utils/SWTBotPluginOperations.java | 32 +++++++++--------- 3 files changed, 48 insertions(+), 19 deletions(-) diff --git a/.github/workflows/run-regression-tests.yml b/.github/workflows/run-regression-tests.yml index 7676f714..1afaed8f 100644 --- a/.github/workflows/run-regression-tests.yml +++ b/.github/workflows/run-regression-tests.yml @@ -29,8 +29,39 @@ jobs: - name: 'Setup: Checkout plugin' uses: actions/checkout@v2 + # Cache Maven and Tycho dependencies + - name: 'Cache: Maven and Tycho repository' + uses: actions/cache@v4 + with: + path: | + ~/.m2/repository + !~/.m2/repository/io/openliberty/tools/eclipse + key: ${{ runner.os }}-maven-tycho-${{ hashFiles('pom.xml', '.mvn/**', 'releng/**') }} + restore-keys: | + ${{ runner.os }}-maven-tycho- + + # Cache Gradle dependencies + - name: 'Cache: Gradle' + uses: actions/cache@v4 + with: + path: | + ~/.gradle/caches + ~/.gradle/wrapper + key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} + restore-keys: | + ${{ runner.os }}-gradle- + + # Cache downloaded tools (JDK, Maven, Gradle) + - name: 'Cache: Downloaded tools' + uses: actions/cache@v4 + with: + path: test-tools/liberty-dev-tools + key: ${{ runner.os }}-tools-jdk21.0.3-maven3.9.6-gradle8.8 + restore-keys: | + ${{ runner.os }}-tools- + # Install the required software. - - name: 'Setup: Install required software' + - name: 'Setup: Install required software' run: bash ./tests/resources/ci/scripts/setup.sh # Build the plugin. diff --git a/tests/pom.xml b/tests/pom.xml index 6398c258..3afb2c10 100755 --- a/tests/pom.xml +++ b/tests/pom.xml @@ -39,7 +39,7 @@ junit5 false alphabetical - 3600 + 7200 20000 ${mvnLogFile} diff --git a/tests/src/main/java/io/openliberty/tools/eclipse/test/it/utils/SWTBotPluginOperations.java b/tests/src/main/java/io/openliberty/tools/eclipse/test/it/utils/SWTBotPluginOperations.java index b08766b6..4d363f24 100644 --- a/tests/src/main/java/io/openliberty/tools/eclipse/test/it/utils/SWTBotPluginOperations.java +++ b/tests/src/main/java/io/openliberty/tools/eclipse/test/it/utils/SWTBotPluginOperations.java @@ -27,7 +27,6 @@ import java.util.Iterator; import java.util.List; -import org.eclipse.core.runtime.Platform; import org.eclipse.swt.SWT; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Combo; @@ -60,6 +59,7 @@ import org.eclipse.swtbot.swt.finder.widgets.SWTBotTree; import org.eclipse.swtbot.swt.finder.widgets.SWTBotTreeItem; import org.eclipse.ui.IWorkbench; +import org.eclipse.ui.IWorkbenchWindow; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.WorkbenchException; import org.eclipse.ui.part.ViewPart; @@ -239,11 +239,11 @@ public static void openJavaPerspectiveViaMenu() { public static SWTBotTable getDashboardTable() { openDashboardUsingToolbar(); - + // Ensure the dashboard view is actually shown and has focus // This prevents finding the wrong view (like ConsoleView) when the console takes focus after server start Object dashboardView = findGlobal(DASHBOARD_VIEW_TITLE, Option.factory().widgetClass(ViewPart.class).build()); - + // Explicitly show and activate the dashboard view to ensure it has focus if (dashboardView instanceof ViewPart) { final ViewPart vp = (ViewPart) dashboardView; @@ -261,11 +261,11 @@ public void run() { } } }); - + // Give the UI a moment to update after activation - pause(500); + MagicWidgetFinder.pause(500); } - + Table table = ((DashboardView) dashboardView).getTable(); return new SWTBotTable(table); } @@ -417,22 +417,21 @@ public static SWTBotMenu getAppDebugAsMenu(SWTWorkbenchBot bot, String item) { public static void setBuildCmdPathInPreferences(SWTWorkbenchBot bot, String buildTool) { // Use Eclipse preference store API directly instead of UI navigation // This avoids issues with menu accessibility in headless CI environments - + String finalMvnExecutableLoc = AbstractLibertyPluginSWTBotTest.getMvnCmdPath(); String finalGradleExecutableLoc = AbstractLibertyPluginSWTBotTest.getGradleCmdPath(); - + // Get the preference store for the Liberty Tools plugin - org.eclipse.jface.preference.IPreferenceStore prefStore = - new org.eclipse.ui.preferences.ScopedPreferenceStore( + org.eclipse.jface.preference.IPreferenceStore prefStore = new org.eclipse.ui.preferences.ScopedPreferenceStore( org.eclipse.core.runtime.preferences.InstanceScope.INSTANCE, "io.openliberty.tools.eclipse.ui"); - + if ("Maven".equals(buildTool)) { prefStore.setValue("MVNPATH", finalMvnExecutableLoc); } else if ("Gradle".equals(buildTool)) { prefStore.setValue("GRADLEPATH", finalGradleExecutableLoc); } - + // Save the preference store if (prefStore instanceof org.eclipse.ui.preferences.ScopedPreferenceStore) { try { @@ -447,20 +446,19 @@ public static void setBuildCmdPathInPreferences(SWTWorkbenchBot bot, String buil public static void unsetBuildCmdPathInPreferences(SWTWorkbenchBot bot, String buildTool) { // Use Eclipse preference store API directly instead of UI navigation // This avoids issues with menu accessibility in headless CI environments - + // Get the preference store for the Liberty Tools plugin - org.eclipse.jface.preference.IPreferenceStore prefStore = - new org.eclipse.ui.preferences.ScopedPreferenceStore( + org.eclipse.jface.preference.IPreferenceStore prefStore = new org.eclipse.ui.preferences.ScopedPreferenceStore( org.eclipse.core.runtime.preferences.InstanceScope.INSTANCE, "io.openliberty.tools.eclipse.ui"); - + // Reset to default values (empty strings) if ("Maven".equals(buildTool)) { prefStore.setToDefault("MVNPATH"); } else if ("Gradle".equals(buildTool)) { prefStore.setToDefault("GRADLEPATH"); } - + // Save the preference store if (prefStore instanceof org.eclipse.ui.preferences.ScopedPreferenceStore) { try { From 174d6c027e4949cc27d4d2338dda77cbfc8a6dcc Mon Sep 17 00:00:00 2001 From: Adam Wisniewski Date: Thu, 13 Nov 2025 13:32:27 -0500 Subject: [PATCH 07/12] Fix invalid thread access errors at cleanup Signed-off-by: Adam Wisniewski --- .../test/it/utils/SWTBotPluginOperations.java | 61 +++++++++++-------- 1 file changed, 36 insertions(+), 25 deletions(-) diff --git a/tests/src/main/java/io/openliberty/tools/eclipse/test/it/utils/SWTBotPluginOperations.java b/tests/src/main/java/io/openliberty/tools/eclipse/test/it/utils/SWTBotPluginOperations.java index 4d363f24..97a751a3 100644 --- a/tests/src/main/java/io/openliberty/tools/eclipse/test/it/utils/SWTBotPluginOperations.java +++ b/tests/src/main/java/io/openliberty/tools/eclipse/test/it/utils/SWTBotPluginOperations.java @@ -164,46 +164,57 @@ public static void disconnectDebugTarget(Object debugTarget) { * Terminate the launch */ public static void terminateLaunch() { - openDebugPerspective(); - Object windowMenu = findGlobal("Window", Option.factory().widgetClass(MenuItem.class).build()); - goMenuItem(windowMenu, "Show View", "Debug"); + Display.getDefault().syncExec(new Runnable() { + @Override + public void run() { + openDebugPerspective(); + Object windowMenu = findGlobal("Window", Option.factory().widgetClass(MenuItem.class).build()); + goMenuItem(windowMenu, "Show View", "Debug"); - Object debugView = MagicWidgetFinder.findGlobal("Debug"); + Object debugView = MagicWidgetFinder.findGlobal("Debug"); - Object launch = MagicWidgetFinder.find("[Liberty]", debugView, - Option.factory().useContains(true).setThrowExceptionOnNotFound(false).build()); + Object launch = MagicWidgetFinder.find("[Liberty]", debugView, + Option.factory().useContains(true).setThrowExceptionOnNotFound(false).build()); - MagicWidgetFinder.context(launch, "Terminate and Remove"); + MagicWidgetFinder.context(launch, "Terminate and Remove"); - try { - Shell confirm = (Shell) findGlobal("Terminate and Remove", Option.factory().widgetClass(Shell.class).build()); - - MagicWidgetFinder.go("Yes", confirm); - MagicWidgetFinder.pause(3000); - } catch (Exception e) { - // The configrmation pop up window only shows if the launch has not yet been terminated. - // If it has been terminated (or stopped), there is no confirmation. - } + try { + Shell confirm = (Shell) findGlobal("Terminate and Remove", Option.factory().widgetClass(Shell.class).build()); + MagicWidgetFinder.go("Yes", confirm); + MagicWidgetFinder.pause(3000); + } catch (Exception e) { + // The configrmation pop up window only shows if the launch has not yet been terminated. + // If it has been terminated (or stopped), there is no confirmation. + } + } + }); } /** * Returns the debug object item in the Debug View with the given name. * The debug object can either be a launch, a debug target, or a process in the Debug View. - * + * * @param objectName - The name of the object in the Debug View. - * + * * @return */ - public static Object getObjectInDebugView(String objectName) { - openDebugPerspective(); - Object windowMenu = findGlobal("Window", Option.factory().widgetClass(MenuItem.class).build()); - goMenuItem(windowMenu, "Show View", "Debug"); + public static Object getObjectInDebugView(final String objectName) { + final Object[] result = new Object[1]; + Display.getDefault().syncExec(new Runnable() { + @Override + public void run() { + openDebugPerspective(); + Object windowMenu = findGlobal("Window", Option.factory().widgetClass(MenuItem.class).build()); + goMenuItem(windowMenu, "Show View", "Debug"); - Object debugView = MagicWidgetFinder.findGlobal("Debug"); + Object debugView = MagicWidgetFinder.findGlobal("Debug"); - return MagicWidgetFinder.find(objectName, debugView, - Option.factory().useContains(true).setThrowExceptionOnNotFound(false).widgetClass(TreeItem.class).build()); + result[0] = MagicWidgetFinder.find(objectName, debugView, + Option.factory().useContains(true).setThrowExceptionOnNotFound(false).widgetClass(TreeItem.class).build()); + } + }); + return result[0]; } /** From 293f392da9b4e47774bca910c452dcd9ed1a690f Mon Sep 17 00:00:00 2001 From: Adam Wisniewski Date: Thu, 13 Nov 2025 13:39:56 -0500 Subject: [PATCH 08/12] Check for null when terminating Signed-off-by: Adam Wisniewski --- .../test/it/utils/SWTBotPluginOperations.java | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/tests/src/main/java/io/openliberty/tools/eclipse/test/it/utils/SWTBotPluginOperations.java b/tests/src/main/java/io/openliberty/tools/eclipse/test/it/utils/SWTBotPluginOperations.java index 97a751a3..5bb22d47 100644 --- a/tests/src/main/java/io/openliberty/tools/eclipse/test/it/utils/SWTBotPluginOperations.java +++ b/tests/src/main/java/io/openliberty/tools/eclipse/test/it/utils/SWTBotPluginOperations.java @@ -176,16 +176,19 @@ public void run() { Object launch = MagicWidgetFinder.find("[Liberty]", debugView, Option.factory().useContains(true).setThrowExceptionOnNotFound(false).build()); - MagicWidgetFinder.context(launch, "Terminate and Remove"); + // Only attempt to terminate if launch exists + if (launch != null) { + MagicWidgetFinder.context(launch, "Terminate and Remove"); - try { - Shell confirm = (Shell) findGlobal("Terminate and Remove", Option.factory().widgetClass(Shell.class).build()); + try { + Shell confirm = (Shell) findGlobal("Terminate and Remove", Option.factory().widgetClass(Shell.class).build()); - MagicWidgetFinder.go("Yes", confirm); - MagicWidgetFinder.pause(3000); - } catch (Exception e) { - // The configrmation pop up window only shows if the launch has not yet been terminated. - // If it has been terminated (or stopped), there is no confirmation. + MagicWidgetFinder.go("Yes", confirm); + MagicWidgetFinder.pause(3000); + } catch (Exception e) { + // The configrmation pop up window only shows if the launch has not yet been terminated. + // If it has been terminated (or stopped), there is no confirmation. + } } } }); From 534eebb643eaf284b39938b88ec218151d341b4f Mon Sep 17 00:00:00 2001 From: Adam Wisniewski Date: Thu, 13 Nov 2025 13:47:19 -0500 Subject: [PATCH 09/12] Create cache despite test failures Signed-off-by: Adam Wisniewski --- .github/workflows/run-regression-tests.yml | 46 +++++++++++++++++----- 1 file changed, 37 insertions(+), 9 deletions(-) diff --git a/.github/workflows/run-regression-tests.yml b/.github/workflows/run-regression-tests.yml index 1afaed8f..44d48940 100644 --- a/.github/workflows/run-regression-tests.yml +++ b/.github/workflows/run-regression-tests.yml @@ -29,9 +29,9 @@ jobs: - name: 'Setup: Checkout plugin' uses: actions/checkout@v2 - # Cache Maven and Tycho dependencies - - name: 'Cache: Maven and Tycho repository' - uses: actions/cache@v4 + # Restore Maven and Tycho dependencies cache + - name: 'Cache: Maven and Tycho repository (restore)' + uses: actions/cache/restore@v4 with: path: | ~/.m2/repository @@ -40,9 +40,9 @@ jobs: restore-keys: | ${{ runner.os }}-maven-tycho- - # Cache Gradle dependencies - - name: 'Cache: Gradle' - uses: actions/cache@v4 + # Restore Gradle dependencies cache + - name: 'Cache: Gradle (restore)' + uses: actions/cache/restore@v4 with: path: | ~/.gradle/caches @@ -51,9 +51,9 @@ jobs: restore-keys: | ${{ runner.os }}-gradle- - # Cache downloaded tools (JDK, Maven, Gradle) - - name: 'Cache: Downloaded tools' - uses: actions/cache@v4 + # Restore downloaded tools cache (JDK, Maven, Gradle) + - name: 'Cache: Downloaded tools (restore)' + uses: actions/cache/restore@v4 with: path: test-tools/liberty-dev-tools key: ${{ runner.os }}-tools-jdk21.0.3-maven3.9.6-gradle8.8 @@ -81,3 +81,31 @@ jobs: with: name: test-app-logs path: logs + + # Save Maven and Tycho dependencies cache (even on failure) + - name: 'Cache: Maven and Tycho repository (save)' + uses: actions/cache/save@v4 + if: always() + with: + path: | + ~/.m2/repository + !~/.m2/repository/io/openliberty/tools/eclipse + key: ${{ runner.os }}-maven-tycho-${{ hashFiles('pom.xml', '.mvn/**', 'releng/**') }} + + # Save Gradle dependencies cache (even on failure) + - name: 'Cache: Gradle (save)' + uses: actions/cache/save@v4 + if: always() + with: + path: | + ~/.gradle/caches + ~/.gradle/wrapper + key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} + + # Save downloaded tools cache (even on failure) + - name: 'Cache: Downloaded tools (save)' + uses: actions/cache/save@v4 + if: always() + with: + path: test-tools/liberty-dev-tools + key: ${{ runner.os }}-tools-jdk21.0.3-maven3.9.6-gradle8.8 From 7fa469405be7669db3ec61c55048f05f7bd9f70e Mon Sep 17 00:00:00 2001 From: Adam Wisniewski Date: Fri, 14 Nov 2025 09:09:41 -0500 Subject: [PATCH 10/12] Skip installs if already cached Signed-off-by: Adam Wisniewski --- tests/resources/ci/scripts/setup.sh | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/tests/resources/ci/scripts/setup.sh b/tests/resources/ci/scripts/setup.sh index 87717a39..3744c98b 100755 --- a/tests/resources/ci/scripts/setup.sh +++ b/tests/resources/ci/scripts/setup.sh @@ -95,6 +95,17 @@ installCustomSoftware() { installJDK() { local javaHome="${SOFTWARE_INSTALL_DIR}/jdk-${SEMERU_OPEN_JDK_VERSION}+${SEMERU_OPEN_JDK_BUILD}" + # Skip installation if JDK is already installed (from cache) + if [[ -d "${javaHome}" ]] || [[ -d "${javaHome}/Contents/Home" ]]; then + echo "JDK ${SEMERU_OPEN_JDK_VERSION}+${SEMERU_OPEN_JDK_BUILD} already installed, skipping download" + if [[ $OS == "Darwin" ]]; then + javaHome="${javaHome}/Contents/Home" + fi + echo "JAVA_HOME=${javaHome}" >> $GITHUB_ENV + echo "${javaHome}/bin" >> $GITHUB_PATH + return 0 + fi + # Download, validate, and expand the JDK archive. if [[ $OS == "Linux" ]]; then local url="https://github.com/ibmruntimes/semeru${SEMERU_OPEN_JDK_MAJOR}-binaries/releases/download/jdk-${SEMERU_OPEN_JDK_VERSION}%2B${SEMERU_OPEN_JDK_BUILD}_openj9-${SEMERU_OPENJ9_VERSION}/ibm-semeru-open-jdk_x64_linux_${SEMERU_OPEN_JDK_VERSION}_${SEMERU_OPEN_JDK_BUILD}_openj9-${SEMERU_OPENJ9_VERSION}.tar.gz" @@ -142,6 +153,13 @@ installMaven() { local mavenHome="${SOFTWARE_INSTALL_DIR}/apache-maven-${MAVEN_VERSION}" local url="https://archive.apache.org/dist/maven/maven-3/${MAVEN_VERSION}/binaries/apache-maven-${MAVEN_VERSION}-bin.zip" + # Skip installation if Maven is already installed (from cache) + if [[ -d "${mavenHome}" ]]; then + echo "Maven ${MAVEN_VERSION} already installed at ${mavenHome}, skipping download" + echo "${mavenHome}/bin" >> $GITHUB_PATH + return 0 + fi + # Download the Maven archive. curl -fsSL -o /tmp/liberty-dev-tool-apache-maven.zip "$url" @@ -171,6 +189,13 @@ installGradle() { local gradleHome="${SOFTWARE_INSTALL_DIR}/gradle-${GRADLE_VERSION}" local url="https://services.gradle.org/distributions/gradle-${GRADLE_VERSION}-bin.zip" + # Skip installation if Gradle is already installed (from cache) + if [[ -d "${gradleHome}" ]]; then + echo "Gradle ${GRADLE_VERSION} already installed at ${gradleHome}, skipping download" + echo "${gradleHome}/bin" >> $GITHUB_PATH + return 0 + fi + # Download the Gradle archive. curl -fsSL -o /tmp/liberty-dev-tool-gradle.zip "$url" From 97698c4b2b55858938169f87d9bd5bafec8680c9 Mon Sep 17 00:00:00 2001 From: Adam Wisniewski Date: Fri, 14 Nov 2025 10:02:41 -0500 Subject: [PATCH 11/12] Wait for UI to load Signed-off-by: Adam Wisniewski --- .../test/it/utils/SWTBotPluginOperations.java | 159 +++++++++++++----- 1 file changed, 118 insertions(+), 41 deletions(-) diff --git a/tests/src/main/java/io/openliberty/tools/eclipse/test/it/utils/SWTBotPluginOperations.java b/tests/src/main/java/io/openliberty/tools/eclipse/test/it/utils/SWTBotPluginOperations.java index 5bb22d47..dfb7e07f 100644 --- a/tests/src/main/java/io/openliberty/tools/eclipse/test/it/utils/SWTBotPluginOperations.java +++ b/tests/src/main/java/io/openliberty/tools/eclipse/test/it/utils/SWTBotPluginOperations.java @@ -135,10 +135,17 @@ public void run() { */ public static SWTBotMenu getDebuggerConnectMenuForDebugObject(Object debugObject) { openDebugPerspective(); - Object windowMenu = findGlobal("Window", Option.factory().widgetClass(MenuItem.class).build()); - goMenuItem(windowMenu, "Show View", "Debug"); + // Open Debug view using Eclipse API instead of menu navigation + // This is more reliable in headless CI environments + showDebugView(); SWTBotTreeItem obj = new SWTBotTreeItem((TreeItem) debugObject); + + // Ensure the tree item is properly selected and focused before accessing context menu + // This is critical for headless CI environments where context menus can hang + obj.select(); + obj.setFocus(); + MagicWidgetFinder.pause(500); return obj.contextMenu("Connect Liberty Debugger"); } @@ -152,8 +159,15 @@ public static SWTBotMenu getDebuggerConnectMenuForDebugObject(Object debugObject */ public static void disconnectDebugTarget(Object debugTarget) { openDebugPerspective(); - Object windowMenu = findGlobal("Window", Option.factory().widgetClass(MenuItem.class).build()); - goMenuItem(windowMenu, "Show View", "Debug"); + // Open Debug view using Eclipse API instead of menu navigation + // This is more reliable in headless CI environments + showDebugView(); + + // Ensure proper selection before accessing context menu + SWTBotTreeItem obj = new SWTBotTreeItem((TreeItem) debugTarget); + obj.select(); + obj.setFocus(); + MagicWidgetFinder.pause(500); MagicWidgetFinder.context(debugTarget, "Disconnect"); @@ -164,34 +178,30 @@ public static void disconnectDebugTarget(Object debugTarget) { * Terminate the launch */ public static void terminateLaunch() { - Display.getDefault().syncExec(new Runnable() { - @Override - public void run() { - openDebugPerspective(); - Object windowMenu = findGlobal("Window", Option.factory().widgetClass(MenuItem.class).build()); - goMenuItem(windowMenu, "Show View", "Debug"); + // Don't wrap in syncExec - MagicWidgetFinder methods already handle thread synchronization + // Nested syncExec calls can cause deadlocks in headless CI environments + openDebugPerspective(); + showDebugView(); - Object debugView = MagicWidgetFinder.findGlobal("Debug"); + Object debugView = MagicWidgetFinder.findGlobal("Debug"); - Object launch = MagicWidgetFinder.find("[Liberty]", debugView, - Option.factory().useContains(true).setThrowExceptionOnNotFound(false).build()); + Object launch = MagicWidgetFinder.find("[Liberty]", debugView, + Option.factory().useContains(true).setThrowExceptionOnNotFound(false).build()); - // Only attempt to terminate if launch exists - if (launch != null) { - MagicWidgetFinder.context(launch, "Terminate and Remove"); + // Only attempt to terminate if launch exists + if (launch != null) { + MagicWidgetFinder.context(launch, "Terminate and Remove"); - try { - Shell confirm = (Shell) findGlobal("Terminate and Remove", Option.factory().widgetClass(Shell.class).build()); + try { + Shell confirm = (Shell) findGlobal("Terminate and Remove", Option.factory().widgetClass(Shell.class).build()); - MagicWidgetFinder.go("Yes", confirm); - MagicWidgetFinder.pause(3000); - } catch (Exception e) { - // The configrmation pop up window only shows if the launch has not yet been terminated. - // If it has been terminated (or stopped), there is no confirmation. - } - } + MagicWidgetFinder.go("Yes", confirm); + MagicWidgetFinder.pause(3000); + } catch (Exception e) { + // The configrmation pop up window only shows if the launch has not yet been terminated. + // If it has been terminated (or stopped), there is no confirmation. } - }); + } } /** @@ -203,21 +213,37 @@ public void run() { * @return */ public static Object getObjectInDebugView(final String objectName) { - final Object[] result = new Object[1]; - Display.getDefault().syncExec(new Runnable() { - @Override - public void run() { - openDebugPerspective(); - Object windowMenu = findGlobal("Window", Option.factory().widgetClass(MenuItem.class).build()); - goMenuItem(windowMenu, "Show View", "Debug"); + // Don't wrap in syncExec - MagicWidgetFinder methods already handle thread synchronization + // Nested syncExec calls can cause deadlocks in headless CI environments + openDebugPerspective(); + showDebugView(); - Object debugView = MagicWidgetFinder.findGlobal("Debug"); + Object debugView = MagicWidgetFinder.findGlobal("Debug"); - result[0] = MagicWidgetFinder.find(objectName, debugView, - Option.factory().useContains(true).setThrowExceptionOnNotFound(false).widgetClass(TreeItem.class).build()); - } - }); - return result[0]; + // Explicitly activate the Debug view to ensure widgets are properly rendered + // This is critical for headless CI environments + if (debugView instanceof ViewPart) { + final ViewPart vp = (ViewPart) debugView; + Display.getDefault().syncExec(new Runnable() { + @Override + public void run() { + try { + IWorkbench wb = PlatformUI.getWorkbench(); + IWorkbenchWindow window = wb.getActiveWorkbenchWindow(); + if (window != null && window.getActivePage() != null) { + window.getActivePage().activate(vp); + } + } catch (Exception e) { + System.err.println("Failed to activate Debug view: " + e.getMessage()); + } + } + }); + // Give the view time to activate + MagicWidgetFinder.pause(500); + } + + return MagicWidgetFinder.find(objectName, debugView, + Option.factory().useContains(true).setThrowExceptionOnNotFound(false).widgetClass(TreeItem.class).build()); } /** @@ -240,6 +266,31 @@ public void run() { Display.getDefault().syncExec(runnable); } + /** + * Opens the Debug view using Eclipse API directly. + * This is more reliable than menu navigation in headless CI environments. + */ + private static void showDebugView() { + Display.getDefault().syncExec(new Runnable() { + @Override + public void run() { + try { + IWorkbench wb = PlatformUI.getWorkbench(); + IWorkbenchWindow window = wb.getActiveWorkbenchWindow(); + if (window != null && window.getActivePage() != null) { + // Show the Debug view using its ID + window.getActivePage().showView("org.eclipse.debug.ui.DebugView"); + } + } catch (Exception e) { + System.err.println("Failed to open Debug view: " + e.getMessage()); + e.printStackTrace(); + } + } + }); + // Give the view time to open + MagicWidgetFinder.pause(500); + } + public static void openJavaPerspectiveViaMenu() { Object windowMenu = findGlobal("Window", Option.factory().widgetClass(MenuItem.class).build()); @@ -723,8 +774,9 @@ public static void checkRunCleanProjectCheckBox(Shell shell, String runDebugConf public static Object getAppInPackageExplorerTree(String appName) { openJavaPerspectiveViaMenu(); - Object windowMenu = findGlobal("Window", Option.factory().widgetClass(MenuItem.class).build()); - goMenuItem(windowMenu, "Show View", "Package Explorer"); + // Open Package Explorer view using Eclipse API instead of menu navigation + // This is more reliable in headless CI environments + showPackageExplorerView(); Object peView = MagicWidgetFinder.findGlobal("Package Explorer"); Object project = MagicWidgetFinder.find(appName, peView, Option.factory().useContains(true).widgetClass(TreeItem.class).build()); @@ -988,6 +1040,31 @@ public static void closeDashboardView(SWTWorkbenchBot bot) { } } + /** + * Opens the Package Explorer view using Eclipse API directly. + * This is more reliable than menu navigation in headless CI environments. + */ + private static void showPackageExplorerView() { + Display.getDefault().syncExec(new Runnable() { + @Override + public void run() { + try { + IWorkbench wb = PlatformUI.getWorkbench(); + IWorkbenchWindow window = wb.getActiveWorkbenchWindow(); + if (window != null && window.getActivePage() != null) { + // Show the Package Explorer view using its ID + window.getActivePage().showView("org.eclipse.jdt.ui.PackageExplorer"); + } + } catch (Exception e) { + System.err.println("Failed to open Package Explorer view: " + e.getMessage()); + e.printStackTrace(); + } + } + }); + // Give the view time to open + MagicWidgetFinder.pause(500); + } + /** * Switches the Liberty run configuration main tab to the JRE Tab. A Liberty configuration must be opened prior to calling this * method. From ca37b3c5dc4dca6509043dde2a2eabe596c361f1 Mon Sep 17 00:00:00 2001 From: Adam Wisniewski Date: Mon, 17 Nov 2025 08:59:30 -0500 Subject: [PATCH 12/12] Make cleanup more robust Signed-off-by: Adam Wisniewski --- ...LibertyPluginSWTBotMavenWithSpaceTest.java | 2 +- .../test/it/utils/SWTBotPluginOperations.java | 83 ++++++++++++------- 2 files changed, 53 insertions(+), 32 deletions(-) diff --git a/tests/src/main/java/io/openliberty/tools/eclipse/test/it/LibertyPluginSWTBotMavenWithSpaceTest.java b/tests/src/main/java/io/openliberty/tools/eclipse/test/it/LibertyPluginSWTBotMavenWithSpaceTest.java index d5cc45f3..25405875 100644 --- a/tests/src/main/java/io/openliberty/tools/eclipse/test/it/LibertyPluginSWTBotMavenWithSpaceTest.java +++ b/tests/src/main/java/io/openliberty/tools/eclipse/test/it/LibertyPluginSWTBotMavenWithSpaceTest.java @@ -92,7 +92,7 @@ public void afterEach(TestInfo info) { Assertions.assertNull(launch); super.afterEach(info); - } + } @AfterAll public static void cleanup() throws IOException { diff --git a/tests/src/main/java/io/openliberty/tools/eclipse/test/it/utils/SWTBotPluginOperations.java b/tests/src/main/java/io/openliberty/tools/eclipse/test/it/utils/SWTBotPluginOperations.java index dfb7e07f..8186a259 100644 --- a/tests/src/main/java/io/openliberty/tools/eclipse/test/it/utils/SWTBotPluginOperations.java +++ b/tests/src/main/java/io/openliberty/tools/eclipse/test/it/utils/SWTBotPluginOperations.java @@ -178,18 +178,12 @@ public static void disconnectDebugTarget(Object debugTarget) { * Terminate the launch */ public static void terminateLaunch() { - // Don't wrap in syncExec - MagicWidgetFinder methods already handle thread synchronization - // Nested syncExec calls can cause deadlocks in headless CI environments - openDebugPerspective(); - showDebugView(); - - Object debugView = MagicWidgetFinder.findGlobal("Debug"); - - Object launch = MagicWidgetFinder.find("[Liberty]", debugView, - Option.factory().useContains(true).setThrowExceptionOnNotFound(false).build()); + // Use getObjectInDebugView to find the Liberty launch with retry logic + Object launch = getObjectInDebugView("[Liberty]"); // Only attempt to terminate if launch exists if (launch != null) { + System.out.println("Found Liberty launch, attempting to terminate"); MagicWidgetFinder.context(launch, "Terminate and Remove"); try { @@ -198,9 +192,11 @@ public static void terminateLaunch() { MagicWidgetFinder.go("Yes", confirm); MagicWidgetFinder.pause(3000); } catch (Exception e) { - // The configrmation pop up window only shows if the launch has not yet been terminated. + // The confirmation pop up window only shows if the launch has not yet been terminated. // If it has been terminated (or stopped), there is no confirmation. } + } else { + System.out.println("No Liberty launch found in Debug view to terminate"); } } @@ -218,32 +214,57 @@ public static Object getObjectInDebugView(final String objectName) { openDebugPerspective(); showDebugView(); - Object debugView = MagicWidgetFinder.findGlobal("Debug"); - - // Explicitly activate the Debug view to ensure widgets are properly rendered - // This is critical for headless CI environments - if (debugView instanceof ViewPart) { - final ViewPart vp = (ViewPart) debugView; - Display.getDefault().syncExec(new Runnable() { - @Override - public void run() { - try { - IWorkbench wb = PlatformUI.getWorkbench(); - IWorkbenchWindow window = wb.getActiveWorkbenchWindow(); - if (window != null && window.getActivePage() != null) { - window.getActivePage().activate(vp); + // Get the Debug view directly using Eclipse API instead of text search + // This is more reliable than findGlobal("Debug") which could find the wrong view + final Object[] debugViewHolder = new Object[1]; + Display.getDefault().syncExec(new Runnable() { + @Override + public void run() { + try { + IWorkbench wb = PlatformUI.getWorkbench(); + IWorkbenchWindow window = wb.getActiveWorkbenchWindow(); + if (window != null && window.getActivePage() != null) { + // Get the Debug view by its ID + ViewPart debugView = (ViewPart) window.getActivePage().findView("org.eclipse.debug.ui.DebugView"); + if (debugView != null) { + // Activate it to ensure widgets are rendered + window.getActivePage().activate(debugView); + debugViewHolder[0] = debugView; } - } catch (Exception e) { - System.err.println("Failed to activate Debug view: " + e.getMessage()); } + } catch (Exception e) { + System.err.println("Failed to get Debug view: " + e.getMessage()); + e.printStackTrace(); } - }); - // Give the view time to activate - MagicWidgetFinder.pause(500); + } + }); + + // Give the view time to activate and render + MagicWidgetFinder.pause(500); + + Object debugView = debugViewHolder[0]; + if (debugView == null) { + System.err.println("Debug view not found, cannot find object: " + objectName); + return null; + } + + // Try multiple times to find the object, as it may take time to appear in headless CI + Object result = null; + for (int attempt = 0; attempt < 3 && result == null; attempt++) { + if (attempt > 0) { + System.out.println("Retry attempt " + attempt + " to find object: " + objectName); + MagicWidgetFinder.pause(1000); + } + + result = MagicWidgetFinder.find(objectName, debugView, + Option.factory().useContains(true).setThrowExceptionOnNotFound(false).widgetClass(TreeItem.class).build()); + } + + if (result == null) { + System.out.println("Object not found in Debug view after 3 attempts: " + objectName); } - return MagicWidgetFinder.find(objectName, debugView, - Option.factory().useContains(true).setThrowExceptionOnNotFound(false).widgetClass(TreeItem.class).build()); + return result; } /**