diff --git a/Jenkinsfile b/Jenkinsfile index a53b41e132..f0e272a34d 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -21,7 +21,7 @@ pipeline { withCredentials([string(credentialsId: 'osate-ci_sonarcloud', variable: 'SONARTOKEN')]) { wrap([$class: 'Xvnc', takeScreenshot: false, useXauthority: true]) { sh 'mvn -s releng/osate.releng/seisettings.xml clean verify \ - -Plocal -Dsonar.token=$SONARTOKEN \ + -Plocal,with-ge-tests -Dsonar.token=$SONARTOKEN \ -Dsonar.pullrequest.provider=GitHub \ -Dsonar.pullrequest.github.repository=$(echo $CHANGE_URL | cut -d/ -f4,5) \ -Dsonar.pullrequest.key=$CHANGE_ID \ @@ -43,7 +43,7 @@ pipeline { withCredentials([string(credentialsId: 'osate-ci_sonarcloud', variable: 'SONARTOKEN')]) { wrap([$class: 'Xvnc', takeScreenshot: false, useXauthority: true]) { sh 'mvn -s releng/osate.releng/seisettings.xml clean verify \ - -Pfull -Dsonar.token=$SONARTOKEN \ + -Pfull,with-ge-tests -Dsonar.token=$SONARTOKEN \ -Declipse.p2.mirrors=false -DfailIfNoTests=false \ -Dcodecoverage=true -Dspotbugs=true -Djavadoc=true' } @@ -100,4 +100,4 @@ pipeline { ) } } -} \ No newline at end of file +} diff --git a/ge/org.osate.ge.errormodel/src/org/osate/ge/errormodel/ui/viewmodels/BaseTypeSetTypeTokensModel.java b/ge/org.osate.ge.errormodel/src/org/osate/ge/errormodel/ui/viewmodels/BaseTypeSetTypeTokensModel.java index 389749b54e..f893d2e3c3 100644 --- a/ge/org.osate.ge.errormodel/src/org/osate/ge/errormodel/ui/viewmodels/BaseTypeSetTypeTokensModel.java +++ b/ge/org.osate.ge.errormodel/src/org/osate/ge/errormodel/ui/viewmodels/BaseTypeSetTypeTokensModel.java @@ -145,7 +145,7 @@ public final void setTypeTokens(final List value) { // Modify the AADL model modifyTypeSets(ts -> { ts.getTypeTokens().clear(); - ts.getTypeTokens().addAll(inner.getTypeTokenList()); + ts.getTypeTokens().addAll(EcoreUtil.copyAll(inner.getTypeTokenList())); }); } } diff --git a/ge/org.osate.ge.tests/.launch/org.osate.ge.tests.launch b/ge/org.osate.ge.tests/.launch/org.osate.ge.tests.launch index 51c8cfd8f2..ff57dd34c9 100644 --- a/ge/org.osate.ge.tests/.launch/org.osate.ge.tests.launch +++ b/ge/org.osate.ge.tests/.launch/org.osate.ge.tests.launch @@ -2,7 +2,7 @@ - + @@ -29,65 +29,40 @@ - + - + - + - - - - - - - + - - - - + - - - - + + - - + - - - - - - - - - - - - - - - - + + + @@ -100,175 +75,92 @@ - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - + - - - - + + + - - - + + - - + + - - - + + - - + - + + - + - - - + + + - - + - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + - - - - - - - - - - - + - + + @@ -278,426 +170,165 @@ - - - - - - - - - - - - - - - - - + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + - - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + - - - - - - - - - - - - - - + + - - - - - + - - - - + - - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - - - - - - - - - - - - - - - - - - - - - - - - @@ -706,13 +337,11 @@ - - - + @@ -720,46 +349,16 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -767,37 +366,20 @@ - - - - - - - - - - - - - - - - - - - - + + + diff --git a/ge/org.osate.ge.tests/src/org/osate/ge/tests/endToEnd/PrimaryEndToEndTest.java b/ge/org.osate.ge.tests/src/org/osate/ge/tests/endToEnd/PrimaryEndToEndTest.java index 3d1849df12..44cc01f7ee 100644 --- a/ge/org.osate.ge.tests/src/org/osate/ge/tests/endToEnd/PrimaryEndToEndTest.java +++ b/ge/org.osate.ge.tests/src/org/osate/ge/tests/endToEnd/PrimaryEndToEndTest.java @@ -793,10 +793,10 @@ private void createSoftwareProject() { threadsImplElement.join(getFeatureRelativeReference("point_cloud")), getFlowSpecificationRelativeReference("threads_new_flow_spec"), "point_cloud_source"); - // Create flow sink threads.impl.integrator.point_cloud + // Create flow source threads.impl.integrator.point_cloud createFlowIndicatorAndLayout(softwareDiagram, integratorScImpl, - "Flow Sink Specification", integratorScImpl.join(getFeatureRelativeReference("point_cloud")), - getFlowSpecificationRelativeReference("integrator_new_flow_spec"), "point_cloud_sink"); + "Flow Source Specification", integratorScImpl.join(getFeatureRelativeReference("point_cloud")), + getFlowSpecificationRelativeReference("integrator_new_flow_spec"), "point_cloud_source"); // Create connection threads.impl.point_cloud -> threads.impl.integrator.point_cloud createConnectionAndLayout(softwareDiagram, @@ -835,7 +835,7 @@ private void createSoftwareProject() { element(getRelativeReferenceForPackage(SOFTWARE), getClassifierRelativeReference( "threads.impl"), getSubcomponentRelativeReference("integrator"), - getFlowSpecificationRelativeReference("point_cloud_sink")), + getFlowSpecificationRelativeReference("point_cloud_source")), element(getRelativeReferenceForPackage(SOFTWARE), getClassifierRelativeReference( "threads.impl"), getConnectionRelativeReference("point_cloud_connection")), diff --git a/ge/org.osate.ge.tests/src/org/osate/ge/tests/endToEnd/util/OsateGeTestUtil.java b/ge/org.osate.ge.tests/src/org/osate/ge/tests/endToEnd/util/OsateGeTestUtil.java index 2658d256b6..e8692ab73c 100644 --- a/ge/org.osate.ge.tests/src/org/osate/ge/tests/endToEnd/util/OsateGeTestUtil.java +++ b/ge/org.osate.ge.tests/src/org/osate/ge/tests/endToEnd/util/OsateGeTestUtil.java @@ -92,6 +92,9 @@ public static void openDiagramEditor(final DiagramReference diagram) { // Don't do anything if diagram is active if (!isDiagramEditorActive(diagram)) { doubleClickInAadlNavigator(diagram.pathSegments.toArray(new String[diagram.pathSegments.size()])); + waitUntil(() -> isDiagramEditorOpen(diagram), + "Editor for diagram path segments '" + diagram + "' did not open."); + activateDiagramEditor(diagram); waitForDiagramActive(diagram); } } diff --git a/ge/org.osate.ge.tests/src/org/osate/ge/tests/endToEnd/util/UiTestUtil.java b/ge/org.osate.ge.tests/src/org/osate/ge/tests/endToEnd/util/UiTestUtil.java index 2972e03d25..97084e615b 100644 --- a/ge/org.osate.ge.tests/src/org/osate/ge/tests/endToEnd/util/UiTestUtil.java +++ b/ge/org.osate.ge.tests/src/org/osate/ge/tests/endToEnd/util/UiTestUtil.java @@ -34,7 +34,6 @@ import java.util.Objects; import java.util.Optional; import java.util.Set; -import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.BooleanSupplier; import org.eclipse.swt.SWT; @@ -48,15 +47,16 @@ import org.eclipse.swtbot.eclipse.finder.finders.WorkbenchContentsFinder; import org.eclipse.swtbot.eclipse.finder.widgets.SWTBotEditor; import org.eclipse.swtbot.eclipse.finder.widgets.SWTBotView; +import org.eclipse.swtbot.swt.finder.SWTBot; import org.eclipse.swtbot.swt.finder.finders.UIThreadRunnable; import org.eclipse.swtbot.swt.finder.utils.SWTBotPreferences; -import org.eclipse.swtbot.swt.finder.waits.Conditions; import org.eclipse.swtbot.swt.finder.waits.DefaultCondition; import org.eclipse.swtbot.swt.finder.widgets.AbstractSWTBot; import org.eclipse.swtbot.swt.finder.widgets.AbstractSWTBotControl; import org.eclipse.swtbot.swt.finder.widgets.SWTBotButton; import org.eclipse.swtbot.swt.finder.widgets.SWTBotCLabel; import org.eclipse.swtbot.swt.finder.widgets.SWTBotCanvas; +import org.eclipse.swtbot.swt.finder.widgets.SWTBotShell; import org.eclipse.swtbot.swt.finder.widgets.SWTBotSpinner; import org.eclipse.swtbot.swt.finder.widgets.SWTBotStyledText; import org.eclipse.swtbot.swt.finder.widgets.SWTBotTable; @@ -91,6 +91,7 @@ import javafx.scene.Scene; import javafx.scene.control.Button; import javafx.scene.control.TextField; +import javafx.scene.control.ToggleButton; /** * Provides functions for controlling the user interface. @@ -103,6 +104,9 @@ public class UiTestUtil { private static final SWTWorkbenchBot bot; private static final JavaFXBot fxBot = new JavaFXBot(); + private static Shell interactionShell; + private static AbstractSWTBot contextMenuTarget; + private static Control pendingFocusOutControl; private static final HashSet allowedViewTitles = Sets.newHashSet("AADL Navigator", "AADL Diagrams", "Properties", "Outline"); @@ -161,14 +165,85 @@ public String getFailureMessage() { * Clicks a menu in the top level menu. */ public static void clickMenu(final String... texts) { - bot.menu().menu(texts).click(); + bot.menu(getWorkbenchWindowShell()).menu(texts).click(); + } + + private static SWTBotShell getWorkbenchWindowShell() { + final Shell shell = UIThreadRunnable.syncExec(() -> { + final var window = PlatformUI.getWorkbench().getActiveWorkbenchWindow(); + return window == null ? null : window.getShell(); + }); + return new SWTBotShell(shell); + } + + private static SWTBot getInteractionBot() { + final Shell shell = UIThreadRunnable.syncExec(() -> { + if (interactionShell != null && !interactionShell.isDisposed() && interactionShell.isVisible()) { + return interactionShell; + } + + final var window = PlatformUI.getWorkbench().getActiveWorkbenchWindow(); + final Shell workbenchShell = window == null ? null : window.getShell(); + Shell topmostShell = workbenchShell; + int greatestDepth = -1; + for (final Shell candidate : Display.getDefault().getShells()) { + if (candidate == workbenchShell || candidate.isDisposed() || !candidate.isVisible() + || candidate.getText().isEmpty()) { + continue; + } + + int depth = 0; + for (Control parent = candidate.getParent(); parent != null; parent = parent.getParent()) { + depth++; + } + if (depth >= greatestDepth) { + topmostShell = candidate; + greatestDepth = depth; + } + } + + interactionShell = topmostShell; + return topmostShell; + }); + return new SWTBot(shell); + } + + private static SWTBot getShellBot(final String title) { + final Shell shell = UIThreadRunnable.syncExec(() -> { + if (interactionShell != null && !interactionShell.isDisposed() && interactionShell.isVisible() + && interactionShell.getChildren().length > 0 + && Objects.equals(title, interactionShell.getText())) { + return interactionShell; + } + + return Arrays.stream(Display.getDefault().getShells()) + .filter(candidate -> !candidate.isDisposed() && candidate.isVisible() + && candidate.getChildren().length > 0 + && Objects.equals(title, candidate.getText())) + .findFirst() + .orElse(null); + }); + if (shell != null) { + interactionShell = shell; + return new SWTBot(shell); + } + + waitForWindowWithTitle(title); + return new SWTBot(interactionShell); } /** * Waits for a window with the specified title to appear. */ public static void waitForWindowWithTitle(final String title) { - bot.waitUntil(Conditions.shellIsActive(title)); + waitUntil(() -> UIThreadRunnable.syncExec(() -> { + interactionShell = Arrays.stream(Display.getDefault().getShells()) + .filter(shell -> !shell.isDisposed() && shell.isVisible() && shell.getChildren().length > 0 + && Objects.equals(title, shell.getText())) + .findFirst() + .orElse(null); + return interactionShell != null; + }), "Unable to find initialized window with title '" + title + "'"); } /** @@ -176,7 +251,7 @@ public static void waitForWindowWithTitle(final String title) { * Throws an exception if it is unable to do so. */ public static void checkTreeItemInWindowWithTitle(final String title, final String... itemTexts) { - final Optional item = getItemInTree(bot.shell(title).bot().tree(0), itemTexts); + final Optional item = getItemInTree(getShellBot(title).tree(0), itemTexts); assertTrue("Item with texts '" + String.join(",", itemTexts) + "' not found in tree", item.isPresent()); item.orElseThrow().check(); } @@ -185,16 +260,29 @@ public static void checkTreeItemInWindowWithTitle(final String title, final Stri * Waits for a window with the specified title to appear but is not the specified window. */ public static void waitForOtherWindowWithTitle(final String title, final Shell windowToIgnore) { - waitUntil(() -> { - final AtomicBoolean result = new AtomicBoolean(false); - Display.getDefault().syncExec(() -> { - final Shell activeShell = bot.getFinder().activeShell(); - result.set(activeShell != null && activeShell != windowToIgnore - && Objects.equals(title, bot.activeShell().getText())); - }); + waitUntil(() -> UIThreadRunnable.syncExec(() -> { + Shell bestMatch = null; + int bestScore = -1; + for (final Shell shell : Display.getDefault().getShells()) { + if (shell != windowToIgnore && shell.isVisible() && shell.getChildren().length > 0 + && Objects.equals(title, shell.getText())) { + int depth = 0; + boolean descendsFromIgnoredShell = false; + for (Control parent = shell.getParent(); parent != null; parent = parent.getParent()) { + depth++; + descendsFromIgnoredShell |= parent == windowToIgnore; + } + final int score = depth + (descendsFromIgnoredShell ? 1_000 : 0); + if (score >= bestScore) { + bestMatch = shell; + bestScore = score; + } + } + } - return result.get(); - }, "Unable to find window with title '" + title + "' which is also not the specified window"); + interactionShell = bestMatch; + return bestMatch != null; + }), "Unable to find window with title '" + title + "' which is also not the specified window"); } /** @@ -202,18 +290,19 @@ public static void waitForOtherWindowWithTitle(final String title, final Shell w * @return the active shell */ public static Shell getActiveWindow() { - return bot.getFinder().activeShell(); + return interactionShell; } public static void setFocusToShell(final String title) { - bot.shell(title).setFocus(); + getShellBot(title); + new SWTBotShell(interactionShell).setFocus(); } /** * Asserts that the nth text field has the specified value. */ public static void assertTextFieldText(final String message, final int index, final String expectedValue) { - final SWTBotText text = bot.text(index); + final SWTBotText text = getInteractionBot().text(index); assertEquals(message, expectedValue, text.getText()); } @@ -223,7 +312,7 @@ public static void assertTextFieldText(final String message, final int index, fi * @param value */ public static void setTextFieldText(final int index, final String value) { - bot.text(index).setText(value); + getInteractionBot().text(index).setText(value); assertTextFieldText("New value not valid", index, value); } @@ -231,7 +320,7 @@ public static void setTextFieldText(final int index, final String value) { * Asserts that the text field with the specified ID has the specified value. */ public static void assertTextFieldWithIdText(final String message, final String id, final String expectedValue) { - final SWTBotText text = bot.textWithId(id); + final SWTBotText text = getInteractionBot().textWithId(id); assertEquals(message, expectedValue, text.getText()); } @@ -241,7 +330,9 @@ public static void assertTextFieldWithIdText(final String message, final String * @param value is the new value */ public static void setTextFieldWithIdText(final String id, final String value) { - bot.textWithId(id).setText(value); + final SWTBotText text = getInteractionBot().textWithId(id); + text.setText(value); + pendingFocusOutControl = (Control) getInteractionBot().getFinder().findControls(withId(id)).get(0); assertTextFieldWithIdText("New value not valid", id, value); } @@ -250,14 +341,14 @@ public static void setTextFieldWithIdText(final String id, final String value) { * @param id is the ID of the text widget */ public static void setFocusToTextFieldWithId(final String id) { - bot.textWithId(id).setFocus(); + getInteractionBot().textWithId(id).setFocus(); } /** * Asserts that the nth spinner has the specified value. */ public static void assertSpinnerValue(final String message, final int index, final int expectedValue) { - final SWTBotSpinner spinner = bot.spinner(index); + final SWTBotSpinner spinner = getInteractionBot().spinner(index); assertEquals(message, expectedValue, spinner.getSelection()); } @@ -267,7 +358,7 @@ public static void assertSpinnerValue(final String message, final int index, fin * @param value */ public static void setSpinnerValue(final int index, final int value) { - bot.spinner(index).setSelection(value); + getInteractionBot().spinner(index).setSelection(value); assertSpinnerValue("New value not valid", index, value); } @@ -275,14 +366,14 @@ public static void setSpinnerValue(final int index, final int value) { * Asserts that the nth combo box has the specified selection. */ public static void assertComboBoxSelection(final String message, final int index, final String expected) { - assertEquals(message, expected, bot.comboBox(index).getText()); + assertEquals(message, expected, getInteractionBot().comboBox(index).getText()); } /** * Waits until the nth combo box has the specified selection */ public static void waitUntilComboBoxSelect(final int comboIndex, final String text) { - waitUntil(() -> Objects.equals(text, bot.comboBox(comboIndex).getText()), + waitUntil(() -> Objects.equals(text, getInteractionBot().comboBox(comboIndex).getText()), "Combo selection does not match '" + text + "'"); } @@ -290,7 +381,7 @@ public static void waitUntilComboBoxSelect(final int comboIndex, final String te * Waits until the combo box with the specified ID has the specified selection */ public static void waitUntilComboBoxWithIdSelect(final String id, final String text) { - waitUntil(() -> Objects.equals(text, bot.comboBoxWithId(id).getText()), + waitUntil(() -> Objects.equals(text, getInteractionBot().comboBoxWithId(id).getText()), "Combo selection does not match '" + text + "'"); } @@ -298,7 +389,7 @@ public static void waitUntilComboBoxWithIdSelect(final String id, final String t * Sets the selection of the nth combo box to the specified value. */ public static void setComboBoxSelection(final int index, final String value) { - bot.comboBox(index).setSelection(value); + getInteractionBot().comboBox(index).setSelection(value); assertComboBoxSelection("New value not valid", index, value); } @@ -306,35 +397,35 @@ public static void setComboBoxSelection(final int index, final String value) { * Sets the combo box with specified ID to the specified value. */ public static void setComboBoxWithIdSelection(final String id, final String value) { - bot.comboBoxWithId(id).setSelection(value); + getInteractionBot().comboBoxWithId(id).setSelection(value); } /** * Clicks the radio button which has the specified text. */ public static void clickRadioButton(final String text) { - bot.radio(text).click(); + getInteractionBot().radio(text).click(); } /** * Returns whether the radio button with the specified mnemonic is selected. */ public static boolean isRadioButtonSelected(final String text) { - return bot.radio(text).isSelected(); + return getInteractionBot().radio(text).isSelected(); } /** * Clicks the check box at specified index. */ public static void clickCheckbox(final int index) { - bot.checkBox(index).click(); + getInteractionBot().checkBox(index).click(); } /** * Types the specified text in the StyledText with the specified id. */ public static void typeInStyledText(final String id, final String text) { - final SWTBotStyledText styledText = bot.styledTextWithId(id); + final SWTBotStyledText styledText = getInteractionBot().styledTextWithId(id); styledText.setText(text); Display.getDefault().syncExec(() -> { @@ -364,12 +455,12 @@ public static void executeHandlerServiceCommandWithId(final String cmdId, * Returns the text of the StyledText with the specified id. */ public static String getStyledTextWithIdText(final String id) { - final SWTBotStyledText styledText = bot.styledTextWithId(id); + final SWTBotStyledText styledText = getInteractionBot().styledTextWithId(id); return styledText.getText(); } public static void waitForStyledTextToMatch(final String id, final String text) { - final SWTBotStyledText styledText = bot.styledTextWithId(id); + final SWTBotStyledText styledText = getInteractionBot().styledTextWithId(id); waitUntil(() -> styledText.getText().equals(text), "StyledText text '" + styledText.getText() + "' does not match expected '" + text + "'"); } @@ -378,35 +469,35 @@ public static void waitForStyledTextToMatch(final String id, final String text) * Clicks the checkbox with the specified mnemonic text */ public static void clickCheckbox(final String text) { - bot.checkBox(text).click(); + getInteractionBot().checkBox(text).click(); } /** * Clicks the checkbox with the specified id */ public static void clickCheckboxById(final String id) { - bot.checkBoxWithId(id).click(); + getInteractionBot().checkBoxWithId(id).click(); } /** * Returns whether check box with the specified mnemonic text is checked */ public static boolean isCheckboxChecked(final String text) { - return bot.checkBox(text).isChecked(); + return getInteractionBot().checkBox(text).isChecked(); } /** * Returns whether check box with the specified id is checked */ public static boolean isCheckboxCheckedById(final String id) { - return bot.checkBoxWithId(id).isChecked(); + return getInteractionBot().checkBoxWithId(id).isChecked(); } /** * Clicks the button which has the specified text. */ public static void clickButton(final String text) { - final SWTBotButton btn = bot.button(text); + final SWTBotButton btn = getInteractionBot().button(text); btn.click(); } @@ -414,7 +505,7 @@ public static void clickButton(final String text) { * Clicks the nth button which has the specified text. */ public static void clickButton(final String text, final int index) { - final SWTBotButton btn = bot.button(text, index); + final SWTBotButton btn = getInteractionBot().button(text, index); btn.click(); } @@ -422,7 +513,7 @@ public static void clickButton(final String text, final int index) { * Clicks the button which has the specified testing ID. */ public static void clickButtonWithId(final String id) { - final SWTBotButton btn = bot.buttonWithId(id); + final SWTBotButton btn = getInteractionBot().buttonWithId(id); btn.click(); } @@ -430,25 +521,50 @@ public static void clickButtonWithId(final String id) { * Clicks the checkbox which has the specified testing ID. */ public static void clickCheckboxWithId(final String id) { - bot.checkBoxWithId(id).click(); + getInteractionBot().checkBoxWithId(id).click(); } public static void clickToolbarItem(final String title) { - bot.toolbarButtonWithTooltip(title).click(); + final SWTBotShell shell = getWorkbenchWindowShell(); + shell.bot().toolbarButtonWithTooltip(title).click(); } /** * Clicks the context menu of the focused widget */ public static void clickContextMenuOfFocused(final String... texts) { - getFocusedWidget().contextMenu().menu(texts).click(); + assertNotNull("Context menu target is null", contextMenuTarget); + contextMenuTarget.contextMenu().menu(texts).click(); } /** * Asserts that the title of the active title contains the specified string */ public static void assertActiveWindowTitleContains(final String value) { - assertContains(value, bot.activeShell().getText()); + bot.waitUntil(new DefaultCondition() { + @Override + public boolean test() { + return UIThreadRunnable.syncExec(() -> { + final var window = PlatformUI.getWorkbench().getActiveWorkbenchWindow(); + if (window == null) { + return false; + } + + final Shell shell = window.getShell(); + if (shell == null || shell.isDisposed() || !shell.isVisible()) { + return false; + } + + interactionShell = shell; + return shell.getText().contains(value); + }); + } + + @Override + public String getFailureMessage() { + return "Visible workbench window with title containing '" + value + "' was not found"; + } + }); } /** @@ -472,15 +588,15 @@ public static void clickPropertiesViewTab(final String title) { } public static void clickTableItem(final int tableIndex, final String tableItem) { - bot.table(tableIndex).getTableItem(tableItem).click(); + getInteractionBot().table(tableIndex).getTableItem(tableItem).click(); } public static void clickTableItem(final int tableIndex, final int rowIndex) { - bot.table(tableIndex).getTableItem(rowIndex).click(); + getInteractionBot().table(tableIndex).getTableItem(rowIndex).click(); } public static int getNumberOfTableRows(final int tableIndex) { - return bot.table(tableIndex).rowCount(); + return getInteractionBot().table(tableIndex).rowCount(); } public static void assertNumberOfTableRows(final int tableIndex, final int expectedValue) { @@ -489,11 +605,11 @@ public static void assertNumberOfTableRows(final int tableIndex, final int expec public static void assertTableItemText(final int tableIndex, final int rowIndex, final String expectedValue) { assertEquals("Unexpected table item text", expectedValue, - bot.table(tableIndex).getTableItem(rowIndex).getText()); + getInteractionBot().table(tableIndex).getTableItem(rowIndex).getText()); } public static void selectListWithIdItems(final String id, final String... texts) { - bot.listWithId(id).select(texts); + getInteractionBot().listWithId(id).select(texts); } public static void selectListWithIdItem(final String id, final String text) { @@ -501,7 +617,7 @@ public static void selectListWithIdItem(final String id, final String text) { } public static void selectListItems(final int listIndex, final String... texts) { - bot.list(listIndex).select(texts); + getInteractionBot().list(listIndex).select(texts); } public static void selectListItem(final int listIndex, final String text) { @@ -509,14 +625,14 @@ public static void selectListItem(final int listIndex, final String text) { } public static void doubleClickListItem(final int listIndex, final String text) { - bot.list(listIndex).doubleClick(text); + getInteractionBot().list(listIndex).doubleClick(text); } /** * Returns whether the text for a Label with the specified id */ public static String getTextForLabelWithId(final String id) { - return bot.labelWithId(id).getText(); + return getInteractionBot().labelWithId(id).getText(); } /** @@ -524,7 +640,7 @@ public static String getTextForLabelWithId(final String id) { */ public static String getTextForBorderedClabelWithId(final String id) { @SuppressWarnings("unchecked") - final BorderedCLabel label = bot.widget(allOf(widgetOfType(BorderedCLabel.class), withId(id)), 0); + final BorderedCLabel label = getInteractionBot().widget(allOf(widgetOfType(BorderedCLabel.class), withId(id)), 0); final String[] value = { "" }; UIThreadRunnable.syncExec(() -> { value[0] = new SWTBotCLabel((CLabel) label.getChildren()[0]).getText(); @@ -537,21 +653,21 @@ public static String getTextForBorderedClabelWithId(final String id) { * Returns the text for the text field with the specified id */ public static String getTextForTextFieldWithId(final String id) { - return bot.textWithId(id).getText(); + return getInteractionBot().textWithId(id).getText(); } /** * Returns whether an item with the specified text is contained in the list with the specified ID. */ public static boolean doesItemExistsInListWithId(final String id, final String text) { - return Arrays.asList(bot.listWithId(id).getItems()).contains(text); + return Arrays.asList(getInteractionBot().listWithId(id).getItems()).contains(text); } /** * Returns whether the text of the items in the list with a specified ID matches a specified value. */ public static boolean itemsMatchInListWithId(final String id, final String[] texts) { - return Arrays.deepEquals(bot.listWithId(id).getItems(), texts); + return Arrays.deepEquals(getInteractionBot().listWithId(id).getItems(), texts); } /** @@ -567,9 +683,11 @@ public static boolean doesItemExistsInTreeView(final String viewTitle, final Str * Throws an exception if it is unable to do so. */ public static void selectItemInTreeView(final String viewTitle, final String... itemTexts) { + bot.viewByTitle(viewTitle).setFocus(); final Optional item = getItemInTree(getFirstTreeInView(viewTitle), itemTexts); assertTrue("Item with texts '" + String.join(",", itemTexts) + "' not found in tree", item.isPresent()); - item.orElseThrow().select(); + contextMenuTarget = item.orElseThrow(); + ((SWTBotTreeItem) contextMenuTarget).select(); } /** @@ -674,7 +792,16 @@ public void describeTo(final Description description) { * Focus the specified editor */ public static void focusDiagramEditor(final DiagramReference diagram) { - getDiagramEditorBot(diagram).setFocus(); + final AgeEditor editor = getDiagramEditor(diagram); + UIThreadRunnable.syncExec(() -> { + if (pendingFocusOutControl != null && !pendingFocusOutControl.isDisposed()) { + pendingFocusOutControl.notifyListeners(SWT.FocusOut, new Event()); + pendingFocusOutControl = null; + } + PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().activate(editor); + editor.setFocus(); + editor.getFxCanvas().forceFocus(); + }); } /** @@ -726,7 +853,7 @@ public void describeTo(final Description description) { * Returns a bot for the focused widget */ public static AbstractSWTBot getFocusedWidget() { - final Control focused = bot.getFocusedWidget(); + final Control focused = getInteractionBot().getFocusedWidget(); assertTrue("Focused widget is null", focused != null); return new AbstractSWTBotControl(focused); } @@ -736,7 +863,7 @@ public static AbstractSWTBot getFocusedWidget() { * Assumes the table is a simple table with checkboxes. Such a table does not have any columns. */ public static void checkItemInSimpleTable(final int tableIndex, final String text) { - final SWTBotTable table = bot.table(tableIndex); + final SWTBotTable table = getInteractionBot().table(tableIndex); for (int row = 0; row < table.rowCount(); row++) { final SWTBotTableItem rowItem = table.getTableItem(row); if (Objects.equals(rowItem.getText(), text)) { @@ -786,12 +913,12 @@ public static void selectPaletteItem(final DiagramReference diagram, final Strin // If there is a palette group, expand it if necessary if (paletteGroup != null && !paletteGroup.isExpanded()) { - fxBot.click(paletteGroup); + UIThreadRunnable.syncExec(() -> ((ToggleButton) paletteGroup.getChildren().get(0)).fire()); waitUntil(() -> paletteGroup.isExpanded(), "Palette group not expanded"); } // Click the item to select it - fxBot.click(paletteItem); + UIThreadRunnable.syncExec(() -> paletteItem.getButton().fire()); // Wait for the item to be active final AgeEditorPaletteModel paletteModel = editor.getPaletteModel(); @@ -825,7 +952,7 @@ public static void clickDiagramElement(final DiagramReference diagram, DiagramEl Display.getDefault().syncExec(() -> editor.scrollToTopLeft(sceneNode)); - fxBot.click(sceneNode); + fxBot.firePressAndReleasePrimaryMouseButtonEventsAsync(sceneNode); } private static SWTBotCanvas findViewCanvasByTitle(final String title) { @@ -850,7 +977,9 @@ public void describeTo(final Description description) { * @param menuItem is the menu item to select */ public static void clickContextMenuOfOutlineViewItem(final String[] treeItems, final String[] menuItem) { - final SWTBotTree tree = bot.viewByTitle("Outline").bot().tree(); + final SWTBotView outline = bot.viewByTitle("Outline"); + outline.setFocus(); + final SWTBotTree tree = outline.bot().tree(); SWTBotTreeItem treeItem = findTreeItem(tree.getAllItems(), treeItems[0]); final String[] nodes = Arrays.copyOfRange(treeItems, 1, treeItems.length); @@ -858,6 +987,7 @@ public static void clickContextMenuOfOutlineViewItem(final String[] treeItems, f treeItem = findTreeItem(treeItem.getItems(), node).expand(); } + treeItem.select(); treeItem.contextMenu().menu(menuItem).click(); } @@ -866,7 +996,9 @@ public static void clickContextMenuOfOutlineViewItem(final String[] treeItems, f * @param treeItems is the name of the elements to traverse in the outline view */ public static void clickElementInOutlineView(final String... treeItems) { - final SWTBotTree tree = bot.viewByTitle("Outline").bot().tree(); + final SWTBotView outline = bot.viewByTitle("Outline"); + outline.setFocus(); + final SWTBotTree tree = outline.bot().tree(); SWTBotTreeItem treeItem = findTreeItem(tree.getAllItems(), treeItems[0]); final String[] nodes = Arrays.copyOfRange(treeItems, 1, treeItems.length); @@ -892,7 +1024,7 @@ private static SWTBotTreeItem findTreeItem(final SWTBotTreeItem[] items, final S * Sets the nth text for the window with specified title. */ public static void setTextForWindow(final String title, final int index, final String text) { - bot.shell(title).bot().text(index).setText(text); + getShellBot(title).text(index).setText(text); } /** @@ -903,9 +1035,10 @@ public static void setTextForWindow(final String title, final int index, final S * @param event the event to send to listeners */ public static void sendTextKeyUpEvent(final String title, final int index, final int eventType, final Event event) { - Display.getDefault().syncExec(() -> { + final SWTBotText text = getShellBot(title).text(index); + UIThreadRunnable.syncExec(() -> { // Send notification - bot.shell(title).bot().text(index).widget.notifyListeners(eventType, event); + text.widget.notifyListeners(eventType, event); }); } @@ -913,8 +1046,7 @@ public static void sendTextKeyUpEvent(final String title, final int index, final * Clicks the button with specified text on the window with specified title. */ public static void clickButtonForWindow(final String title, final String text) { - final SWTBotButton btn = bot.shell(title).bot().button(text); - btn.click(); + getShellBot(title).button(text).click(); } /** @@ -927,12 +1059,21 @@ public static AgeEditor getDiagramEditor(final DiagramReference diagram) { return editor; } + public static void activateDiagramEditor(final DiagramReference diagram) { + final AgeEditor editor = getDiagramEditor(diagram); + UIThreadRunnable.syncExec(() -> PlatformUI.getWorkbench() + .getActiveWorkbenchWindow() + .getActivePage() + .activate(editor)); + } + /** * Does not open or activate the editor the specified diagram. */ public static void selectDiagramElements(final DiagramReference diagram, final DiagramElementReference... elements) { final AgeEditor editor = getDiagramEditor(diagram); + contextMenuTarget = new SWTBotCanvas(editor.getFxCanvas()); final Set diagramElementsToSelect = new HashSet<>(); for (int i = 0; i < elements.length; i++) { final DiagramElementReference element = elements[i]; @@ -942,6 +1083,8 @@ public static void selectDiagramElements(final DiagramReference diagram, } Display.getDefault().syncExec(() -> { + PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().activate(editor); + editor.setFocus(); editor.getFxCanvas().forceFocus(); editor.selectDiagramNodes(diagramElementsToSelect); }); diff --git a/ge/org.osate.ge.tests/src/org/osate/ge/tests/fx/JavaFXBot.java b/ge/org.osate.ge.tests/src/org/osate/ge/tests/fx/JavaFXBot.java index b632079e27..11904b3de6 100644 --- a/ge/org.osate.ge.tests/src/org/osate/ge/tests/fx/JavaFXBot.java +++ b/ge/org.osate.ge.tests/src/org/osate/ge/tests/fx/JavaFXBot.java @@ -96,15 +96,27 @@ public void click(final Node node) { */ public void firePressAndReleasePrimaryMouseButtonEvents(final Node node) { Display.getDefault().syncExec(() -> { - javafx.event.Event.fireEvent(node, - new MouseEvent(MouseEvent.MOUSE_PRESSED, 0.0, 0.0, 0.0, 0.0, MouseButton.PRIMARY, 1, false, false, - false, false, false, false, false, false, false, false, null)); - javafx.event.Event.fireEvent(node, - new MouseEvent(MouseEvent.MOUSE_RELEASED, 0.0, 0.0, 0.0, 0.0, MouseButton.PRIMARY, 1, false, false, - false, false, false, false, false, false, false, false, null)); + firePressAndReleasePrimaryMouseButtonEventsOnDisplayThread(node); }); } + /** + * Asynchronously generates mouse pressed and released events for a node. This allows a handler to open a modal + * dialog without blocking the test thread which must interact with that dialog. + */ + public void firePressAndReleasePrimaryMouseButtonEventsAsync(final Node node) { + Display.getDefault().asyncExec(() -> firePressAndReleasePrimaryMouseButtonEventsOnDisplayThread(node)); + } + + private static void firePressAndReleasePrimaryMouseButtonEventsOnDisplayThread(final Node node) { + javafx.event.Event.fireEvent(node, + new MouseEvent(MouseEvent.MOUSE_PRESSED, 0.0, 0.0, 0.0, 0.0, MouseButton.PRIMARY, 1, false, false, + false, false, false, false, false, false, false, false, null)); + javafx.event.Event.fireEvent(node, + new MouseEvent(MouseEvent.MOUSE_RELEASED, 0.0, 0.0, 0.0, 0.0, MouseButton.PRIMARY, 1, false, false, + false, false, false, false, false, false, false, false, null)); + } + /** * Generate key typed events for the specified text. Does not use the FXRobot for compatibility reasons. See {@link #pressAndReleaseEnterKey(Node)} * @param target is the target for the event. diff --git a/ge/org.osate.ge/src/org/osate/ge/aadl2/ui/internal/tools/CreateEndToEndFlowSpecificationTool.java b/ge/org.osate.ge/src/org/osate/ge/aadl2/ui/internal/tools/CreateEndToEndFlowSpecificationTool.java index f314cd08f2..fae730971a 100644 --- a/ge/org.osate.ge/src/org/osate/ge/aadl2/ui/internal/tools/CreateEndToEndFlowSpecificationTool.java +++ b/ge/org.osate.ge/src/org/osate/ge/aadl2/ui/internal/tools/CreateEndToEndFlowSpecificationTool.java @@ -121,9 +121,8 @@ public CreateEndToEndFlowSpecificationTool(final InternalDiagramEditor editor, f final ReferenceService referenceService = Objects.requireNonNull(Adapters.adapt(editor, ReferenceService.class), "unable to retrieve reference service"); - final Display display = Display.getCurrent(); final UiService uiService = Adapters.adapt(editor, UiService.class); - createFlowDialog = new CreateFlowsToolsDialog(display.getActiveShell(), container, endToEndFlow, uiService); + createFlowDialog = new CreateFlowsToolsDialog(editor.getSite().getShell(), container, endToEndFlow, uiService); createFlowDialog.setEndToEndFlowName(endToEndFlow.getName()); // Find segments in order @@ -142,9 +141,8 @@ public CreateEndToEndFlowSpecificationTool(final InternalDiagramEditor editor, f public CreateEndToEndFlowSpecificationTool(final InternalDiagramEditor editor) { this.editor = editor; - final Display display = Display.getCurrent(); final UiService uiService = Adapters.adapt(editor, UiService.class); - createFlowDialog = new CreateFlowsToolsDialog(display.getActiveShell(), null, null, uiService); + createFlowDialog = new CreateFlowsToolsDialog(editor.getSite().getShell(), null, null, uiService); } @Override @@ -843,7 +841,7 @@ protected void configureShell(final Shell newShell) { super.configureShell(newShell); newShell.setText("End To End Flow Specification Tool"); newShell.setLocation( - UiUtil.getOffsetRectangleLocation(Display.getCurrent().getActiveShell().getBounds(), 50, 50)); + UiUtil.getOffsetRectangleLocation(newShell.getParent().getBounds(), 50, 50)); newShell.setSize(800, 400); newShell.setMinimumSize(460, 215); } @@ -1140,7 +1138,7 @@ protected void configureShell(final Shell newShell) { super.configureShell(newShell); newShell.setText("Element Selection"); newShell.setLocation( - UiUtil.getOffsetRectangleLocation(Display.getCurrent().getActiveShell().getBounds(), 50, 50)); + UiUtil.getOffsetRectangleLocation(newShell.getParent().getBounds(), 50, 50)); newShell.setSize(400, 200); newShell.setMinimumSize(400, 200); } @@ -1232,7 +1230,7 @@ protected void configureShell(final Shell newShell) { super.configureShell(newShell); newShell.setText("Element Selection"); newShell.setLocation( - UiUtil.getOffsetRectangleLocation(Display.getCurrent().getActiveShell().getBounds(), 50, 50)); + UiUtil.getOffsetRectangleLocation(newShell.getParent().getBounds(), 50, 50)); newShell.setSize(400, 200); newShell.setMinimumSize(400, 200); } diff --git a/ge/org.osate.ge/src/org/osate/ge/aadl2/ui/internal/tools/CreateFlowImplementationTool.java b/ge/org.osate.ge/src/org/osate/ge/aadl2/ui/internal/tools/CreateFlowImplementationTool.java index 4ae74bb88e..b1ae1228ea 100644 --- a/ge/org.osate.ge/src/org/osate/ge/aadl2/ui/internal/tools/CreateFlowImplementationTool.java +++ b/ge/org.osate.ge/src/org/osate/ge/aadl2/ui/internal/tools/CreateFlowImplementationTool.java @@ -107,8 +107,7 @@ public CreateFlowImplementationTool(final InternalDiagramEditor editor, final Di "ui service must not be null"); this.referenceService = Objects.requireNonNull(Adapters.adapt(editor, ReferenceService.class), "unable to retrieve reference service"); - final Display display = Display.getCurrent(); - createFlowImplDlg = new CreateFlowImplementationDialog(display.getActiveShell(), flowImpl, uiService); + createFlowImplDlg = new CreateFlowImplementationDialog(editor.getSite().getShell(), flowImpl, uiService); // Populate segments final FlowSpecification flowSpec = flowImpl.getSpecification(); @@ -138,12 +137,11 @@ public CreateFlowImplementationTool(final InternalDiagramEditor editor, final Di } public CreateFlowImplementationTool(final InternalDiagramEditor editor) { - final Display display = Display.getCurrent(); final UiService uiService = Objects.requireNonNull(Adapters.adapt(editor, UiService.class), "ui service must not be null"); this.referenceService = Objects.requireNonNull(Adapters.adapt(editor, ReferenceService.class), "unable to retrieve reference service"); - createFlowImplDlg = new CreateFlowImplementationDialog(display.getActiveShell(), null, uiService); + createFlowImplDlg = new CreateFlowImplementationDialog(editor.getSite().getShell(), null, uiService); } @Override @@ -1032,7 +1030,7 @@ protected void configureShell(final Shell newShell) { super.configureShell(newShell); newShell.setText("Element Selection"); newShell.setLocation( - UiUtil.getOffsetRectangleLocation(Display.getCurrent().getActiveShell().getBounds(), 50, 50)); + UiUtil.getOffsetRectangleLocation(newShell.getParent().getBounds(), 50, 50)); newShell.setSize(400, 200); newShell.setMinimumSize(400, 200); } @@ -1114,7 +1112,7 @@ protected void configureShell(final Shell newShell) { super.configureShell(newShell); newShell.setText("Flow Implementation Tool"); newShell.setLocation( - UiUtil.getOffsetRectangleLocation(Display.getCurrent().getActiveShell().getBounds(), 50, 50)); + UiUtil.getOffsetRectangleLocation(newShell.getParent().getBounds(), 50, 50)); newShell.setSize(800, 400); newShell.setMinimumSize(300, 215); }