mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2024-11-26 14:12:17 +00: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
@ -23,6 +23,7 @@ import javax.swing.*;
|
||||
import docking.ActionContext;
|
||||
import docking.WindowPosition;
|
||||
import docking.options.editor.ButtonPanelFactory;
|
||||
import docking.util.image.ToolIconURL;
|
||||
import docking.widgets.OptionDialog;
|
||||
import docking.widgets.label.*;
|
||||
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.FieldHeaderLocation;
|
||||
import ghidra.framework.plugintool.ComponentProviderAdapter;
|
||||
import ghidra.framework.project.tool.ToolIconURL;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
import ghidra.util.HelpLocation;
|
||||
import ghidra.util.layout.VerticalLayout;
|
||||
|
@ -22,8 +22,6 @@ import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.swing.JFrame;
|
||||
|
||||
import org.jdom.Element;
|
||||
|
||||
import docking.ComponentProvider;
|
||||
@ -332,6 +330,7 @@ public class TestEnv {
|
||||
* <P>This method is considered sub-standard and users should prefer instead
|
||||
* {@link #launchDefaultTool()} or {@link #launchDefaultTool(Program)}.
|
||||
*
|
||||
* @param p the program
|
||||
* @return the newly shown tool
|
||||
*/
|
||||
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
|
||||
* {@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.
|
||||
*
|
||||
* @param ghidraClass The class of the dialog the user desires
|
||||
@ -369,8 +368,7 @@ public class TestEnv {
|
||||
@Deprecated
|
||||
public <T extends DialogComponentProvider> T waitForDialogComponent(Class<T> ghidraClass,
|
||||
int maxTimeMS) {
|
||||
JFrame frame = lazyTool().getToolFrame();
|
||||
return AbstractDockingTest.waitForDialogComponent(frame, ghidraClass, maxTimeMS);
|
||||
return AbstractDockingTest.waitForDialogComponent(ghidraClass);
|
||||
}
|
||||
|
||||
private static GhidraProject createGhidraTestProject(String projectName) throws IOException {
|
||||
@ -381,7 +379,20 @@ public class TestEnv {
|
||||
deleteSavedFrontEndTool();
|
||||
|
||||
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() {
|
||||
@ -466,6 +477,7 @@ public class TestEnv {
|
||||
/**
|
||||
* 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.
|
||||
* @return the tool
|
||||
*/
|
||||
public PluginTool createDefaultTool() {
|
||||
PluginTool newTool = launchDefaultToolByName(AbstractGenericTest.DEFAULT_TEST_TOOL_NAME);
|
||||
@ -497,15 +509,14 @@ public class TestEnv {
|
||||
return tool;
|
||||
}
|
||||
|
||||
protected PluginTool launchDefaultToolByName(final String toolName) {
|
||||
AtomicReference<PluginTool> ref = new AtomicReference<>();
|
||||
AbstractGenericTest.runSwing(() -> {
|
||||
protected PluginTool launchDefaultToolByName(String toolName) {
|
||||
|
||||
ToolTemplate toolTemplate =
|
||||
ToolUtils.readToolTemplate("defaultTools/" + toolName + ".tool");
|
||||
return AbstractGenericTest.runSwing(() -> {
|
||||
|
||||
ToolTemplate toolTemplate = getToolTemplate(toolName);
|
||||
if (toolTemplate == null) {
|
||||
Msg.debug(this, "Unable to find tool: " + toolName);
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
boolean wasErrorGUIEnabled = AbstractDockingTest.isUseErrorGUI();
|
||||
@ -515,11 +526,23 @@ public class TestEnv {
|
||||
Project project = frontEndToolInstance.getProject();
|
||||
ToolManager toolManager = project.getToolManager();
|
||||
Workspace workspace = toolManager.getActiveWorkspace();
|
||||
ref.set((PluginTool) workspace.runTool(toolTemplate));
|
||||
|
||||
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 {
|
||||
@ -550,6 +573,7 @@ public class TestEnv {
|
||||
|
||||
/**
|
||||
* Returns GhidraProject associated with this environment
|
||||
* @return the project
|
||||
*/
|
||||
public GhidraProject getGhidraProject() {
|
||||
return gp;
|
||||
@ -559,6 +583,7 @@ public class 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
|
||||
* the project to its previous state.
|
||||
* @throws IOException if any exception occurs while saving and reopening
|
||||
*/
|
||||
public void closeAndReopenProject() throws IOException {
|
||||
gp.setDeleteOnClose(false);
|
||||
@ -579,9 +604,6 @@ public class TestEnv {
|
||||
return gp.getProjectManager();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns Project associated with this environment
|
||||
*/
|
||||
public Project getProject() {
|
||||
return gp.getProject();
|
||||
}
|
||||
@ -646,6 +668,8 @@ public class TestEnv {
|
||||
* the only reason to use this method vice openProgram().
|
||||
*
|
||||
* @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 {
|
||||
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"
|
||||
* director or relative to the "typeinfo" directory. The name should
|
||||
* include the ".gdt" suffix.
|
||||
* @param domainFolder the folder in the test project where the archive should be created.
|
||||
* @param monitor monitor for canceling this restore.
|
||||
* @param domainFolder the folder in the test project where the archive should be created
|
||||
* @return the domain file that was created in the project
|
||||
* @throws Exception if an exception occurs
|
||||
*/
|
||||
public DomainFile restoreDataTypeArchive(String relativePathName, DomainFolder domainFolder)
|
||||
throws InvalidNameException, IOException, VersionException {
|
||||
throws Exception {
|
||||
|
||||
File gdtFile;
|
||||
try {
|
||||
@ -738,10 +762,10 @@ public class TestEnv {
|
||||
* @param program program object
|
||||
* @param replace if true any existing cached database with the same name will be replaced
|
||||
* @param monitor task monitor
|
||||
* @throws DuplicateNameException if already cached
|
||||
* @throws Exception if already cached
|
||||
*/
|
||||
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);
|
||||
}
|
||||
@ -826,7 +850,8 @@ public class TestEnv {
|
||||
* Launches a tool of the given name using the given domain file.
|
||||
* <p>
|
||||
* Note: the tool returned will have auto save disabled by default.
|
||||
*
|
||||
*
|
||||
* @param toolName the tool's name
|
||||
* @return the tool that is launched
|
||||
*/
|
||||
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.tool.ToolConstants;
|
||||
import docking.util.image.ToolIconURL;
|
||||
import docking.widgets.OptionDialog;
|
||||
import docking.widgets.filechooser.GhidraFileChooser;
|
||||
import generic.test.AbstractGTest;
|
||||
@ -46,7 +47,6 @@ import ghidra.framework.model.ToolTemplate;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.framework.plugintool.util.PluginException;
|
||||
import ghidra.framework.preferences.Preferences;
|
||||
import ghidra.framework.project.tool.ToolIconURL;
|
||||
import ghidra.test.AbstractGhidraHeadedIntegrationTest;
|
||||
import ghidra.test.TestEnv;
|
||||
import ghidra.util.exception.AssertException;
|
||||
|
@ -23,12 +23,12 @@ import org.junit.*;
|
||||
|
||||
import docking.action.DockingActionIf;
|
||||
import docking.action.ToggleDockingAction;
|
||||
import docking.util.image.ToolIconURL;
|
||||
import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
|
||||
import ghidra.app.plugin.core.progmgr.ProgramManagerPlugin;
|
||||
import ghidra.framework.main.FrontEndPlugin;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.framework.project.tool.GhidraTool;
|
||||
import ghidra.framework.project.tool.ToolIconURL;
|
||||
import ghidra.program.database.ProgramDB;
|
||||
import ghidra.test.ClassicSampleX86ProgramBuilder;
|
||||
import ghidra.test.TestEnv;
|
||||
|
@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.framework.project.tool;
|
||||
package docking.util.image;
|
||||
|
||||
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;
|
||||
|
||||
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";
|
||||
|
||||
private static boolean initialized = false;
|
||||
|
@ -40,6 +40,7 @@ import docking.help.Help;
|
||||
import docking.help.HelpService;
|
||||
import docking.tool.ToolConstants;
|
||||
import docking.util.AnimationUtils;
|
||||
import docking.util.image.ToolIconURL;
|
||||
import docking.widgets.OptionDialog;
|
||||
import generic.jar.ResourceFile;
|
||||
import generic.util.WindowUtilities;
|
||||
|
@ -32,11 +32,11 @@ import docking.dnd.*;
|
||||
import docking.help.Help;
|
||||
import docking.help.HelpService;
|
||||
import docking.tool.ToolConstants;
|
||||
import docking.util.image.ToolIconURL;
|
||||
import docking.widgets.EmptyBorderButton;
|
||||
import ghidra.framework.main.datatree.*;
|
||||
import ghidra.framework.model.*;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.framework.project.tool.ToolIconURL;
|
||||
import ghidra.util.*;
|
||||
import ghidra.util.bean.GGlassPane;
|
||||
import ghidra.util.exception.AssertException;
|
||||
|
@ -21,8 +21,8 @@ import java.beans.PropertyVetoException;
|
||||
import org.jdom.Element;
|
||||
|
||||
import docking.DockingTool;
|
||||
import docking.util.image.ToolIconURL;
|
||||
import ghidra.framework.plugintool.PluginEvent;
|
||||
import ghidra.framework.project.tool.ToolIconURL;
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -19,7 +19,7 @@ import javax.swing.ImageIcon;
|
||||
|
||||
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.
|
||||
|
@ -42,6 +42,7 @@ import docking.help.Help;
|
||||
import docking.help.HelpService;
|
||||
import docking.tool.ToolConstants;
|
||||
import docking.tool.util.DockingToolConstants;
|
||||
import docking.util.image.ToolIconURL;
|
||||
import docking.widgets.OptionDialog;
|
||||
import ghidra.framework.OperatingSystem;
|
||||
import ghidra.framework.Platform;
|
||||
@ -56,7 +57,6 @@ import ghidra.framework.plugintool.dialog.ManagePluginsDialog;
|
||||
import ghidra.framework.plugintool.mgr.*;
|
||||
import ghidra.framework.plugintool.util.*;
|
||||
import ghidra.framework.project.ProjectDataService;
|
||||
import ghidra.framework.project.tool.ToolIconURL;
|
||||
import ghidra.util.*;
|
||||
import ghidra.util.task.Task;
|
||||
import ghidra.util.task.TaskLauncher;
|
||||
|
@ -1,6 +1,5 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -16,10 +15,9 @@
|
||||
*/
|
||||
package ghidra.framework.plugintool.dialog;
|
||||
|
||||
import ghidra.framework.project.tool.ToolIconURL;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import docking.util.image.ToolIconURL;
|
||||
import resources.ResourceManager;
|
||||
|
||||
/**
|
||||
|
@ -27,6 +27,7 @@ import javax.swing.event.*;
|
||||
|
||||
import docking.DialogComponentProvider;
|
||||
import docking.options.editor.ButtonPanelFactory;
|
||||
import docking.util.image.ToolIconURL;
|
||||
import docking.widgets.OptionDialog;
|
||||
import docking.widgets.filechooser.GhidraFileChooser;
|
||||
import docking.widgets.label.GLabel;
|
||||
@ -34,7 +35,6 @@ import ghidra.framework.model.*;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.framework.preferences.Preferences;
|
||||
import ghidra.framework.project.tool.GhidraToolTemplate;
|
||||
import ghidra.framework.project.tool.ToolIconURL;
|
||||
import ghidra.util.HelpLocation;
|
||||
import ghidra.util.NamingUtilities;
|
||||
import ghidra.util.filechooser.ExtensionFileFilter;
|
||||
|
@ -22,8 +22,8 @@ import javax.swing.BorderFactory;
|
||||
import javax.swing.JList;
|
||||
import javax.swing.border.Border;
|
||||
|
||||
import docking.util.image.ToolIconURL;
|
||||
import docking.widgets.list.GListCellRenderer;
|
||||
import ghidra.framework.project.tool.ToolIconURL;
|
||||
|
||||
class ToolIconUrlRenderer extends GListCellRenderer<ToolIconURL> {
|
||||
private Border emptyBorder = BorderFactory.createEmptyBorder(5, 5, 5, 5);
|
||||
|
@ -21,6 +21,7 @@ import javax.swing.ImageIcon;
|
||||
|
||||
import org.jdom.Element;
|
||||
|
||||
import docking.util.image.ToolIconURL;
|
||||
import ghidra.framework.model.*;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.NumericUtilities;
|
||||
|
@ -30,10 +30,10 @@ import docking.*;
|
||||
import docking.action.DockingActionIf;
|
||||
import docking.actions.DockingToolActions;
|
||||
import docking.actions.PopupActionProvider;
|
||||
import docking.util.image.ToolIconURL;
|
||||
import ghidra.framework.model.*;
|
||||
import ghidra.framework.options.ToolOptions;
|
||||
import ghidra.framework.plugintool.PluginEvent;
|
||||
import ghidra.framework.project.tool.ToolIconURL;
|
||||
import ghidra.program.model.listing.Program;
|
||||
|
||||
public class DummyTool implements Tool {
|
||||
@ -361,8 +361,7 @@ public class DummyTool implements Tool {
|
||||
|
||||
@Override
|
||||
public void setMenuGroup(String[] menuPath, String group, String menuSubGroup) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
//do nothing
|
||||
}
|
||||
|
||||
@Override
|
@ -19,8 +19,8 @@ import javax.swing.ImageIcon;
|
||||
|
||||
import org.jdom.Element;
|
||||
|
||||
import docking.util.image.ToolIconURL;
|
||||
import ghidra.framework.model.*;
|
||||
import ghidra.framework.project.tool.ToolIconURL;
|
||||
import ghidra.program.model.listing.Program;
|
||||
|
||||
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…
Reference in New Issue
Block a user