diff --git a/.github/workflows/run-regression-tests.yml b/.github/workflows/run-regression-tests.yml index 7676f714..44d48940 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 + # Restore Maven and Tycho dependencies cache + - name: 'Cache: Maven and Tycho repository (restore)' + uses: actions/cache/restore@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- + + # Restore Gradle dependencies cache + - name: 'Cache: Gradle (restore)' + uses: actions/cache/restore@v4 + with: + path: | + ~/.gradle/caches + ~/.gradle/wrapper + key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} + restore-keys: | + ${{ runner.os }}-gradle- + + # 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 + 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. @@ -50,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 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..3afb2c10 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 @@ -39,6 +39,7 @@ junit5 false alphabetical + 7200 20000 ${mvnLogFile} 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" 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) { 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 7dbc1689..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 @@ -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; @@ -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,46 +178,93 @@ 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"); + // Use getObjectInDebugView to find the Liberty launch with retry logic + Object launch = getObjectInDebugView("[Liberty]"); - Object debugView = MagicWidgetFinder.findGlobal("Debug"); + // 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"); - Object launch = MagicWidgetFinder.find("[Liberty]", debugView, - Option.factory().useContains(true).setThrowExceptionOnNotFound(false).build()); - - 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 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"); } - } /** * 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) { + public static Object getObjectInDebugView(final String objectName) { + // Don't wrap in syncExec - MagicWidgetFinder methods already handle thread synchronization + // Nested syncExec calls can cause deadlocks in headless CI environments openDebugPerspective(); - Object windowMenu = findGlobal("Window", Option.factory().widgetClass(MenuItem.class).build()); - goMenuItem(windowMenu, "Show View", "Debug"); + showDebugView(); - Object debugView = MagicWidgetFinder.findGlobal("Debug"); + // 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 get Debug view: " + e.getMessage()); + e.printStackTrace(); + } + } + }); + + // 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; } /** @@ -226,6 +287,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()); @@ -239,7 +325,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 + MagicWidgetFinder.pause(500); + } + Table table = ((DashboardView) dashboardView).getTable(); return new SWTBotTable(table); } @@ -389,56 +501,59 @@ 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) { + // Use Eclipse preference store API directly instead of UI navigation + // This avoids issues with menu accessibility in headless CI environments - /* 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; - } + String finalMvnExecutableLoc = AbstractLibertyPluginSWTBotTest.getMvnCmdPath(); + String finalGradleExecutableLoc = AbstractLibertyPluginSWTBotTest.getGradleCmdPath(); - String finalMvnExecutableLoc = null; - String finalGradleExecutableLoc = null; - Object locationLabel = null; - Object locationText = null; + // 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"); - 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); + if ("Maven".equals(buildTool)) { + prefStore.setValue("MVNPATH", finalMvnExecutableLoc); + } else if ("Gradle".equals(buildTool)) { + prefStore.setValue("GRADLEPATH", finalGradleExecutableLoc); } - goGlobal("Apply and Close"); + // 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(); + } + } } 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"); } - 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"); + // 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(); + } + } } /** @@ -680,8 +795,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()); @@ -945,6 +1061,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.