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

View file

@ -77,12 +77,13 @@ public class TableChooserDialogTest extends AbstractGhidraHeadedIntegrationTest
tool.setVisible(true);
Program program = new ToyProgramBuilder("Test", true).getProgram();
Navigatable navigatable = null;
dialog = new TableChooserDialog(tool, executor, program, "Title", navigatable);
dialog = new TableChooserDialog(tool, executor, program, "Dialog Title", navigatable);
testAction = new TestAction();
dialog.addAction(testAction);
dialog.show();
waitForDialogComponent(TableChooserDialog.class);
loadData();
}
@ -289,7 +290,8 @@ public class TableChooserDialogTest extends AbstractGhidraHeadedIntegrationTest
KeyStroke newKs = KeyStroke.getKeyStroke('A', 0, false);
setKeyBindingViaF4Dialog(testAction, newKs);
triggerKey(dialog.getComponent(), newKs);
assertTrue(testAction.wasInvoked());
assertTrue("Action was not invoked from the new key binding: " + newKs,
testAction.wasInvoked());
}
@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
* @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) {
ComponentPlaceholder placeholder = getActivePlaceholder(provider);
@ -366,6 +366,24 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
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) {
return placeholderManager.getActivePlaceholder(provider);
}

View file

@ -24,6 +24,7 @@ import java.awt.event.KeyListener;
import javax.swing.*;
import javax.swing.text.JTextComponent;
import docking.action.MultipleKeyAction;
import docking.actions.KeyBindingUtils;
import ghidra.util.bean.GGlassPane;
import ghidra.util.exception.AssertException;
@ -136,7 +137,7 @@ public class KeyBindingOverrideKeyEventDispatcher implements KeyEventDispatcher
return false;
}
KeyBindingPrecedence keyBindingPrecedence = action.getKeyBindingPrecedence();
KeyBindingPrecedence keyBindingPrecedence = getValidKeyBindingPrecedence(action);
if (keyBindingPrecedence == null) {
// 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()
@ -158,6 +159,15 @@ public class KeyBindingOverrideKeyEventDispatcher implements KeyEventDispatcher
// @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).
*/

View file

@ -15,6 +15,7 @@
*/
package docking.action;
import java.awt.Component;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.util.*;
@ -23,10 +24,11 @@ import javax.swing.*;
import docking.*;
import docking.actions.KeyBindingUtils;
import generic.util.WindowUtilities;
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 {
private List<ActionData> actions = new ArrayList<>();
@ -228,7 +230,19 @@ public class MultipleKeyAction extends DockingKeyBindingAction {
@Override
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()) {
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) {
DockingWindowManager dwm = tool.getWindowManager();
Window window = dwm.getActiveWindow();
Window window = getWindow(dwm, eventSource);
if (window instanceof DockingDialog) {
DockingDialog dockingDialog = (DockingDialog) window;
DialogComponentProvider provider = dockingDialog.getDialogComponent();
@ -258,13 +272,27 @@ public class MultipleKeyAction extends DockingKeyBindingAction {
return validActions;
}
ComponentProvider localProvider = dwm.getActiveComponentProvider();
ComponentProvider localProvider = getProvider(dwm, eventSource);
ActionContext localContext = getLocalContext(localProvider);
localContext.setSourceObject(eventSource);
List<ExecutableKeyActionAdapter> validActions = getValidContextActions(localContext);
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() {
List<DockingActionIf> list = new ArrayList<>(actions.size());
for (ActionData actionData : actions) {