mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-05-29 01:35:32 +08:00
Test fixes for recent FG layout updates
This commit is contained in:
+30
-24
@@ -189,8 +189,8 @@ class FGActionManager {
|
|||||||
return !(context instanceof FunctionGraphVertexLocationInFullViewModeActionContext);
|
return !(context instanceof FunctionGraphVertexLocationInFullViewModeActionContext);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
zoomOutAction.setPopupMenuData(
|
zoomOutAction
|
||||||
new MenuData(new String[] { "Zoom Out" }, popuEndPopupGroup));
|
.setPopupMenuData(new MenuData(new String[] { "Zoom Out" }, popuEndPopupGroup));
|
||||||
zoomOutAction.setKeyBindingData(new KeyBindingData(
|
zoomOutAction.setKeyBindingData(new KeyBindingData(
|
||||||
KeyStroke.getKeyStroke(KeyEvent.VK_MINUS, DockingUtils.CONTROL_KEY_MODIFIER_MASK)));
|
KeyStroke.getKeyStroke(KeyEvent.VK_MINUS, DockingUtils.CONTROL_KEY_MODIFIER_MASK)));
|
||||||
zoomOutAction.setHelpLocation(new HelpLocation("FunctionGraphPlugin", "Zoom"));
|
zoomOutAction.setHelpLocation(new HelpLocation("FunctionGraphPlugin", "Zoom"));
|
||||||
@@ -325,8 +325,8 @@ class FGActionManager {
|
|||||||
menuData.setMenuSubGroup(Integer.toString(vertexGroupingSubgroupOffset++));
|
menuData.setMenuSubGroup(Integer.toString(vertexGroupingSubgroupOffset++));
|
||||||
editLabelAction.setDescription("Change the label for the code block");
|
editLabelAction.setDescription("Change the label for the code block");
|
||||||
editLabelAction.setPopupMenuData(menuData);
|
editLabelAction.setPopupMenuData(menuData);
|
||||||
editLabelAction.setHelpLocation(
|
editLabelAction
|
||||||
new HelpLocation("FunctionGraphPlugin", "Vertex_Action_Label"));
|
.setHelpLocation(new HelpLocation("FunctionGraphPlugin", "Vertex_Action_Label"));
|
||||||
|
|
||||||
DockingAction fullViewAction = new DockingAction("Vertex View Mode", plugin.getName()) {
|
DockingAction fullViewAction = new DockingAction("Vertex View Mode", plugin.getName()) {
|
||||||
@Override
|
@Override
|
||||||
@@ -717,8 +717,8 @@ class FGActionManager {
|
|||||||
};
|
};
|
||||||
selectHoveredEdgesAction.setPopupMenuData(new MenuData(
|
selectHoveredEdgesAction.setPopupMenuData(new MenuData(
|
||||||
new String[] { selectionMenuName, "From Hovered Edges" }, popupSelectionGroup2));
|
new String[] { selectionMenuName, "From Hovered Edges" }, popupSelectionGroup2));
|
||||||
selectHoveredEdgesAction.setHelpLocation(
|
selectHoveredEdgesAction
|
||||||
new HelpLocation("FunctionGraphPlugin", "Path_Selection"));
|
.setHelpLocation(new HelpLocation("FunctionGraphPlugin", "Path_Selection"));
|
||||||
|
|
||||||
DockingAction selectFocusedEdgesAction =
|
DockingAction selectFocusedEdgesAction =
|
||||||
new DockingAction("Make Selection From Focused Edges", plugin.getName()) {
|
new DockingAction("Make Selection From Focused Edges", plugin.getName()) {
|
||||||
@@ -751,8 +751,8 @@ class FGActionManager {
|
|||||||
};
|
};
|
||||||
selectFocusedEdgesAction.setPopupMenuData(new MenuData(
|
selectFocusedEdgesAction.setPopupMenuData(new MenuData(
|
||||||
new String[] { selectionMenuName, "From Focused Edges" }, popupSelectionGroup2));
|
new String[] { selectionMenuName, "From Focused Edges" }, popupSelectionGroup2));
|
||||||
selectFocusedEdgesAction.setHelpLocation(
|
selectFocusedEdgesAction
|
||||||
new HelpLocation("FunctionGraphPlugin", "Path_Selection"));
|
.setHelpLocation(new HelpLocation("FunctionGraphPlugin", "Path_Selection"));
|
||||||
|
|
||||||
DockingAction clearCurrentSelectionAction =
|
DockingAction clearCurrentSelectionAction =
|
||||||
new DockingAction("Clear Current Selection", plugin.getName()) {
|
new DockingAction("Clear Current Selection", plugin.getName()) {
|
||||||
@@ -775,8 +775,8 @@ class FGActionManager {
|
|||||||
};
|
};
|
||||||
clearCurrentSelectionAction.setPopupMenuData(new MenuData(
|
clearCurrentSelectionAction.setPopupMenuData(new MenuData(
|
||||||
new String[] { selectionMenuName, "Clear Graph Selection" }, popupSelectionGroup3));
|
new String[] { selectionMenuName, "Clear Graph Selection" }, popupSelectionGroup3));
|
||||||
clearCurrentSelectionAction.setHelpLocation(
|
clearCurrentSelectionAction
|
||||||
new HelpLocation("FunctionGraphPlugin", "Path_Selection"));
|
.setHelpLocation(new HelpLocation("FunctionGraphPlugin", "Path_Selection"));
|
||||||
|
|
||||||
DockingAction selectAllAction =
|
DockingAction selectAllAction =
|
||||||
new DockingAction("Select All Code Units", plugin.getName()) {
|
new DockingAction("Select All Code Units", plugin.getName()) {
|
||||||
@@ -815,12 +815,12 @@ class FGActionManager {
|
|||||||
return isValidContext(context);
|
return isValidContext(context);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
selectAllAction.setKeyBindingData(
|
selectAllAction
|
||||||
new KeyBindingData(KeyEvent.VK_A, InputEvent.CTRL_DOWN_MASK));
|
.setKeyBindingData(new KeyBindingData(KeyEvent.VK_A, InputEvent.CTRL_DOWN_MASK));
|
||||||
selectAllAction.setPopupMenuData(new MenuData(
|
selectAllAction.setPopupMenuData(new MenuData(
|
||||||
new String[] { selectionMenuName, "Select All Code Units" }, popupSelectionGroup3));
|
new String[] { selectionMenuName, "Select All Code Units" }, popupSelectionGroup3));
|
||||||
selectAllAction.setHelpLocation(
|
selectAllAction
|
||||||
new HelpLocation("FunctionGraphPlugin", "Code_Unit_Selection"));
|
.setHelpLocation(new HelpLocation("FunctionGraphPlugin", "Code_Unit_Selection"));
|
||||||
|
|
||||||
provider.addLocalAction(chooseFormatsAction);
|
provider.addLocalAction(chooseFormatsAction);
|
||||||
provider.addLocalAction(homeAction);
|
provider.addLocalAction(homeAction);
|
||||||
@@ -885,9 +885,7 @@ class FGActionManager {
|
|||||||
// icon to be blank in the menu, but to use this icon on the toolbar.
|
// icon to be blank in the menu, but to use this icon on the toolbar.
|
||||||
layoutAction.setDefaultIcon(ResourceManager.loadImage("images/preferences-system.png"));
|
layoutAction.setDefaultIcon(ResourceManager.loadImage("images/preferences-system.png"));
|
||||||
|
|
||||||
List<ActionState<FGLayoutProvider>> actionStates =
|
List<ActionState<FGLayoutProvider>> actionStates = loadActionStatesForLayoutProviders();
|
||||||
loadActionStatesForLayoutProviders();
|
|
||||||
|
|
||||||
for (ActionState<FGLayoutProvider> actionState : actionStates) {
|
for (ActionState<FGLayoutProvider> actionState : actionStates) {
|
||||||
layoutAction.addActionState(actionState);
|
layoutAction.addActionState(actionState);
|
||||||
}
|
}
|
||||||
@@ -900,15 +898,19 @@ class FGActionManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private List<ActionState<FGLayoutProvider>> loadActionStatesForLayoutProviders() {
|
private List<ActionState<FGLayoutProvider>> loadActionStatesForLayoutProviders() {
|
||||||
|
|
||||||
List<FGLayoutProvider> layoutInstances = plugin.getLayoutProviders();
|
List<FGLayoutProvider> layoutInstances = plugin.getLayoutProviders();
|
||||||
|
return createActionStates(layoutInstances);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<ActionState<FGLayoutProvider>> createActionStates(
|
||||||
|
List<FGLayoutProvider> layoutProviders) {
|
||||||
List<ActionState<FGLayoutProvider>> list = new ArrayList<>();
|
List<ActionState<FGLayoutProvider>> list = new ArrayList<>();
|
||||||
HelpLocation layoutHelpLocation =
|
HelpLocation layoutHelpLocation =
|
||||||
new HelpLocation("FunctionGraphPlugin", "Function_Graph_Action_Layout");
|
new HelpLocation("FunctionGraphPlugin", "Function_Graph_Action_Layout");
|
||||||
for (FGLayoutProvider layout : layoutInstances) {
|
for (FGLayoutProvider layout : layoutProviders) {
|
||||||
|
|
||||||
ActionState<FGLayoutProvider> layoutState = new ActionState<>(
|
ActionState<FGLayoutProvider> layoutState =
|
||||||
layout.getLayoutName(), layout.getActionIcon(), layout);
|
new ActionState<>(layout.getLayoutName(), layout.getActionIcon(), layout);
|
||||||
layoutState.setHelpLocation(layoutHelpLocation);
|
layoutState.setHelpLocation(layoutHelpLocation);
|
||||||
list.add(layoutState);
|
list.add(layoutState);
|
||||||
}
|
}
|
||||||
@@ -1157,9 +1159,8 @@ class FGActionManager {
|
|||||||
private void makeSelectionFromAddresses(AddressSet addresses) {
|
private void makeSelectionFromAddresses(AddressSet addresses) {
|
||||||
ProgramSelection selection = new ProgramSelection(addresses);
|
ProgramSelection selection = new ProgramSelection(addresses);
|
||||||
plugin.getTool()
|
plugin.getTool()
|
||||||
.firePluginEvent(
|
.firePluginEvent(new ProgramSelectionPluginEvent("Spoof!", selection,
|
||||||
new ProgramSelectionPluginEvent("Spoof!", selection,
|
provider.getCurrentProgram()));
|
||||||
provider.getCurrentProgram()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ungroupVertices(Set<GroupedFunctionGraphVertex> groupVertices) {
|
private void ungroupVertices(Set<GroupedFunctionGraphVertex> groupVertices) {
|
||||||
@@ -1214,6 +1215,11 @@ class FGActionManager {
|
|||||||
layoutAction.setCurrentActionState(state);
|
layoutAction.setCurrentActionState(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setLayouts(List<FGLayoutProvider> layouts) {
|
||||||
|
List<ActionState<FGLayoutProvider>> states = createActionStates(layouts);
|
||||||
|
layoutAction.setActionStates(states);
|
||||||
|
}
|
||||||
|
|
||||||
ActionState<FGLayoutProvider> getCurrentLayoutState() {
|
ActionState<FGLayoutProvider> getCurrentLayoutState() {
|
||||||
return layoutAction.getCurrentState();
|
return layoutAction.getCurrentState();
|
||||||
}
|
}
|
||||||
|
|||||||
+91
-87
@@ -54,6 +54,7 @@ import ghidra.app.plugin.core.clipboard.ClipboardPlugin;
|
|||||||
import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
|
import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
|
||||||
import ghidra.app.plugin.core.functiongraph.graph.*;
|
import ghidra.app.plugin.core.functiongraph.graph.*;
|
||||||
import ghidra.app.plugin.core.functiongraph.graph.layout.FGLayoutProvider;
|
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.graph.vertex.*;
|
||||||
import ghidra.app.plugin.core.functiongraph.mvc.*;
|
import ghidra.app.plugin.core.functiongraph.mvc.*;
|
||||||
import ghidra.app.services.*;
|
import ghidra.app.services.*;
|
||||||
@@ -74,6 +75,7 @@ import ghidra.program.util.ProgramLocation;
|
|||||||
import ghidra.program.util.ProgramSelection;
|
import ghidra.program.util.ProgramSelection;
|
||||||
import ghidra.test.*;
|
import ghidra.test.*;
|
||||||
import ghidra.util.Msg;
|
import ghidra.util.Msg;
|
||||||
|
import ghidra.util.exception.AssertException;
|
||||||
import ghidra.util.task.RunManager;
|
import ghidra.util.task.RunManager;
|
||||||
|
|
||||||
public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedIntegrationTest {
|
public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
@@ -552,11 +554,19 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte
|
|||||||
|
|
||||||
protected void showFunctionGraphProvider() {
|
protected void showFunctionGraphProvider() {
|
||||||
|
|
||||||
ComponentProvider provider = tool.getComponentProvider("Function Graph");
|
FGProvider provider = (FGProvider) tool.getComponentProvider("Function Graph");
|
||||||
tool.showComponentProvider(provider, true);
|
tool.showComponentProvider(provider, true);
|
||||||
|
|
||||||
graphProvider = waitForComponentProvider(FGProvider.class);
|
graphProvider = waitForComponentProvider(FGProvider.class);
|
||||||
assertNotNull("Graph not shown", graphProvider);
|
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() {
|
protected ProgramSelection makeSingleVertexSelectionInCodeBrowser() {
|
||||||
@@ -1003,7 +1013,7 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte
|
|||||||
combined.add(groupedVertex);
|
combined.add(groupedVertex);
|
||||||
pickVertices(combined);
|
pickVertices(combined);
|
||||||
|
|
||||||
// execute the 'add to group' action
|
// execute the 'add to group' action
|
||||||
JComponent component = getComponent(groupedVertex);
|
JComponent component = getComponent(groupedVertex);
|
||||||
DockingAction action =
|
DockingAction action =
|
||||||
(DockingAction) TestUtils.getInstanceField("addToGroupAction", component);
|
(DockingAction) TestUtils.getInstanceField("addToGroupAction", component);
|
||||||
@@ -1025,7 +1035,7 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte
|
|||||||
Collection<FGEdge> ungroupedEdges) {
|
Collection<FGEdge> ungroupedEdges) {
|
||||||
for (FGEdge edge : 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.
|
// to locate the edge in the graph that may represent the given edge.
|
||||||
//
|
//
|
||||||
FGEdge currentEdge = getCurrentEdge(functionGraph, edge);
|
FGEdge currentEdge = getCurrentEdge(functionGraph, edge);
|
||||||
@@ -1222,12 +1232,6 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte
|
|||||||
|
|
||||||
protected FGData create12345Graph() {
|
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
|
// function sscanf
|
||||||
FGData funtionGraphData = graphFunction("100415a");
|
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
|
// 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
|
// found upon restoring settings. If we want to put that code back, then this test
|
||||||
// is again valid.
|
// is again valid.
|
||||||
//
|
//
|
||||||
public void dontTestRestoringWhenCodeBlocksHaveChanged_DoesntRegroup() {
|
public void dontTestRestoringWhenCodeBlocksHaveChanged_DoesntRegroup() {
|
||||||
int transactionID = -1;
|
int transactionID = -1;
|
||||||
try {
|
try {
|
||||||
@@ -1390,38 +1394,38 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte
|
|||||||
FGData graphData = graphFunction("01002cf5");
|
FGData graphData = graphFunction("01002cf5");
|
||||||
FunctionGraph functionGraph = graphData.getFunctionGraph();
|
FunctionGraph functionGraph = graphData.getFunctionGraph();
|
||||||
Graph<FGVertex, FGEdge> graph = functionGraph;
|
Graph<FGVertex, FGEdge> graph = functionGraph;
|
||||||
|
|
||||||
Set<FGVertex> ungroupedVertices = selectVertices( functionGraph,
|
Set<FGVertex> ungroupedVertices = selectVertices( functionGraph,
|
||||||
"01002d2b" /* Another Local*/,
|
"01002d2b" /* Another Local*/,
|
||||||
"01002d1f" /* MyLocal */);
|
"01002d1f" /* MyLocal */);
|
||||||
Set<FGEdge> ungroupedEdges = getEdges(graph, ungroupedVertices);
|
Set<FGEdge> ungroupedEdges = getEdges(graph, ungroupedVertices);
|
||||||
assertEquals("Did not grab all known edges for vertices", 4, ungroupedEdges.size());
|
assertEquals("Did not grab all known edges for vertices", 4, ungroupedEdges.size());
|
||||||
|
|
||||||
group(ungroupedVertices);
|
group(ungroupedVertices);
|
||||||
|
|
||||||
assertVerticesRemoved(graph, ungroupedVertices);
|
assertVerticesRemoved(graph, ungroupedVertices);
|
||||||
assertEdgesRemoved(graph, ungroupedEdges);
|
assertEdgesRemoved(graph, ungroupedEdges);
|
||||||
|
|
||||||
// -1 because one one of the edges was between two of the vertices being grouped
|
// -1 because one one of the edges was between two of the vertices being grouped
|
||||||
int expectedGroupedEdgeCount = ungroupedEdges.size() - 1;
|
int expectedGroupedEdgeCount = ungroupedEdges.size() - 1;
|
||||||
GroupedFunctionGraphVertex groupedVertex =
|
GroupedFunctionGraphVertex groupedVertex =
|
||||||
validateNewGroupedVertexFromVertices(functionGraph, ungroupedVertices,
|
validateNewGroupedVertexFromVertices(functionGraph, ungroupedVertices,
|
||||||
expectedGroupedEdgeCount);
|
expectedGroupedEdgeCount);
|
||||||
|
|
||||||
ungroup(groupedVertex);
|
ungroup(groupedVertex);
|
||||||
|
|
||||||
assertVertexRemoved(graph, groupedVertex);
|
assertVertexRemoved(graph, groupedVertex);
|
||||||
assertVerticesAdded(graph, ungroupedVertices);
|
assertVerticesAdded(graph, ungroupedVertices);
|
||||||
assertEdgesAdded(functionGraph, ungroupedEdges);
|
assertEdgesAdded(functionGraph, ungroupedEdges);
|
||||||
assertSelected(ungroupedVertices);
|
assertSelected(ungroupedVertices);
|
||||||
|
|
||||||
}
|
}
|
||||||
// @formatter:on
|
// @formatter:on
|
||||||
|
|
||||||
protected void doTestGroupingProperlyTranslatesEdgesFromGroupedVerticesToRealVertices() {
|
protected void doTestGroupingProperlyTranslatesEdgesFromGroupedVerticesToRealVertices() {
|
||||||
//
|
//
|
||||||
// WARNING!!! WARNING!!! WARNING!!! WARNING!!! WARNING!!! WARNING!!!
|
// WARNING!!! WARNING!!! WARNING!!! WARNING!!! WARNING!!! WARNING!!!
|
||||||
// This is not a junit test in that it is long, involved, hidden and complicated. We
|
// 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
|
// need to test this functionality, but we don't have a jComplicatedTest, so we will do
|
||||||
// it here.
|
// 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
|
// 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
|
// 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
|
// and find vertices for edges *whether or now those vertices have been
|
||||||
// grouped or ungrouped*
|
// 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.
|
// 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.
|
// 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.
|
// of the other groups.
|
||||||
// 3) Now, ungroup the 1 remaining originally grouped vertex (A).
|
// 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
|
// that used to be from Z->A. Which vertex inside of A represented the connection
|
||||||
// pointing into Z (by way of B).
|
// 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
|
0) Initial Graph
|
||||||
|
|
||||||
1 -> 2 -> 3 -> 4
|
1 -> 2 -> 3 -> 4
|
||||||
|
|
|
|
||||||
*
|
*
|
||||||
5
|
5
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
create12345Graph();
|
create12345Graph();
|
||||||
@@ -1469,7 +1473,7 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte
|
|||||||
FGVertex v4 = vertex("1004196");
|
FGVertex v4 = vertex("1004196");
|
||||||
FGVertex v5 = vertex("100419c");
|
FGVertex v5 = vertex("100419c");
|
||||||
|
|
||||||
// verify initial graph
|
// verify initial graph
|
||||||
verifyEdge(v1, v2);
|
verifyEdge(v1, v2);
|
||||||
verifyEdge(v2, v3);
|
verifyEdge(v2, v3);
|
||||||
verifyEdge(v3, v4);
|
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.
|
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})
|
A (v:{1,2} e:{1->2, 2->3}) -> B (v:{3,4} e:{2->3,3->4,3->5})
|
||||||
|
|
|
|
||||||
*
|
*
|
||||||
5
|
5
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
GroupedFunctionGraphVertex groupA = group("A", v1, v2);
|
GroupedFunctionGraphVertex groupA = group("A", v1, v2);
|
||||||
@@ -1494,13 +1498,13 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte
|
|||||||
verifyEdgeCount(2);// no other edges
|
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).
|
of the other groups (B).
|
||||||
|
|
||||||
A (v:{1,2} e:{1->2, 2->3}) -> Z (
|
A (v:{1,2} e:{1->2, 2->3}) -> Z (
|
||||||
v:{B (v:{3,4} e:{2->3,3->4,3->5}), 5}
|
v:{B (v:{3,4} e:{2->3,3->4,3->5}), 5}
|
||||||
e:{2->3, 3->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).
|
3) Now, ungroup the 1 remaining originally grouped vertex (A).
|
||||||
|
|
||||||
1 -> 2 -> Z (
|
1 -> 2 -> Z (
|
||||||
v:{B (v:{3,4} e:{2->3,3->4,3->5}), 5}
|
v:{B (v:{3,4} e:{2->3,3->4,3->5}), 5}
|
||||||
e:{2->3, 3->5}
|
e:{2->3, 3->5}
|
||||||
)
|
)
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
ungroup(groupA);
|
ungroup(groupA);
|
||||||
@@ -1526,14 +1530,14 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte
|
|||||||
verifyEdgeCount(2);
|
verifyEdgeCount(2);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
||||||
4) Now, ungroup Z and go back to having one remaining group vertex (B)
|
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})
|
1 -> 2 -> -> B (v:{3,4} e:{2->3,3->4,3->5})
|
||||||
|
|
|
|
||||||
*
|
*
|
||||||
5
|
5
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
ungroup(groupZ);
|
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
|
5) Finally, ungroup the last group and make sure the graph is restored
|
||||||
|
|
||||||
1 -> 2 -> 3 -> 4
|
1 -> 2 -> 3 -> 4
|
||||||
|
|
|
|
||||||
*
|
*
|
||||||
5
|
5
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
ungroup(groupB);
|
ungroup(groupB);
|
||||||
@@ -1564,19 +1568,19 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void doTestRestoringWhenCodeBlocksHaveChanged_DoesntRegroup() {
|
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
|
// 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
|
// 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
|
// 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
|
// be found by the regrouping algorithm. Furthermore, the regrouping will not take place
|
||||||
// if at least two vertices cannot be found.
|
// if at least two vertices cannot be found.
|
||||||
//
|
//
|
||||||
|
|
||||||
//
|
//
|
||||||
// Pick a function and group some nodes.
|
// Pick a function and group some nodes.
|
||||||
//
|
//
|
||||||
FGData graphData = graphFunction("01002cf5");
|
FGData graphData = graphFunction("01002cf5");
|
||||||
@@ -1587,8 +1591,8 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte
|
|||||||
|
|
||||||
group(ungroupedVertices);
|
group(ungroupedVertices);
|
||||||
|
|
||||||
// 5 edges expected:
|
// 5 edges expected:
|
||||||
// -01002cf5: 2 out
|
// -01002cf5: 2 out
|
||||||
// -01002cf5: 2 in, 1 out
|
// -01002cf5: 2 in, 1 out
|
||||||
int expectedGroupedEdgeCount = 5;
|
int expectedGroupedEdgeCount = 5;
|
||||||
GroupedFunctionGraphVertex groupedVertex = validateNewGroupedVertexFromVertices(
|
GroupedFunctionGraphVertex groupedVertex = validateNewGroupedVertexFromVertices(
|
||||||
@@ -1598,10 +1602,10 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte
|
|||||||
Address minAddress = addresses.getMinAddress();
|
Address minAddress = addresses.getMinAddress();
|
||||||
|
|
||||||
//
|
//
|
||||||
// Ideally, we would like to save, close and re-open the program so that we can get
|
// 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
|
// 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
|
// 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.
|
// data saved in the program has been used to re-group.
|
||||||
//
|
//
|
||||||
graphFunction("0100415a");
|
graphFunction("0100415a");
|
||||||
@@ -1623,19 +1627,19 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void doTestRestoringWhenCodeBlocksHaveChanged_WillRegroup() {
|
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
|
// 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
|
// 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 3 nodes such
|
// 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
|
// be found by the regrouping algorithm. Furthermore, the regrouping *will* still
|
||||||
// take place, as at least two vertices cannot be found.
|
// take place, as at least two vertices cannot be found.
|
||||||
//
|
//
|
||||||
|
|
||||||
//
|
//
|
||||||
// Pick a function and group some nodes.
|
// Pick a function and group some nodes.
|
||||||
//
|
//
|
||||||
FGData graphData = graphFunction("01002cf5");
|
FGData graphData = graphFunction("01002cf5");
|
||||||
@@ -1646,8 +1650,8 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte
|
|||||||
|
|
||||||
group(ungroupedVertices);
|
group(ungroupedVertices);
|
||||||
|
|
||||||
// 5 edges expected:
|
// 5 edges expected:
|
||||||
// -01002cf5: 2 out
|
// -01002cf5: 2 out
|
||||||
// -01002d11: 2 in, (1 out that was removed)
|
// -01002d11: 2 in, (1 out that was removed)
|
||||||
// -01002d1f: 2 out (1 in that was removed)
|
// -01002d1f: 2 out (1 in that was removed)
|
||||||
int expectedGroupedEdgeCount = 6;
|
int expectedGroupedEdgeCount = 6;
|
||||||
@@ -1659,10 +1663,10 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte
|
|||||||
Address maxAddress = addresses.getMaxAddress();
|
Address maxAddress = addresses.getMaxAddress();
|
||||||
|
|
||||||
//
|
//
|
||||||
// Ideally, we would like to save, close and re-open the program so that we can get
|
// 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
|
// 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
|
// 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.
|
// data saved in the program has been used to re-group.
|
||||||
//
|
//
|
||||||
graphFunction("0100415a");
|
graphFunction("0100415a");
|
||||||
@@ -1706,12 +1710,12 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte
|
|||||||
protected void doTestSymbolAddedWhenGrouped_SymbolInsideOfGroupNode() {
|
protected void doTestSymbolAddedWhenGrouped_SymbolInsideOfGroupNode() {
|
||||||
//
|
//
|
||||||
// By default, if the FunctionGraph detects a symbol addition to one of the code blocks
|
// 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
|
// However, if the affected vertex is grouped, then the FG will not split the node, but
|
||||||
// should still show the 'stale' indicator.
|
// should still show the 'stale' indicator.
|
||||||
//
|
//
|
||||||
|
|
||||||
//
|
//
|
||||||
// Pick a function and group some nodes.
|
// Pick a function and group some nodes.
|
||||||
//
|
//
|
||||||
FGData graphData = graphFunction("01002cf5");
|
FGData graphData = graphFunction("01002cf5");
|
||||||
@@ -1722,8 +1726,8 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte
|
|||||||
|
|
||||||
group(ungroupedVertices);
|
group(ungroupedVertices);
|
||||||
|
|
||||||
// 5 edges expected:
|
// 5 edges expected:
|
||||||
// -01002cf5: 2 out
|
// -01002cf5: 2 out
|
||||||
// -01002cf5: 2 in, 1 out
|
// -01002cf5: 2 in, 1 out
|
||||||
int expectedGroupedEdgeCount = 5;
|
int expectedGroupedEdgeCount = 5;
|
||||||
GroupedFunctionGraphVertex groupedVertex = validateNewGroupedVertexFromVertices(
|
GroupedFunctionGraphVertex groupedVertex = validateNewGroupedVertexFromVertices(
|
||||||
@@ -1748,8 +1752,8 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte
|
|||||||
return reference.get();
|
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.
|
* the same (==) edge instances.
|
||||||
*/
|
*/
|
||||||
private FGEdge getCurrentEdge(FunctionGraph functionGraph, FGEdge edge) {
|
private FGEdge getCurrentEdge(FunctionGraph functionGraph, FGEdge edge) {
|
||||||
@@ -1998,29 +2002,29 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte
|
|||||||
private void setMinCrossLayout() {
|
private void setMinCrossLayout() {
|
||||||
Object actionManager = getInstanceField("actionManager", graphProvider);
|
Object actionManager = getInstanceField("actionManager", graphProvider);
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
final MultiStateDockingAction<Class<? extends FGLayoutProvider>> action =
|
final MultiStateDockingAction<FGLayoutProvider> action =
|
||||||
(MultiStateDockingAction<Class<? extends FGLayoutProvider>>) getInstanceField(
|
(MultiStateDockingAction<FGLayoutProvider>) getInstanceField("layoutAction",
|
||||||
"layoutAction", actionManager);
|
actionManager);
|
||||||
runSwing(() -> {
|
runSwing(() -> {
|
||||||
List<ActionState<Class<? extends FGLayoutProvider>>> states =
|
List<ActionState<FGLayoutProvider>> states = action.getAllActionStates();
|
||||||
action.getAllActionStates();
|
for (ActionState<FGLayoutProvider> state : states) {
|
||||||
for (ActionState<Class<? extends FGLayoutProvider>> state : states) {
|
FGLayoutProvider layoutProvider = state.getUserData();
|
||||||
Class<? extends FGLayoutProvider> layoutClass = state.getUserData();
|
if (layoutProvider.getLayoutName().contains("MinCross")) {
|
||||||
if (layoutClass.getSimpleName().contains("MinCross")) {
|
|
||||||
action.setCurrentActionState(state);
|
action.setCurrentActionState(state);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
throw new AssertException("Unable to find MinCross layout");
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected FGData triggerPersistenceAndReload(String functionAddress) {
|
protected FGData triggerPersistenceAndReload(String functionAddress) {
|
||||||
//
|
//
|
||||||
// Ideally, we would like to save, close and re-open the program so that we can get
|
// 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
|
// 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
|
// 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.
|
// data saved in the program has been used to re-group.
|
||||||
//
|
//
|
||||||
String otherAddress = "0100415a";
|
String otherAddress = "0100415a";
|
||||||
@@ -2277,7 +2281,7 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte
|
|||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// Private Methods
|
// Private Methods
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
|
|
||||||
protected void moveView(int amount) {
|
protected void moveView(int amount) {
|
||||||
Point translation = new Point(amount, amount);
|
Point translation = new Point(amount, amount);
|
||||||
@@ -2316,11 +2320,11 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte
|
|||||||
DockingActionIf action = getAction(tool, "FunctionGraphPlugin", name);
|
DockingActionIf action = getAction(tool, "FunctionGraphPlugin", name);
|
||||||
ToggleDockingAction displayAction = (ToggleDockingAction) action;
|
ToggleDockingAction displayAction = (ToggleDockingAction) action;
|
||||||
setToggleActionSelected(displayAction, new ActionContext(), expectedVisible);
|
setToggleActionSelected(displayAction, new ActionContext(), expectedVisible);
|
||||||
//
|
//
|
||||||
// // make sure the action is not already in the state we expect
|
// // make sure the action is not already in the state we expect
|
||||||
// assertEquals(name + " action is not selected as expected", !expectedVisible,
|
// assertEquals(name + " action is not selected as expected", !expectedVisible,
|
||||||
// displayAction.isSelected());
|
// displayAction.isSelected());
|
||||||
//
|
//
|
||||||
// performAction(displayAction, true);
|
// performAction(displayAction, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+81
-86
@@ -65,12 +65,12 @@ public class FunctionGraphGroupVertices1Test extends AbstractFunctionGraphTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGroupingPersistence() throws Exception {
|
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.
|
// program.
|
||||||
//
|
//
|
||||||
|
|
||||||
//
|
//
|
||||||
// Pick a function and group some nodes.
|
// Pick a function and group some nodes.
|
||||||
//
|
//
|
||||||
FGData graphData = graphFunction("01002cf5");
|
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
|
// 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
|
// toString() on the edges will call back to its vertices, which will later have been
|
||||||
// disposed.
|
// 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());
|
List<String> originalEdgeStrings = new ArrayList<>(oringalGroupedEdges.size());
|
||||||
for (FGEdge edge : oringalGroupedEdges) {
|
for (FGEdge edge : oringalGroupedEdges) {
|
||||||
originalEdgeStrings.add(edge.toString());
|
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,
|
* 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.
|
* and generate the appropriate error message when trying to create a function graph.
|
||||||
*
|
*
|
||||||
* Step 1: Make sure the function graph window is closed.
|
* Step 1: Make sure the function graph window is closed.
|
||||||
* Step 2: Clear the entry point bytes
|
* Step 2: Clear the entry point bytes
|
||||||
* Step 3: Open the function graph window to generate the graph again.
|
* Step 3: Open the function graph window to generate the graph again.
|
||||||
@@ -144,8 +144,8 @@ public class FunctionGraphGroupVertices1Test extends AbstractFunctionGraphTest {
|
|||||||
*/
|
*/
|
||||||
public void testInvalidFunctionEntryPoint() {
|
public void testInvalidFunctionEntryPoint() {
|
||||||
|
|
||||||
// First thing we need to do is close the function graph window. It's opened on
|
// 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
|
// startup by default in this test suite but we want it closed until we clear the
|
||||||
// function code bytes.
|
// function code bytes.
|
||||||
this.getFunctionGraphController().getProvider().closeComponent();
|
this.getFunctionGraphController().getProvider().closeComponent();
|
||||||
|
|
||||||
@@ -170,7 +170,7 @@ public class FunctionGraphGroupVertices1Test extends AbstractFunctionGraphTest {
|
|||||||
runSwing(() -> clearAction.actionPerformed(context));
|
runSwing(() -> clearAction.actionPerformed(context));
|
||||||
waitForBusyTool(tool);
|
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.
|
// an error message.
|
||||||
DockingActionIf openGraphAction;
|
DockingActionIf openGraphAction;
|
||||||
openGraphAction = getAction(fgp, "Display Function Graph");
|
openGraphAction = getAction(fgp, "Display Function Graph");
|
||||||
@@ -186,8 +186,8 @@ public class FunctionGraphGroupVertices1Test extends AbstractFunctionGraphTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGroupAndUngroup_WhenOneOfTheGroupIsAGroup() {
|
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.
|
// those vertices is itself a group.
|
||||||
//
|
//
|
||||||
|
|
||||||
@@ -217,7 +217,7 @@ public class FunctionGraphGroupVertices1Test extends AbstractFunctionGraphTest {
|
|||||||
|
|
||||||
group(secondUngroupedVertices);
|
group(secondUngroupedVertices);
|
||||||
|
|
||||||
// 5 edges expected:
|
// 5 edges expected:
|
||||||
// -ungrouped vertex: 1 in, 1 out
|
// -ungrouped vertex: 1 in, 1 out
|
||||||
// -grouped vertex : 1 in, 2 out
|
// -grouped vertex : 1 in, 2 out
|
||||||
expectedGroupedEdgeCount = 5;
|
expectedGroupedEdgeCount = 5;
|
||||||
@@ -248,7 +248,7 @@ public class FunctionGraphGroupVertices1Test extends AbstractFunctionGraphTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGroupingPersistence_WhenOneOfTheGroupIsAGroup() throws Exception {
|
public void testGroupingPersistence_WhenOneOfTheGroupIsAGroup() throws Exception {
|
||||||
//
|
//
|
||||||
// This test seeks to ensure that groups within groups are persisted and restored.
|
// This test seeks to ensure that groups within groups are persisted and restored.
|
||||||
//
|
//
|
||||||
|
|
||||||
@@ -284,7 +284,7 @@ public class FunctionGraphGroupVertices1Test extends AbstractFunctionGraphTest {
|
|||||||
//printEdges(outerUngroupedEdges);
|
//printEdges(outerUngroupedEdges);
|
||||||
group(outerUngroupedVertices);
|
group(outerUngroupedVertices);
|
||||||
|
|
||||||
// 5 edges expected:
|
// 5 edges expected:
|
||||||
// -ungrouped vertex: 1 in, 1 out
|
// -ungrouped vertex: 1 in, 1 out
|
||||||
// -grouped vertex : 1 in, 2 out
|
// -grouped vertex : 1 in, 2 out
|
||||||
expectedGroupedEdgeCount = 5;
|
expectedGroupedEdgeCount = 5;
|
||||||
@@ -353,8 +353,8 @@ public class FunctionGraphGroupVertices1Test extends AbstractFunctionGraphTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testUngroupAll() {
|
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
|
// recursively/nested grouping. Also create a second top-level group. Make sure the
|
||||||
// ungroup all action will restore the original graph.
|
// ungroup all action will restore the original graph.
|
||||||
//
|
//
|
||||||
@@ -434,8 +434,8 @@ public class FunctionGraphGroupVertices1Test extends AbstractFunctionGraphTest {
|
|||||||
//
|
//
|
||||||
// The coloring algorithm:
|
// The coloring algorithm:
|
||||||
// 1) If the grouped vertices are not colored, then use the default group color
|
// 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,
|
// 2) If the grouped vertices are colored, but not all the same color,
|
||||||
// then use the default group color=
|
// then use the default group color=
|
||||||
// 3) If all grouped vertices share the same color, then make the group that color
|
// 3) If all grouped vertices share the same color, then make the group that color
|
||||||
//
|
//
|
||||||
// This test is for 1)
|
// This test is for 1)
|
||||||
@@ -459,7 +459,7 @@ public class FunctionGraphGroupVertices1Test extends AbstractFunctionGraphTest {
|
|||||||
//
|
//
|
||||||
ungroup(group);
|
ungroup(group);
|
||||||
|
|
||||||
//
|
//
|
||||||
// Test the grouped vertices colors
|
// Test the grouped vertices colors
|
||||||
//
|
//
|
||||||
verifyDefaultColor(v1, v2);
|
verifyDefaultColor(v1, v2);
|
||||||
@@ -470,8 +470,8 @@ public class FunctionGraphGroupVertices1Test extends AbstractFunctionGraphTest {
|
|||||||
//
|
//
|
||||||
// The coloring algorithm:
|
// The coloring algorithm:
|
||||||
// 1) If the grouped vertices are not colored, then use the default group color
|
// 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,
|
// 2) If the grouped vertices are colored, but not all the same color,
|
||||||
// then use the default group color=
|
// then use the default group color=
|
||||||
// 3) If all grouped vertices share the same color, then make the group that color
|
// 3) If all grouped vertices share the same color, then make the group that color
|
||||||
//
|
//
|
||||||
// This test is for 2)
|
// This test is for 2)
|
||||||
@@ -500,7 +500,7 @@ public class FunctionGraphGroupVertices1Test extends AbstractFunctionGraphTest {
|
|||||||
//
|
//
|
||||||
ungroup(group);
|
ungroup(group);
|
||||||
|
|
||||||
//
|
//
|
||||||
// Test the grouped vertices colors
|
// Test the grouped vertices colors
|
||||||
//
|
//
|
||||||
verifyColor(v1, newColor);
|
verifyColor(v1, newColor);
|
||||||
@@ -512,8 +512,8 @@ public class FunctionGraphGroupVertices1Test extends AbstractFunctionGraphTest {
|
|||||||
//
|
//
|
||||||
// The coloring algorithm:
|
// The coloring algorithm:
|
||||||
// 1) If the grouped vertices are not colored, then use the default group color
|
// 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,
|
// 2) If the grouped vertices are colored, but not all the same color,
|
||||||
// then use the default group color=
|
// then use the default group color=
|
||||||
// 3) If all grouped vertices share the same color, then make the group that color
|
// 3) If all grouped vertices share the same color, then make the group that color
|
||||||
//
|
//
|
||||||
// This test is for 3)
|
// This test is for 3)
|
||||||
@@ -543,7 +543,7 @@ public class FunctionGraphGroupVertices1Test extends AbstractFunctionGraphTest {
|
|||||||
//
|
//
|
||||||
ungroup(group);
|
ungroup(group);
|
||||||
|
|
||||||
//
|
//
|
||||||
// Test the grouped vertices colors
|
// Test the grouped vertices colors
|
||||||
//
|
//
|
||||||
verifyColor(v1, newColor);
|
verifyColor(v1, newColor);
|
||||||
@@ -572,7 +572,7 @@ public class FunctionGraphGroupVertices1Test extends AbstractFunctionGraphTest {
|
|||||||
//
|
//
|
||||||
ungroup(group);
|
ungroup(group);
|
||||||
|
|
||||||
//
|
//
|
||||||
// Test the grouped vertices colors
|
// Test the grouped vertices colors
|
||||||
//
|
//
|
||||||
verifyColor(v1, newGroupColor);
|
verifyColor(v1, newGroupColor);
|
||||||
@@ -607,7 +607,7 @@ public class FunctionGraphGroupVertices1Test extends AbstractFunctionGraphTest {
|
|||||||
//
|
//
|
||||||
ungroup(group);
|
ungroup(group);
|
||||||
|
|
||||||
//
|
//
|
||||||
// Test the grouped vertices colors
|
// Test the grouped vertices colors
|
||||||
//
|
//
|
||||||
verifyColor(v1, newGroupColor);
|
verifyColor(v1, newGroupColor);
|
||||||
@@ -642,7 +642,7 @@ public class FunctionGraphGroupVertices1Test extends AbstractFunctionGraphTest {
|
|||||||
//
|
//
|
||||||
ungroup(group);
|
ungroup(group);
|
||||||
|
|
||||||
//
|
//
|
||||||
// Test the grouped vertices colors
|
// Test the grouped vertices colors
|
||||||
//
|
//
|
||||||
verifyColor(v1, newGroupColor);
|
verifyColor(v1, newGroupColor);
|
||||||
@@ -666,7 +666,7 @@ public class FunctionGraphGroupVertices1Test extends AbstractFunctionGraphTest {
|
|||||||
Color newGroupColor = Color.CYAN;
|
Color newGroupColor = Color.CYAN;
|
||||||
color(group, newGroupColor);
|
color(group, newGroupColor);
|
||||||
|
|
||||||
//
|
//
|
||||||
// Trigger persistence
|
// Trigger persistence
|
||||||
//
|
//
|
||||||
Address groupAddress = group.getVertexAddress();
|
Address groupAddress = group.getVertexAddress();
|
||||||
@@ -683,7 +683,7 @@ public class FunctionGraphGroupVertices1Test extends AbstractFunctionGraphTest {
|
|||||||
//
|
//
|
||||||
ungroup(group);
|
ungroup(group);
|
||||||
|
|
||||||
//
|
//
|
||||||
// Test the grouped vertices colors
|
// Test the grouped vertices colors
|
||||||
//
|
//
|
||||||
v1 = vertex("01002d06");
|
v1 = vertex("01002d06");
|
||||||
@@ -703,7 +703,7 @@ public class FunctionGraphGroupVertices1Test extends AbstractFunctionGraphTest {
|
|||||||
FGVertex v2 = vertex("01002d0f");
|
FGVertex v2 = vertex("01002d0f");
|
||||||
GroupedFunctionGraphVertex group = group("A", v1, v2);
|
GroupedFunctionGraphVertex group = group("A", v1, v2);
|
||||||
|
|
||||||
//
|
//
|
||||||
// Trigger persistence
|
// Trigger persistence
|
||||||
//
|
//
|
||||||
Address groupAddress = group.getVertexAddress();
|
Address groupAddress = group.getVertexAddress();
|
||||||
@@ -720,7 +720,7 @@ public class FunctionGraphGroupVertices1Test extends AbstractFunctionGraphTest {
|
|||||||
//
|
//
|
||||||
ungroup(group);
|
ungroup(group);
|
||||||
|
|
||||||
//
|
//
|
||||||
// Test the grouped vertices colors
|
// Test the grouped vertices colors
|
||||||
//
|
//
|
||||||
v1 = vertex("01002d06");
|
v1 = vertex("01002d06");
|
||||||
@@ -737,16 +737,16 @@ public class FunctionGraphGroupVertices1Test extends AbstractFunctionGraphTest {
|
|||||||
|
|
||||||
//
|
//
|
||||||
// Color just one of the vertices
|
// Color just one of the vertices
|
||||||
//
|
//
|
||||||
Color newColor = Color.RED;
|
Color newColor = Color.RED;
|
||||||
color(v1, newColor);
|
color(v1, newColor);
|
||||||
|
|
||||||
//
|
//
|
||||||
// Group a node
|
// Group a node
|
||||||
//
|
//
|
||||||
GroupedFunctionGraphVertex group = group("A", v1, v2);
|
GroupedFunctionGraphVertex group = group("A", v1, v2);
|
||||||
|
|
||||||
//
|
//
|
||||||
// Trigger persistence
|
// Trigger persistence
|
||||||
//
|
//
|
||||||
Address groupAddress = group.getVertexAddress();
|
Address groupAddress = group.getVertexAddress();
|
||||||
@@ -763,7 +763,7 @@ public class FunctionGraphGroupVertices1Test extends AbstractFunctionGraphTest {
|
|||||||
//
|
//
|
||||||
ungroup(group);
|
ungroup(group);
|
||||||
|
|
||||||
//
|
//
|
||||||
// Test the grouped vertices colors
|
// Test the grouped vertices colors
|
||||||
//
|
//
|
||||||
v1 = vertex("01002d06");
|
v1 = vertex("01002d06");
|
||||||
@@ -781,16 +781,16 @@ public class FunctionGraphGroupVertices1Test extends AbstractFunctionGraphTest {
|
|||||||
|
|
||||||
//
|
//
|
||||||
// Color just one of the vertices
|
// Color just one of the vertices
|
||||||
//
|
//
|
||||||
Color newColor = Color.RED;
|
Color newColor = Color.RED;
|
||||||
color(v1, newColor);
|
color(v1, newColor);
|
||||||
|
|
||||||
//
|
//
|
||||||
// Group a node
|
// Group a node
|
||||||
//
|
//
|
||||||
GroupedFunctionGraphVertex group = group("A", v1, v2);
|
GroupedFunctionGraphVertex group = group("A", v1, v2);
|
||||||
|
|
||||||
//
|
//
|
||||||
// Trigger reset
|
// Trigger reset
|
||||||
//
|
//
|
||||||
Address groupAddress = group.getVertexAddress();
|
Address groupAddress = group.getVertexAddress();
|
||||||
@@ -800,9 +800,9 @@ public class FunctionGraphGroupVertices1Test extends AbstractFunctionGraphTest {
|
|||||||
// Make sure the group is gone
|
// Make sure the group is gone
|
||||||
//
|
//
|
||||||
FGVertex vertex = graphData.getFunctionGraph().getVertexForAddress(groupAddress);
|
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
|
// Test the grouped vertices colors
|
||||||
//
|
//
|
||||||
v1 = vertex("01002d06");
|
v1 = vertex("01002d06");
|
||||||
@@ -838,11 +838,6 @@ public class FunctionGraphGroupVertices1Test extends AbstractFunctionGraphTest {
|
|||||||
assertEquals(alpha, alphAfterGroup);
|
assertEquals(alpha, alphAfterGroup);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testSymbolAddedWhenGrouped_SymbolOutsideOfGroupNode() {
|
|
||||||
// TODO
|
|
||||||
}
|
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// Private Methods
|
// Private Methods
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
@@ -853,85 +848,85 @@ public class FunctionGraphGroupVertices1Test extends AbstractFunctionGraphTest {
|
|||||||
FGData graphData = graphFunction("01002cf5");
|
FGData graphData = graphFunction("01002cf5");
|
||||||
FunctionGraph functionGraph = graphData.getFunctionGraph();
|
FunctionGraph functionGraph = graphData.getFunctionGraph();
|
||||||
Graph<FGVertex, FGEdge> graph = functionGraph;
|
Graph<FGVertex, FGEdge> graph = functionGraph;
|
||||||
|
|
||||||
Set<FGVertex> ungroupedVertices = selectVertices( functionGraph,
|
Set<FGVertex> ungroupedVertices = selectVertices( functionGraph,
|
||||||
"01002d2b" /* Another Local*/,
|
"01002d2b" /* Another Local*/,
|
||||||
"01002d1f" /* MyLocal */);
|
"01002d1f" /* MyLocal */);
|
||||||
Set<FGEdge> ungroupedEdges = getEdges(graph, ungroupedVertices);
|
Set<FGEdge> ungroupedEdges = getEdges(graph, ungroupedVertices);
|
||||||
assertEquals("Did not grab all known edges for vertices", 4, ungroupedEdges.size());
|
assertEquals("Did not grab all known edges for vertices", 4, ungroupedEdges.size());
|
||||||
|
|
||||||
group(ungroupedVertices);
|
group(ungroupedVertices);
|
||||||
|
|
||||||
assertVerticesRemoved(graph, ungroupedVertices);
|
assertVerticesRemoved(graph, ungroupedVertices);
|
||||||
assertEdgesRemoved(graph, ungroupedEdges);
|
assertEdgesRemoved(graph, ungroupedEdges);
|
||||||
|
|
||||||
// -1 because one one of the edges was between two of the vertices being grouped
|
// -1 because one one of the edges was between two of the vertices being grouped
|
||||||
int expectedGroupedEdgeCount = ungroupedEdges.size() - 1;
|
int expectedGroupedEdgeCount = ungroupedEdges.size() - 1;
|
||||||
GroupedFunctionGraphVertex groupedVertex =
|
GroupedFunctionGraphVertex groupedVertex =
|
||||||
validateNewGroupedVertexFromVertices(functionGraph, ungroupedVertices,
|
validateNewGroupedVertexFromVertices(functionGraph, ungroupedVertices,
|
||||||
expectedGroupedEdgeCount);
|
expectedGroupedEdgeCount);
|
||||||
|
|
||||||
ungroup(groupedVertex);
|
ungroup(groupedVertex);
|
||||||
|
|
||||||
assertVertexRemoved(graph, groupedVertex);
|
assertVertexRemoved(graph, groupedVertex);
|
||||||
assertVerticesAdded(graph, ungroupedVertices);
|
assertVerticesAdded(graph, ungroupedVertices);
|
||||||
assertEdgesAdded(functionGraph, ungroupedEdges);
|
assertEdgesAdded(functionGraph, ungroupedEdges);
|
||||||
assertSelected(ungroupedVertices);
|
assertSelected(ungroupedVertices);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doTestRestoringWhenCodeBlocksHaveChanged_WillRegroup() {
|
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
|
// 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
|
// 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 3 nodes such
|
// 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
|
// be found by the regrouping algorithm. Furthermore, the regrouping *will* still
|
||||||
// take place, as at least two vertices cannot be found.
|
// take place, as at least two vertices cannot be found.
|
||||||
//
|
//
|
||||||
|
|
||||||
//
|
//
|
||||||
// Pick a function and group some nodes.
|
// Pick a function and group some nodes.
|
||||||
//
|
//
|
||||||
FGData graphData = graphFunction("01002cf5");
|
FGData graphData = graphFunction("01002cf5");
|
||||||
FunctionGraph functionGraph = graphData.getFunctionGraph();
|
FunctionGraph functionGraph = graphData.getFunctionGraph();
|
||||||
|
|
||||||
Set<FGVertex> ungroupedVertices = selectVertices(functionGraph,
|
Set<FGVertex> ungroupedVertices = selectVertices(functionGraph,
|
||||||
"01002d11" /* LAB_01002d11 */, "01002cf5" /* ghidra */, "01002d1f" /* MyLocal */);
|
"01002d11" /* LAB_01002d11 */, "01002cf5" /* ghidra */, "01002d1f" /* MyLocal */);
|
||||||
|
|
||||||
group(ungroupedVertices);
|
group(ungroupedVertices);
|
||||||
|
|
||||||
// 5 edges expected:
|
// 5 edges expected:
|
||||||
// -01002cf5: 2 out
|
// -01002cf5: 2 out
|
||||||
// -01002d11: 2 in, (1 out that was removed)
|
// -01002d11: 2 in, (1 out that was removed)
|
||||||
// -01002d1f: 2 out (1 in that was removed)
|
// -01002d1f: 2 out (1 in that was removed)
|
||||||
int expectedGroupedEdgeCount = 6;
|
int expectedGroupedEdgeCount = 6;
|
||||||
GroupedFunctionGraphVertex groupedVertex = validateNewGroupedVertexFromVertices(
|
GroupedFunctionGraphVertex groupedVertex = validateNewGroupedVertexFromVertices(
|
||||||
functionGraph, ungroupedVertices, expectedGroupedEdgeCount);
|
functionGraph, ungroupedVertices, expectedGroupedEdgeCount);
|
||||||
|
|
||||||
AddressSetView addresses = groupedVertex.getAddresses();
|
AddressSetView addresses = groupedVertex.getAddresses();
|
||||||
Address minAddress = addresses.getMinAddress();
|
Address minAddress = addresses.getMinAddress();
|
||||||
Address maxAddress = addresses.getMaxAddress();
|
Address maxAddress = addresses.getMaxAddress();
|
||||||
|
|
||||||
//
|
//
|
||||||
// Ideally, we would like to save, close and re-open the program so that we can get
|
// 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
|
// 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
|
// 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.
|
// data saved in the program has been used to re-group.
|
||||||
//
|
//
|
||||||
graphFunction("0100415a");
|
graphFunction("0100415a");
|
||||||
clearCache();
|
clearCache();
|
||||||
|
|
||||||
//
|
//
|
||||||
// Add a label to trigger a code block change
|
// Add a label to trigger a code block change
|
||||||
//
|
//
|
||||||
Address labelAddress = createLabel("01002d18");// in the middle of the LAB_01002d11 code block
|
Address labelAddress = createLabel("01002d18");// in the middle of the LAB_01002d11 code block
|
||||||
|
|
||||||
//
|
//
|
||||||
// Relaunch the graph, which will use the above persisted group settings...
|
// 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);
|
FGVertex expectedGroupVertex = functionGraph.getVertexForAddress(minAddress);
|
||||||
assertTrue(expectedGroupVertex instanceof GroupedFunctionGraphVertex);
|
assertTrue(expectedGroupVertex instanceof GroupedFunctionGraphVertex);
|
||||||
assertEquals(maxAddress, expectedGroupVertex.getAddresses().getMaxAddress());
|
assertEquals(maxAddress, expectedGroupVertex.getAddresses().getMaxAddress());
|
||||||
|
|
||||||
// ...we expect that the two original grouped vertices have again been grouped...
|
// ...we expect that the two original grouped vertices have again been grouped...
|
||||||
FGVertex splitVertex =
|
FGVertex splitVertex =
|
||||||
functionGraph.getVertexForAddress(getAddress("01002d11") /* LAB_01002d11 */);
|
functionGraph.getVertexForAddress(getAddress("01002d11") /* LAB_01002d11 */);
|
||||||
assertTrue("The split vertex should not have been regrouped",
|
assertTrue("The split vertex should not have been regrouped",
|
||||||
!(splitVertex instanceof GroupedFunctionGraphVertex));
|
!(splitVertex instanceof GroupedFunctionGraphVertex));
|
||||||
|
|
||||||
FGVertex unchangedVertex =
|
FGVertex unchangedVertex =
|
||||||
functionGraph.getVertexForAddress(getAddress("01002cf5") /* ghidra */);
|
functionGraph.getVertexForAddress(getAddress("01002cf5") /* ghidra */);
|
||||||
assertTrue("An unchanged vertex should have been regrouped: " + unchangedVertex,
|
assertTrue("An unchanged vertex should have been regrouped: " + unchangedVertex,
|
||||||
(unchangedVertex instanceof GroupedFunctionGraphVertex));
|
(unchangedVertex instanceof GroupedFunctionGraphVertex));
|
||||||
|
|
||||||
unchangedVertex = functionGraph.getVertexForAddress(getAddress("01002d1f") /* MyLocal */);
|
unchangedVertex = functionGraph.getVertexForAddress(getAddress("01002d1f") /* MyLocal */);
|
||||||
assertTrue("An unchanged vertex should have been regrouped: " + unchangedVertex,
|
assertTrue("An unchanged vertex should have been regrouped: " + unchangedVertex,
|
||||||
(unchangedVertex instanceof GroupedFunctionGraphVertex));
|
(unchangedVertex instanceof GroupedFunctionGraphVertex));
|
||||||
|
|
||||||
// ...but the newly created code block has not
|
// ...but the newly created code block has not
|
||||||
FGVertex newlyCreatedVertex = functionGraph.getVertexForAddress(labelAddress);
|
FGVertex newlyCreatedVertex = functionGraph.getVertexForAddress(labelAddress);
|
||||||
assertNotNull(newlyCreatedVertex);
|
assertNotNull(newlyCreatedVertex);
|
||||||
@@ -966,34 +961,34 @@ public class FunctionGraphGroupVertices1Test extends AbstractFunctionGraphTest {
|
|||||||
protected void doTestSymbolAddedWhenGrouped_SymbolInsideOfGroupNode() {
|
protected void doTestSymbolAddedWhenGrouped_SymbolInsideOfGroupNode() {
|
||||||
//
|
//
|
||||||
// By default, if the FunctionGraph detects a symbol addition to one of the code blocks
|
// 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
|
// However, if the affected vertex is grouped, then the FG will not split the node, but
|
||||||
// should still show the 'stale' indicator.
|
// should still show the 'stale' indicator.
|
||||||
//
|
//
|
||||||
|
|
||||||
//
|
//
|
||||||
// Pick a function and group some nodes.
|
// Pick a function and group some nodes.
|
||||||
//
|
//
|
||||||
FGData graphData = graphFunction("01002cf5");
|
FGData graphData = graphFunction("01002cf5");
|
||||||
FunctionGraph functionGraph = graphData.getFunctionGraph();
|
FunctionGraph functionGraph = graphData.getFunctionGraph();
|
||||||
|
|
||||||
Set<FGVertex> ungroupedVertices =
|
Set<FGVertex> ungroupedVertices =
|
||||||
selectVertices(functionGraph, "01002d11" /* LAB_01002d11 */, "01002cf5" /* ghidra */);
|
selectVertices(functionGraph, "01002d11" /* LAB_01002d11 */, "01002cf5" /* ghidra */);
|
||||||
|
|
||||||
group(ungroupedVertices);
|
group(ungroupedVertices);
|
||||||
|
|
||||||
// 5 edges expected:
|
// 5 edges expected:
|
||||||
// -01002cf5: 2 out
|
// -01002cf5: 2 out
|
||||||
// -01002cf5: 2 in, 1 out
|
// -01002cf5: 2 in, 1 out
|
||||||
int expectedGroupedEdgeCount = 5;
|
int expectedGroupedEdgeCount = 5;
|
||||||
GroupedFunctionGraphVertex groupedVertex = validateNewGroupedVertexFromVertices(
|
GroupedFunctionGraphVertex groupedVertex = validateNewGroupedVertexFromVertices(
|
||||||
functionGraph, ungroupedVertices, expectedGroupedEdgeCount);
|
functionGraph, ungroupedVertices, expectedGroupedEdgeCount);
|
||||||
|
|
||||||
//
|
//
|
||||||
// Add a label to trigger a code block change
|
// Add a label to trigger a code block change
|
||||||
//
|
//
|
||||||
Address labelAddress = createLabel("01002d18");// in the middle of the LAB_01002d11 code block
|
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
|
// Make sure the newly created code block does not have a corresponding vertex
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -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
|
* 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
|
* the user presses the button, it will execute the action corresponding to the current
|
||||||
* state.
|
* state.
|
||||||
*
|
*
|
||||||
* <p>Warning: if you use this action in a toolbar, then be sure to call the
|
* <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
|
* {@link #MultiStateDockingAction(String, String, boolean) correct constructor}. If you call
|
||||||
* another constructor, or pass false for this boolean above, your
|
* 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 static Icon EMPTY_ICON = new EmptyIcon(16, 16);
|
||||||
|
|
||||||
private List<ActionState<T>> actionStates = new ArrayList<>();
|
private List<ActionState<T>> actionStates = new ArrayList<>();
|
||||||
private int currentStateIndex = 0;
|
private int currentStateIndex = -1;
|
||||||
private MultiActionDockingActionIf multiActionGenerator;
|
private MultiActionDockingActionIf multiActionGenerator;
|
||||||
private MultipleActionDockingToolbarButton multipleButton;
|
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
|
* Call this constructor with this action will not be added to a toolbar
|
||||||
*
|
*
|
||||||
* @param name the action name
|
* @param name the action name
|
||||||
* @param owner the owner
|
* @param owner the owner
|
||||||
* @see #MultiStateDockingAction(String, String, boolean)
|
* @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
|
* Use this constructor explicitly when this action is used in a toolbar, passing true
|
||||||
* for <code>isToolbarAction</code> (see the javadoc header note).
|
* for <code>isToolbarAction</code> (see the javadoc header note).
|
||||||
*
|
*
|
||||||
* @param name the action name
|
* @param name the action name
|
||||||
* @param owner the owner
|
* @param owner the owner
|
||||||
* @param isToolbarAction true if this action is a toolbar action
|
* @param isToolbarAction true if this action is a toolbar action
|
||||||
@@ -110,7 +110,7 @@ public abstract class MultiStateDockingAction<T> extends DockingAction {
|
|||||||
* <p>
|
* <p>
|
||||||
* Also, if the parameter is true, then the button will behave like a button in terms of
|
* 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.
|
* mouse feedback. If false, then the button will behave more like a label.
|
||||||
*
|
*
|
||||||
* @param doPerformAction true to call {@link #doActionPerformed(ActionContext)} when the
|
* @param doPerformAction true to call {@link #doActionPerformed(ActionContext)} when the
|
||||||
* user presses the button for this action (not the drop-down menu; see above)
|
* 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}.
|
* 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
|
* 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.
|
* checkbox icon will be used to show the active action state.
|
||||||
*
|
*
|
||||||
* @param useCheckboxForIcons true to use a checkbox
|
* @param useCheckboxForIcons true to use a checkbox
|
||||||
*/
|
*/
|
||||||
public void setUseCheckboxForIcons(boolean useCheckboxForIcons) {
|
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
|
* 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
|
* you wish for your action states to not use icon, but desire the action itself to have an
|
||||||
* icon.
|
* icon.
|
||||||
*
|
*
|
||||||
* @param icon the icon
|
* @param icon the icon
|
||||||
*/
|
*/
|
||||||
public void setDefaultIcon(Icon 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
|
* 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
|
* presses that are on the button and not the drop-down. This will only be called if
|
||||||
* {@link #performActionOnPrimaryButtonClick} is true.
|
* {@link #performActionOnPrimaryButtonClick} is true.
|
||||||
*
|
*
|
||||||
* @param context the action context
|
* @param context the action context
|
||||||
*/
|
*/
|
||||||
protected void doActionPerformed(ActionContext context) {
|
protected void doActionPerformed(ActionContext context) {
|
||||||
@@ -270,10 +270,7 @@ public abstract class MultiStateDockingAction<T> extends DockingAction {
|
|||||||
}
|
}
|
||||||
currentStateIndex = indexOf;
|
currentStateIndex = indexOf;
|
||||||
|
|
||||||
// we set the icon here to handle the odd case where this action is not used in a toolbar
|
setButtonState(actionState);
|
||||||
if (multipleButton != null) {
|
|
||||||
setButtonState(actionState);
|
|
||||||
}
|
|
||||||
|
|
||||||
ToolBarData tbd = getToolBarData();
|
ToolBarData tbd = getToolBarData();
|
||||||
tbd.setIcon(getIcon(actionState));
|
tbd.setIcon(getIcon(actionState));
|
||||||
@@ -317,6 +314,16 @@ public abstract class MultiStateDockingAction<T> extends DockingAction {
|
|||||||
|
|
||||||
private void setButtonState(ActionState<T> actionState) {
|
private void setButtonState(ActionState<T> actionState) {
|
||||||
|
|
||||||
|
if (multipleButton == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (actionState == null) {
|
||||||
|
multipleButton.setIcon(null);
|
||||||
|
multipleButton.setToolTipText(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Icon icon = getIcon(actionState);
|
Icon icon = getIcon(actionState);
|
||||||
multipleButton.setIcon(icon);
|
multipleButton.setIcon(icon);
|
||||||
multipleButton.setToolTipText(actionState.getName());
|
multipleButton.setToolTipText(actionState.getName());
|
||||||
@@ -337,6 +344,9 @@ public abstract class MultiStateDockingAction<T> extends DockingAction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public String getToolTipText() {
|
public String getToolTipText() {
|
||||||
|
if (actionStates.isEmpty()) {
|
||||||
|
return getName() + ": <no action states installed>";
|
||||||
|
}
|
||||||
return getName() + ": " + getCurrentState().getName();
|
return getName() + ": " + getCurrentState().getName();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -355,8 +365,7 @@ public abstract class MultiStateDockingAction<T> extends DockingAction {
|
|||||||
|
|
||||||
setSelected(isSelected);
|
setSelected(isSelected);
|
||||||
|
|
||||||
setMenuBarData(
|
setMenuBarData(new MenuData(new String[] { actionState.getName() }));
|
||||||
new MenuData(new String[] { actionState.getName() }));
|
|
||||||
HelpLocation helpLocation = actionState.getHelpLocation();
|
HelpLocation helpLocation = actionState.getHelpLocation();
|
||||||
if (helpLocation != null) {
|
if (helpLocation != null) {
|
||||||
setHelpLocation(helpLocation);
|
setHelpLocation(helpLocation);
|
||||||
|
|||||||
Reference in New Issue
Block a user