Fixed tests failing due to focus timing issues by allowing the test

framework to specify the source of key bindings
This commit is contained in:
dragonmacher 2020-03-13 16:50:54 -04:00
parent 3418492a8c
commit 3f62f91a2e
5 changed files with 71 additions and 10 deletions

View file

@ -337,7 +337,7 @@ public class ComponentProviderActionsTest extends AbstractGhidraHeadedIntegratio
// Note: there may be a test focus issue here. If this test fails sporadically due to // Note: there may be a test focus issue here. If this test fails sporadically due to
// how the action context is generated (it depends on focus). It is only useful to fail // how the action context is generated (it depends on focus). It is only useful to fail
// here in development mode. // here in development mode.
triggerKey(tool.getToolFrame(), controlEsc); triggerKey(provider.getComponent(), controlEsc);
assertProviderIsHidden_InNonBatchMode(); assertProviderIsHidden_InNonBatchMode();
} }
@ -376,6 +376,7 @@ public class ComponentProviderActionsTest extends AbstractGhidraHeadedIntegratio
provider.addToTool(); provider.addToTool();
tool.showComponentProvider(provider, true); tool.showComponentProvider(provider, true);
waitForSwing(); waitForSwing();
waitForCondition(() -> provider.isVisible());
} }
private void hideProvider() { private void hideProvider() {
@ -546,6 +547,7 @@ public class ComponentProviderActionsTest extends AbstractGhidraHeadedIntegratio
// simulate the user mousing over the toolbar button // simulate the user mousing over the toolbar button
DockingActionIf closeAction = getAction(tool, provider.getOwner(), "Close Window"); DockingActionIf closeAction = getAction(tool, provider.getOwner(), "Close Window");
assertNotNull("Provider action not installed in toolbar", closeAction); assertNotNull("Provider action not installed in toolbar", closeAction);
DockingWindowManager.setMouseOverAction(closeAction); DockingWindowManager.setMouseOverAction(closeAction);
performLaunchKeyStrokeDialogAction(); performLaunchKeyStrokeDialogAction();
@ -554,6 +556,7 @@ public class ComponentProviderActionsTest extends AbstractGhidraHeadedIntegratio
runSwing(() -> dialog.setKeyStroke(ks)); runSwing(() -> dialog.setKeyStroke(ks));
pressButtonByText(dialog, "OK"); pressButtonByText(dialog, "OK");
waitForSwing();
assertFalse("Invalid key stroke: " + ks, runSwing(() -> dialog.isVisible())); assertFalse("Invalid key stroke: " + ks, runSwing(() -> dialog.isVisible()));
} }

View file

@ -77,12 +77,13 @@ public class TableChooserDialogTest extends AbstractGhidraHeadedIntegrationTest
tool.setVisible(true); tool.setVisible(true);
Program program = new ToyProgramBuilder("Test", true).getProgram(); Program program = new ToyProgramBuilder("Test", true).getProgram();
Navigatable navigatable = null; Navigatable navigatable = null;
dialog = new TableChooserDialog(tool, executor, program, "Title", navigatable); dialog = new TableChooserDialog(tool, executor, program, "Dialog Title", navigatable);
testAction = new TestAction(); testAction = new TestAction();
dialog.addAction(testAction); dialog.addAction(testAction);
dialog.show(); dialog.show();
waitForDialogComponent(TableChooserDialog.class);
loadData(); loadData();
} }
@ -289,7 +290,8 @@ public class TableChooserDialogTest extends AbstractGhidraHeadedIntegrationTest
KeyStroke newKs = KeyStroke.getKeyStroke('A', 0, false); KeyStroke newKs = KeyStroke.getKeyStroke('A', 0, false);
setKeyBindingViaF4Dialog(testAction, newKs); setKeyBindingViaF4Dialog(testAction, newKs);
triggerKey(dialog.getComponent(), newKs); triggerKey(dialog.getComponent(), newKs);
assertTrue(testAction.wasInvoked()); assertTrue("Action was not invoked from the new key binding: " + newKs,
testAction.wasInvoked());
} }
@Test @Test

View file

@ -354,9 +354,9 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
} }
/** /**
* Get the window which contains the specified Provider's component. * Get the window that contains the specified Provider's component
* @param provider component provider * @param provider component provider
* @return window or null if component is not visible or not found. * @return window or null if component is not visible or not found
*/ */
public Window getProviderWindow(ComponentProvider provider) { public Window getProviderWindow(ComponentProvider provider) {
ComponentPlaceholder placeholder = getActivePlaceholder(provider); ComponentPlaceholder placeholder = getActivePlaceholder(provider);
@ -366,6 +366,24 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
return null; return null;
} }
/**
* Get the provider that contains the specified component
* @param c the component
* @return the provider; null if now containing provider is found
*/
public ComponentProvider getProvider(Component c) {
if (c != null) {
DockableComponent dc = getDockableComponent(c);
if (dc != null) {
ComponentPlaceholder placeholder = dc.getComponentWindowingPlaceholder();
if (placeholder != null) {
return placeholder.getProvider();
}
}
}
return null;
}
ComponentPlaceholder getActivePlaceholder(ComponentProvider provider) { ComponentPlaceholder getActivePlaceholder(ComponentProvider provider) {
return placeholderManager.getActivePlaceholder(provider); return placeholderManager.getActivePlaceholder(provider);
} }

View file

