mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-04 02:09:44 +02:00
GT-3035 - Restore Integration Tests - more missing test files; updated
test environment to install a default tool
This commit is contained in:
parent
4dc8e77172
commit
554bce2407
26 changed files with 4989 additions and 40 deletions
|
@ -23,6 +23,7 @@ import javax.swing.*;
|
||||||
import docking.ActionContext;
|
import docking.ActionContext;
|
||||||
import docking.WindowPosition;
|
import docking.WindowPosition;
|
||||||
import docking.options.editor.ButtonPanelFactory;
|
import docking.options.editor.ButtonPanelFactory;
|
||||||
|
import docking.util.image.ToolIconURL;
|
||||||
import docking.widgets.OptionDialog;
|
import docking.widgets.OptionDialog;
|
||||||
import docking.widgets.label.*;
|
import docking.widgets.label.*;
|
||||||
import ghidra.app.context.ListingActionContext;
|
import ghidra.app.context.ListingActionContext;
|
||||||
|
@ -32,7 +33,6 @@ import ghidra.app.util.HelpTopics;
|
||||||
import ghidra.app.util.viewer.format.FieldHeaderComp;
|
import ghidra.app.util.viewer.format.FieldHeaderComp;
|
||||||
import ghidra.app.util.viewer.format.FieldHeaderLocation;
|
import ghidra.app.util.viewer.format.FieldHeaderLocation;
|
||||||
import ghidra.framework.plugintool.ComponentProviderAdapter;
|
import ghidra.framework.plugintool.ComponentProviderAdapter;
|
||||||
import ghidra.framework.project.tool.ToolIconURL;
|
|
||||||
import ghidra.program.util.ProgramLocation;
|
import ghidra.program.util.ProgramLocation;
|
||||||
import ghidra.util.HelpLocation;
|
import ghidra.util.HelpLocation;
|
||||||
import ghidra.util.layout.VerticalLayout;
|
import ghidra.util.layout.VerticalLayout;
|
||||||
|
|
|
@ -22,8 +22,6 @@ import java.util.*;
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import javax.swing.JFrame;
|
|
||||||
|
|
||||||
import org.jdom.Element;
|
import org.jdom.Element;
|
||||||
|
|
||||||
import docking.ComponentProvider;
|
import docking.ComponentProvider;
|
||||||
|
@ -332,6 +330,7 @@ public class TestEnv {
|
||||||
* <P>This method is considered sub-standard and users should prefer instead
|
* <P>This method is considered sub-standard and users should prefer instead
|
||||||
* {@link #launchDefaultTool()} or {@link #launchDefaultTool(Program)}.
|
* {@link #launchDefaultTool()} or {@link #launchDefaultTool(Program)}.
|
||||||
*
|
*
|
||||||
|
* @param p the program
|
||||||
* @return the newly shown tool
|
* @return the newly shown tool
|
||||||
*/
|
*/
|
||||||
public PluginTool showTool(Program p) {
|
public PluginTool showTool(Program p) {
|
||||||
|
@ -357,7 +356,7 @@ public class TestEnv {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Waits for the first window of the given class. This method is the same as
|
* Waits for the first window of the given class. This method is the same as
|
||||||
* {@link #waitForDialogComponent(Window, Class, int)} with the exception that the parent
|
* {@link #waitForDialogComponent(Class, int)} with the exception that the parent
|
||||||
* window is assumed to be this instance's tool frame.
|
* window is assumed to be this instance's tool frame.
|
||||||
*
|
*
|
||||||
* @param ghidraClass The class of the dialog the user desires
|
* @param ghidraClass The class of the dialog the user desires
|
||||||
|
@ -369,8 +368,7 @@ public class TestEnv {
|
||||||
@Deprecated
|
@Deprecated
|
||||||
public <T extends DialogComponentProvider> T waitForDialogComponent(Class<T> ghidraClass,
|
public <T extends DialogComponentProvider> T waitForDialogComponent(Class<T> ghidraClass,
|
||||||
int maxTimeMS) {
|
int maxTimeMS) {
|
||||||
JFrame frame = lazyTool().getToolFrame();
|
return AbstractDockingTest.waitForDialogComponent(ghidraClass);
|
||||||
return AbstractDockingTest.waitForDialogComponent(frame, ghidraClass, maxTimeMS);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static GhidraProject createGhidraTestProject(String projectName) throws IOException {
|
private static GhidraProject createGhidraTestProject(String projectName) throws IOException {
|
||||||
|
@ -381,7 +379,20 @@ public class TestEnv {
|
||||||
deleteSavedFrontEndTool();
|
deleteSavedFrontEndTool();
|
||||||
|
|
||||||
String projectDirectoryName = AbstractGTest.getTestDirectoryPath();
|
String projectDirectoryName = AbstractGTest.getTestDirectoryPath();
|
||||||
return GhidraProject.createProject(projectDirectoryName, projectName, true);
|
GhidraProject gp = GhidraProject.createProject(projectDirectoryName, projectName, true);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Unusual Code Alert: The default tool is not always found in the testing environment,
|
||||||
|
// depending upon where the test lives. This code maps the test tool to that tool name
|
||||||
|
// so that tests will have the default tool as needed.
|
||||||
|
//
|
||||||
|
Project project = gp.getProject();
|
||||||
|
ToolChest toolChest = project.getLocalToolChest();
|
||||||
|
ToolTemplate template = getToolTemplate(AbstractGenericTest.DEFAULT_TEST_TOOL_NAME);
|
||||||
|
template.setName(AbstractGenericTest.DEFAULT_TOOL_NAME);
|
||||||
|
AbstractGenericTest.runSwing(() -> toolChest.replaceToolTemplate(template));
|
||||||
|
|
||||||
|
return gp;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void deleteOldTestTools() {
|
private static void deleteOldTestTools() {
|
||||||
|
@ -466,6 +477,7 @@ public class TestEnv {
|
||||||
/**
|
/**
|
||||||
* This method differs from {@link #launchDefaultTool()} in that this method does not set the
|
* This method differs from {@link #launchDefaultTool()} in that this method does not set the
|
||||||
* <tt>tool</tt> variable in of this <tt>TestEnv</tt> instance.
|
* <tt>tool</tt> variable in of this <tt>TestEnv</tt> instance.
|
||||||
|
* @return the tool
|
||||||
*/
|
*/
|
||||||
public PluginTool createDefaultTool() {
|
public PluginTool createDefaultTool() {
|
||||||
PluginTool newTool = launchDefaultToolByName(AbstractGenericTest.DEFAULT_TEST_TOOL_NAME);
|
PluginTool newTool = launchDefaultToolByName(AbstractGenericTest.DEFAULT_TEST_TOOL_NAME);
|
||||||
|
@ -497,15 +509,14 @@ public class TestEnv {
|
||||||
return tool;
|
return tool;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected PluginTool launchDefaultToolByName(final String toolName) {
|
protected PluginTool launchDefaultToolByName(String toolName) {
|
||||||
AtomicReference<PluginTool> ref = new AtomicReference<>();
|
|
||||||
AbstractGenericTest.runSwing(() -> {
|
|
||||||
|
|
||||||
ToolTemplate toolTemplate =
|
return AbstractGenericTest.runSwing(() -> {
|
||||||
ToolUtils.readToolTemplate("defaultTools/" + toolName + ".tool");
|
|
||||||
|
ToolTemplate toolTemplate = getToolTemplate(toolName);
|
||||||
if (toolTemplate == null) {
|
if (toolTemplate == null) {
|
||||||
Msg.debug(this, "Unable to find tool: " + toolName);
|
Msg.debug(this, "Unable to find tool: " + toolName);
|
||||||
return;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean wasErrorGUIEnabled = AbstractDockingTest.isUseErrorGUI();
|
boolean wasErrorGUIEnabled = AbstractDockingTest.isUseErrorGUI();
|
||||||
|
@ -515,11 +526,23 @@ public class TestEnv {
|
||||||
Project project = frontEndToolInstance.getProject();
|
Project project = frontEndToolInstance.getProject();
|
||||||
ToolManager toolManager = project.getToolManager();
|
ToolManager toolManager = project.getToolManager();
|
||||||
Workspace workspace = toolManager.getActiveWorkspace();
|
Workspace workspace = toolManager.getActiveWorkspace();
|
||||||
ref.set((PluginTool) workspace.runTool(toolTemplate));
|
|
||||||
|
|
||||||
AbstractDockingTest.setErrorGUIEnabled(wasErrorGUIEnabled);
|
AbstractDockingTest.setErrorGUIEnabled(wasErrorGUIEnabled);
|
||||||
|
return (PluginTool) workspace.runTool(toolTemplate);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ToolTemplate getToolTemplate(String toolName) {
|
||||||
|
|
||||||
|
return AbstractGenericTest.runSwing(() -> {
|
||||||
|
ToolTemplate toolTemplate =
|
||||||
|
ToolUtils.readToolTemplate("defaultTools/" + toolName + ".tool");
|
||||||
|
if (toolTemplate == null) {
|
||||||
|
Msg.debug(TestEnv.class, "Unable to find tool: " + toolName);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return toolTemplate;
|
||||||
});
|
});
|
||||||
return ref.get();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ScriptTaskListener runScript(File script) throws PluginException {
|
public ScriptTaskListener runScript(File script) throws PluginException {
|
||||||
|
@ -550,6 +573,7 @@ public class TestEnv {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns GhidraProject associated with this environment
|
* Returns GhidraProject associated with this environment
|
||||||
|
* @return the project
|
||||||
*/
|
*/
|
||||||
public GhidraProject getGhidraProject() {
|
public GhidraProject getGhidraProject() {
|
||||||
return gp;
|
return gp;
|
||||||
|
@ -559,6 +583,7 @@ public class TestEnv {
|
||||||
* A convenience method to close and then reopen the default project created by this TestEnv
|
* A convenience method to close and then reopen the default project created by this TestEnv
|
||||||
* instance. This will not delete the project between opening and closing and will restore
|
* instance. This will not delete the project between opening and closing and will restore
|
||||||
* the project to its previous state.
|
* the project to its previous state.
|
||||||
|
* @throws IOException if any exception occurs while saving and reopening
|
||||||
*/
|
*/
|
||||||
public void closeAndReopenProject() throws IOException {
|
public void closeAndReopenProject() throws IOException {
|
||||||
gp.setDeleteOnClose(false);
|
gp.setDeleteOnClose(false);
|
||||||
|
@ -579,9 +604,6 @@ public class TestEnv {
|
||||||
return gp.getProjectManager();
|
return gp.getProjectManager();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns Project associated with this environment
|
|
||||||
*/
|
|
||||||
public Project getProject() {
|
public Project getProject() {
|
||||||
return gp.getProject();
|
return gp.getProject();
|
||||||
}
|
}
|
||||||
|
@ -646,6 +668,8 @@ public class TestEnv {
|
||||||
* the only reason to use this method vice openProgram().
|
* the only reason to use this method vice openProgram().
|
||||||
*
|
*
|
||||||
* @param programName the name of the program zip file without the ".gzf" extension.
|
* @param programName the name of the program zip file without the ".gzf" extension.
|
||||||
|
* @return the restored domain file
|
||||||
|
* @throws FileNotFoundException if the program file cannot be found
|
||||||
*/
|
*/
|
||||||
public DomainFile restoreProgram(String programName) throws FileNotFoundException {
|
public DomainFile restoreProgram(String programName) throws FileNotFoundException {
|
||||||
DomainFile df = programManager.addProgramToProject(getProject(), programName);
|
DomainFile df = programManager.addProgramToProject(getProject(), programName);
|
||||||
|
@ -674,12 +698,12 @@ public class TestEnv {
|
||||||
* @param relativePathName This should be a pathname relative to the "test_resources/testdata"
|
* @param relativePathName This should be a pathname relative to the "test_resources/testdata"
|
||||||
* director or relative to the "typeinfo" directory. The name should
|
* director or relative to the "typeinfo" directory. The name should
|
||||||
* include the ".gdt" suffix.
|
* include the ".gdt" suffix.
|
||||||
* @param domainFolder the folder in the test project where the archive should be created.
|
* @param domainFolder the folder in the test project where the archive should be created
|
||||||
* @param monitor monitor for canceling this restore.
|
|
||||||
* @return the domain file that was created in the project
|
* @return the domain file that was created in the project
|
||||||
|
* @throws Exception if an exception occurs
|
||||||
*/
|
*/
|
||||||
public DomainFile restoreDataTypeArchive(String relativePathName, DomainFolder domainFolder)
|
public DomainFile restoreDataTypeArchive(String relativePathName, DomainFolder domainFolder)
|
||||||
throws InvalidNameException, IOException, VersionException {
|
throws Exception {
|
||||||
|
|
||||||
File gdtFile;
|
File gdtFile;
|
||||||
try {
|
try {
|
||||||
|
@ -738,10 +762,10 @@ public class TestEnv {
|
||||||
* @param program program object
|
* @param program program object
|
||||||
* @param replace if true any existing cached database with the same name will be replaced
|
* @param replace if true any existing cached database with the same name will be replaced
|
||||||
* @param monitor task monitor
|
* @param monitor task monitor
|
||||||
* @throws DuplicateNameException if already cached
|
* @throws Exception if already cached
|
||||||
*/
|
*/
|
||||||
public void saveToCache(String progName, ProgramDB program, boolean replace,
|
public void saveToCache(String progName, ProgramDB program, boolean replace,
|
||||||
TaskMonitor monitor) throws IOException, DuplicateNameException, CancelledException {
|
TaskMonitor monitor) throws Exception {
|
||||||
|
|
||||||
programManager.saveToCache(progName, program, replace, monitor);
|
programManager.saveToCache(progName, program, replace, monitor);
|
||||||
}
|
}
|
||||||
|
@ -826,7 +850,8 @@ public class TestEnv {
|
||||||
* Launches a tool of the given name using the given domain file.
|
* Launches a tool of the given name using the given domain file.
|
||||||
* <p>
|
* <p>
|
||||||
* Note: the tool returned will have auto save disabled by default.
|
* Note: the tool returned will have auto save disabled by default.
|
||||||
*
|
*
|
||||||
|
* @param toolName the tool's name
|
||||||
* @return the tool that is launched
|
* @return the tool that is launched
|
||||||
*/
|
*/
|
||||||
public PluginTool launchTool(String toolName) {
|
public PluginTool launchTool(String toolName) {
|
||||||
|
|
|
@ -0,0 +1,604 @@
|
||||||
|
/* ###
|
||||||
|
* IP: GHIDRA
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package docking.widgets.tree;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.swing.Icon;
|
||||||
|
import javax.swing.JComponent;
|
||||||
|
|
||||||
|
import org.junit.*;
|
||||||
|
|
||||||
|
import docking.*;
|
||||||
|
import docking.test.AbstractDockingTest;
|
||||||
|
import docking.widgets.filter.*;
|
||||||
|
import ghidra.test.DummyTool;
|
||||||
|
import ghidra.util.StringUtilities;
|
||||||
|
|
||||||
|
public class GTreeFilterTest extends AbstractDockingTest {
|
||||||
|
|
||||||
|
private GTree gTree;
|
||||||
|
private FilterTextField filterField;
|
||||||
|
|
||||||
|
private GTreeRootNode root;
|
||||||
|
|
||||||
|
private DockingWindowManager winMgr;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() throws Exception {
|
||||||
|
root = new TestRootNode();
|
||||||
|
gTree = new GTree(root);
|
||||||
|
|
||||||
|
filterField = (FilterTextField) gTree.getFilterField();
|
||||||
|
|
||||||
|
winMgr = new DockingWindowManager(new DummyTool(), null);
|
||||||
|
winMgr.addComponent(new TestTreeComponentProvider());
|
||||||
|
winMgr.setVisible(true);
|
||||||
|
|
||||||
|
waitForTree();
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void tearDown() throws Exception {
|
||||||
|
winMgr.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testContains() {
|
||||||
|
setFilterOptions(TextFilterStrategy.CONTAINS, false);
|
||||||
|
// no filter text - make sure all 5 nodes are there
|
||||||
|
assertEquals(5, root.getChildCount());
|
||||||
|
|
||||||
|
setFilterText("ABC");
|
||||||
|
assertEquals("Expected 4 of nodes to be in filtered tree!", 4, root.getChildCount());
|
||||||
|
|
||||||
|
checkContainsNode("ABC");
|
||||||
|
checkContainsNode("XABC");
|
||||||
|
checkContainsNode("ABCX");
|
||||||
|
checkContainsNode("XABCX");
|
||||||
|
|
||||||
|
setFilterText("MMM");
|
||||||
|
assertEquals("Expected 4 of nodes to be in filtered tree!", 0, root.getChildCount());
|
||||||
|
|
||||||
|
setFilterText("");
|
||||||
|
assertEquals("Expected all 5 nodes to be back", 5, root.getChildCount());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMultiWordContains() {
|
||||||
|
setFilterOptions(TextFilterStrategy.CONTAINS, false);
|
||||||
|
// no filter text - make sure all 5 nodes are there
|
||||||
|
assertEquals(5, root.getChildCount());
|
||||||
|
|
||||||
|
setFilterOptions(TextFilterStrategy.CONTAINS, false, true, ' ',
|
||||||
|
MultitermEvaluationMode.AND);
|
||||||
|
|
||||||
|
setFilterText("CX AB");
|
||||||
|
assertEquals(2, root.getChildCount());
|
||||||
|
|
||||||
|
setFilterOptions(TextFilterStrategy.CONTAINS, false, true, ' ', MultitermEvaluationMode.OR);
|
||||||
|
|
||||||
|
setFilterText("CX AB");
|
||||||
|
assertEquals(4, root.getChildCount());
|
||||||
|
|
||||||
|
checkContainsNode("ABCX");
|
||||||
|
checkContainsNode("XABCX");
|
||||||
|
|
||||||
|
setFilterText("");
|
||||||
|
assertEquals("Expected all 5 nodes to be back", 5, root.getChildCount());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMultiWordContainsDelimiters() {
|
||||||
|
|
||||||
|
setFilterOptions(TextFilterStrategy.CONTAINS, false);
|
||||||
|
// no filter text - make sure all 5 nodes are there
|
||||||
|
assertEquals(5, root.getChildCount());
|
||||||
|
|
||||||
|
for (char delim : FilterOptions.VALID_MULTITERM_DELIMITERS.toCharArray()) {
|
||||||
|
setFilterOptions(TextFilterStrategy.CONTAINS, false, true, delim,
|
||||||
|
MultitermEvaluationMode.AND);
|
||||||
|
|
||||||
|
setFilterText("CX" + delim + "AB");
|
||||||
|
assertEquals(2, root.getChildCount());
|
||||||
|
|
||||||
|
setFilterOptions(TextFilterStrategy.CONTAINS, false, true, delim,
|
||||||
|
MultitermEvaluationMode.OR);
|
||||||
|
|
||||||
|
setFilterText("CX" + delim + "AB");
|
||||||
|
assertEquals(4, root.getChildCount());
|
||||||
|
|
||||||
|
checkContainsNode("ABCX");
|
||||||
|
checkContainsNode("XABCX");
|
||||||
|
|
||||||
|
setFilterText("");
|
||||||
|
assertEquals("Expected all 5 nodes to be back", 5, root.getChildCount());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMultiWordContainsDelimitersWithLeadingSpaces() {
|
||||||
|
|
||||||
|
setFilterOptions(TextFilterStrategy.CONTAINS, false);
|
||||||
|
// no filter text - make sure all 5 nodes are there
|
||||||
|
assertEquals(5, root.getChildCount());
|
||||||
|
|
||||||
|
String delimPad = StringUtilities.pad("", ' ', 1);
|
||||||
|
|
||||||
|
for (char delim : FilterOptions.VALID_MULTITERM_DELIMITERS.toCharArray()) {
|
||||||
|
setFilterOptions(TextFilterStrategy.CONTAINS, false, true, delim,
|
||||||
|
MultitermEvaluationMode.AND);
|
||||||
|
|
||||||
|
String delimStr = delimPad + delim;
|
||||||
|
|
||||||
|
setFilterText("CX" + delimStr + "AB");
|
||||||
|
assertEquals(2, root.getChildCount());
|
||||||
|
|
||||||
|
setFilterOptions(TextFilterStrategy.CONTAINS, false, true, delim,
|
||||||
|
MultitermEvaluationMode.OR);
|
||||||
|
|
||||||
|
setFilterText("CX" + delimStr + "AB");
|
||||||
|
assertEquals(4, root.getChildCount());
|
||||||
|
|
||||||
|
checkContainsNode("ABCX");
|
||||||
|
checkContainsNode("XABCX");
|
||||||
|
|
||||||
|
setFilterText("");
|
||||||
|
assertEquals("Expected all 5 nodes to be back", 5, root.getChildCount());
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMultiWordContainsDelimitersWithTrailingSpaces() {
|
||||||
|
|
||||||
|
setFilterOptions(TextFilterStrategy.CONTAINS, false);
|
||||||
|
// no filter text - make sure all 5 nodes are there
|
||||||
|
assertEquals(5, root.getChildCount());
|
||||||
|
|
||||||
|
String delimPad = StringUtilities.pad("", ' ', 1);
|
||||||
|
|
||||||
|
for (char delim : FilterOptions.VALID_MULTITERM_DELIMITERS.toCharArray()) {
|
||||||
|
setFilterOptions(TextFilterStrategy.CONTAINS, false, true, delim,
|
||||||
|
MultitermEvaluationMode.AND);
|
||||||
|
|
||||||
|
String delimStr = delim + delimPad;
|
||||||
|
|
||||||
|
setFilterText("CX" + delimStr + "AB");
|
||||||
|
assertEquals(2, root.getChildCount());
|
||||||
|
|
||||||
|
setFilterOptions(TextFilterStrategy.CONTAINS, false, true, delim,
|
||||||
|
MultitermEvaluationMode.OR);
|
||||||
|
|
||||||
|
setFilterText("CX" + delimStr + "AB");
|
||||||
|
assertEquals(4, root.getChildCount());
|
||||||
|
|
||||||
|
checkContainsNode("ABCX");
|
||||||
|
checkContainsNode("XABCX");
|
||||||
|
|
||||||
|
setFilterText("");
|
||||||
|
assertEquals("Expected all 5 nodes to be back", 5, root.getChildCount());
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMultiWordContainsDelimitersWithBoundingSpaces() {
|
||||||
|
|
||||||
|
setFilterOptions(TextFilterStrategy.CONTAINS, false);
|
||||||
|
// no filter text - make sure all 5 nodes are there
|
||||||
|
assertEquals(5, root.getChildCount());
|
||||||
|
|
||||||
|
String delimPad = StringUtilities.pad("", ' ', 1);
|
||||||
|
|
||||||
|
for (char delim : FilterOptions.VALID_MULTITERM_DELIMITERS.toCharArray()) {
|
||||||
|
setFilterOptions(TextFilterStrategy.CONTAINS, false, true, delim,
|
||||||
|
MultitermEvaluationMode.AND);
|
||||||
|
|
||||||
|
String delimStr = delimPad + delim + delimPad;
|
||||||
|
|
||||||
|
setFilterText("CX" + delimStr + "AB");
|
||||||
|
assertEquals(2, root.getChildCount());
|
||||||
|
|
||||||
|
setFilterOptions(TextFilterStrategy.CONTAINS, false, true, delim,
|
||||||
|
MultitermEvaluationMode.OR);
|
||||||
|
|
||||||
|
setFilterText("CX" + delimStr + "AB");
|
||||||
|
assertEquals(4, root.getChildCount());
|
||||||
|
|
||||||
|
checkContainsNode("ABCX");
|
||||||
|
checkContainsNode("XABCX");
|
||||||
|
|
||||||
|
setFilterText("");
|
||||||
|
assertEquals("Expected all 5 nodes to be back", 5, root.getChildCount());
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testInvertedContains() {
|
||||||
|
setFilterOptions(TextFilterStrategy.CONTAINS, true);
|
||||||
|
|
||||||
|
assertEquals(5, root.getChildCount());
|
||||||
|
|
||||||
|
setFilterText("ABC");
|
||||||
|
assertEquals(1, root.getChildCount());
|
||||||
|
|
||||||
|
checkDoesNotContainsNode("ABC");
|
||||||
|
checkDoesNotContainsNode("XABC");
|
||||||
|
checkDoesNotContainsNode("ABCX");
|
||||||
|
checkDoesNotContainsNode("XABCX");
|
||||||
|
|
||||||
|
setFilterText("MMM");
|
||||||
|
assertEquals(5, root.getChildCount());
|
||||||
|
|
||||||
|
setFilterText("");
|
||||||
|
assertEquals("Expected all 5 nodes to be back", 5, root.getChildCount());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testInvertedMultiWordContains() {
|
||||||
|
setFilterOptions(TextFilterStrategy.CONTAINS, true, true, ' ', MultitermEvaluationMode.AND);
|
||||||
|
// no filter text - make sure all 5 nodes are there
|
||||||
|
assertEquals(5, root.getChildCount());
|
||||||
|
|
||||||
|
setFilterText("CX AB");
|
||||||
|
|
||||||
|
checkDoesNotContainsNode("ABCX");
|
||||||
|
checkDoesNotContainsNode("XABCX");
|
||||||
|
assertEquals(3, root.getChildCount());
|
||||||
|
|
||||||
|
setFilterOptions(TextFilterStrategy.CONTAINS, true, true, ' ', MultitermEvaluationMode.OR);
|
||||||
|
setFilterText("");
|
||||||
|
// no filter text - make sure all 5 nodes are there
|
||||||
|
assertEquals(5, root.getChildCount());
|
||||||
|
|
||||||
|
setFilterText("CX AB");
|
||||||
|
|
||||||
|
checkDoesNotContainsNode("ABCX");
|
||||||
|
checkDoesNotContainsNode("XABCX");
|
||||||
|
assertEquals(1, root.getChildCount());
|
||||||
|
|
||||||
|
setFilterText("");
|
||||||
|
assertEquals("Expected all 5 nodes to be back", 5, root.getChildCount());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testStartsWith() {
|
||||||
|
setFilterOptions(TextFilterStrategy.STARTS_WITH, false);
|
||||||
|
// no filter text - make sure all 5 nodes are there
|
||||||
|
assertEquals(5, root.getChildCount());
|
||||||
|
|
||||||
|
setFilterText("ABC");
|
||||||
|
checkContainsNode("ABC");
|
||||||
|
checkContainsNode("ABCX");
|
||||||
|
assertEquals(2, root.getChildCount());
|
||||||
|
|
||||||
|
setFilterText("MMM");
|
||||||
|
assertEquals(0, root.getChildCount());
|
||||||
|
|
||||||
|
setFilterText("");
|
||||||
|
assertEquals("Expected all 5 nodes to be back", 5, root.getChildCount());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testInvertedStartsWith() {
|
||||||
|
setFilterOptions(TextFilterStrategy.STARTS_WITH, true);
|
||||||
|
// no filter text - make sure all 5 nodes are there
|
||||||
|
assertEquals(5, root.getChildCount());
|
||||||
|
|
||||||
|
setFilterText("ABC");
|
||||||
|
checkDoesNotContainsNode("ABC");
|
||||||
|
checkDoesNotContainsNode("ABCX");
|
||||||
|
assertEquals(3, root.getChildCount());
|
||||||
|
|
||||||
|
setFilterText("MMM");
|
||||||
|
assertEquals(5, root.getChildCount());
|
||||||
|
|
||||||
|
setFilterText("");
|
||||||
|
assertEquals("Expected all 5 nodes to be back", 5, root.getChildCount());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testExactMatch() {
|
||||||
|
setFilterOptions(TextFilterStrategy.MATCHES_EXACTLY, false);
|
||||||
|
// no filter text - make sure all 5 nodes are there
|
||||||
|
assertEquals(5, root.getChildCount());
|
||||||
|
|
||||||
|
setFilterText("ABC");
|
||||||
|
checkContainsNode("ABC");
|
||||||
|
assertEquals(1, root.getChildCount());
|
||||||
|
|
||||||
|
setFilterText("MMM");
|
||||||
|
assertEquals(0, root.getChildCount());
|
||||||
|
|
||||||
|
setFilterText("");
|
||||||
|
assertEquals("Expected all 5 nodes to be back", 5, root.getChildCount());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testInvertedExactMatch() {
|
||||||
|
setFilterOptions(TextFilterStrategy.MATCHES_EXACTLY, true);
|
||||||
|
// no filter text - make sure all 5 nodes are there
|
||||||
|
assertEquals(5, root.getChildCount());
|
||||||
|
|
||||||
|
setFilterText("ABC");
|
||||||
|
checkDoesNotContainsNode("ABC");
|
||||||
|
assertEquals(4, root.getChildCount());
|
||||||
|
|
||||||
|
setFilterText("MMM");
|
||||||
|
assertEquals(5, root.getChildCount());
|
||||||
|
|
||||||
|
setFilterText("");
|
||||||
|
assertEquals("Expected all 5 nodes to be back", 5, root.getChildCount());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRegExMatch() {
|
||||||
|
setFilterOptions(TextFilterStrategy.REGULAR_EXPRESSION, false);
|
||||||
|
// no filter text - make sure all 5 nodes are there
|
||||||
|
assertEquals(5, root.getChildCount());
|
||||||
|
|
||||||
|
setFilterText("^ABC$");
|
||||||
|
checkContainsNode("ABC");
|
||||||
|
assertEquals("Expected 1 node match exacly match ABC!", 1, root.getChildCount());
|
||||||
|
|
||||||
|
setFilterText("ABC");
|
||||||
|
checkContainsNode("ABC");
|
||||||
|
checkContainsNode("XABC");
|
||||||
|
checkContainsNode("ABCX");
|
||||||
|
checkContainsNode("XABCX");
|
||||||
|
assertEquals("Expected 4 of nodes that contain the text ABC!", 4, root.getChildCount());
|
||||||
|
|
||||||
|
setFilterText("XA.{0,2}X");
|
||||||
|
checkContainsNode("XABCX");
|
||||||
|
assertEquals(1, root.getChildCount());
|
||||||
|
|
||||||
|
setFilterText("X{0,1}A.{0,2}X");
|
||||||
|
checkContainsNode("XABCX");
|
||||||
|
checkContainsNode("ABCX");
|
||||||
|
assertEquals(2, root.getChildCount());
|
||||||
|
|
||||||
|
setFilterText("");
|
||||||
|
assertEquals("Expected all 5 nodes to be back", 5, root.getChildCount());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testInvertedRegExMatch() {
|
||||||
|
setFilterOptions(TextFilterStrategy.REGULAR_EXPRESSION, true);
|
||||||
|
// no filter text - make sure all 5 nodes are there
|
||||||
|
assertEquals(5, root.getChildCount());
|
||||||
|
|
||||||
|
setFilterText("^ABC$");
|
||||||
|
checkDoesNotContainsNode("ABC");
|
||||||
|
assertEquals(4, root.getChildCount());
|
||||||
|
|
||||||
|
setFilterText("ABC");
|
||||||
|
checkDoesNotContainsNode("ABC");
|
||||||
|
checkDoesNotContainsNode("XABC");
|
||||||
|
checkDoesNotContainsNode("ABCX");
|
||||||
|
checkDoesNotContainsNode("XABCX");
|
||||||
|
assertEquals(1, root.getChildCount());
|
||||||
|
|
||||||
|
setFilterText("XA.{0,2}X");
|
||||||
|
checkDoesNotContainsNode("XABCX");
|
||||||
|
assertEquals(4, root.getChildCount());
|
||||||
|
|
||||||
|
setFilterText("X{0,1}A.{0,2}X");
|
||||||
|
checkDoesNotContainsNode("XABCX");
|
||||||
|
checkDoesNotContainsNode("ABCX");
|
||||||
|
assertEquals(3, root.getChildCount());
|
||||||
|
|
||||||
|
setFilterText("");
|
||||||
|
assertEquals("Expected all 5 nodes to be back", 5, root.getChildCount());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSwitchFilterTypes() {
|
||||||
|
setFilterOptions(TextFilterStrategy.STARTS_WITH, false);
|
||||||
|
setFilterText("ABC");
|
||||||
|
checkContainsNode("ABC");
|
||||||
|
checkContainsNode("ABCX");
|
||||||
|
assertEquals(2, root.getChildCount());
|
||||||
|
|
||||||
|
setFilterOptions(TextFilterStrategy.MATCHES_EXACTLY, false);
|
||||||
|
checkContainsNode("ABC");
|
||||||
|
assertEquals(1, root.getChildCount());
|
||||||
|
|
||||||
|
setFilterOptions(TextFilterStrategy.CONTAINS, false);
|
||||||
|
assertEquals("Expected 4 of nodes to be in filtered tree!", 4, root.getChildCount());
|
||||||
|
checkContainsNode("ABC");
|
||||||
|
checkContainsNode("XABC");
|
||||||
|
checkContainsNode("ABCX");
|
||||||
|
checkContainsNode("XABCX");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSavingSelectedFilterType() {
|
||||||
|
setFilterOptions(TextFilterStrategy.MATCHES_EXACTLY, false);
|
||||||
|
setFilterText("ABC");
|
||||||
|
checkContainsNode("ABC");
|
||||||
|
assertEquals(1, root.getChildCount());
|
||||||
|
|
||||||
|
Object originalValue = getInstanceField("uniquePreferenceKey", gTree);
|
||||||
|
setInstanceField("preferenceKey", gTree.getFilterProvider(), "XYZ");
|
||||||
|
setFilterOptions(TextFilterStrategy.STARTS_WITH, false);
|
||||||
|
checkContainsNode("ABC");
|
||||||
|
checkContainsNode("ABCX");
|
||||||
|
assertEquals(2, root.getChildCount());
|
||||||
|
|
||||||
|
setInstanceField("preferenceKey", gTree.getFilterProvider(), originalValue);
|
||||||
|
setInstanceField("optionsSet", gTree.getFilterProvider(), false);
|
||||||
|
restorePreferences();
|
||||||
|
checkContainsNode("ABC");
|
||||||
|
assertEquals(1, root.getChildCount());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void restorePreferences() {
|
||||||
|
runSwing(() -> {
|
||||||
|
GTreeFilterProvider filterProvider = gTree.getFilterProvider();
|
||||||
|
String key = (String) getInstanceField("uniquePreferenceKey", gTree);
|
||||||
|
Class<?>[] classes = new Class[] { DockingWindowManager.class, String.class };
|
||||||
|
Object[] objs = new Object[] { winMgr, key };
|
||||||
|
invokeInstanceMethod("loadFilterPreference", filterProvider, classes, objs);
|
||||||
|
});
|
||||||
|
waitForTree();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkContainsNode(String string) {
|
||||||
|
List<GTreeNode> children = root.getChildren();
|
||||||
|
for (GTreeNode gTreeNode : children) {
|
||||||
|
if (gTreeNode.getName().equals(string)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Assert.fail("Expected node " + string + " to be included in filter, but was not found!");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkDoesNotContainsNode(String string) {
|
||||||
|
List<GTreeNode> children = root.getChildren();
|
||||||
|
for (GTreeNode gTreeNode : children) {
|
||||||
|
if (gTreeNode.getName().equals(string)) {
|
||||||
|
Assert.fail("Expected node " + string +
|
||||||
|
" to be NOT be included in filter, but was not found!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setFilterText(final String text) {
|
||||||
|
runSwing(() -> {
|
||||||
|
filterField.setText(text);
|
||||||
|
});
|
||||||
|
waitForTree();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setFilterOptions(final TextFilterStrategy filterStrategy, final boolean inverted) {
|
||||||
|
|
||||||
|
runSwing(() -> {
|
||||||
|
FilterOptions filterOptions = new FilterOptions(filterStrategy, false, false, inverted);
|
||||||
|
((DefaultGTreeFilterProvider) gTree.getFilterProvider()).setFilterOptions(
|
||||||
|
filterOptions);
|
||||||
|
});
|
||||||
|
waitForTree();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setFilterOptions(TextFilterStrategy filterStrategy, boolean inverted,
|
||||||
|
boolean multiTerm, char splitCharacter, MultitermEvaluationMode evalMode) {
|
||||||
|
runSwing(() -> {
|
||||||
|
FilterOptions filterOptions = new FilterOptions(filterStrategy, false, false, inverted,
|
||||||
|
multiTerm, splitCharacter, evalMode);
|
||||||
|
((DefaultGTreeFilterProvider) gTree.getFilterProvider()).setFilterOptions(
|
||||||
|
filterOptions);
|
||||||
|
});
|
||||||
|
waitForTree();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void waitForTree() {
|
||||||
|
waitForTree(gTree);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TestRootNode extends AbstractGTreeRootNode {
|
||||||
|
|
||||||
|
TestRootNode() {
|
||||||
|
List<GTreeNode> children = new ArrayList<>();
|
||||||
|
children.add(new LeafNode("XYZ"));
|
||||||
|
children.add(new LeafNode("ABC"));
|
||||||
|
children.add(new LeafNode("ABCX"));
|
||||||
|
children.add(new LeafNode("XABC"));
|
||||||
|
children.add(new LeafNode("XABCX"));
|
||||||
|
setChildren(children);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Icon getIcon(boolean expanded) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "Root";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getToolTip() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isLeaf() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A basic leaf node
|
||||||
|
*/
|
||||||
|
private class LeafNode extends AbstractGTreeNode {
|
||||||
|
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
LeafNode(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Icon getIcon(boolean expanded) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getToolTip() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isLeaf() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class TestTreeComponentProvider extends ComponentProvider {
|
||||||
|
|
||||||
|
public TestTreeComponentProvider() {
|
||||||
|
super(null, "Test", "Test");
|
||||||
|
setDefaultWindowPosition(WindowPosition.STACK);
|
||||||
|
setTabText("Test");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JComponent getComponent() {
|
||||||
|
return gTree;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getTitle() {
|
||||||
|
return "Test Tree";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -28,6 +28,7 @@ import org.junit.*;
|
||||||
|
|
||||||
import docking.action.DockingActionIf;
|
import docking.action.DockingActionIf;
|
||||||
import docking.tool.ToolConstants;
|
import docking.tool.ToolConstants;
|
||||||
|
import docking.util.image.ToolIconURL;
|
||||||
import docking.widgets.OptionDialog;
|
import docking.widgets.OptionDialog;
|
||||||
import docking.widgets.filechooser.GhidraFileChooser;
|
import docking.widgets.filechooser.GhidraFileChooser;
|
||||||
import generic.test.AbstractGTest;
|
import generic.test.AbstractGTest;
|
||||||
|
@ -46,7 +47,6 @@ import ghidra.framework.model.ToolTemplate;
|
||||||
import ghidra.framework.plugintool.PluginTool;
|
import ghidra.framework.plugintool.PluginTool;
|
||||||
import ghidra.framework.plugintool.util.PluginException;
|
import ghidra.framework.plugintool.util.PluginException;
|
||||||
import ghidra.framework.preferences.Preferences;
|
import ghidra.framework.preferences.Preferences;
|
||||||
import ghidra.framework.project.tool.ToolIconURL;
|
|
||||||
import ghidra.test.AbstractGhidraHeadedIntegrationTest;
|
import ghidra.test.AbstractGhidraHeadedIntegrationTest;
|
||||||
import ghidra.test.TestEnv;
|
import ghidra.test.TestEnv;
|
||||||
import ghidra.util.exception.AssertException;
|
import ghidra.util.exception.AssertException;
|
||||||
|
|
|
@ -23,12 +23,12 @@ import org.junit.*;
|
||||||
|
|
||||||
import docking.action.DockingActionIf;
|
import docking.action.DockingActionIf;
|
||||||
import docking.action.ToggleDockingAction;
|
import docking.action.ToggleDockingAction;
|
||||||
|
import docking.util.image.ToolIconURL;
|
||||||
import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
|
import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
|
||||||
import ghidra.app.plugin.core.progmgr.ProgramManagerPlugin;
|
import ghidra.app.plugin.core.progmgr.ProgramManagerPlugin;
|
||||||
import ghidra.framework.main.FrontEndPlugin;
|
import ghidra.framework.main.FrontEndPlugin;
|
||||||
import ghidra.framework.plugintool.PluginTool;
|
import ghidra.framework.plugintool.PluginTool;
|
||||||
import ghidra.framework.project.tool.GhidraTool;
|
import ghidra.framework.project.tool.GhidraTool;
|
||||||
import ghidra.framework.project.tool.ToolIconURL;
|
|
||||||
import ghidra.program.database.ProgramDB;
|
import ghidra.program.database.ProgramDB;
|
||||||
import ghidra.test.ClassicSampleX86ProgramBuilder;
|
import ghidra.test.ClassicSampleX86ProgramBuilder;
|
||||||
import ghidra.test.TestEnv;
|
import ghidra.test.TestEnv;
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package ghidra.framework.project.tool;
|
package docking.util.image;
|
||||||
|
|
||||||
import generic.Images;
|
import generic.Images;
|
||||||
|
|
|
@ -0,0 +1,309 @@
|
||||||
|
/* ###
|
||||||
|
* IP: GHIDRA
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package docking.widgets.tree;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
|
||||||
|
import org.junit.*;
|
||||||
|
|
||||||
|
import docking.test.AbstractDockingTest;
|
||||||
|
import docking.widgets.OptionDialog;
|
||||||
|
import ghidra.util.exception.CancelledException;
|
||||||
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
|
public class GTreeSlowLoadingNode1Test extends AbstractDockingTest {
|
||||||
|
|
||||||
|
private static final int MAX_DEPTH = 4;
|
||||||
|
private static final int MIN_CHILD_COUNT = 3;
|
||||||
|
private static final int MAX_CHILD_COUNT = 40;
|
||||||
|
|
||||||
|
private volatile boolean pauseChildLoading = false;
|
||||||
|
|
||||||
|
private JFrame frame;
|
||||||
|
private GTree gTree;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() throws Exception {
|
||||||
|
|
||||||
|
gTree = new GTree(new EmptyRootNode());
|
||||||
|
|
||||||
|
frame = new JFrame("GTree Test");
|
||||||
|
frame.getContentPane().add(gTree);
|
||||||
|
frame.setSize(400, 400);
|
||||||
|
frame.setVisible(true);
|
||||||
|
|
||||||
|
waitForTree();
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void tearDown() throws Exception {
|
||||||
|
gTree.dispose();
|
||||||
|
frame.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBasicLoading() {
|
||||||
|
gTree.setRootNode(new TestRootNode(100));
|
||||||
|
|
||||||
|
waitForTree();
|
||||||
|
|
||||||
|
// make sure we have some children
|
||||||
|
GTreeRootNode rootNode = gTree.getRootNode();
|
||||||
|
GTreeNode nonLeaf1 = rootNode.getChild(0);
|
||||||
|
assertNotNull(nonLeaf1);
|
||||||
|
GTreeNode leaf1 = rootNode.getChild(1);
|
||||||
|
assertNotNull(leaf1);
|
||||||
|
GTreeNode nonLeaf2 = rootNode.getChild(2);
|
||||||
|
assertNotNull(nonLeaf2);
|
||||||
|
|
||||||
|
int childCount = nonLeaf1.getChildCount();
|
||||||
|
assertTrue("Did not find children for: " + nonLeaf1, childCount > 1);
|
||||||
|
assertEquals("An expected leaf node has some children", 0, leaf1.getChildCount());
|
||||||
|
childCount = nonLeaf2.getChildCount();
|
||||||
|
assertTrue("Did not find children for: " + nonLeaf2, childCount > 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSlowNodeShowsProgressBar() {
|
||||||
|
gTree.setRootNode(new TestRootNode(5000));
|
||||||
|
|
||||||
|
waitForTree();
|
||||||
|
|
||||||
|
GTreeRootNode rootNode = gTree.getRootNode();
|
||||||
|
GTreeNode nonLeaf1 = rootNode.getChild(0);
|
||||||
|
assertNotNull(nonLeaf1);
|
||||||
|
|
||||||
|
gTree.expandPath(nonLeaf1);
|
||||||
|
|
||||||
|
assertProgressPanel(true);
|
||||||
|
|
||||||
|
assertTrue(nonLeaf1.isInProgress());
|
||||||
|
|
||||||
|
// Press the cancel button on the progress monitor
|
||||||
|
pressProgressPanelCancelButton();
|
||||||
|
|
||||||
|
waitForTree();
|
||||||
|
|
||||||
|
// Verify no progress component
|
||||||
|
assertProgressPanel(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Private Methods
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
private void waitForTree() {
|
||||||
|
waitForTree(gTree);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertProgressPanel(boolean isShowing) {
|
||||||
|
JComponent panel = (JComponent) getInstanceField("progressPanel", gTree);
|
||||||
|
if (!isShowing) {
|
||||||
|
assertNull("Panel is showing when it should not be", panel);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (panel == null || !panel.isShowing()) {
|
||||||
|
int maxWaits = 50;// wait a couple seconds, as the progress bar may be delayed
|
||||||
|
int tryCount = 0;
|
||||||
|
while (tryCount < maxWaits) {
|
||||||
|
panel = (JComponent) getInstanceField("progressPanel", gTree);
|
||||||
|
if (panel != null && panel.isShowing()) {
|
||||||
|
return;// finally showing!
|
||||||
|
}
|
||||||
|
tryCount++;
|
||||||
|
try {
|
||||||
|
Thread.sleep(50);
|
||||||
|
}
|
||||||
|
catch (Exception e) {
|
||||||
|
// who cares?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.fail("Progress panel is not showing as expected");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void pressProgressPanelCancelButton() {
|
||||||
|
Object taskMonitorComponent = getInstanceField("monitor", gTree);
|
||||||
|
final JButton cancelButton =
|
||||||
|
(JButton) getInstanceField("cancelButton", taskMonitorComponent);
|
||||||
|
runSwing(() -> cancelButton.doClick(), false);
|
||||||
|
|
||||||
|
OptionDialog confirDialog = waitForDialogComponent(frame, OptionDialog.class, 2000);
|
||||||
|
final JButton confirmCancelButton = findButtonByText(confirDialog, "Yes");
|
||||||
|
runSwing(() -> confirmCancelButton.doClick());
|
||||||
|
}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Inner Classes
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
private class EmptyRootNode extends AbstractGTreeRootNode {
|
||||||
|
|
||||||
|
EmptyRootNode() {
|
||||||
|
setChildren(new ArrayList<GTreeNode>());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Icon getIcon(boolean expanded) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "Empty Test GTree Root Node";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getToolTip() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isLeaf() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TestRootNode extends AbstractGTreeRootNode {
|
||||||
|
|
||||||
|
TestRootNode(int loadDelayMillis) {
|
||||||
|
List<GTreeNode> children = new ArrayList<>();
|
||||||
|
children.add(new TestSlowLoadingNode(loadDelayMillis, 1));
|
||||||
|
children.add(new TestLeafNode());
|
||||||
|
children.add(new TestSlowLoadingNode(loadDelayMillis, 1));
|
||||||
|
setChildren(children);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Icon getIcon(boolean expanded) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "Test GTree Root Node";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getToolTip() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isLeaf() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TestSlowLoadingNode extends GTreeSlowLoadingNode {
|
||||||
|
|
||||||
|
private final long loadDelayMillis;
|
||||||
|
private final int depth;
|
||||||
|
|
||||||
|
TestSlowLoadingNode(long loadDelayMillis, int depth) {
|
||||||
|
this.loadDelayMillis = loadDelayMillis;
|
||||||
|
this.depth = depth;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<GTreeNode> generateChildren(TaskMonitor monitor) throws CancelledException {
|
||||||
|
|
||||||
|
if (depth > MAX_DEPTH) {
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (monitor.isCancelled()) {
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
sleep(loadDelayMillis);
|
||||||
|
|
||||||
|
while (pauseChildLoading) {
|
||||||
|
sleep(100);
|
||||||
|
}
|
||||||
|
|
||||||
|
int childCount = getRandomInt(MIN_CHILD_COUNT, MAX_CHILD_COUNT);
|
||||||
|
List<GTreeNode> children = new ArrayList<>();
|
||||||
|
for (int i = 0; i < childCount; i++) {
|
||||||
|
if (monitor.isCancelled()) {
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
int value = getRandomInt(0, 1);
|
||||||
|
if (value == 0) {
|
||||||
|
children.add(new TestSlowLoadingNode(loadDelayMillis, depth + 1));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
children.add(new TestLeafNode());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return children;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Icon getIcon(boolean expanded) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return getClass().getSimpleName();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getToolTip() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isLeaf() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TestLeafNode extends AbstractGTreeNode {
|
||||||
|
|
||||||
|
private String name = getClass().getSimpleName() + getRandomString();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Icon getIcon(boolean expanded) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getToolTip() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isLeaf() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,255 @@
|
||||||
|
/* ###
|
||||||
|
* IP: GHIDRA
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package docking.widgets.tree;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
|
||||||
|
import org.junit.*;
|
||||||
|
|
||||||
|
import docking.test.AbstractDockingTest;
|
||||||
|
import docking.widgets.filter.FilterTextField;
|
||||||
|
import ghidra.util.exception.CancelledException;
|
||||||
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
|
public class GTreeSlowLoadingNode2Test extends AbstractDockingTest {
|
||||||
|
|
||||||
|
private static final int MAX_DEPTH = 4;
|
||||||
|
private static final int MIN_CHILD_COUNT = 3;
|
||||||
|
private static final int MAX_CHILD_COUNT = 3;
|
||||||
|
|
||||||
|
private volatile boolean pauseChildLoading = false;
|
||||||
|
|
||||||
|
private JFrame frame;
|
||||||
|
private GTree gTree;
|
||||||
|
private FilterTextField filterField;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() throws Exception {
|
||||||
|
|
||||||
|
gTree = new GTree(new EmptyRootNode());
|
||||||
|
filterField = (FilterTextField) gTree.getFilterField();
|
||||||
|
|
||||||
|
frame = new JFrame("GTree Test");
|
||||||
|
frame.getContentPane().add(gTree);
|
||||||
|
frame.setSize(400, 400);
|
||||||
|
frame.setVisible(true);
|
||||||
|
|
||||||
|
waitForTree();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void tearDown() throws Exception {
|
||||||
|
gTree.dispose();
|
||||||
|
frame.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBasicLoading() {
|
||||||
|
gTree.setRootNode(new TestRootNode(0));
|
||||||
|
waitForTree();
|
||||||
|
// make sure we have some children
|
||||||
|
GTreeRootNode rootNode = gTree.getRootNode();
|
||||||
|
List<GTreeNode> allChildren = rootNode.getAllChildren();
|
||||||
|
typeFilterText("Many B1");
|
||||||
|
clearFilterText();
|
||||||
|
List<GTreeNode> allChildren2 = rootNode.getAllChildren();
|
||||||
|
assertEquals("Children were reloaded instead of being reused", allChildren, allChildren2);
|
||||||
|
}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Private Methods
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
private void typeFilterText(String text) {
|
||||||
|
JTextField textField = (JTextField) getInstanceField("textField", filterField);
|
||||||
|
triggerText(textField, text);
|
||||||
|
waitForTree();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setFilterText(final String text) {
|
||||||
|
runSwing(() -> filterField.setText(text));
|
||||||
|
waitForTree();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void clearFilterText() {
|
||||||
|
setFilterText("");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void waitForTree() {
|
||||||
|
waitForTree(gTree);
|
||||||
|
}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Inner Classes
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
private class EmptyRootNode extends AbstractGTreeRootNode {
|
||||||
|
|
||||||
|
EmptyRootNode() {
|
||||||
|
setChildren(new ArrayList<GTreeNode>());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Icon getIcon(boolean expanded) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "Empty Test GTree Root Node";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getToolTip() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isLeaf() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TestRootNode extends TestSlowLoadingNode implements GTreeRootNode {
|
||||||
|
private GTree tree;
|
||||||
|
|
||||||
|
TestRootNode(int loadDelayMillis) {
|
||||||
|
super(loadDelayMillis, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setGTree(GTree tree) {
|
||||||
|
this.tree = tree;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GTree getGTree() {
|
||||||
|
return tree;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Icon getIcon(boolean expanded) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "Test GTree Root Node";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getToolTip() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isLeaf() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TestSlowLoadingNode extends GTreeSlowLoadingNode {
|
||||||
|
|
||||||
|
private final long loadDelayMillis;
|
||||||
|
private final int depth;
|
||||||
|
|
||||||
|
TestSlowLoadingNode(long loadDelayMillis, int depth) {
|
||||||
|
this.loadDelayMillis = loadDelayMillis;
|
||||||
|
this.depth = depth;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<GTreeNode> generateChildren(TaskMonitor monitor) throws CancelledException {
|
||||||
|
|
||||||
|
if (depth > MAX_DEPTH) {
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (monitor.isCancelled()) {
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
sleep(loadDelayMillis);
|
||||||
|
|
||||||
|
while (pauseChildLoading) {
|
||||||
|
sleep(100);
|
||||||
|
}
|
||||||
|
|
||||||
|
int childCount = getRandomInt(MIN_CHILD_COUNT, MAX_CHILD_COUNT);
|
||||||
|
List<GTreeNode> children = new ArrayList<>();
|
||||||
|
for (int i = 0; i < childCount; i++) {
|
||||||
|
if (monitor.isCancelled()) {
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
children.add(new TestSlowLoadingNode(0, depth + 1));
|
||||||
|
}
|
||||||
|
return children;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Icon getIcon(boolean expanded) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return getClass().getSimpleName();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getToolTip() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isLeaf() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TestLeafNode extends AbstractGTreeNode {
|
||||||
|
|
||||||
|
private String name = getClass().getSimpleName() + getRandomString();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Icon getIcon(boolean expanded) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getToolTip() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isLeaf() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,309 @@
|
||||||
|
/* ###
|
||||||
|
* IP: GHIDRA
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package docking.widgets.tree;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
|
||||||
|
import org.junit.*;
|
||||||
|
|
||||||
|
import docking.test.AbstractDockingTest;
|
||||||
|
import docking.widgets.OptionDialog;
|
||||||
|
import ghidra.util.exception.CancelledException;
|
||||||
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
|
public class GTreeSlowLoadingNodeTest extends AbstractDockingTest {
|
||||||
|
|
||||||
|
private static final int MAX_DEPTH = 4;
|
||||||
|
private static final int MIN_CHILD_COUNT = 3;
|
||||||
|
private static final int MAX_CHILD_COUNT = 40;
|
||||||
|
|
||||||
|
private volatile boolean pauseChildLoading = false;
|
||||||
|
|
||||||
|
private JFrame frame;
|
||||||
|
private GTree gTree;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() throws Exception {
|
||||||
|
|
||||||
|
gTree = new GTree(new EmptyRootNode());
|
||||||
|
|
||||||
|
frame = new JFrame("GTree Test");
|
||||||
|
frame.getContentPane().add(gTree);
|
||||||
|
frame.setSize(400, 400);
|
||||||
|
frame.setVisible(true);
|
||||||
|
|
||||||
|
waitForTree();
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void tearDown() throws Exception {
|
||||||
|
gTree.dispose();
|
||||||
|
frame.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBasicLoading() {
|
||||||
|
gTree.setRootNode(new TestRootNode(100));
|
||||||
|
|
||||||
|
waitForTree();
|
||||||
|
|
||||||
|
// make sure we have some children
|
||||||
|
GTreeRootNode rootNode = gTree.getRootNode();
|
||||||
|
GTreeNode nonLeaf1 = rootNode.getChild(0);
|
||||||
|
assertNotNull(nonLeaf1);
|
||||||
|
GTreeNode leaf1 = rootNode.getChild(1);
|
||||||
|
assertNotNull(leaf1);
|
||||||
|
GTreeNode nonLeaf2 = rootNode.getChild(2);
|
||||||
|
assertNotNull(nonLeaf2);
|
||||||
|
|
||||||
|
int childCount = nonLeaf1.getChildCount();
|
||||||
|
assertTrue("Did not find children for: " + nonLeaf1, childCount > 1);
|
||||||
|
assertEquals("An expected leaf node has some children", 0, leaf1.getChildCount());
|
||||||
|
childCount = nonLeaf2.getChildCount();
|
||||||
|
assertTrue("Did not find children for: " + nonLeaf2, childCount > 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSlowNodeShowsProgressBar() {
|
||||||
|
gTree.setRootNode(new TestRootNode(5000));
|
||||||
|
|
||||||
|
waitForTree();
|
||||||
|
|
||||||
|
GTreeRootNode rootNode = gTree.getRootNode();
|
||||||
|
GTreeNode nonLeaf1 = rootNode.getChild(0);
|
||||||
|
assertNotNull(nonLeaf1);
|
||||||
|
|
||||||
|
gTree.expandPath(nonLeaf1);
|
||||||
|
|
||||||
|
assertProgressPanel(true);
|
||||||
|
|
||||||
|
assertTrue(nonLeaf1.isInProgress());
|
||||||
|
|
||||||
|
// Press the cancel button on the progress monitor
|
||||||
|
pressProgressPanelCancelButton();
|
||||||
|
|
||||||
|
waitForTree();
|
||||||
|
|
||||||
|
// Verify no progress component
|
||||||
|
assertProgressPanel(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Private Methods
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
private void waitForTree() {
|
||||||
|
waitForTree(gTree);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertProgressPanel(boolean isShowing) {
|
||||||
|
JComponent panel = (JComponent) getInstanceField("progressPanel", gTree);
|
||||||
|
if (!isShowing) {
|
||||||
|
assertNull("Panel is showing when it should not be", panel);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (panel == null || !panel.isShowing()) {
|
||||||
|
int maxWaits = 50;// wait a couple seconds, as the progress bar may be delayed
|
||||||
|
int tryCount = 0;
|
||||||
|
while (tryCount < maxWaits) {
|
||||||
|
panel = (JComponent) getInstanceField("progressPanel", gTree);
|
||||||
|
if (panel != null && panel.isShowing()) {
|
||||||
|
return;// finally showing!
|
||||||
|
}
|
||||||
|
tryCount++;
|
||||||
|
try {
|
||||||
|
Thread.sleep(50);
|
||||||
|
}
|
||||||
|
catch (Exception e) {
|
||||||
|
// who cares?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.fail("Progress panel is not showing as expected");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void pressProgressPanelCancelButton() {
|
||||||
|
Object taskMonitorComponent = getInstanceField("monitor", gTree);
|
||||||
|
final JButton cancelButton =
|
||||||
|
(JButton) getInstanceField("cancelButton", taskMonitorComponent);
|
||||||
|
runSwing(() -> cancelButton.doClick(), false);
|
||||||
|
|
||||||
|
OptionDialog confirDialog = waitForDialogComponent(OptionDialog.class);
|
||||||
|
final JButton confirmCancelButton = findButtonByText(confirDialog, "Yes");
|
||||||
|
runSwing(() -> confirmCancelButton.doClick());
|
||||||
|
}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Inner Classes
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
private class EmptyRootNode extends AbstractGTreeRootNode {
|
||||||
|
|
||||||
|
EmptyRootNode() {
|
||||||
|
setChildren(new ArrayList<GTreeNode>());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Icon getIcon(boolean expanded) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "Empty Test GTree Root Node";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getToolTip() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isLeaf() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TestRootNode extends AbstractGTreeRootNode {
|
||||||
|
|
||||||
|
TestRootNode(int loadDelayMillis) {
|
||||||
|
List<GTreeNode> children = new ArrayList<>();
|
||||||
|
children.add(new TestSlowLoadingNode(loadDelayMillis, 1));
|
||||||
|
children.add(new TestLeafNode());
|
||||||
|
children.add(new TestSlowLoadingNode(loadDelayMillis, 1));
|
||||||
|
setChildren(children);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Icon getIcon(boolean expanded) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "Test GTree Root Node";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getToolTip() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isLeaf() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TestSlowLoadingNode extends GTreeSlowLoadingNode {
|
||||||
|
|
||||||
|
private final long loadDelayMillis;
|
||||||
|
private final int depth;
|
||||||
|
|
||||||
|
TestSlowLoadingNode(long loadDelayMillis, int depth) {
|
||||||
|
this.loadDelayMillis = loadDelayMillis;
|
||||||
|
this.depth = depth;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<GTreeNode> generateChildren(TaskMonitor monitor) throws CancelledException {
|
||||||
|
|
||||||
|
if (depth > MAX_DEPTH) {
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (monitor.isCancelled()) {
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
sleep(loadDelayMillis);
|
||||||
|
|
||||||
|
while (pauseChildLoading) {
|
||||||
|
sleep(100);
|
||||||
|
}
|
||||||
|
|
||||||
|
int childCount = getRandomInt(MIN_CHILD_COUNT, MAX_CHILD_COUNT);
|
||||||
|
List<GTreeNode> children = new ArrayList<>();
|
||||||
|
for (int i = 0; i < childCount; i++) {
|
||||||
|
if (monitor.isCancelled()) {
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
int value = getRandomInt(0, 1);
|
||||||
|
if (value == 0) {
|
||||||
|
children.add(new TestSlowLoadingNode(loadDelayMillis, depth + 1));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
children.add(new TestLeafNode());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return children;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Icon getIcon(boolean expanded) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return getClass().getSimpleName();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getToolTip() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isLeaf() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TestLeafNode extends AbstractGTreeNode {
|
||||||
|
|
||||||
|
private String name = getClass().getSimpleName() + getRandomString();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Icon getIcon(boolean expanded) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getToolTip() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isLeaf() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load diff
|
@ -62,6 +62,7 @@ public abstract class AbstractGenericTest extends AbstractGTest {
|
||||||
private static File debugDirectory;
|
private static File debugDirectory;
|
||||||
|
|
||||||
public static final String TESTDATA_DIRECTORY_NAME = "testdata";
|
public static final String TESTDATA_DIRECTORY_NAME = "testdata";
|
||||||
|
public static final String DEFAULT_TOOL_NAME = "CodeBrowser";
|
||||||
public static final String DEFAULT_TEST_TOOL_NAME = "TestCodeBrowser";
|
public static final String DEFAULT_TEST_TOOL_NAME = "TestCodeBrowser";
|
||||||
|
|
||||||
private static boolean initialized = false;
|
private static boolean initialized = false;
|
||||||
|
|
|
@ -40,6 +40,7 @@ import docking.help.Help;
|
||||||
import docking.help.HelpService;
|
import docking.help.HelpService;
|
||||||
import docking.tool.ToolConstants;
|
import docking.tool.ToolConstants;
|
||||||
import docking.util.AnimationUtils;
|
import docking.util.AnimationUtils;
|
||||||
|
import docking.util.image.ToolIconURL;
|
||||||
import docking.widgets.OptionDialog;
|
import docking.widgets.OptionDialog;
|
||||||
import generic.jar.ResourceFile;
|
import generic.jar.ResourceFile;
|
||||||
import generic.util.WindowUtilities;
|
import generic.util.WindowUtilities;
|
||||||
|
|
|
@ -32,11 +32,11 @@ import docking.dnd.*;
|
||||||
import docking.help.Help;
|
import docking.help.Help;
|
||||||
import docking.help.HelpService;
|
import docking.help.HelpService;
|
||||||
import docking.tool.ToolConstants;
|
import docking.tool.ToolConstants;
|
||||||
|
import docking.util.image.ToolIconURL;
|
||||||
import docking.widgets.EmptyBorderButton;
|
import docking.widgets.EmptyBorderButton;
|
||||||
import ghidra.framework.main.datatree.*;
|
import ghidra.framework.main.datatree.*;
|
||||||
import ghidra.framework.model.*;
|
import ghidra.framework.model.*;
|
||||||
import ghidra.framework.plugintool.PluginTool;
|
import ghidra.framework.plugintool.PluginTool;
|
||||||
import ghidra.framework.project.tool.ToolIconURL;
|
|
||||||
import ghidra.util.*;
|
import ghidra.util.*;
|
||||||
import ghidra.util.bean.GGlassPane;
|
import ghidra.util.bean.GGlassPane;
|
||||||
import ghidra.util.exception.AssertException;
|
import ghidra.util.exception.AssertException;
|
||||||
|
|
|
@ -21,8 +21,8 @@ import java.beans.PropertyVetoException;
|
||||||
import org.jdom.Element;
|
import org.jdom.Element;
|
||||||
|
|
||||||
import docking.DockingTool;
|
import docking.DockingTool;
|
||||||
|
import docking.util.image.ToolIconURL;
|
||||||
import ghidra.framework.plugintool.PluginEvent;
|
import ghidra.framework.plugintool.PluginEvent;
|
||||||
import ghidra.framework.project.tool.ToolIconURL;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
|
|
@ -19,7 +19,7 @@ import javax.swing.ImageIcon;
|
||||||
|
|
||||||
import org.jdom.Element;
|
import org.jdom.Element;
|
||||||
|
|
||||||
import ghidra.framework.project.tool.ToolIconURL;
|
import docking.util.image.ToolIconURL;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configuration of a tool that knows how to create tools.
|
* Configuration of a tool that knows how to create tools.
|
||||||
|
|
|
@ -42,6 +42,7 @@ import docking.help.Help;
|
||||||
import docking.help.HelpService;
|
import docking.help.HelpService;
|
||||||
import docking.tool.ToolConstants;
|
import docking.tool.ToolConstants;
|
||||||
import docking.tool.util.DockingToolConstants;
|
import docking.tool.util.DockingToolConstants;
|
||||||
|
import docking.util.image.ToolIconURL;
|
||||||
import docking.widgets.OptionDialog;
|
import docking.widgets.OptionDialog;
|
||||||
import ghidra.framework.OperatingSystem;
|
import ghidra.framework.OperatingSystem;
|
||||||
import ghidra.framework.Platform;
|
import ghidra.framework.Platform;
|
||||||
|
@ -56,7 +57,6 @@ import ghidra.framework.plugintool.dialog.ManagePluginsDialog;
|
||||||
import ghidra.framework.plugintool.mgr.*;
|
import ghidra.framework.plugintool.mgr.*;
|
||||||
import ghidra.framework.plugintool.util.*;
|
import ghidra.framework.plugintool.util.*;
|
||||||
import ghidra.framework.project.ProjectDataService;
|
import ghidra.framework.project.ProjectDataService;
|
||||||
import ghidra.framework.project.tool.ToolIconURL;
|
|
||||||
import ghidra.util.*;
|
import ghidra.util.*;
|
||||||
import ghidra.util.task.Task;
|
import ghidra.util.task.Task;
|
||||||
import ghidra.util.task.TaskLauncher;
|
import ghidra.util.task.TaskLauncher;
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
/* ###
|
/* ###
|
||||||
* IP: GHIDRA
|
* IP: GHIDRA
|
||||||
* REVIEWED: YES
|
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -16,10 +15,9 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.framework.plugintool.dialog;
|
package ghidra.framework.plugintool.dialog;
|
||||||
|
|
||||||
import ghidra.framework.project.tool.ToolIconURL;
|
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
|
import docking.util.image.ToolIconURL;
|
||||||
import resources.ResourceManager;
|
import resources.ResourceManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -27,6 +27,7 @@ import javax.swing.event.*;
|
||||||
|
|
||||||
import docking.DialogComponentProvider;
|
import docking.DialogComponentProvider;
|
||||||
import docking.options.editor.ButtonPanelFactory;
|
import docking.options.editor.ButtonPanelFactory;
|
||||||
|
import docking.util.image.ToolIconURL;
|
||||||
import docking.widgets.OptionDialog;
|
import docking.widgets.OptionDialog;
|
||||||
import docking.widgets.filechooser.GhidraFileChooser;
|
import docking.widgets.filechooser.GhidraFileChooser;
|
||||||
import docking.widgets.label.GLabel;
|
import docking.widgets.label.GLabel;
|
||||||
|
@ -34,7 +35,6 @@ import ghidra.framework.model.*;
|
||||||
import ghidra.framework.plugintool.PluginTool;
|
import ghidra.framework.plugintool.PluginTool;
|
||||||
import ghidra.framework.preferences.Preferences;
|
import ghidra.framework.preferences.Preferences;
|
||||||
import ghidra.framework.project.tool.GhidraToolTemplate;
|
import ghidra.framework.project.tool.GhidraToolTemplate;
|
||||||
import ghidra.framework.project.tool.ToolIconURL;
|
|
||||||
import ghidra.util.HelpLocation;
|
import ghidra.util.HelpLocation;
|
||||||
import ghidra.util.NamingUtilities;
|
import ghidra.util.NamingUtilities;
|
||||||
import ghidra.util.filechooser.ExtensionFileFilter;
|
import ghidra.util.filechooser.ExtensionFileFilter;
|
||||||
|
|
|
@ -22,8 +22,8 @@ import javax.swing.BorderFactory;
|
||||||
import javax.swing.JList;
|
import javax.swing.JList;
|
||||||
import javax.swing.border.Border;
|
import javax.swing.border.Border;
|
||||||
|
|
||||||
|
import docking.util.image.ToolIconURL;
|
||||||
import docking.widgets.list.GListCellRenderer;
|
import docking.widgets.list.GListCellRenderer;
|
||||||
import ghidra.framework.project.tool.ToolIconURL;
|
|
||||||
|
|
||||||
class ToolIconUrlRenderer extends GListCellRenderer<ToolIconURL> {
|
class ToolIconUrlRenderer extends GListCellRenderer<ToolIconURL> {
|
||||||
private Border emptyBorder = BorderFactory.createEmptyBorder(5, 5, 5, 5);
|
private Border emptyBorder = BorderFactory.createEmptyBorder(5, 5, 5, 5);
|
||||||
|
|
|
@ -21,6 +21,7 @@ import javax.swing.ImageIcon;
|
||||||
|
|
||||||
import org.jdom.Element;
|
import org.jdom.Element;
|
||||||
|
|
||||||
|
import docking.util.image.ToolIconURL;
|
||||||
import ghidra.framework.model.*;
|
import ghidra.framework.model.*;
|
||||||
import ghidra.util.Msg;
|
import ghidra.util.Msg;
|
||||||
import ghidra.util.NumericUtilities;
|
import ghidra.util.NumericUtilities;
|
||||||
|
|
|
@ -30,10 +30,10 @@ import docking.*;
|
||||||
import docking.action.DockingActionIf;
|
import docking.action.DockingActionIf;
|
||||||
import docking.actions.DockingToolActions;
|
import docking.actions.DockingToolActions;
|
||||||
import docking.actions.PopupActionProvider;
|
import docking.actions.PopupActionProvider;
|
||||||
|
import docking.util.image.ToolIconURL;
|
||||||
import ghidra.framework.model.*;
|
import ghidra.framework.model.*;
|
||||||
import ghidra.framework.options.ToolOptions;
|
import ghidra.framework.options.ToolOptions;
|
||||||
import ghidra.framework.plugintool.PluginEvent;
|
import ghidra.framework.plugintool.PluginEvent;
|
||||||
import ghidra.framework.project.tool.ToolIconURL;
|
|
||||||
import ghidra.program.model.listing.Program;
|
import ghidra.program.model.listing.Program;
|
||||||
|
|
||||||
public class DummyTool implements Tool {
|
public class DummyTool implements Tool {
|
||||||
|
@ -361,8 +361,7 @@ public class DummyTool implements Tool {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setMenuGroup(String[] menuPath, String group, String menuSubGroup) {
|
public void setMenuGroup(String[] menuPath, String group, String menuSubGroup) {
|
||||||
// TODO Auto-generated method stub
|
//do nothing
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
|
@ -19,8 +19,8 @@ import javax.swing.ImageIcon;
|
||||||
|
|
||||||
import org.jdom.Element;
|
import org.jdom.Element;
|
||||||
|
|
||||||
|
import docking.util.image.ToolIconURL;
|
||||||
import ghidra.framework.model.*;
|
import ghidra.framework.model.*;
|
||||||
import ghidra.framework.project.tool.ToolIconURL;
|
|
||||||
import ghidra.program.model.listing.Program;
|
import ghidra.program.model.listing.Program;
|
||||||
|
|
||||||
public class DummyToolTemplate implements ToolTemplate {
|
public class DummyToolTemplate implements ToolTemplate {
|
|
@ -0,0 +1,131 @@
|
||||||
|
/* ###
|
||||||
|
* IP: GHIDRA
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ghidra.app.cmd.function;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
import org.junit.*;
|
||||||
|
|
||||||
|
import ghidra.app.plugin.core.analysis.AnalysisBackgroundCommand;
|
||||||
|
import ghidra.app.plugin.core.analysis.AutoAnalysisManager;
|
||||||
|
import ghidra.framework.cmd.Command;
|
||||||
|
import ghidra.framework.options.Options;
|
||||||
|
import ghidra.framework.plugintool.PluginTool;
|
||||||
|
import ghidra.program.database.ProgramBuilder;
|
||||||
|
import ghidra.program.model.data.DataType;
|
||||||
|
import ghidra.program.model.listing.Function;
|
||||||
|
import ghidra.program.model.listing.Program;
|
||||||
|
import ghidra.test.AbstractGhidraHeadedIntegrationTest;
|
||||||
|
import ghidra.test.TestEnv;
|
||||||
|
|
||||||
|
public class CreateFunctionThunkTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
|
|
||||||
|
private TestEnv env;
|
||||||
|
private PluginTool tool;
|
||||||
|
|
||||||
|
private Program program;
|
||||||
|
|
||||||
|
private ProgramBuilder builder;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() throws Exception {
|
||||||
|
env = new TestEnv();
|
||||||
|
tool = env.getTool();
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void tearDown() {
|
||||||
|
if (program != null) {
|
||||||
|
env.release(program);
|
||||||
|
}
|
||||||
|
program = null;
|
||||||
|
env.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void analyze() {
|
||||||
|
// turn off some analyzers
|
||||||
|
setAnalysisOptions("Stack");
|
||||||
|
setAnalysisOptions("Embedded Media");
|
||||||
|
setAnalysisOptions("DWARF");
|
||||||
|
setAnalysisOptions("Create Address Tables");
|
||||||
|
setAnalysisOptions("MIPS Constant Reference Analyzer");
|
||||||
|
|
||||||
|
AutoAnalysisManager analysisMgr = AutoAnalysisManager.getAnalysisManager(program);
|
||||||
|
analysisMgr.reAnalyzeAll(null);
|
||||||
|
|
||||||
|
Command cmd = new AnalysisBackgroundCommand(analysisMgr, false);
|
||||||
|
tool.execute(cmd, program);
|
||||||
|
waitForBusyTool(tool);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setAnalysisOptions(String optionName) {
|
||||||
|
int txId = program.startTransaction("Analyze");
|
||||||
|
Options analysisOptions = program.getOptions(Program.ANALYSIS_PROPERTIES);
|
||||||
|
analysisOptions.setBoolean(optionName, false);
|
||||||
|
program.endTransaction(txId, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDelaySlotThunk() throws Exception {
|
||||||
|
|
||||||
|
builder = new ProgramBuilder("thunk", ProgramBuilder._MIPS);
|
||||||
|
|
||||||
|
builder.setBytes("0x1000", "08 22 96 44 24 04 00 02 08 11 96 44 00 00 00 00");
|
||||||
|
builder.disassemble("0x1000", 27, false);
|
||||||
|
builder.disassemble("0x1008", 27, false);
|
||||||
|
builder.createFunction("0x1000");
|
||||||
|
builder.createFunction("0x1008");
|
||||||
|
|
||||||
|
builder.analyze();
|
||||||
|
|
||||||
|
program = builder.getProgram();
|
||||||
|
|
||||||
|
Function noThunk = program.getFunctionManager().getFunctionAt(builder.addr(0x1000));
|
||||||
|
assertEquals(false, noThunk.isThunk());
|
||||||
|
|
||||||
|
Function isThunk = program.getFunctionManager().getFunctionAt(builder.addr(0x1008));
|
||||||
|
assertEquals(true, isThunk.isThunk());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This tests the forcing of a function to be a thunk with CreateThunkFunctionCmd
|
||||||
|
* Tests that the Function start analyzer will create a thunk given the thunk tag on a matching function
|
||||||
|
* That the MIPS BE language has a thunking pattern.
|
||||||
|
* That the MIPS 64/32 hybrid with sign extension of registers still gets found as a thunk.
|
||||||
|
* That the thunking function can be found with out the constant reference analyzer
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testDelayMips6432SlotThunk() throws Exception {
|
||||||
|
|
||||||
|
builder = new ProgramBuilder("thunk", ProgramBuilder._MIPS_6432);
|
||||||
|
|
||||||
|
builder.setBytes("0x466050", "3c 0f 00 47 8d f9 72 24 03 20 00 08 25 f8 72 24");
|
||||||
|
builder.setBytes("0x477224", "00 47 99 c0");
|
||||||
|
builder.createEmptyFunction("chdir", "0x4799c0", 1, DataType.VOID);
|
||||||
|
builder.disassemble("0x466050", 27, true);
|
||||||
|
|
||||||
|
builder.createFunction("0x466050");
|
||||||
|
|
||||||
|
program = builder.getProgram();
|
||||||
|
|
||||||
|
analyze();
|
||||||
|
|
||||||
|
Function isThunk = program.getFunctionManager().getFunctionAt(builder.addr(0x466050));
|
||||||
|
assertEquals(true, isThunk.isThunk());
|
||||||
|
assertEquals("chdir", isThunk.getName());
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,307 @@
|
||||||
|
/* ###
|
||||||
|
* IP: GHIDRA
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ghidra.app.plugin.core.processors;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
import java.math.BigInteger;
|
||||||
|
|
||||||
|
import org.junit.*;
|
||||||
|
|
||||||
|
import docking.ActionContext;
|
||||||
|
import docking.action.DockingActionIf;
|
||||||
|
import docking.widgets.MultiLineLabel;
|
||||||
|
import docking.widgets.OptionDialog;
|
||||||
|
import docking.widgets.tree.GTree;
|
||||||
|
import docking.widgets.tree.GTreeNode;
|
||||||
|
import ghidra.framework.main.FrontEndTool;
|
||||||
|
import ghidra.framework.main.datatree.DomainFileNode;
|
||||||
|
import ghidra.framework.model.DomainFile;
|
||||||
|
import ghidra.framework.model.DomainFolder;
|
||||||
|
import ghidra.plugin.importer.NewLanguagePanel;
|
||||||
|
import ghidra.program.database.ProgramBuilder;
|
||||||
|
import ghidra.program.model.address.*;
|
||||||
|
import ghidra.program.model.lang.*;
|
||||||
|
import ghidra.program.model.listing.*;
|
||||||
|
import ghidra.program.model.symbol.RefType;
|
||||||
|
import ghidra.program.model.symbol.SourceType;
|
||||||
|
import ghidra.test.AbstractGhidraHeadedIntegrationTest;
|
||||||
|
import ghidra.test.TestEnv;
|
||||||
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
|
public class SetLanguageTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
|
|
||||||
|
private TestEnv env;
|
||||||
|
private FrontEndTool frontEndTool;
|
||||||
|
|
||||||
|
private DockingActionIf setLanguageAction;
|
||||||
|
private GTreeNode notepadNode;
|
||||||
|
private DomainFile notepadFile;
|
||||||
|
private GTreeNode xyzFolderNode;
|
||||||
|
|
||||||
|
private AddressFactory addrFactory;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() throws Exception {
|
||||||
|
env = new TestEnv();
|
||||||
|
|
||||||
|
setErrorGUIEnabled(true);
|
||||||
|
frontEndTool = env.getFrontEndTool();
|
||||||
|
env.showFrontEndTool();
|
||||||
|
|
||||||
|
setLanguageAction = getAction(frontEndTool, "LanguageProviderPlugin", "Set Language");
|
||||||
|
|
||||||
|
// NOTE: Only test translation from a supported language to another supported language
|
||||||
|
|
||||||
|
// TODO: Change test data to a supported case (e.g., MIPS-32 to MIPS-64)
|
||||||
|
|
||||||
|
DomainFolder rootFolder = env.getProject().getProjectData().getRootFolder();
|
||||||
|
|
||||||
|
ProgramBuilder builder = new ProgramBuilder("notepad", "x86:LE:32:default");
|
||||||
|
Program p = builder.getProgram();
|
||||||
|
|
||||||
|
assertEquals(new LanguageID("x86:LE:32:default"), p.getLanguageID());
|
||||||
|
rootFolder.createFile("notepad", p, TaskMonitor.DUMMY);
|
||||||
|
env.release(p);
|
||||||
|
builder.dispose();
|
||||||
|
|
||||||
|
rootFolder.createFolder("XYZ");
|
||||||
|
GTree tree = findComponent(frontEndTool.getToolFrame(), GTree.class);
|
||||||
|
waitForTree(tree);
|
||||||
|
|
||||||
|
GTreeNode rootNode = tree.getRootNode();
|
||||||
|
xyzFolderNode = rootNode.getChild(0);
|
||||||
|
notepadNode = rootNode.getChild(1);
|
||||||
|
notepadFile = ((DomainFileNode) notepadNode).getDomainFile();
|
||||||
|
|
||||||
|
waitForSwing();
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void tearDown() throws Exception {
|
||||||
|
env.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testActionEnablement() throws Exception {
|
||||||
|
assertTrue(setLanguageAction.isEnabled());
|
||||||
|
assertTrue(!setLanguageAction.isEnabledForContext(createContext(xyzFolderNode)));
|
||||||
|
assertTrue(setLanguageAction.isEnabledForContext(createContext(notepadNode)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Address addr(String address) {
|
||||||
|
return addrFactory.getAddress(address);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void startSetLanguage(final LanguageID languageID, final CompilerSpecID compilerSpecID,
|
||||||
|
boolean isFailureCase) throws Exception {
|
||||||
|
if (languageID == null) {
|
||||||
|
throw new RuntimeException("languageID == null not allowed");
|
||||||
|
}
|
||||||
|
if (compilerSpecID == null) {
|
||||||
|
throw new RuntimeException("compilerSpecID == null not allowed");
|
||||||
|
}
|
||||||
|
|
||||||
|
// this triggers a modal dialog
|
||||||
|
runSwing(() -> {
|
||||||
|
ActionContext context = createContext(notepadNode);
|
||||||
|
assertTrue(setLanguageAction.isEnabledForContext(context));
|
||||||
|
setLanguageAction.actionPerformed(context);
|
||||||
|
}, false);
|
||||||
|
|
||||||
|
OptionDialog confirmDlg = waitForDialogComponent(OptionDialog.class);
|
||||||
|
assertNotNull(confirmDlg);
|
||||||
|
MultiLineLabel msgLabel = findComponent(confirmDlg, MultiLineLabel.class);
|
||||||
|
assertNotNull(msgLabel);
|
||||||
|
assertTrue(msgLabel.getLabel().indexOf("Setting the language can not be undone") >= 0);
|
||||||
|
assertTrue(msgLabel.getLabel().indexOf("make a copy") > 0);
|
||||||
|
|
||||||
|
pressButtonByText(confirmDlg, "Ok");
|
||||||
|
|
||||||
|
final SetLanguageDialog dlg = waitForDialogComponent(SetLanguageDialog.class);
|
||||||
|
assertNotNull(dlg);
|
||||||
|
final NewLanguagePanel languagePanel =
|
||||||
|
(NewLanguagePanel) getInstanceField("selectLangPanel", dlg);
|
||||||
|
assertNotNull(languagePanel);
|
||||||
|
|
||||||
|
waitForSwing();
|
||||||
|
|
||||||
|
runSwing(() -> {
|
||||||
|
NewLanguagePanel selectLangPanel =
|
||||||
|
(NewLanguagePanel) getInstanceField("selectLangPanel", dlg);
|
||||||
|
selectLangPanel.setSelectedLcsPair(
|
||||||
|
new LanguageCompilerSpecPair(languageID, compilerSpecID));
|
||||||
|
}, true);
|
||||||
|
|
||||||
|
waitForSwing();
|
||||||
|
|
||||||
|
pressButtonByText(dlg, "OK");
|
||||||
|
|
||||||
|
if (!isFailureCase) {
|
||||||
|
confirmDlg = waitForDialogComponent(OptionDialog.class);
|
||||||
|
assertNotNull(confirmDlg);
|
||||||
|
msgLabel = findComponent(confirmDlg, MultiLineLabel.class);
|
||||||
|
assertNotNull(msgLabel);
|
||||||
|
assertTrue(msgLabel.getLabel().indexOf("Would you like to Save") >= 0);
|
||||||
|
|
||||||
|
pressButtonByText(confirmDlg, "Save");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testReplaceLanguage() throws Exception {
|
||||||
|
|
||||||
|
startSetLanguage(new LanguageID("x86:LE:32:System Management Mode"),
|
||||||
|
new CompilerSpecID("default"), false);
|
||||||
|
|
||||||
|
waitForTasks();
|
||||||
|
|
||||||
|
Program p = (Program) notepadFile.getDomainObject(this, false, false, TaskMonitor.DUMMY);
|
||||||
|
assertNotNull(p);
|
||||||
|
try {
|
||||||
|
assertEquals(new LanguageID("x86:LE:32:System Management Mode"),
|
||||||
|
p.getLanguage().getLanguageID());
|
||||||
|
|
||||||
|
// TODO: Other checks needed ??
|
||||||
|
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
p.release(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testReplaceLanguageFailure() throws Exception {
|
||||||
|
|
||||||
|
startSetLanguage(new LanguageID("8051:BE:16:default"), new CompilerSpecID("default"), true);
|
||||||
|
|
||||||
|
final OptionDialog errDlg = waitForDialogComponent(OptionDialog.class);
|
||||||
|
assertNotNull(errDlg);
|
||||||
|
MultiLineLabel msgLabel = findComponent(errDlg, MultiLineLabel.class);
|
||||||
|
assertNotNull(msgLabel);
|
||||||
|
assertTrue(msgLabel.getLabel().indexOf("Language translation not supported") >= 0);
|
||||||
|
|
||||||
|
pressButtonByText(errDlg, "OK");
|
||||||
|
closeAllWindows();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testReplaceLanguage2() throws Exception {
|
||||||
|
|
||||||
|
Program p = (Program) notepadFile.getDomainObject(this, false, false, TaskMonitor.DUMMY);
|
||||||
|
try {
|
||||||
|
int txId = p.startTransaction("set Language");
|
||||||
|
addrFactory = p.getAddressFactory();
|
||||||
|
ProgramContext pc = p.getProgramContext();
|
||||||
|
Register ax = pc.getRegister("ax");
|
||||||
|
Register ebp = pc.getRegister("ebp");
|
||||||
|
Register ebx = pc.getRegister("ebx");
|
||||||
|
pc.setValue(ax, addr("0x1001000"), addr("0x1001000"), BigInteger.valueOf(0x1234));
|
||||||
|
pc.setValue(ebp, addr("0x1001000"), addr("0x1001000"), BigInteger.valueOf(0x12345678));
|
||||||
|
pc.setValue(ebx, addr("0x1001000"), addr("0x1001000"), BigInteger.valueOf(0x12345678));
|
||||||
|
assertEquals(0x1234, pc.getValue(ax, addr("0x1001000"), false).longValue());
|
||||||
|
assertEquals(0x12345678, pc.getValue(ebp, addr("0x1001000"), false).longValue());
|
||||||
|
assertEquals(0x12345678, pc.getValue(ebx, addr("0x1001000"), false).longValue());
|
||||||
|
p.endTransaction(txId, true);
|
||||||
|
|
||||||
|
p.save(null, TaskMonitor.DUMMY);
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
p.release(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
startSetLanguage(new LanguageID("x86:LE:32:default"), new CompilerSpecID("gcc"), false);
|
||||||
|
waitForTasks();
|
||||||
|
|
||||||
|
p = (Program) notepadFile.getDomainObject(this, true, false, TaskMonitor.DUMMY);
|
||||||
|
try {
|
||||||
|
addrFactory = p.getAddressFactory();
|
||||||
|
ProgramContext pc = p.getProgramContext();
|
||||||
|
Register ax = pc.getRegister("ax");
|
||||||
|
Register ebp = pc.getRegister("ebp");
|
||||||
|
Register ebx = pc.getRegister("ebx");
|
||||||
|
assertEquals(0x1234, pc.getValue(ax, addr("0x1001000"), false).longValue());
|
||||||
|
assertEquals(0x12345678, pc.getValue(ebp, addr("0x1001000"), false).longValue());
|
||||||
|
assertEquals(0x12345678, pc.getValue(ebx, addr("0x1001000"), false).longValue());
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
p.release(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testReplaceLanguage3() throws Exception {
|
||||||
|
|
||||||
|
Program p = (Program) notepadFile.getDomainObject(this, false, false, TaskMonitor.DUMMY);
|
||||||
|
addrFactory = p.getAddressFactory();
|
||||||
|
ProgramContext pc = p.getProgramContext();
|
||||||
|
Register eax = pc.getRegister("eax");
|
||||||
|
Register esi = pc.getRegister("esi");
|
||||||
|
Register edi = pc.getRegister("edi");
|
||||||
|
try {
|
||||||
|
int txId = p.startTransaction("set Language");
|
||||||
|
|
||||||
|
Function f = p.getListing().createFunction("BOB", addr("0x10041a8"),
|
||||||
|
new AddressSet(addr("0x10041a8"), addr("0x10041c0")), SourceType.USER_DEFINED);
|
||||||
|
f.setCustomVariableStorage(true);
|
||||||
|
ParameterImpl param = new ParameterImpl("PARAM_ONE", null, eax, p);
|
||||||
|
f.addParameter(param, SourceType.USER_DEFINED);
|
||||||
|
LocalVariableImpl local1 = new LocalVariableImpl("LOCAL_ONE", 0, null, esi, p);
|
||||||
|
LocalVariableImpl local2 = new LocalVariableImpl("LOCAL_TWO", 0, null, edi, p);
|
||||||
|
|
||||||
|
f.addLocalVariable(local1, SourceType.USER_DEFINED);
|
||||||
|
f.addLocalVariable(local2, SourceType.USER_DEFINED);
|
||||||
|
|
||||||
|
p.getReferenceManager().addRegisterReference(addr("0x10041b2"), 0, esi, RefType.DATA,
|
||||||
|
SourceType.USER_DEFINED);
|
||||||
|
p.getReferenceManager().addRegisterReference(addr("0x10041b3"), 0, edi, RefType.DATA,
|
||||||
|
SourceType.USER_DEFINED);
|
||||||
|
|
||||||
|
p.endTransaction(txId, true);
|
||||||
|
|
||||||
|
p.save(null, TaskMonitor.DUMMY);
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
p.release(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
startSetLanguage(new LanguageID("x86:LE:32:default"), new CompilerSpecID("gcc"), false);
|
||||||
|
waitForTasks();
|
||||||
|
|
||||||
|
p = (Program) notepadFile.getDomainObject(this, true, false, TaskMonitor.DUMMY);
|
||||||
|
try {
|
||||||
|
addrFactory = p.getAddressFactory();
|
||||||
|
|
||||||
|
Function fun = p.getListing().getFunctionAt(addr("0x10041a8"));
|
||||||
|
Parameter[] params = fun.getParameters();
|
||||||
|
assertEquals(1, params.length);
|
||||||
|
assertEquals("PARAM_ONE", params[0].getName());
|
||||||
|
assertTrue(params[0].isRegisterVariable());
|
||||||
|
assertEquals(eax, params[0].getRegister());
|
||||||
|
|
||||||
|
Variable[] locals = fun.getLocalVariables();
|
||||||
|
assertEquals(2, locals.length);
|
||||||
|
assertEquals("LOCAL_ONE", locals[0].getName());
|
||||||
|
assertEquals("LOCAL_TWO", locals[1].getName());
|
||||||
|
assertTrue(params[0].isRegisterVariable());
|
||||||
|
assertEquals(esi, locals[0].getRegister());
|
||||||
|
assertEquals(edi, locals[1].getRegister());
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
p.release(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue