Merge remote-tracking branch 'origin/GP-0-dragonmacher-test-fixes-9-22-21'

This commit is contained in:
Ryan Kurtz 2021-09-22 12:58:46 -04:00
commit 761fffc004
4 changed files with 225 additions and 211 deletions

View File

@ -189,8 +189,8 @@ class FGActionManager {
return !(context instanceof FunctionGraphVertexLocationInFullViewModeActionContext);
}
};
zoomOutAction.setPopupMenuData(
new MenuData(new String[] { "Zoom Out" }, popuEndPopupGroup));
zoomOutAction
.setPopupMenuData(new MenuData(new String[] { "Zoom Out" }, popuEndPopupGroup));
zoomOutAction.setKeyBindingData(new KeyBindingData(
KeyStroke.getKeyStroke(KeyEvent.VK_MINUS, DockingUtils.CONTROL_KEY_MODIFIER_MASK)));
zoomOutAction.setHelpLocation(new HelpLocation("FunctionGraphPlugin", "Zoom"));
@ -325,8 +325,8 @@ class FGActionManager {
menuData.setMenuSubGroup(Integer.toString(vertexGroupingSubgroupOffset++));
editLabelAction.setDescription("Change the label for the code block");
editLabelAction.setPopupMenuData(menuData);
editLabelAction.setHelpLocation(
new HelpLocation("FunctionGraphPlugin", "Vertex_Action_Label"));
editLabelAction
.setHelpLocation(new HelpLocation("FunctionGraphPlugin", "Vertex_Action_Label"));
DockingAction fullViewAction = new DockingAction("Vertex View Mode", plugin.getName()) {
@Override
@ -717,8 +717,8 @@ class FGActionManager {
};
selectHoveredEdgesAction.setPopupMenuData(new MenuData(
new String[] { selectionMenuName, "From Hovered Edges" }, popupSelectionGroup2));
selectHoveredEdgesAction.setHelpLocation(
new HelpLocation("FunctionGraphPlugin", "Path_Selection"));
selectHoveredEdgesAction
.setHelpLocation(new HelpLocation("FunctionGraphPlugin", "Path_Selection"));
DockingAction selectFocusedEdgesAction =
new DockingAction("Make Selection From Focused Edges", plugin.getName()) {
@ -751,8 +751,8 @@ class FGActionManager {
};
selectFocusedEdgesAction.setPopupMenuData(new MenuData(
new String[] { selectionMenuName, "From Focused Edges" }, popupSelectionGroup2));
selectFocusedEdgesAction.setHelpLocation(
new HelpLocation("FunctionGraphPlugin", "Path_Selection"));
selectFocusedEdgesAction
.setHelpLocation(new HelpLocation("FunctionGraphPlugin", "Path_Selection"));
DockingAction clearCurrentSelectionAction =
new DockingAction("Clear Current Selection", plugin.getName()) {
@ -775,8 +775,8 @@ class FGActionManager {
};
clearCurrentSelectionAction.setPopupMenuData(new MenuData(
new String[] { selectionMenuName, "Clear Graph Selection" }, popupSelectionGroup3));
clearCurrentSelectionAction.setHelpLocation(
new HelpLocation("FunctionGraphPlugin", "Path_Selection"));
clearCurrentSelectionAction
.setHelpLocation(new HelpLocation("FunctionGraphPlugin", "Path_Selection"));
DockingAction selectAllAction =
new DockingAction("Select All Code Units", plugin.getName()) {
@ -815,12 +815,12 @@ class FGActionManager {
return isValidContext(context);
}
};
selectAllAction.setKeyBindingData(
new KeyBindingData(KeyEvent.VK_A, InputEvent.CTRL_DOWN_MASK));
selectAllAction
.setKeyBindingData(new KeyBindingData(KeyEvent.VK_A, InputEvent.CTRL_DOWN_MASK));
selectAllAction.setPopupMenuData(new MenuData(
new String[] { selectionMenuName, "Select All Code Units" }, popupSelectionGroup3));
selectAllAction.setHelpLocation(
new HelpLocation("FunctionGraphPlugin", "Code_Unit_Selection"));
selectAllAction
.setHelpLocation(new HelpLocation("FunctionGraphPlugin", "Code_Unit_Selection"));
provider.addLocalAction(chooseFormatsAction);
provider.addLocalAction(homeAction);
@ -885,9 +885,7 @@ class FGActionManager {
// icon to be blank in the menu, but to use this icon on the toolbar.
layoutAction.setDefaultIcon(ResourceManager.loadImage("images/preferences-system.png"));
List<ActionState<FGLayoutProvider>> actionStates =
loadActionStatesForLayoutProviders();
List<ActionState<FGLayoutProvider>> actionStates = loadActionStatesForLayoutProviders();
for (ActionState<FGLayoutProvider> actionState : actionStates) {
layoutAction.addActionState(actionState);
}
@ -900,15 +898,19 @@ class FGActionManager {
}
private List<ActionState<FGLayoutProvider>> loadActionStatesForLayoutProviders() {
List<FGLayoutProvider> layoutInstances = plugin.getLayoutProviders();
return createActionStates(layoutInstances);
}
private List<ActionState<FGLayoutProvider>> createActionStates(
List<FGLayoutProvider> layoutProviders) {
List<ActionState<FGLayoutProvider>> list = new ArrayList<>();
HelpLocation layoutHelpLocation =
new HelpLocation("FunctionGraphPlugin", "Function_Graph_Action_Layout");
for (FGLayoutProvider layout : layoutInstances) {
for (FGLayoutProvider layout : layoutProviders) {
ActionState<FGLayoutProvider> layoutState = new ActionState<>(
layout.getLayoutName(), layout.getActionIcon(), layout);
ActionState<FGLayoutProvider> layoutState =
new ActionState<>(layout.getLayoutName(), layout.getActionIcon(), layout);
layoutState.setHelpLocation(layoutHelpLocation);
list.add(layoutState);
}
@ -1157,9 +1159,8 @@ class FGActionManager {
private void makeSelectionFromAddresses(AddressSet addresses) {
ProgramSelection selection = new ProgramSelection(addresses);
plugin.getTool()
.firePluginEvent(
new ProgramSelectionPluginEvent("Spoof!", selection,
provider.getCurrentProgram()));
.firePluginEvent(new ProgramSelectionPluginEvent("Spoof!", selection,
provider.getCurrentProgram()));
}
private void ungroupVertices(Set<GroupedFunctionGraphVertex> groupVertices) {
@ -1214,6 +1215,11 @@ class FGActionManager {
layoutAction.setCurrentActionState(state);
}
void setLayouts(List<FGLayoutProvider> layouts) {
List<ActionState<FGLayoutProvider>> states = createActionStates(layouts);
layoutAction.setActionStates(states);
}
ActionState<FGLayoutProvider> getCurrentLayoutState() {
return layoutAction.getCurrentState();
}

View File

@ -54,6 +54,7 @@ import ghidra.app.plugin.core.clipboard.ClipboardPlugin;
import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
import ghidra.app.plugin.core.functiongraph.graph.*;
import ghidra.app.plugin.core.functiongraph.graph.layout.FGLayoutProvider;
import ghidra.app.plugin.core.functiongraph.graph.layout.TestFGLayoutProvider;
import ghidra.app.plugin.core.functiongraph.graph.vertex.*;
import ghidra.app.plugin.core.functiongraph.mvc.*;
import ghidra.app.services.*;
@ -74,6 +75,7 @@ import ghidra.program.util.ProgramLocation;
import ghidra.program.util.ProgramSelection;
import ghidra.test.*;
import ghidra.util.Msg;
import ghidra.util.exception.AssertException;
import ghidra.util.task.RunManager;
public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedIntegrationTest {
@ -552,11 +554,19 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte
protected void showFunctionGraphProvider() {
ComponentProvider provider = tool.getComponentProvider("Function Graph");
FGProvider provider = (FGProvider) tool.getComponentProvider("Function Graph");
tool.showComponentProvider(provider, true);
graphProvider = waitForComponentProvider(FGProvider.class);
assertNotNull("Graph not shown", graphProvider);
installTestGraphLayout(provider);
}
protected void installTestGraphLayout(FGProvider provider) {
FGActionManager actionManager = provider.getActionManager();
List<FGLayoutProvider> layouts = List.of(new TestFGLayoutProvider());
runSwing(() -> actionManager.setLayouts(layouts));
}
protected ProgramSelection makeSingleVertexSelectionInCodeBrowser() {
@ -1003,7 +1013,7 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte
combined.add(groupedVertex);
pickVertices(combined);
// execute the 'add to group' action
// execute the 'add to group' action
JComponent component = getComponent(groupedVertex);
DockingAction action =
(DockingAction) TestUtils.getInstanceField("addToGroupAction", component);
@ -1025,7 +1035,7 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte
Collection<FGEdge> ungroupedEdges) {
for (FGEdge edge : ungroupedEdges) {
//
// note: the edges we are given *may* be linked to disposed vertices, so we have
// note: the edges we are given *may* be linked to disposed vertices, so we have
// to locate the edge in the graph that may represent the given edge.
//
FGEdge currentEdge = getCurrentEdge(functionGraph, edge);
@ -1222,12 +1232,6 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte
protected FGData create12345Graph() {
//
// Note: we are manipulating the graph for testing by removing vertices. Some layouts
// do not handle this well, so we will use one we know works.
//
setMinCrossLayout();
// function sscanf
FGData funtionGraphData = graphFunction("100415a");
@ -1316,7 +1320,7 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte
// This tests verifies that a group will not be created if there is only one vertex
// found upon restoring settings. If we want to put that code back, then this test
// is again valid.
//
//
public void dontTestRestoringWhenCodeBlocksHaveChanged_DoesntRegroup() {
int transactionID = -1;
try {
@ -1390,38 +1394,38 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte
FGData graphData = graphFunction("01002cf5");
FunctionGraph functionGraph = graphData.getFunctionGraph();
Graph<FGVertex, FGEdge> graph = functionGraph;
Set<FGVertex> ungroupedVertices = selectVertices( functionGraph,
"01002d2b" /* Another Local*/,
Set<FGVertex> ungroupedVertices = selectVertices( functionGraph,
"01002d2b" /* Another Local*/,
"01002d1f" /* MyLocal */);
Set<FGEdge> ungroupedEdges = getEdges(graph, ungroupedVertices);
assertEquals("Did not grab all known edges for vertices", 4, ungroupedEdges.size());
group(ungroupedVertices);
assertVerticesRemoved(graph, ungroupedVertices);
assertEdgesRemoved(graph, ungroupedEdges);
// -1 because one one of the edges was between two of the vertices being grouped
int expectedGroupedEdgeCount = ungroupedEdges.size() - 1;
GroupedFunctionGraphVertex groupedVertex =
validateNewGroupedVertexFromVertices(functionGraph, ungroupedVertices,
validateNewGroupedVertexFromVertices(functionGraph, ungroupedVertices,
expectedGroupedEdgeCount);
ungroup(groupedVertex);
assertVertexRemoved(graph, groupedVertex);
assertVerticesAdded(graph, ungroupedVertices);
assertEdgesAdded(functionGraph, ungroupedEdges);
assertSelected(ungroupedVertices);
}
// @formatter:on
protected void doTestGroupingProperlyTranslatesEdgesFromGroupedVerticesToRealVertices() {
//
// WARNING!!! WARNING!!! WARNING!!! WARNING!!! WARNING!!! WARNING!!!
// This is not a junit test in that it is long, involved, hidden and complicated. We
// WARNING!!! WARNING!!! WARNING!!! WARNING!!! WARNING!!! WARNING!!!
// This is not a junit test in that it is long, involved, hidden and complicated. We
// need to test this functionality, but we don't have a jComplicatedTest, so we will do
// it here.
//
@ -1429,32 +1433,32 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte
//
// Desired Behavior: We want to be able to group vertices, group grouped vertices and then
// ungroup them in any order. For us to be able to do this, our group
// vertices must store enough edge information to be able to ungroup
// vertices must store enough edge information to be able to ungroup
// and find vertices for edges *whether or now those vertices have been
// grouped or ungrouped*
//
// Original Bug: We had a bug loosely described here:
//
// Original Bug: We had a bug loosely described here:
// 0) Start with a directed graph of vertices.
// 1) Create two separate group vertices (A and B), such that A has an edge to B.
// 2) Create a third group vertex (Z) that contains a non-grouped vertex (B) *and* one
// 2) Create a third group vertex (Z) that contains a non-grouped vertex (B) *and* one
// of the other groups.
// 3) Now, ungroup the 1 remaining originally grouped vertex (A).
// 4) **At this point, the code could not determine which endpoint to pick for the edge
// 4) **At this point, the code could not determine which endpoint to pick for the edge
// that used to be from Z->A. Which vertex inside of A represented the connection
// pointing into Z (by way of B).
//
// The fix is mentioned in the Desired Behavior section.
//
// The fix is mentioned in the Desired Behavior section.
//
/*
0) Initial Graph
1 -> 2 -> 3 -> 4
|
*
5
*/
create12345Graph();
@ -1469,7 +1473,7 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte
FGVertex v4 = vertex("1004196");
FGVertex v5 = vertex("100419c");
// verify initial graph
// verify initial graph
verifyEdge(v1, v2);
verifyEdge(v2, v3);
verifyEdge(v3, v4);
@ -1478,12 +1482,12 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte
/*
1) Create two separate group vertices (A and B), such that A has an edge to B.
A (v:{1,2} e:{1->2, 2->3}) -> B (v:{3,4} e:{2->3,3->4,3->5})
|
*
5
*/
GroupedFunctionGraphVertex groupA = group("A", v1, v2);
@ -1494,13 +1498,13 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte
verifyEdgeCount(2);// no other edges
/*
2) Create a third group vertex (Z) that contains a non-grouped vertex *and* one
2) Create a third group vertex (Z) that contains a non-grouped vertex *and* one
of the other groups (B).
A (v:{1,2} e:{1->2, 2->3}) -> Z (
v:{B (v:{3,4} e:{2->3,3->4,3->5}), 5}
e:{2->3, 3->5}
)
)
*/
@ -1511,12 +1515,12 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte
/*
3) Now, ungroup the 1 remaining originally grouped vertex (A).
1 -> 2 -> Z (
v:{B (v:{3,4} e:{2->3,3->4,3->5}), 5}
e:{2->3, 3->5}
)
)
*/
ungroup(groupA);
@ -1526,14 +1530,14 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte
verifyEdgeCount(2);
/*
4) Now, ungroup Z and go back to having one remaining group vertex (B)
1 -> 2 -> -> B (v:{3,4} e:{2->3,3->4,3->5})
|
*
5
*/
ungroup(groupZ);
@ -1545,12 +1549,12 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte
/*
5) Finally, ungroup the last group and make sure the graph is restored
1 -> 2 -> 3 -> 4
|
*
5
5
*/
ungroup(groupB);
@ -1564,19 +1568,19 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte
}
private void doTestRestoringWhenCodeBlocksHaveChanged_DoesntRegroup() {
//
// Tests the behavior of how group vertices are restored when one or more of the vertices
//
// Tests the behavior of how group vertices are restored when one or more of the vertices
// inside of the grouped vertex is no longer available when the graph attempts to restore
// the group vertex user settings (i.e., when restarting Ghidra, the previously grouped
// vertices should reappear).
// vertices should reappear).
//
// In this test, we will be mutating a group of 2 nodes such
// that one of the nodes has been split into two. This leaves only one vertex to
// that one of the nodes has been split into two. This leaves only one vertex to
// be found by the regrouping algorithm. Furthermore, the regrouping will not take place
// if at least two vertices cannot be found.
//
//
//
// Pick a function and group some nodes.
//
FGData graphData = graphFunction("01002cf5");
@ -1587,8 +1591,8 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte
group(ungroupedVertices);
// 5 edges expected:
// -01002cf5: 2 out
// 5 edges expected:
// -01002cf5: 2 out
// -01002cf5: 2 in, 1 out
int expectedGroupedEdgeCount = 5;
GroupedFunctionGraphVertex groupedVertex = validateNewGroupedVertexFromVertices(
@ -1598,10 +1602,10 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte
Address minAddress = addresses.getMinAddress();
//
// Ideally, we would like to save, close and re-open the program so that we can get
// a round-trip saving and reloading. However, in the test environment, we cannot save
// Ideally, we would like to save, close and re-open the program so that we can get
// a round-trip saving and reloading. However, in the test environment, we cannot save
// our programs. So, we will instead just navigate away from the current function, clear
// the cache (to make sure that we read the settings again), and then verify that the
// the cache (to make sure that we read the settings again), and then verify that the
// data saved in the program has been used to re-group.
//
graphFunction("0100415a");
@ -1623,19 +1627,19 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte
}
protected void doTestRestoringWhenCodeBlocksHaveChanged_WillRegroup() {
//
// Tests the behavior of how group vertices are restored when one or more of the vertices
//
// Tests the behavior of how group vertices are restored when one or more of the vertices
// inside of the grouped vertex is no longer available when the graph attempts to restore
// the group vertex user settings (i.e., when restarting Ghidra, the previously grouped
// vertices should reappear).
//
// In this test, we will be mutating a group of 3 nodes such
// that one of the nodes has been split into two. This leaves 2 vertices to
// that one of the nodes has been split into two. This leaves 2 vertices to
// be found by the regrouping algorithm. Furthermore, the regrouping *will* still
// take place, as at least two vertices cannot be found.
//
//
//
// Pick a function and group some nodes.
//
FGData graphData = graphFunction("01002cf5");
@ -1646,8 +1650,8 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte
group(ungroupedVertices);
// 5 edges expected:
// -01002cf5: 2 out
// 5 edges expected:
// -01002cf5: 2 out
// -01002d11: 2 in, (1 out that was removed)
// -01002d1f: 2 out (1 in that was removed)
int expectedGroupedEdgeCount = 6;
@ -1659,10 +1663,10 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte
Address maxAddress = addresses.getMaxAddress();
//
// Ideally, we would like to save, close and re-open the program so that we can get
// a round-trip saving and reloading. However, in the test environment, we cannot save
// Ideally, we would like to save, close and re-open the program so that we can get
// a round-trip saving and reloading. However, in the test environment, we cannot save
// our programs. So, we will instead just navigate away from the current function, clear
// the cache (to make sure that we read the settings again), and then verify that the
// the cache (to make sure that we read the settings again), and then verify that the
// data saved in the program has been used to re-group.
//
graphFunction("0100415a");
@ -1706,12 +1710,12 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte
protected void doTestSymbolAddedWhenGrouped_SymbolInsideOfGroupNode() {
//
// By default, if the FunctionGraph detects a symbol addition to one of the code blocks
// in the graph, then it will split the affected vertex (tested elsewhere).
// in the graph, then it will split the affected vertex (tested elsewhere).
// However, if the affected vertex is grouped, then the FG will not split the node, but
// should still show the 'stale' indicator.
//
//
//
// Pick a function and group some nodes.
//
FGData graphData = graphFunction("01002cf5");
@ -1722,8 +1726,8 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte
group(ungroupedVertices);
// 5 edges expected:
// -01002cf5: 2 out
// 5 edges expected:
// -01002cf5: 2 out
// -01002cf5: 2 in, 1 out
int expectedGroupedEdgeCount = 5;
GroupedFunctionGraphVertex groupedVertex = validateNewGroupedVertexFromVertices(
@ -1748,8 +1752,8 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte
return reference.get();
}
/**
* Finds an edge that represents the given edge, which may no longer exist with
/**
* Finds an edge that represents the given edge, which may no longer exist with
* the same (==) edge instances.
*/
private FGEdge getCurrentEdge(FunctionGraph functionGraph, FGEdge edge) {
@ -1998,29 +2002,29 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte
private void setMinCrossLayout() {
Object actionManager = getInstanceField("actionManager", graphProvider);
@SuppressWarnings("unchecked")
final MultiStateDockingAction<Class<? extends FGLayoutProvider>> action =
(MultiStateDockingAction<Class<? extends FGLayoutProvider>>) getInstanceField(
"layoutAction", actionManager);
final MultiStateDockingAction<FGLayoutProvider> action =
(MultiStateDockingAction<FGLayoutProvider>) getInstanceField("layoutAction",
actionManager);
runSwing(() -> {
List<ActionState<Class<? extends FGLayoutProvider>>> states =
action.getAllActionStates();
for (ActionState<Class<? extends FGLayoutProvider>> state : states) {
Class<? extends FGLayoutProvider> layoutClass = state.getUserData();
if (layoutClass.getSimpleName().contains("MinCross")) {
List<ActionState<FGLayoutProvider>> states = action.getAllActionStates();
for (ActionState<FGLayoutProvider> state : states) {
FGLayoutProvider layoutProvider = state.getUserData();
if (layoutProvider.getLayoutName().contains("MinCross")) {
action.setCurrentActionState(state);
return;
}
}
throw new AssertException("Unable to find MinCross layout");
});
}
protected FGData triggerPersistenceAndReload(String functionAddress) {
//
// Ideally, we would like to save, close and re-open the program so that we can get
// a round-trip saving and reloading. However, in the test environment, we cannot save
// Ideally, we would like to save, close and re-open the program so that we can get
// a round-trip saving and reloading. However, in the test environment, we cannot save
// our programs. So, we will instead just navigate away from the current function, clear
// the cache (to make sure that we read the settings again), and then verify that the
// the cache (to make sure that we read the settings again), and then verify that the
// data saved in the program has been used to re-group.
//
String otherAddress = "0100415a";
@ -2277,7 +2281,7 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte
//==================================================================================================
// Private Methods
//==================================================================================================
//==================================================================================================
protected void moveView(int amount) {
Point translation = new Point(amount, amount);
@ -2316,11 +2320,11 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte
DockingActionIf action = getAction(tool, "FunctionGraphPlugin", name);
ToggleDockingAction displayAction = (ToggleDockingAction) action;
setToggleActionSelected(displayAction, new ActionContext(), expectedVisible);
//
//
// // make sure the action is not already in the state we expect
// assertEquals(name + " action is not selected as expected", !expectedVisible,
// displayAction.isSelected());
//
//
// performAction(displayAction, true);
}

View File

@ -65,12 +65,12 @@ public class FunctionGraphGroupVertices1Test extends AbstractFunctionGraphTest {
@Test
public void testGroupingPersistence() throws Exception {
//
// Round-trip test to ensure that a grouped graph will be restored after re-opening a
//
// Round-trip test to ensure that a grouped graph will be restored after re-opening a
// program.
//
//
//
// Pick a function and group some nodes.
//
FGData graphData = graphFunction("01002cf5");
@ -95,7 +95,7 @@ public class FunctionGraphGroupVertices1Test extends AbstractFunctionGraphTest {
// Record the edges for later validation. Note: we have to keep the string form, as the
// toString() on the edges will call back to its vertices, which will later have been
// disposed.
Collection<FGEdge> oringalGroupedEdges = new HashSet<>(graph.getEdges());// copy so they don't get cleared
Collection<FGEdge> oringalGroupedEdges = new HashSet<>(graph.getEdges());// copy so they don't get cleared
List<String> originalEdgeStrings = new ArrayList<>(oringalGroupedEdges.size());
for (FGEdge edge : oringalGroupedEdges) {
originalEdgeStrings.add(edge.toString());
@ -136,7 +136,7 @@ public class FunctionGraphGroupVertices1Test extends AbstractFunctionGraphTest {
/**
* Tests that the app will recognize the case where the entry point to a function is invalid,
* and generate the appropriate error message when trying to create a function graph.
*
*
* Step 1: Make sure the function graph window is closed.
* Step 2: Clear the entry point bytes
* Step 3: Open the function graph window to generate the graph again.
@ -144,8 +144,8 @@ public class FunctionGraphGroupVertices1Test extends AbstractFunctionGraphTest {
*/
public void testInvalidFunctionEntryPoint() {
// First thing we need to do is close the function graph window. It's opened on
// startup by default in this test suite but we want it closed until we clear the
// First thing we need to do is close the function graph window. It's opened on
// startup by default in this test suite but we want it closed until we clear the
// function code bytes.
this.getFunctionGraphController().getProvider().closeComponent();
@ -170,7 +170,7 @@ public class FunctionGraphGroupVertices1Test extends AbstractFunctionGraphTest {
runSwing(() -> clearAction.actionPerformed(context));
waitForBusyTool(tool);
// Open the window; the tool will try to generate a new graph but should fail and generate
// Open the window; the tool will try to generate a new graph but should fail and generate
// an error message.
DockingActionIf openGraphAction;
openGraphAction = getAction(fgp, "Display Function Graph");
@ -186,8 +186,8 @@ public class FunctionGraphGroupVertices1Test extends AbstractFunctionGraphTest {
@Test
public void testGroupAndUngroup_WhenOneOfTheGroupIsAGroup() {
//
// This test seeks to ensure that you can group a selection of vertices when one of
//
// This test seeks to ensure that you can group a selection of vertices when one of
// those vertices is itself a group.
//
@ -217,7 +217,7 @@ public class FunctionGraphGroupVertices1Test extends AbstractFunctionGraphTest {
group(secondUngroupedVertices);
// 5 edges expected:
// 5 edges expected:
// -ungrouped vertex: 1 in, 1 out
// -grouped vertex : 1 in, 2 out
expectedGroupedEdgeCount = 5;
@ -248,7 +248,7 @@ public class FunctionGraphGroupVertices1Test extends AbstractFunctionGraphTest {
@Test
public void testGroupingPersistence_WhenOneOfTheGroupIsAGroup() throws Exception {
//
//
// This test seeks to ensure that groups within groups are persisted and restored.
//
@ -284,7 +284,7 @@ public class FunctionGraphGroupVertices1Test extends AbstractFunctionGraphTest {
//printEdges(outerUngroupedEdges);
group(outerUngroupedVertices);
// 5 edges expected:
// 5 edges expected:
// -ungrouped vertex: 1 in, 1 out
// -grouped vertex : 1 in, 2 out
expectedGroupedEdgeCount = 5;
@ -353,8 +353,8 @@ public class FunctionGraphGroupVertices1Test extends AbstractFunctionGraphTest {
@Test
public void testUngroupAll() {
//
// Group some vertices and then group that vertex with some vertices to create a
//
// Group some vertices and then group that vertex with some vertices to create a
// recursively/nested grouping. Also create a second top-level group. Make sure the
// ungroup all action will restore the original graph.
//
@ -434,8 +434,8 @@ public class FunctionGraphGroupVertices1Test extends AbstractFunctionGraphTest {
//
// The coloring algorithm:
// 1) If the grouped vertices are not colored, then use the default group color
// 2) If the grouped vertices are colored, but not all the same color,
// then use the default group color=
// 2) If the grouped vertices are colored, but not all the same color,
// then use the default group color=
// 3) If all grouped vertices share the same color, then make the group that color
//
// This test is for 1)
@ -459,7 +459,7 @@ public class FunctionGraphGroupVertices1Test extends AbstractFunctionGraphTest {
//
ungroup(group);
//
//
// Test the grouped vertices colors
//
verifyDefaultColor(v1, v2);
@ -470,8 +470,8 @@ public class FunctionGraphGroupVertices1Test extends AbstractFunctionGraphTest {
//
// The coloring algorithm:
// 1) If the grouped vertices are not colored, then use the default group color
// 2) If the grouped vertices are colored, but not all the same color,
// then use the default group color=
// 2) If the grouped vertices are colored, but not all the same color,
// then use the default group color=
// 3) If all grouped vertices share the same color, then make the group that color
//
// This test is for 2)
@ -500,7 +500,7 @@ public class FunctionGraphGroupVertices1Test extends AbstractFunctionGraphTest {
//
ungroup(group);
//
//
// Test the grouped vertices colors
//
verifyColor(v1, newColor);
@ -512,8 +512,8 @@ public class FunctionGraphGroupVertices1Test extends AbstractFunctionGraphTest {
//
// The coloring algorithm:
// 1) If the grouped vertices are not colored, then use the default group color
// 2) If the grouped vertices are colored, but not all the same color,
// then use the default group color=
// 2) If the grouped vertices are colored, but not all the same color,
// then use the default group color=
// 3) If all grouped vertices share the same color, then make the group that color
//
// This test is for 3)
@ -543,7 +543,7 @@ public class FunctionGraphGroupVertices1Test extends AbstractFunctionGraphTest {
//
ungroup(group);
//
//
// Test the grouped vertices colors
//
verifyColor(v1, newColor);
@ -572,7 +572,7 @@ public class FunctionGraphGroupVertices1Test extends AbstractFunctionGraphTest {
//
ungroup(group);
//
//
// Test the grouped vertices colors
//
verifyColor(v1, newGroupColor);
@ -607,7 +607,7 @@ public class FunctionGraphGroupVertices1Test extends AbstractFunctionGraphTest {
//
ungroup(group);
//
//
// Test the grouped vertices colors
//
verifyColor(v1, newGroupColor);
@ -642,7 +642,7 @@ public class FunctionGraphGroupVertices1Test extends AbstractFunctionGraphTest {
//
ungroup(group);
//
//
// Test the grouped vertices colors
//
verifyColor(v1, newGroupColor);
@ -666,7 +666,7 @@ public class FunctionGraphGroupVertices1Test extends AbstractFunctionGraphTest {
Color newGroupColor = Color.CYAN;
color(group, newGroupColor);
//
//
// Trigger persistence
//
Address groupAddress = group.getVertexAddress();
@ -683,7 +683,7 @@ public class FunctionGraphGroupVertices1Test extends AbstractFunctionGraphTest {
//
ungroup(group);
//
//
// Test the grouped vertices colors
//
v1 = vertex("01002d06");
@ -703,7 +703,7 @@ public class FunctionGraphGroupVertices1Test extends AbstractFunctionGraphTest {
FGVertex v2 = vertex("01002d0f");
GroupedFunctionGraphVertex group = group("A", v1, v2);
//
//
// Trigger persistence
//
Address groupAddress = group.getVertexAddress();
@ -720,7 +720,7 @@ public class FunctionGraphGroupVertices1Test extends AbstractFunctionGraphTest {
//
ungroup(group);
//
//
// Test the grouped vertices colors
//
v1 = vertex("01002d06");
@ -737,16 +737,16 @@ public class FunctionGraphGroupVertices1Test extends AbstractFunctionGraphTest {
//
// Color just one of the vertices
//
//
Color newColor = Color.RED;
color(v1, newColor);
//
// Group a node
//
//
GroupedFunctionGraphVertex group = group("A", v1, v2);
//
//
// Trigger persistence
//
Address groupAddress = group.getVertexAddress();
@ -763,7 +763,7 @@ public class FunctionGraphGroupVertices1Test extends AbstractFunctionGraphTest {
//
ungroup(group);
//
//
// Test the grouped vertices colors
//
v1 = vertex("01002d06");
@ -781,16 +781,16 @@ public class FunctionGraphGroupVertices1Test extends AbstractFunctionGraphTest {
//
// Color just one of the vertices
//
//
Color newColor = Color.RED;
color(v1, newColor);
//
// Group a node
//
//
GroupedFunctionGraphVertex group = group("A", v1, v2);
//
//
// Trigger reset
//
Address groupAddress = group.getVertexAddress();
@ -800,9 +800,9 @@ public class FunctionGraphGroupVertices1Test extends AbstractFunctionGraphTest {
// Make sure the group is gone
//
FGVertex vertex = graphData.getFunctionGraph().getVertexForAddress(groupAddress);
assertFalse(vertex instanceof GroupedFunctionGraphVertex);// the group has been removed
assertFalse(vertex instanceof GroupedFunctionGraphVertex);// the group has been removed
//
//
// Test the grouped vertices colors
//
v1 = vertex("01002d06");
@ -838,11 +838,6 @@ public class FunctionGraphGroupVertices1Test extends AbstractFunctionGraphTest {
assertEquals(alpha, alphAfterGroup);
}
@Test
public void testSymbolAddedWhenGrouped_SymbolOutsideOfGroupNode() {
// TODO
}
//==================================================================================================
// Private Methods
//==================================================================================================
@ -853,85 +848,85 @@ public class FunctionGraphGroupVertices1Test extends AbstractFunctionGraphTest {
FGData graphData = graphFunction("01002cf5");
FunctionGraph functionGraph = graphData.getFunctionGraph();
Graph<FGVertex, FGEdge> graph = functionGraph;
Set<FGVertex> ungroupedVertices = selectVertices( functionGraph,
"01002d2b" /* Another Local*/,
Set<FGVertex> ungroupedVertices = selectVertices( functionGraph,
"01002d2b" /* Another Local*/,
"01002d1f" /* MyLocal */);
Set<FGEdge> ungroupedEdges = getEdges(graph, ungroupedVertices);
assertEquals("Did not grab all known edges for vertices", 4, ungroupedEdges.size());
group(ungroupedVertices);
assertVerticesRemoved(graph, ungroupedVertices);
assertEdgesRemoved(graph, ungroupedEdges);
// -1 because one one of the edges was between two of the vertices being grouped
int expectedGroupedEdgeCount = ungroupedEdges.size() - 1;
GroupedFunctionGraphVertex groupedVertex =
validateNewGroupedVertexFromVertices(functionGraph, ungroupedVertices,
validateNewGroupedVertexFromVertices(functionGraph, ungroupedVertices,
expectedGroupedEdgeCount);
ungroup(groupedVertex);
assertVertexRemoved(graph, groupedVertex);
assertVerticesAdded(graph, ungroupedVertices);
assertEdgesAdded(functionGraph, ungroupedEdges);
assertSelected(ungroupedVertices);
}
@Override
protected void doTestRestoringWhenCodeBlocksHaveChanged_WillRegroup() {
//
// Tests the behavior of how group vertices are restored when one or more of the vertices
//
// Tests the behavior of how group vertices are restored when one or more of the vertices
// inside of the grouped vertex is no longer available when the graph attempts to restore
// the group vertex user settings (i.e., when restarting Ghidra, the previously grouped
// vertices should reappear).
//
// In this test, we will be mutating a group of 3 nodes such
// that one of the nodes has been split into two. This leaves 2 vertices to
// that one of the nodes has been split into two. This leaves 2 vertices to
// be found by the regrouping algorithm. Furthermore, the regrouping *will* still
// take place, as at least two vertices cannot be found.
//
//
//
// Pick a function and group some nodes.
//
FGData graphData = graphFunction("01002cf5");
FunctionGraph functionGraph = graphData.getFunctionGraph();
Set<FGVertex> ungroupedVertices = selectVertices(functionGraph,
"01002d11" /* LAB_01002d11 */, "01002cf5" /* ghidra */, "01002d1f" /* MyLocal */);
group(ungroupedVertices);
// 5 edges expected:
// -01002cf5: 2 out
// 5 edges expected:
// -01002cf5: 2 out
// -01002d11: 2 in, (1 out that was removed)
// -01002d1f: 2 out (1 in that was removed)
int expectedGroupedEdgeCount = 6;
GroupedFunctionGraphVertex groupedVertex = validateNewGroupedVertexFromVertices(
functionGraph, ungroupedVertices, expectedGroupedEdgeCount);
AddressSetView addresses = groupedVertex.getAddresses();
Address minAddress = addresses.getMinAddress();
Address maxAddress = addresses.getMaxAddress();
//
// Ideally, we would like to save, close and re-open the program so that we can get
// a round-trip saving and reloading. However, in the test environment, we cannot save
// Ideally, we would like to save, close and re-open the program so that we can get
// a round-trip saving and reloading. However, in the test environment, we cannot save
// our programs. So, we will instead just navigate away from the current function, clear
// the cache (to make sure that we read the settings again), and then verify that the
// the cache (to make sure that we read the settings again), and then verify that the
// data saved in the program has been used to re-group.
//
graphFunction("0100415a");
clearCache();
//
// Add a label to trigger a code block change
//
Address labelAddress = createLabel("01002d18");// in the middle of the LAB_01002d11 code block
//
// Relaunch the graph, which will use the above persisted group settings...
//
@ -941,22 +936,22 @@ public class FunctionGraphGroupVertices1Test extends AbstractFunctionGraphTest {
FGVertex expectedGroupVertex = functionGraph.getVertexForAddress(minAddress);
assertTrue(expectedGroupVertex instanceof GroupedFunctionGraphVertex);
assertEquals(maxAddress, expectedGroupVertex.getAddresses().getMaxAddress());
// ...we expect that the two original grouped vertices have again been grouped...
FGVertex splitVertex =
functionGraph.getVertexForAddress(getAddress("01002d11") /* LAB_01002d11 */);
assertTrue("The split vertex should not have been regrouped",
!(splitVertex instanceof GroupedFunctionGraphVertex));
FGVertex unchangedVertex =
functionGraph.getVertexForAddress(getAddress("01002cf5") /* ghidra */);
assertTrue("An unchanged vertex should have been regrouped: " + unchangedVertex,
(unchangedVertex instanceof GroupedFunctionGraphVertex));
unchangedVertex = functionGraph.getVertexForAddress(getAddress("01002d1f") /* MyLocal */);
assertTrue("An unchanged vertex should have been regrouped: " + unchangedVertex,
(unchangedVertex instanceof GroupedFunctionGraphVertex));
// ...but the newly created code block has not
FGVertex newlyCreatedVertex = functionGraph.getVertexForAddress(labelAddress);
assertNotNull(newlyCreatedVertex);
@ -966,34 +961,34 @@ public class FunctionGraphGroupVertices1Test extends AbstractFunctionGraphTest {
protected void doTestSymbolAddedWhenGrouped_SymbolInsideOfGroupNode() {
//
// By default, if the FunctionGraph detects a symbol addition to one of the code blocks
// in the graph, then it will split the affected vertex (tested elsewhere).
// in the graph, then it will split the affected vertex (tested elsewhere).
// However, if the affected vertex is grouped, then the FG will not split the node, but
// should still show the 'stale' indicator.
//
//
//
// Pick a function and group some nodes.
//
FGData graphData = graphFunction("01002cf5");
FunctionGraph functionGraph = graphData.getFunctionGraph();
Set<FGVertex> ungroupedVertices =
selectVertices(functionGraph, "01002d11" /* LAB_01002d11 */, "01002cf5" /* ghidra */);
group(ungroupedVertices);
// 5 edges expected:
// -01002cf5: 2 out
// 5 edges expected:
// -01002cf5: 2 out
// -01002cf5: 2 in, 1 out
int expectedGroupedEdgeCount = 5;
GroupedFunctionGraphVertex groupedVertex = validateNewGroupedVertexFromVertices(
functionGraph, ungroupedVertices, expectedGroupedEdgeCount);
//
// Add a label to trigger a code block change
//
Address labelAddress = createLabel("01002d18");// in the middle of the LAB_01002d11 code block
//
// Make sure the newly created code block does not have a corresponding vertex
//

View File

@ -36,7 +36,7 @@ import resources.icons.EmptyIcon;
* drop-down icon that allows users to change the state of the button. Also, by default, as
* the user presses the button, it will execute the action corresponding to the current
* state.
*
*
* <p>Warning: if you use this action in a toolbar, then be sure to call the
* {@link #MultiStateDockingAction(String, String, boolean) correct constructor}. If you call
* another constructor, or pass false for this boolean above, your
@ -50,7 +50,7 @@ public abstract class MultiStateDockingAction<T> extends DockingAction {
private static Icon EMPTY_ICON = new EmptyIcon(16, 16);
private List<ActionState<T>> actionStates = new ArrayList<>();
private int currentStateIndex = 0;
private int currentStateIndex = -1;
private MultiActionDockingActionIf multiActionGenerator;
private MultipleActionDockingToolbarButton multipleButton;
@ -66,7 +66,7 @@ public abstract class MultiStateDockingAction<T> extends DockingAction {
/**
* Call this constructor with this action will not be added to a toolbar
*
*
* @param name the action name
* @param owner the owner
* @see #MultiStateDockingAction(String, String, boolean)
@ -78,7 +78,7 @@ public abstract class MultiStateDockingAction<T> extends DockingAction {
/**
* Use this constructor explicitly when this action is used in a toolbar, passing true
* for <code>isToolbarAction</code> (see the javadoc header note).
*
*
* @param name the action name
* @param owner the owner
* @param isToolbarAction true if this action is a toolbar action
@ -110,7 +110,7 @@ public abstract class MultiStateDockingAction<T> extends DockingAction {
* <p>
* Also, if the parameter is true, then the button will behave like a button in terms of
* mouse feedback. If false, then the button will behave more like a label.
*
*
* @param doPerformAction true to call {@link #doActionPerformed(ActionContext)} when the
* user presses the button for this action (not the drop-down menu; see above)
*/
@ -133,7 +133,7 @@ public abstract class MultiStateDockingAction<T> extends DockingAction {
* default, the popup menu items will use the icons as provided by the {@link ActionState}.
* By passing true to this method, icons will not be used in the popup menu. Instead, a
* checkbox icon will be used to show the active action state.
*
*
* @param useCheckboxForIcons true to use a checkbox
*/
public void setUseCheckboxForIcons(boolean useCheckboxForIcons) {
@ -144,7 +144,7 @@ public abstract class MultiStateDockingAction<T> extends DockingAction {
* Sets the icon to use if the active action state does not supply an icon. This is useful if
* you wish for your action states to not use icon, but desire the action itself to have an
* icon.
*
*
* @param icon the icon
*/
public void setDefaultIcon(Icon icon) {
@ -165,7 +165,7 @@ public abstract class MultiStateDockingAction<T> extends DockingAction {
* This is the callback to be overridden when the child wishes to respond to user button
* presses that are on the button and not the drop-down. This will only be called if
* {@link #performActionOnPrimaryButtonClick} is true.
*
*
* @param context the action context
*/
protected void doActionPerformed(ActionContext context) {
@ -270,10 +270,7 @@ public abstract class MultiStateDockingAction<T> extends DockingAction {
}
currentStateIndex = indexOf;
// we set the icon here to handle the odd case where this action is not used in a toolbar
if (multipleButton != null) {
setButtonState(actionState);
}
setButtonState(actionState);
ToolBarData tbd = getToolBarData();
tbd.setIcon(getIcon(actionState));
@ -317,6 +314,16 @@ public abstract class MultiStateDockingAction<T> extends DockingAction {
private void setButtonState(ActionState<T> actionState) {
if (multipleButton == null) {
return;
}
if (actionState == null) {
multipleButton.setIcon(null);
multipleButton.setToolTipText(null);
return;
}
Icon icon = getIcon(actionState);
multipleButton.setIcon(icon);
multipleButton.setToolTipText(actionState.getName());
@ -337,6 +344,9 @@ public abstract class MultiStateDockingAction<T> extends DockingAction {
}
public String getToolTipText() {
if (actionStates.isEmpty()) {
return getName() + ": <no action states installed>";
}
return getName() + ": " + getCurrentState().getName();
}
@ -355,8 +365,7 @@ public abstract class MultiStateDockingAction<T> extends DockingAction {
setSelected(isSelected);
setMenuBarData(
new MenuData(new String[] { actionState.getName() }));
setMenuBarData(new MenuData(new String[] { actionState.getName() }));
HelpLocation helpLocation = actionState.getHelpLocation();
if (helpLocation != null) {
setHelpLocation(helpLocation);