-
Notifications
You must be signed in to change notification settings - Fork 133
WIP: SWTBot test case: Serial Monitor verification #1297
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -6,7 +6,6 @@ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import java.util.Arrays; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import java.util.Optional; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.eclipse.swt.widgets.Display; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.eclipse.swtbot.eclipse.finder.SWTWorkbenchBot; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.eclipse.swtbot.eclipse.finder.widgets.SWTBotView; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -130,6 +129,7 @@ public boolean test() throws Exception | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| view.show(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| view.setFocus(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| String textString = view.bot().styledText().getText(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return textString.toLowerCase().contains(text.toLowerCase()); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -140,6 +140,25 @@ public String getFailureMessage() | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, timeOut, 3000); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public static void waitUntilTextContains(SWTWorkbenchBot bot, String text, SWTBotView terminalView, long timeOut) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| terminalView.bot().waitUntil(new DefaultCondition() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @Override | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public boolean test() throws Exception { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| String terminalText = terminalView.bot().canvas().getText(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @kolipakakondal @alirana01 hi guys ! I need your advice. I'm writing a test for the Serial Monitor but can't extract text from the Terminal view. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return terminalText.toLowerCase().contains(text.toLowerCase()); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @Override | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public String getFailureMessage() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return "Text not found in terminal!"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, timeOut, 3000); // Wait for up to 'timeOut' milliseconds | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+144
to
+159
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Terminal text read uses Canvas.getText() (nonexistent) — switch to StyledText SWT Canvas doesn’t expose text. This will fail at runtime/compile-time depending on bindings. Use styledText like elsewhere and ensure the view is visible/focused before reading. - public static void waitUntilTextContains(SWTWorkbenchBot bot, String text, SWTBotView terminalView, long timeOut) {
- terminalView.bot().waitUntil(new DefaultCondition() {
+ public static void waitUntilTextContains(SWTWorkbenchBot bot, String text, SWTBotView terminalView, long timeOut) {
+ terminalView.bot().waitUntil(new DefaultCondition() {
@Override
public boolean test() throws Exception {
-
- String terminalText = terminalView.bot().canvas().getText();
-
- return terminalText.toLowerCase().contains(text.toLowerCase());
+ terminalView.show();
+ terminalView.setFocus();
+ String terminalText = terminalView.bot().styledText().getText();
+ return terminalText.toLowerCase().contains(text.toLowerCase());
}
@Override
public String getFailureMessage() {
return "Text not found in terminal!";
}
- }, timeOut, 3000); // Wait for up to 'timeOut' milliseconds
+ }, timeOut, 3000);
}Optional: de-duplicate with waitUntilViewContains by reusing it for terminal checks. 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Waits until the tree contains an item which contains the name in its text | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,139 @@ | ||||||
| /******************************************************************************* | ||||||
| * Copyright 2025 Espressif Systems (Shanghai) PTE LTD. All rights reserved. | ||||||
| * Use is subject to license terms. | ||||||
| *******************************************************************************/ | ||||||
| package com.espressif.idf.ui.test.executable.cases.project; | ||||||
| import java.io.IOException; | ||||||
|
|
||||||
| import org.eclipse.swtbot.eclipse.finder.SWTWorkbenchBot; | ||||||
| import org.eclipse.swtbot.swt.finder.junit.SWTBotJunit4ClassRunner; | ||||||
| import org.eclipse.swtbot.swt.finder.widgets.SWTBotShell; | ||||||
| import org.junit.After; | ||||||
| import org.junit.BeforeClass; | ||||||
| import org.junit.FixMethodOrder; | ||||||
| import org.junit.Test; | ||||||
| import org.junit.runner.RunWith; | ||||||
| import org.junit.runners.MethodSorters; | ||||||
|
|
||||||
| import com.espressif.idf.ui.test.common.WorkBenchSWTBot; | ||||||
| import com.espressif.idf.ui.test.common.utility.TestWidgetWaitUtility; | ||||||
| import com.espressif.idf.ui.test.operations.EnvSetupOperations; | ||||||
| import com.espressif.idf.ui.test.operations.ProjectTestOperations; | ||||||
|
|
||||||
| /** | ||||||
| * Test class to test the Serial Monitor feature | ||||||
| * | ||||||
| * @author Andrii Filippov | ||||||
| * | ||||||
| */ | ||||||
| @SuppressWarnings("restriction") | ||||||
| @RunWith(SWTBotJunit4ClassRunner.class) | ||||||
| @FixMethodOrder(MethodSorters.NAME_ASCENDING) | ||||||
| public class NewEspressifIDFProjectSerialMonitorTest | ||||||
| { | ||||||
| @BeforeClass | ||||||
| public static void beforeTestClass() throws Exception | ||||||
| { | ||||||
| Fixture.loadEnv(); | ||||||
| } | ||||||
|
|
||||||
| @After | ||||||
| public void afterEachTest() | ||||||
| { | ||||||
| try | ||||||
| { | ||||||
| Fixture.cleanTestEnv(); // Make sure test environment is always cleaned up | ||||||
| } | ||||||
| catch (Exception e) | ||||||
| { | ||||||
| System.err.println("Error during cleanup: " + e.getMessage()); | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| @Test | ||||||
| public void givenNewProjectCreatedBuiltWhenFlashProjectThenCheckSerialMonitorOutput() | ||||||
| throws Exception | ||||||
| { | ||||||
| Fixture.givenNewEspressifIDFProjectIsSelected("EspressIf", "Espressif IDF Project"); | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Typo breaks project template selection (“EspressIf” → “Espressif”) Category is case-sensitive in the UI. This will fail to locate the wizard node. - Fixture.givenNewEspressifIDFProjectIsSelected("EspressIf", "Espressif IDF Project");
+ Fixture.givenNewEspressifIDFProjectIsSelected("Espressif", "Espressif IDF Project");📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||
| Fixture.givenProjectNameIs("NewProjectSerialMonitorTest"); | ||||||
| Fixture.whenNewProjectIsSelected(); | ||||||
| Fixture.whenProjectIsBuiltUsingContextMenu(); | ||||||
| Fixture.whenFlashProject(); | ||||||
| Fixture.whenSelectSerialPort(); | ||||||
| Fixture.whenFlashProject(); | ||||||
| Fixture.openSerialMonitorandVerify(); | ||||||
| } | ||||||
|
|
||||||
| private static class Fixture | ||||||
| { | ||||||
| private static SWTWorkbenchBot bot; | ||||||
| private static String category; | ||||||
| private static String subCategory; | ||||||
| private static String projectName; | ||||||
|
|
||||||
| private static void loadEnv() throws Exception | ||||||
| { | ||||||
| bot = WorkBenchSWTBot.getBot(); | ||||||
| EnvSetupOperations.setupEspressifEnv(bot); | ||||||
| bot.sleep(1000); | ||||||
| } | ||||||
|
|
||||||
| private static void givenNewEspressifIDFProjectIsSelected(String category, String subCategory) | ||||||
| { | ||||||
| Fixture.category = category; | ||||||
| Fixture.subCategory = subCategory; | ||||||
| } | ||||||
|
|
||||||
| private static void givenProjectNameIs(String projectName) | ||||||
| { | ||||||
| Fixture.projectName = projectName; | ||||||
| } | ||||||
|
|
||||||
| private static void whenNewProjectIsSelected() throws Exception | ||||||
| { | ||||||
| ProjectTestOperations.setupProject(projectName, category, subCategory, bot); | ||||||
| } | ||||||
|
|
||||||
| private static void whenProjectIsBuiltUsingContextMenu() throws IOException | ||||||
| { | ||||||
| ProjectTestOperations.buildProjectUsingContextMenu(projectName, bot); | ||||||
| ProjectTestOperations.waitForProjectBuild(bot); | ||||||
| TestWidgetWaitUtility.waitForOperationsInProgressToFinishAsync(bot); | ||||||
| } | ||||||
|
|
||||||
| private static void whenFlashProject() throws IOException | ||||||
| { | ||||||
| ProjectTestOperations.launchCommandUsingContextMenu(projectName, bot, "Run Configurations..."); | ||||||
| TestWidgetWaitUtility.waitForDialogToAppear(bot, "Run Configurations", 10000); | ||||||
| SWTBotShell prefrencesShell = bot.shell("Run Configurations"); | ||||||
| prefrencesShell.bot().tree().getTreeItem("ESP-IDF Application").select(); | ||||||
| prefrencesShell.bot().tree().getTreeItem("ESP-IDF Application").expand(); | ||||||
| prefrencesShell.bot().tree().getTreeItem("ESP-IDF Application").getNode("NewProjectSerialMonitorTest").select(); | ||||||
| bot.sleep(1000); | ||||||
| bot.button("Run").click(); | ||||||
| } | ||||||
|
|
||||||
| private static void whenSelectSerialPort() throws IOException | ||||||
| { | ||||||
| TestWidgetWaitUtility.waitForDialogToAppear(bot, "Serial port not found", 30000); | ||||||
| bot.button("OK").click(); | ||||||
| TestWidgetWaitUtility.waitForDialogToAppear(bot, "New ESP Target", 20000); | ||||||
| bot.comboBoxWithLabel("Serial Port:").setSelection("COM5 USB Serial Port (COM5)"); | ||||||
| TestWidgetWaitUtility.waitForOperationsInProgressToFinishAsync(bot); | ||||||
| bot.button("Finish").click(); | ||||||
| } | ||||||
|
Comment on lines
+116
to
+124
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Port selection is Windows-specific (“COM5”); make it portable or property-driven Linux/macOS won’t have COM5. Read a property and fallback to the first item to keep CI portable. - bot.comboBoxWithLabel("Serial Port:").setSelection("COM5 USB Serial Port (COM5)");
+ String preferredPort = DefaultPropertyFetcher.getStringPropertyValue("default.serial.port.label", "");
+ if (preferredPort != null && !preferredPort.isEmpty()) {
+ bot.comboBoxWithLabel("Serial Port:").setSelection(preferredPort);
+ } else {
+ bot.comboBoxWithLabel("Serial Port:").setSelection(0); // fallback: first available
+ }Add missing import at the top of this file: import com.espressif.idf.ui.test.common.configs.DefaultPropertyFetcher; |
||||||
|
|
||||||
| private static void openSerialMonitorandVerify() throws IOException { | ||||||
| ProjectTestOperations.waitProjectFlash(bot); | ||||||
| ProjectTestOperations.waitForProjectMonitorAndDisconnect(bot); | ||||||
| ProjectTestOperations.findTextInSerialMonitorOutput(bot); | ||||||
| } | ||||||
|
|
||||||
| private static void cleanTestEnv() | ||||||
| { | ||||||
| TestWidgetWaitUtility.waitForOperationsInProgressToFinishAsync(bot); | ||||||
| ProjectTestOperations.closeAllProjects(bot); | ||||||
| ProjectTestOperations.deleteAllProjects(bot); | ||||||
| } | ||||||
| } | ||||||
| } | ||||||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -52,6 +52,8 @@ public class ProjectTestOperations | |||||
| { | ||||||
|
|
||||||
| private static final String DEFAULT_PROJECT_BUILD_WAIT_PROPERTY = "default.project.build.wait"; | ||||||
|
|
||||||
| private static final String DEFAULT_FLASH_WAIT_PROPERTY = "default.project.build.wait"; | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Wrong property key for flash timeout (causes hours-long waits) DEFAULT_FLASH_WAIT_PROPERTY points to the build-wait key. This makes flash waits use 90,800,000 ms from the Linux config. - private static final String DEFAULT_FLASH_WAIT_PROPERTY = "default.project.build.wait";
+ private static final String DEFAULT_FLASH_WAIT_PROPERTY = "default.project.flash.wait";📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||
|
|
||||||
| private static final Logger logger = LoggerFactory.getLogger(ProjectTestOperations.class); | ||||||
|
|
||||||
|
|
@@ -99,6 +101,25 @@ public static void waitForProjectNewComponentInstalled(SWTWorkbenchBot bot) thro | |||||
| DefaultPropertyFetcher.getLongPropertyValue("Install New Component", 10000)); | ||||||
| } | ||||||
|
|
||||||
| public static void waitForProjectMonitorAndDisconnect(SWTWorkbenchBot bot) throws IOException | ||||||
| { | ||||||
| bot.sleep(5000); | ||||||
| SWTBotView terminalView = viewTerminal("COM5", bot, 30000); | ||||||
| terminalView.toolbarPushButton("Disconnect Terminal Connection").click(); | ||||||
| terminalView.show(); | ||||||
| terminalView.setFocus(); | ||||||
| } | ||||||
|
|
||||||
| public static void findTextInSerialMonitorOutput(SWTWorkbenchBot bot) throws IOException | ||||||
| { | ||||||
| bot.sleep(5000); | ||||||
| SWTBotView terminalView = viewTerminal("<Closed> COM5", bot, 30000); | ||||||
| terminalView.show(); | ||||||
| terminalView.setFocus(); | ||||||
| TestWidgetWaitUtility.waitUntilTextContains(bot, "Hello", terminalView, | ||||||
| DefaultPropertyFetcher.getLongPropertyValue(DEFAULT_FLASH_WAIT_PROPERTY, 20000)); | ||||||
| } | ||||||
|
|
||||||
| public static SWTBotView viewConsole(String consoleType, SWTWorkbenchBot bot) | ||||||
| { | ||||||
| SWTBotView view = bot.viewByPartName("Console"); | ||||||
|
|
@@ -110,6 +131,22 @@ public static SWTBotView viewConsole(String consoleType, SWTWorkbenchBot bot) | |||||
| view.setFocus(); | ||||||
| return view; | ||||||
| } | ||||||
|
|
||||||
| public static SWTBotView viewTerminal(String terminalType, SWTWorkbenchBot bot, long timeOut) | ||||||
| { | ||||||
| SWTBotView view = bot.viewByPartName("Terminal"); | ||||||
| view.show(); | ||||||
| view.setFocus(); | ||||||
| return view; | ||||||
| } | ||||||
|
|
||||||
| public static void waitProjectFlash(SWTWorkbenchBot bot) throws IOException | ||||||
| { | ||||||
| SWTBotView view = bot.viewByPartName("Console"); | ||||||
| view.setFocus(); | ||||||
| TestWidgetWaitUtility.waitUntilViewContains(bot, "Hard resetting via RTS pin...", view, | ||||||
| DefaultPropertyFetcher.getLongPropertyValue(DEFAULT_FLASH_WAIT_PROPERTY, 40000)); | ||||||
| } | ||||||
|
|
||||||
| public static void createDebugConfiguration(String projectName, SWTWorkbenchBot bot) | ||||||
| { | ||||||
|
|
||||||
Uh oh!
There was an error while loading. Please reload this page.