@ -24,6 +24,7 @@ import java.awt.event.KeyListener;
import javax.swing.*; import javax.swing.*;
import javax.swing.text.JTextComponent; import javax.swing.text.JTextComponent;
import docking.action.MultipleKeyAction;
import docking.actions.KeyBindingUtils; import docking.actions.KeyBindingUtils;
import ghidra.util.bean.GGlassPane; import ghidra.util.bean.GGlassPane;
import ghidra.util.exception.AssertException; import ghidra.util.exception.AssertException;
@ -136,7 +137,7 @@ public class KeyBindingOverrideKeyEventDispatcher implements KeyEventDispatcher
return false; return false;
} }
KeyBindingPrecedence keyBindingPrecedence = action.getKeyBindingPrecedence(); KeyBindingPrecedence keyBindingPrecedence = getValidKeyBindingPrecedence(action);
if (keyBindingPrecedence == null) { if (keyBindingPrecedence == null) {
// Odd Code: we use the precedence as a signal to say that, when it is null, there // Odd Code: we use the precedence as a signal to say that, when it is null, there
// are no valid bindings to be processed. We used to have a isValidContext() // are no valid bindings to be processed. We used to have a isValidContext()
@ -158,6 +159,15 @@ public class KeyBindingOverrideKeyEventDispatcher implements KeyEventDispatcher
// @formatter:on // @formatter:on
} }
private KeyBindingPrecedence getValidKeyBindingPrecedence(DockingKeyBindingAction action) {
if (action instanceof MultipleKeyAction) {
MultipleKeyAction multiAction = (MultipleKeyAction) action;
return multiAction.geValidKeyBindingPrecedence(focusProvider.getFocusOwner());
}
return action.getKeyBindingPrecedence();
}
/** /**
* Returns true if the given key event should be blocked (i.e., not processed by us or Java). * Returns true if the given key event should be blocked (i.e., not processed by us or Java).
*/ */

View file

@ -15,6 +15,7 @@
*/ */
package docking.action; package docking.action;
import java.awt.Component;
import java.awt.Window; import java.awt.Window;
import java.awt.event.ActionEvent; import java.awt.event.ActionEvent;
import java.util.*; import java.util.*;
@ -23,10 +24,11 @@ import javax.swing.*;
import docking.*; import docking.*;
import docking.actions.KeyBindingUtils; import docking.actions.KeyBindingUtils;
import generic.util.WindowUtilities;
import ghidra.util.Swing; import ghidra.util.Swing;
/** /**
* Action that manages multiple PluginActions mapped to this action's key binding. * Action that manages multiple {@link DockingAction}s mapped to a given key binding
*/ */
public class MultipleKeyAction extends DockingKeyBindingAction { public class MultipleKeyAction extends DockingKeyBindingAction {
private List<ActionData> actions = new ArrayList<>(); private List<ActionData> actions = new ArrayList<>();
@ -228,7 +230,19 @@ public class MultipleKeyAction extends DockingKeyBindingAction {
@Override @Override
public KeyBindingPrecedence getKeyBindingPrecedence() { public KeyBindingPrecedence getKeyBindingPrecedence() {
List<ExecutableKeyActionAdapter> validActions = getActionsForCurrentContext(null); return geValidKeyBindingPrecedence(null);
}
/**
* This is a special version of {@link #getKeyBindingPrecedence()} that allows the internal
* key event processing to specify the source component when determining how precedence should
* be established for the actions contained herein.
* @param source the component; may be null
* @return the precedence; may be null
*/
public KeyBindingPrecedence geValidKeyBindingPrecedence(Component source) {
List<ExecutableKeyActionAdapter> validActions = getActionsForCurrentContext(source);
if (validActions.isEmpty()) { if (validActions.isEmpty()) {
return null; // a signal that no actions are valid for the current context return null; // a signal that no actions are valid for the current context
} }
@ -245,7 +259,7 @@ public class MultipleKeyAction extends DockingKeyBindingAction {
private List<ExecutableKeyActionAdapter> getActionsForCurrentContext(Object eventSource) { private List<ExecutableKeyActionAdapter> getActionsForCurrentContext(Object eventSource) {
DockingWindowManager dwm = tool.getWindowManager(); DockingWindowManager dwm = tool.getWindowManager();
Window window = dwm.getActiveWindow(); Window window = getWindow(dwm, eventSource);
if (window instanceof DockingDialog) { if (window instanceof DockingDialog) {
DockingDialog dockingDialog = (DockingDialog) window; DockingDialog dockingDialog = (DockingDialog) window;
DialogComponentProvider provider = dockingDialog.getDialogComponent(); DialogComponentProvider provider = dockingDialog.getDialogComponent();
@ -258,13 +272,27 @@ public class MultipleKeyAction extends DockingKeyBindingAction {
return validActions; return validActions;
} }
ComponentProvider localProvider = dwm.getActiveComponentProvider(); ComponentProvider localProvider = getProvider(dwm, eventSource);
ActionContext localContext = getLocalContext(localProvider); ActionContext localContext = getLocalContext(localProvider);
localContext.setSourceObject(eventSource); localContext.setSourceObject(eventSource);
List<ExecutableKeyActionAdapter> validActions = getValidContextActions(localContext); List<ExecutableKeyActionAdapter> validActions = getValidContextActions(localContext);
return validActions; return validActions;
} }
private ComponentProvider getProvider(DockingWindowManager dwm, Object eventSource) {
if (eventSource instanceof Component) {
return dwm.getProvider((Component) eventSource);
}
return dwm.getActiveComponentProvider();
}
private Window getWindow(DockingWindowManager dwm, Object eventSource) {
if (eventSource instanceof Component) {
return WindowUtilities.windowForComponent((Component) eventSource);
}
return dwm.getActiveWindow();
}
public List<DockingActionIf> getActions() { public List<DockingActionIf> getActions() {
List<DockingActionIf> list = new ArrayList<>(actions.size()); List<DockingActionIf> list = new ArrayList<>(actions.size());
for (ActionData actionData : actions) { for (ActionData actionData : actions) {