mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-05-25 17:56:45 +08:00
Merge remote-tracking branch 'origin/GT-3035-dragonmacher-restore-integration-tests'
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -593,7 +593,7 @@ public class GhidraScriptUtil {
|
||||
* @param name the name of the script
|
||||
* @return the name as a '.java' file path (with '/'s and not '.'s)
|
||||
*/
|
||||
private static String fixupName(String name) {
|
||||
static String fixupName(String name) {
|
||||
if (name.endsWith(".java")) {
|
||||
name = name.substring(0, name.length() - 5);
|
||||
}
|
||||
|
||||
@@ -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,11 @@ public class TestEnv {
|
||||
deleteSavedFrontEndTool();
|
||||
|
||||
String projectDirectoryName = AbstractGTest.getTestDirectoryPath();
|
||||
return GhidraProject.createProject(projectDirectoryName, projectName, true);
|
||||
GhidraProject gp = GhidraProject.createProject(projectDirectoryName, projectName, true);
|
||||
|
||||
installDefaultTool(gp);
|
||||
|
||||
return gp;
|
||||
}
|
||||
|
||||
private static void deleteOldTestTools() {
|
||||
@@ -399,6 +401,19 @@ public class TestEnv {
|
||||
}
|
||||
}
|
||||
|
||||
private static void installDefaultTool(GhidraProject gp) {
|
||||
//
|
||||
// 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));
|
||||
}
|
||||
|
||||
private void initializeSimpleTool() {
|
||||
|
||||
if (tool != null) {
|
||||
@@ -466,6 +481,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 +513,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 +530,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 +577,7 @@ public class TestEnv {
|
||||
|
||||
/**
|
||||
* Returns GhidraProject associated with this environment
|
||||
* @return the project
|
||||
*/
|
||||
public GhidraProject getGhidraProject() {
|
||||
return gp;
|
||||
@@ -559,6 +587,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 +608,6 @@ public class TestEnv {
|
||||
return gp.getProjectManager();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns Project associated with this environment
|
||||
*/
|
||||
public Project getProject() {
|
||||
return gp.getProject();
|
||||
}
|
||||
@@ -646,6 +672,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 +702,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 +766,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 +854,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) {
|
||||
@@ -1216,5 +1245,7 @@ public class TestEnv {
|
||||
|
||||
DefaultProjectManager pm = gp.getProjectManager();
|
||||
pm.addDefaultTools(tc);
|
||||
|
||||
installDefaultTool(gp);
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,61 @@
|
||||
/* ###
|
||||
* 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.script;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import generic.test.AbstractGenericTest;
|
||||
|
||||
public class GhidraScriptUtilTest extends AbstractGenericTest {
|
||||
|
||||
@Test
|
||||
public void fixupName_WithExtension() {
|
||||
String input = "Bob.java";
|
||||
assertEquals(GhidraScriptUtil.fixupName(input), "Bob.java");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fixupName_WithoutExtension() {
|
||||
String input = "Bob";
|
||||
assertEquals(GhidraScriptUtil.fixupName(input), "Bob.java");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fixupName_WithPackageDots() {
|
||||
String input = "a.b.c.Bob";
|
||||
assertEquals(GhidraScriptUtil.fixupName(input), "a/b/c/Bob.java");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fixupName_WithPackageSlashes() {
|
||||
String input = "a/b/c/Bob";
|
||||
assertEquals(GhidraScriptUtil.fixupName(input), "a/b/c/Bob.java");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fixupName_InnerClass() {
|
||||
String input = "Bob$InnerClass";
|
||||
assertEquals(GhidraScriptUtil.fixupName(input), "Bob.java");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fixupName_InnerClass_WithPackageDots() {
|
||||
String input = "a.b.c.Bob$InnerClass";
|
||||
assertEquals(GhidraScriptUtil.fixupName(input), "a/b/c/Bob.java");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/* ###
|
||||
* 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.util;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import org.junit.*;
|
||||
|
||||
import ghidra.program.database.ProgramBuilder;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.test.AbstractGhidraHeadlessIntegrationTest;
|
||||
|
||||
public class PseudoDisassemblerTest extends AbstractGhidraHeadlessIntegrationTest {
|
||||
|
||||
private ProgramBuilder programBuilder;// Instructions are 2-byte aligned
|
||||
private Program program;
|
||||
private PseudoDisassembler disassembler;
|
||||
|
||||
private int txId;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
programBuilder = new ProgramBuilder("Test", ProgramBuilder._ARM);
|
||||
program = programBuilder.getProgram();
|
||||
txId = program.startTransaction("Add Memory");// leave open until tearDown
|
||||
programBuilder.createMemory(".text", "0", 64).setExecute(true);// initialized
|
||||
programBuilder.createUninitializedMemory(".unint", "0x40", 64).setExecute(true);// uninitialized
|
||||
programBuilder.createUninitializedMemory(".dat", "0x80", 64);// no-execute
|
||||
programBuilder.createMemory(".text2", "0x3e0", 0x800).setExecute(true);// initialized
|
||||
|
||||
disassembler = new PseudoDisassembler(program);
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
if (program != null) {
|
||||
program.endTransaction(txId, true);
|
||||
}
|
||||
if (programBuilder != null) {
|
||||
programBuilder.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToStringArmSeparator() throws Exception {
|
||||
programBuilder.setBytes("0", "08 f8 00 00 40 00");// strb.w r0,[r8,r0,0x0]
|
||||
programBuilder.setRegisterValue("TMode", "0", "1", 1);
|
||||
PseudoInstruction instr =
|
||||
disassembler.disassemble(program.getAddressFactory().getAddress("0"));
|
||||
|
||||
String str = instr.toString();
|
||||
assertEquals("strb.w r0,[r8,r0,lsl #0x0]", str);// wan't to make sure all markup is printed
|
||||
|
||||
programBuilder.setBytes("0", "00 f0 20 03");// nopeq
|
||||
programBuilder.setRegisterValue("TMode", "0", "1", 0);
|
||||
instr = disassembler.disassemble(program.getAddressFactory().getAddress("0"));
|
||||
|
||||
str = instr.toString();
|
||||
assertEquals("nopeq", str);
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -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;
|
||||
|
||||
+1
-1
@@ -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;
|
||||
|
||||
+1
-1
@@ -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;
|
||||
|
||||
+122
@@ -0,0 +1,122 @@
|
||||
/* ###
|
||||
* 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.imagepanel;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.awt.Image;
|
||||
|
||||
import javax.swing.Icon;
|
||||
import javax.swing.JFrame;
|
||||
|
||||
import org.junit.*;
|
||||
|
||||
import docking.test.AbstractDockingTest;
|
||||
import resources.ResourceManager;
|
||||
import resources.icons.EmptyIcon;
|
||||
|
||||
public class ImagePanelTest extends AbstractDockingTest {
|
||||
|
||||
private JFrame frame;
|
||||
private ImagePanel imagePanel;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
Icon emptyIcon = new EmptyIcon(32, 32);
|
||||
Image emptyImage = ResourceManager.getImageIcon(emptyIcon).getImage();
|
||||
imagePanel = new ImagePanel(emptyImage);
|
||||
|
||||
frame = new JFrame("ImagePanel Test");
|
||||
frame.getContentPane().add(imagePanel);
|
||||
frame.setSize(400, 400);
|
||||
|
||||
frame.setVisible(true);
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
frame.dispose();
|
||||
}
|
||||
|
||||
private void reset() {
|
||||
imagePanel.setZoomFactor(1.0f);
|
||||
|
||||
assertTrue("Unable to reset zoom factor",
|
||||
Float.compare(imagePanel.getZoomFactor(), 1.0f) == 0);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testZoom_Neutral() {
|
||||
reset();
|
||||
|
||||
imagePanel.setZoomFactor(1.0f);
|
||||
|
||||
assertTrue("Zoom factor not set to 1.0x",
|
||||
Float.compare(imagePanel.getZoomFactor(), 1.0f) == 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testZoom_10Point0f() {
|
||||
reset();
|
||||
|
||||
imagePanel.setZoomFactor(10.0f);
|
||||
|
||||
assertTrue("Zoom factor not set to 10.0x",
|
||||
Float.compare(imagePanel.getZoomFactor(), 10.0f) == 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testZoom_0Point05() {
|
||||
reset();
|
||||
|
||||
imagePanel.setZoomFactor(0.05f);
|
||||
|
||||
assertTrue("Zoom factor not set to 0.05x",
|
||||
Float.compare(imagePanel.getZoomFactor(), 0.05f) == 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testZoom_20Point0() {
|
||||
reset();
|
||||
|
||||
imagePanel.setZoomFactor(20.0f);
|
||||
|
||||
assertTrue("Zoom factor not set to 20.0x; should be 10.0x",
|
||||
Float.compare(imagePanel.getZoomFactor(), 10.0f) == 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testZoom_0Point001() {
|
||||
reset();
|
||||
|
||||
imagePanel.setZoomFactor(0.001f);
|
||||
|
||||
assertTrue("Zoom factor not set to 0.001x; should be 0.05x",
|
||||
Float.compare(imagePanel.getZoomFactor(), 0.05f) == 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testZoom_3Point75() {
|
||||
reset();
|
||||
|
||||
imagePanel.setZoomFactor(3.75f);
|
||||
|
||||
assertTrue("Zoom factor not set to 3.75x; should be 4.0x",
|
||||
Float.compare(imagePanel.getZoomFactor(), 4.0f) == 0);
|
||||
}
|
||||
|
||||
}
|
||||
+519
File diff suppressed because it is too large
Load Diff
+290
@@ -0,0 +1,290 @@
|
||||
/* ###
|
||||
* 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.textfield;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.concurrent.atomic.AtomicIntegerArray;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.ChangeEvent;
|
||||
import javax.swing.event.ChangeListener;
|
||||
|
||||
import org.junit.*;
|
||||
|
||||
import docking.test.AbstractDockingTest;
|
||||
|
||||
public class IntegerTextFieldTest extends AbstractDockingTest {
|
||||
|
||||
private JFrame frame;
|
||||
private IntegerTextField field;
|
||||
private JTextField textField;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
|
||||
field = new IntegerTextField(10);
|
||||
field.setShowNumberMode(true);
|
||||
textField = (JTextField) field.getComponent();
|
||||
frame = new JFrame("Test");
|
||||
frame.getContentPane().add(field.getComponent());
|
||||
frame.pack();
|
||||
frame.setVisible(true);
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
frame.setVisible(false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDefaultState() {
|
||||
assertNull(field.getValue());// no value
|
||||
assertEquals(0, field.getIntValue());// the "int value" return for null is 0
|
||||
assertEquals(0, field.getLongValue());
|
||||
assertTrue(!field.isHexMode());
|
||||
assertNull(field.getMaxValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTypeValidDecimalNumber() {
|
||||
triggerText(textField, "123");
|
||||
assertEquals(123, field.getIntValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTypeValidHexNumber() {
|
||||
triggerText(textField, "0x2abcdef");
|
||||
assertEquals(0x2abcdef, field.getIntValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvalidCharsIgnored() {
|
||||
triggerText(textField, "123ghijklmnopqrstuvwxyz4");
|
||||
assertEquals(1234, field.getIntValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHexCharsIgnoredInDecimalMode() {
|
||||
assertTrue(!field.isHexMode());
|
||||
triggerText(textField, "123ghijklmnopqrstuvwxyz4");
|
||||
assertEquals(1234, field.getIntValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testXchangesHexMode() {
|
||||
assertTrue(!field.isHexMode());
|
||||
triggerText(textField, "0");
|
||||
assertTrue(!field.isHexMode());
|
||||
triggerText(textField, "x");
|
||||
assertTrue(field.isHexMode());
|
||||
triggerBackspaceKey(textField);
|
||||
assertTrue(!field.isHexMode());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHexModeWithoutPrefix() {
|
||||
triggerText(textField, "abc");// not allowed when using hex prefix, so expect empty
|
||||
assertEquals(null, field.getValue());
|
||||
|
||||
field.setAllowsHexPrefix(false);
|
||||
field.setHexMode();
|
||||
triggerText(textField, "abc");
|
||||
assertEquals(0xabc, field.getIntValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNegative() {
|
||||
triggerText(textField, "-123");
|
||||
assertEquals(-123, field.getIntValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNegativeHex() {
|
||||
triggerText(textField, "-0xa");
|
||||
assertEquals(-10, field.getIntValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNegativeNotAllowed() {
|
||||
field.setAllowNegativeValues(false);
|
||||
triggerText(textField, "-123");
|
||||
assertEquals(123, field.getIntValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetNegativeWithCurrentNegativeValue() {
|
||||
field.setValue(-123);
|
||||
field.setAllowNegativeValues(false);
|
||||
assertEquals(null, field.getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMax() {
|
||||
field.setMaxValue(BigInteger.valueOf(13l));
|
||||
triggerText(textField, "12");
|
||||
assertEquals(12, field.getIntValue());
|
||||
|
||||
field.setValue(null);
|
||||
triggerText(textField, "13");
|
||||
assertEquals(13, field.getIntValue());
|
||||
|
||||
field.setValue(null);
|
||||
triggerText(textField, "14");// four should be ignored
|
||||
assertEquals(1, field.getIntValue());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetMaxToValueSmallerThanCurrent() {
|
||||
field.setValue(500);
|
||||
field.setMaxValue(BigInteger.valueOf(400));
|
||||
assertEquals(400, field.getIntValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMaxInHex() {
|
||||
field.setMaxValue(BigInteger.valueOf(0xd));
|
||||
triggerText(textField, "0xc");
|
||||
assertEquals(12, field.getIntValue());
|
||||
|
||||
field.setValue(null);
|
||||
triggerText(textField, "0xd");
|
||||
assertEquals(13, field.getIntValue());
|
||||
|
||||
field.setValue(null);
|
||||
triggerText(textField, "0xe");// e should be ignored
|
||||
assertEquals(0, field.getIntValue());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSwitchingHexMode() {
|
||||
field.setValue(255);
|
||||
assertEquals("255", field.getText());
|
||||
field.setHexMode();
|
||||
assertEquals("0xff", field.getText());
|
||||
field.setDecimalMode();
|
||||
assertEquals("255", field.getText());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testChangeListenerAfterValidInput() {
|
||||
TestChangeListener listener = new TestChangeListener();
|
||||
field.addChangeListener(listener);
|
||||
|
||||
triggerText(textField, "123");
|
||||
assertEquals(3, listener.count);
|
||||
assertEquals(1, listener.values.get(0));
|
||||
assertEquals(12, listener.values.get(1));
|
||||
assertEquals(123, listener.values.get(2));
|
||||
|
||||
triggerBackspaceKey(textField);
|
||||
assertEquals(12, listener.values.get(3));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testChangeListenerAfterSwitchingModes() {
|
||||
triggerText(textField, "123");
|
||||
|
||||
TestChangeListener listener = new TestChangeListener();
|
||||
field.addChangeListener(listener);
|
||||
|
||||
setHexMode();
|
||||
|
||||
assertEquals(2, listener.count);
|
||||
assertEquals(123, listener.values.get(1));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNegativeHexFromValue() {
|
||||
field.setValue(-255);
|
||||
setHexMode();
|
||||
assertEquals("-0xff", field.getText());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNullValue() {
|
||||
field.setValue(12);
|
||||
assertEquals("12", field.getText());
|
||||
field.setValue(null);
|
||||
assertEquals("", field.getText());
|
||||
assertEquals(0, field.getIntValue());
|
||||
assertEquals(0l, field.getLongValue());
|
||||
assertEquals(null, field.getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHexValueInDontRequireHexPrefixMode() {
|
||||
field.setAllowsHexPrefix(false);
|
||||
field.setHexMode();
|
||||
field.setValue(255);
|
||||
assertEquals("ff", field.getText());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetNotAllowNegativeModeWhileCurrentValueIsNegative() {
|
||||
field.setValue(-10);
|
||||
field.setAllowNegativeValues(false);
|
||||
assertEquals("", field.getText());
|
||||
assertEquals(0, field.getIntValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetLongValue() {
|
||||
field.setValue(100L);
|
||||
assertEquals(100L, field.getLongValue());
|
||||
assertEquals(100, field.getIntValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSettingNegativeNumberWhenNegativesArentAllowed() {
|
||||
field.setValue(10);
|
||||
field.setAllowNegativeValues(false);
|
||||
field.setValue(-10);
|
||||
assertEquals("", field.getText());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUseHexPrefixUpdatesTextField() {
|
||||
field.setAllowsHexPrefix(false);
|
||||
field.setHexMode();
|
||||
field.setValue(255);
|
||||
assertEquals("ff", field.getText());
|
||||
field.setAllowsHexPrefix(true);
|
||||
assertEquals("0xff", field.getText());
|
||||
}
|
||||
|
||||
private void setHexMode() {
|
||||
runSwing(() -> field.setHexMode());
|
||||
waitForSwing();
|
||||
}
|
||||
|
||||
class TestChangeListener implements ChangeListener {
|
||||
volatile int count;
|
||||
private AtomicIntegerArray values = new AtomicIntegerArray(10);
|
||||
|
||||
@Override
|
||||
public void stateChanged(ChangeEvent e) {
|
||||
values.set(count++, field.getIntValue());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+309
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+255
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+309
@@ -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
@@ -0,0 +1,67 @@
|
||||
/* ###
|
||||
* 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.util.task;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import docking.test.AbstractDockingTest;
|
||||
import generic.test.AbstractGenericTest;
|
||||
|
||||
public class TaskMonitorSplitterTest extends AbstractDockingTest {
|
||||
TaskMonitor baseMonitor;
|
||||
|
||||
public TaskMonitorSplitterTest() {
|
||||
super();
|
||||
baseMonitor = new TaskMonitorComponent();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBasicUse() {
|
||||
TaskMonitor[] monitors = TaskMonitorSplitter.splitTaskMonitor(baseMonitor, 4);
|
||||
|
||||
monitors[0].initialize(100);
|
||||
monitors[0].setProgress(1);
|
||||
assertEquals(1, monitors[0].getProgress());
|
||||
assertEquals(TaskMonitorSplitter.MONITOR_SIZE / 400, baseMonitor.getProgress());
|
||||
|
||||
monitors[0].incrementProgress(1);
|
||||
assertEquals(2 * TaskMonitorSplitter.MONITOR_SIZE / 400, baseMonitor.getProgress());
|
||||
|
||||
monitors[0].setProgress(10);
|
||||
assertEquals(10 * TaskMonitorSplitter.MONITOR_SIZE / 400, baseMonitor.getProgress());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMaxSettings() {
|
||||
TaskMonitor[] monitors = TaskMonitorSplitter.splitTaskMonitor(baseMonitor, 4);
|
||||
|
||||
monitors[0].initialize(100);
|
||||
monitors[0].setProgress(50);
|
||||
assertEquals(50 * TaskMonitorSplitter.MONITOR_SIZE / 400, baseMonitor.getProgress());
|
||||
|
||||
monitors[0].setMaximum(25);
|
||||
assertEquals(25, monitors[0].getMaximum());
|
||||
assertEquals(TaskMonitorSplitter.MONITOR_SIZE / 4, baseMonitor.getProgress());
|
||||
|
||||
monitors[0].setMaximum(100);
|
||||
assertEquals(25 * TaskMonitorSplitter.MONITOR_SIZE / 400, baseMonitor.getProgress());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -15,13 +15,16 @@
|
||||
*/
|
||||
package utilities.util;
|
||||
|
||||
import static generic.test.AbstractGenericTest.assertListEqualsArrayOrdered;
|
||||
import static generic.test.AbstractGTest.assertListEqualsArrayOrdered;
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.*;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.junit.Test;
|
||||
|
||||
@@ -159,4 +162,62 @@ public class FileUtilitiesTest {
|
||||
assertFalse(FileUtilities.isPathContainedWithin(new File("/a/b"), new File("/a/b/../c")));
|
||||
assertFalse(FileUtilities.isPathContainedWithin(new File("/a/b"), new File("/a/bc")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void copyFile_ResourceFile_To_ResourceFile() throws Exception {
|
||||
|
||||
File from = File.createTempFile("from.file", ".txt");
|
||||
FileUtilities.writeLinesToFile(from, Arrays.asList("From file contents"));
|
||||
from.deleteOnExit();
|
||||
|
||||
File to = File.createTempFile("to.file", ".txt");
|
||||
to.deleteOnExit();
|
||||
FileUtilities.writeLinesToFile(to, Arrays.asList("To file contents"));
|
||||
|
||||
FileUtilities.copyFile(new ResourceFile(from), new ResourceFile(to), null);
|
||||
|
||||
String text = FileUtils.readFileToString(to, Charset.defaultCharset());
|
||||
assertThat(text, equalTo("From file contents\n"));
|
||||
}
|
||||
|
||||
@Test(expected = IOException.class)
|
||||
public void copyFile_ExceptionFromInputStream() throws Exception {
|
||||
|
||||
ResourceFile from = new ResourceFile(new File("/fake.from.file")) {
|
||||
@Override
|
||||
public InputStream getInputStream() throws IOException {
|
||||
throw new IOException("Test Exception");
|
||||
}
|
||||
};
|
||||
|
||||
File to = File.createTempFile("to.file", ".txt");
|
||||
to.deleteOnExit();
|
||||
|
||||
// should fail
|
||||
FileUtilities.copyFile(from, new ResourceFile(to), null);
|
||||
}
|
||||
|
||||
@Test(expected = IOException.class)
|
||||
public void copyFile_ExceptionFromOutputStream() throws Exception {
|
||||
|
||||
File from = File.createTempFile("from.file", ".txt");
|
||||
from.deleteOnExit();
|
||||
|
||||
ResourceFile to = new ResourceFile(new File("/to.from.file")) {
|
||||
|
||||
@Override
|
||||
public OutputStream getOutputStream() throws FileNotFoundException {
|
||||
throw new FileNotFoundException("Test Exception");
|
||||
}
|
||||
};
|
||||
|
||||
// should fail
|
||||
FileUtilities.copyFile(new ResourceFile(from), to, null);
|
||||
}
|
||||
|
||||
public void copyFile_WithMonitor() {
|
||||
// too slow due to the nature of how the progress is reported in chunks--we would
|
||||
// have to generate too much data, which would take seconds to test that progress
|
||||
// is correctly reported
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,196 @@
|
||||
/* ###
|
||||
* 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 help;
|
||||
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.*;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
|
||||
import generic.test.AbstractGenericTest;
|
||||
import utilities.util.FileUtilities;
|
||||
|
||||
public abstract class AbstractHelpTest extends AbstractGenericTest {
|
||||
|
||||
protected static final String HELP_FILENAME_PREFIX = "Fake";
|
||||
protected static final String HELP_FILENAME = HELP_FILENAME_PREFIX + ".html";
|
||||
|
||||
private Path testTempDir;
|
||||
|
||||
public AbstractHelpTest() {
|
||||
super();
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
|
||||
testTempDir = Files.createTempDirectory(testName.getMethodName());
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
FileUtilities.deleteDir(testTempDir.toFile());
|
||||
}
|
||||
|
||||
protected Path createHelpBuildOutputDir() throws IOException {
|
||||
Path out = testTempDir.resolve("build/help/main/help");
|
||||
Files.createDirectories(out);
|
||||
return out;
|
||||
}
|
||||
|
||||
protected Path createFakeHelpTopic(Path helpDir) throws IOException {
|
||||
return createFakeHelpTopic("FakeTopic", helpDir);
|
||||
}
|
||||
|
||||
protected Path createFakeHelpTopic(String topicName, Path helpDir) throws IOException {
|
||||
Path topicsDir = helpDir.resolve("topics");
|
||||
Path fakeTopicDir = topicsDir.resolve(topicName);
|
||||
Files.createDirectories(fakeTopicDir);
|
||||
return fakeTopicDir;
|
||||
}
|
||||
|
||||
protected Path createTempHelpDir() throws IOException {
|
||||
Path helpDir = testTempDir.resolve("help");
|
||||
Files.createDirectory(helpDir);
|
||||
return helpDir;
|
||||
}
|
||||
|
||||
protected void addRequiredHelpDirStructure(Path helpDir) throws IOException {
|
||||
|
||||
// HelpFile wants to read one of these, so put one there
|
||||
createEmpty_TOC_Source_File(helpDir);
|
||||
createSharedDir(helpDir);
|
||||
}
|
||||
|
||||
protected Path createSharedDir(Path helpDir) throws IOException {
|
||||
Path sharedDir = helpDir.resolve("shared");
|
||||
Files.createDirectory(sharedDir);
|
||||
|
||||
Path css = sharedDir.resolve("Frontpage.css");
|
||||
Files.createFile(css);
|
||||
|
||||
Path png = sharedDir.resolve("test.png");
|
||||
Files.createFile(png);
|
||||
|
||||
return sharedDir;
|
||||
}
|
||||
|
||||
protected Path createEmpty_TOC_Source_File(Path dir) throws IOException {
|
||||
|
||||
Path fullTOCPath = dir.resolve("TOC_Source.xml");
|
||||
Path file = Files.createFile(fullTOCPath);
|
||||
|
||||
//@formatter:off
|
||||
String TOCXML = "<?xml version='1.0' encoding='ISO-8859-1' ?>\n" +
|
||||
"<!-- Auto-generated on Fri Apr 03 09:37:08 EDT 2015 -->\n\n" +
|
||||
|
||||
"<tocroot>\n" +
|
||||
"</tocroot>\n";
|
||||
//@formatter:on
|
||||
|
||||
Files.write(file, TOCXML.getBytes(), StandardOpenOption.CREATE);
|
||||
return file;
|
||||
}
|
||||
|
||||
protected Path createHelpContent(Path topic, String anchor) throws IOException {
|
||||
Path htmlPath = topic.resolve(HELP_FILENAME);
|
||||
Path file = Files.createFile(htmlPath);
|
||||
|
||||
if (anchor == null) {
|
||||
anchor = "Default_Anchor";
|
||||
}
|
||||
|
||||
//@formatter:off
|
||||
String HTML =
|
||||
"<HTML>\n" +
|
||||
"<HEAD>\n" +
|
||||
"<TITLE>Configure Tool</TITLE>\n" +
|
||||
"<LINK rel=\"stylesheet\" type=\"text/css\" href=\"../../shared/Frontpage.css\">\n" +
|
||||
"</HEAD>\n" +
|
||||
|
||||
"<BODY>\n" +
|
||||
" <H1><A name=\""+anchor+"\"></A>Configure Tool</H1>\n" +
|
||||
" Some text with reference to shared image <IMG src=\"../../shared/test.png\">\n" +
|
||||
" \n" +
|
||||
"</BODY>\n" +
|
||||
"</HTML>\n";
|
||||
//@formatter:on
|
||||
|
||||
Files.write(file, HTML.getBytes(), StandardOpenOption.CREATE);
|
||||
return file;
|
||||
}
|
||||
|
||||
protected Path createHelpContent_WithReferenceHREF(Path topic, String HREF) throws IOException {
|
||||
Path htmlPath = topic.resolve(HELP_FILENAME);
|
||||
Path file = Files.createFile(htmlPath);
|
||||
|
||||
assertNotNull("Must specify the A tag HREF attribute", HREF);
|
||||
|
||||
//@formatter:off
|
||||
String HTML =
|
||||
"<HTML>\n" +
|
||||
"<HEAD>\n" +
|
||||
"<TITLE>Configure Tool</TITLE>\n" +
|
||||
"<LINK rel=\"stylesheet\" type=\"text/css\" href=\"../../shared/Frontpage.css\">\n" +
|
||||
"</HEAD>\n" +
|
||||
|
||||
"<BODY>\n" +
|
||||
" <H1><A name=\"Fake_Anchor\"></A>Configure Tool</H1>\n" +
|
||||
" And this is a link <A HREF=\""+HREF+"\">Click Me</A>" +
|
||||
" \n" +
|
||||
"</BODY>\n" +
|
||||
"</HTML>\n";
|
||||
//@formatter:on
|
||||
|
||||
Files.write(file, HTML.getBytes(), StandardOpenOption.CREATE);
|
||||
return file;
|
||||
}
|
||||
|
||||
protected Path createHelpContent_WithReferenceIMG_SRC(Path topic, String SRC)
|
||||
throws IOException {
|
||||
Path htmlPath = topic.resolve(HELP_FILENAME);
|
||||
Path file = Files.createFile(htmlPath);
|
||||
|
||||
assertNotNull("Must specify the A tag SRC attribute", SRC);
|
||||
|
||||
//@formatter:off
|
||||
String HTML =
|
||||
"<HTML>\n" +
|
||||
"<HEAD>\n" +
|
||||
"<TITLE>Configure Tool</TITLE>\n" +
|
||||
"<LINK rel=\"stylesheet\" type=\"text/css\" href=\"../../shared/Frontpage.css\">\n" +
|
||||
"</HEAD>\n" +
|
||||
|
||||
"<BODY>\n" +
|
||||
" <H1><A name=\"Fake_Anchor\"></A>Configure Tool</H1>\n" +
|
||||
" Some text with reference to shared image <IMG src=\""+SRC+"\">\n" +
|
||||
" \n" +
|
||||
"</BODY>\n" +
|
||||
"</HTML>\n";
|
||||
//@formatter:on
|
||||
|
||||
Files.write(file, HTML.getBytes(), StandardOpenOption.CREATE);
|
||||
return file;
|
||||
}
|
||||
|
||||
protected void copy(Path from, Path to) throws Exception {
|
||||
|
||||
FileUtilities.copyDir(from.toFile(), to.toFile(), null);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
/* ###
|
||||
* 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 help;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.net.URISyntaxException;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class HelpBuildUtilsTest extends AbstractHelpTest {
|
||||
|
||||
private static final String HELP_TOPIC_PATH = "/some/fake/path/to/help/topics";
|
||||
private static final String TOPIC_AND_FILENAME = "FooTopic/FooFile.html";
|
||||
private static final String HTML_FILE_PATH = HELP_TOPIC_PATH + '/' + TOPIC_AND_FILENAME;
|
||||
|
||||
public HelpBuildUtilsTest() {
|
||||
super();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetRelativeHelpPath() {
|
||||
String relativeString = "help/topics/FooTopic/FooFile.html";
|
||||
Path path = Paths.get("/some/fake/path/to/" + relativeString);
|
||||
Path relative = HelpBuildUtils.relativizeWithHelpTopics(path);
|
||||
assertEquals(relativeString, relative.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetRelativeHelpPath_NoHelpTopicInPath() {
|
||||
String invalidRelativeString = "help/topicz/" + TOPIC_AND_FILENAME;
|
||||
Path path = Paths.get("/some/fake/path/to/" + invalidRelativeString);
|
||||
Path relative = HelpBuildUtils.relativizeWithHelpTopics(path);
|
||||
assertNull(relative);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLocateReference_Local_HelpSystemSyntax() throws URISyntaxException {
|
||||
Path sourceFile = Paths.get(HTML_FILE_PATH);
|
||||
String reference = "help/topics/shared/foo.png";
|
||||
Path resolved = HelpBuildUtils.locateReference(sourceFile, reference);
|
||||
assertEquals("Help System syntax was not preserved", Paths.get(reference), resolved);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLocateReference_Local_RelativeSyntax() throws URISyntaxException {
|
||||
Path sourceFile = Paths.get(HTML_FILE_PATH);
|
||||
String reference = "../shared/foo.png";// go up one to the help dir
|
||||
Path resolved = HelpBuildUtils.locateReference(sourceFile, reference);
|
||||
assertEquals("Relative syntax did not locate file",
|
||||
Paths.get(HELP_TOPIC_PATH + "/shared/foo.png"), resolved);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLocateReference_Remote() throws URISyntaxException {
|
||||
Path sourceFile = Paths.get(HTML_FILE_PATH);
|
||||
String reference = "http://some.fake.server/foo.png";
|
||||
Path resolved = HelpBuildUtils.locateReference(sourceFile, reference);
|
||||
assertNull(resolved);
|
||||
boolean isRemote = HelpBuildUtils.isRemote(reference);
|
||||
assertTrue(isRemote);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLocateReferences_Icons() throws URISyntaxException {
|
||||
Path sourceFile = Paths.get(HTML_FILE_PATH);
|
||||
String reference = "Icons.REFRESH_ICON"; // see Icons class
|
||||
ImageLocation location = HelpBuildUtils.locateImageReference(sourceFile, reference);
|
||||
Path resolved = location.getResolvedPath();
|
||||
String name = resolved.getFileName().toString();
|
||||
assertEquals("Help System syntax was not preserved", "reload3.png", name);
|
||||
assertTrue(location.isRuntime());
|
||||
assertFalse(location.isRemote());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLocateReferences_Icons_BadName() throws URISyntaxException {
|
||||
Path sourceFile = Paths.get(HTML_FILE_PATH);
|
||||
String reference = "Icons.REFRESH_ICON_BAD"; // non-existent
|
||||
ImageLocation location = HelpBuildUtils.locateImageReference(sourceFile, reference);
|
||||
Path resolved = location.getResolvedPath();
|
||||
assertNull(resolved);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,418 @@
|
||||
/* ###
|
||||
* 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 help;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.*;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.help.HelpSet;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import help.validator.LinkDatabase;
|
||||
import help.validator.location.*;
|
||||
import help.validator.model.*;
|
||||
|
||||
public class OverlayHelpTreeTest {
|
||||
|
||||
@Test
|
||||
public void testSourceTOCFileThatDependsUponPreBuiltHelp() {
|
||||
//
|
||||
// We want to make sure the overlay tree will properly resolve help TOC items being
|
||||
// built from TOC_Source.xml files when that file uses <TOCREF> items that are defined
|
||||
// in a help <TOCITEM> that lives inside of a pre-built jar file.
|
||||
//
|
||||
/*
|
||||
|
||||
Example makeup we will create:
|
||||
|
||||
PreBuild_TOC.xml
|
||||
|
||||
<tocitem id="root" target="fake">
|
||||
<tocitem id="child_1" target="fake" />
|
||||
</tocitem>
|
||||
|
||||
|
||||
TOC_Source.xml
|
||||
|
||||
<tocref id="root">
|
||||
<tocref="child_1">
|
||||
<tocdef id="child_2" target="fake" />
|
||||
</tocref>
|
||||
</tocref>
|
||||
|
||||
*/
|
||||
|
||||
TOCItemExternal root = externalItem("root");
|
||||
TOCItemExternal child_1 = externalItem(root, "child_1");
|
||||
|
||||
Path tocSourceFile = Paths.get("/fake/path_2/TOC_Source.xml");
|
||||
String root_ID = root.getIDAttribute();
|
||||
TOCItemReference root_ref = referenceItem(root_ID, tocSourceFile);
|
||||
|
||||
String child_1_ID = child_1.getIDAttribute();
|
||||
TOCItemReference child_1_ref = referenceItem(root_ref, child_1_ID, tocSourceFile);
|
||||
|
||||
TOCItemDefinition child_2 = definitionItem(child_1_ref, "child_2", tocSourceFile);
|
||||
|
||||
TOCItemProviderTestStub tocProvider = new TOCItemProviderTestStub();
|
||||
tocProvider.addExternal(root);
|
||||
tocProvider.addExternal(child_1);
|
||||
tocProvider.addDefinition(child_2);
|
||||
|
||||
TOCSpyWriter spy = printOverlayTree(tocProvider, tocSourceFile);
|
||||
|
||||
assertNodeCount(spy, 3);
|
||||
assertOrder(spy, 1, root);
|
||||
assertOrder(spy, 2, child_1);
|
||||
assertOrder(spy, 3, child_2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSourceTOCFileThatDependsUponPreBuiltHelp_MultiplePreBuiltInputs() {
|
||||
//
|
||||
// We want to make sure the overlay tree will properly resolve help TOC items being
|
||||
// built from TOC_Source.xml files when that file uses <TOCREF> items that are defined
|
||||
// in a help <TOCITEM> that lives inside of multiple pre-built jar files.
|
||||
//
|
||||
/*
|
||||
|
||||
Example makeup we will create:
|
||||
|
||||
PreBuild_TOC.xml
|
||||
|
||||
<tocitem id="root" target="fake">
|
||||
<tocitem id="child_1" target="fake">
|
||||
<tocitem="prebuilt_a_child" target="fake" />
|
||||
</tocitem>
|
||||
</tocitem>
|
||||
|
||||
Another PreBuild_TOC.xml
|
||||
|
||||
<tocitem id="root" target="fake">
|
||||
<tocitem id="child_1" target="fake">
|
||||
<tocitem="prebuilt_b_child" target="fake" />
|
||||
</tocitem>
|
||||
</tocitem>
|
||||
|
||||
|
||||
TOC_Source.xml
|
||||
|
||||
<tocref id="root">
|
||||
<tocref="child_1">
|
||||
<tocdef id="child_2" target="fake" />
|
||||
</tocref>
|
||||
</tocref>
|
||||
|
||||
*/
|
||||
|
||||
TOCItemExternal root_a = externalItem("root");
|
||||
TOCItemExternal child_1_a = externalItem(root_a, "child_1");
|
||||
TOCItemExternal prebuilt_a_child = externalItem(child_1_a, "prebuilt_a_child");
|
||||
|
||||
// note: same ID values, since they represent the same nodes, but from different TOC files
|
||||
TOCItemExternal root_b = externalItemAlt(null, "root");
|
||||
TOCItemExternal child_1_b = externalItemAlt(root_b, "child_1");
|
||||
TOCItemExternal prebuilt_b_child = externalItemAlt(child_1_b, "prebuilt_b_child");
|
||||
|
||||
Path tocSourceFile = Paths.get("/fake/path_2/TOC_Source.xml");
|
||||
String root_ID = root_a.getIDAttribute();
|
||||
TOCItemReference root_ref = referenceItem(root_ID, tocSourceFile);
|
||||
|
||||
String child_1_ID = child_1_a.getIDAttribute();
|
||||
TOCItemReference child_1_ref = referenceItem(root_ref, child_1_ID, tocSourceFile);
|
||||
|
||||
TOCItemDefinition child_2 = definitionItem(child_1_ref, "child_2", tocSourceFile);
|
||||
|
||||
TOCItemProviderTestStub tocProvider = new TOCItemProviderTestStub();
|
||||
tocProvider.addExternal(root_a);
|
||||
tocProvider.addExternal(root_b);
|
||||
tocProvider.addExternal(child_1_a);
|
||||
tocProvider.addExternal(child_1_b);
|
||||
tocProvider.addExternal(prebuilt_a_child);
|
||||
tocProvider.addExternal(prebuilt_b_child);
|
||||
tocProvider.addDefinition(child_2);
|
||||
|
||||
TOCSpyWriter spy = printOverlayTree(tocProvider, tocSourceFile);
|
||||
|
||||
assertNodeCount(spy, 3);
|
||||
assertOrder(spy, 1, root_a);// could also be root_b, same ID
|
||||
assertOrder(spy, 2, child_1_a);// could also be child_1_b, same ID
|
||||
assertOrder(spy, 3, child_2);
|
||||
|
||||
// note: prebuilt_a_child and prebuilt_b_child don't get output, since they do not have
|
||||
// the same TOC file ID as the help file being processed (in other words, they don't
|
||||
// live in the TOC_Source.xml being processes, so they are not part of the output).
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSourceTOCFileThatDependsAnotherTOCSourceFile() {
|
||||
|
||||
/*
|
||||
|
||||
The first source file defines attributes that the second file references.
|
||||
|
||||
Example makeup we will create:
|
||||
|
||||
TOC_Source.xml
|
||||
|
||||
<tocdef id="root" target="fake">
|
||||
<tocdef id="child_1" target="fake" />
|
||||
</tocdef>
|
||||
|
||||
|
||||
Another TOC_Source.xml
|
||||
|
||||
<tocref id="root">
|
||||
<tocref="child_1">
|
||||
<tocdef id="child_2" target="fake" />
|
||||
</tocref>
|
||||
</tocref>
|
||||
|
||||
*/
|
||||
|
||||
Path toc_1 = Paths.get("/fake/path_1/TOC_Source.xml");
|
||||
TOCItemDefinition root = definitionItem("root", toc_1);
|
||||
TOCItemDefinition child_1 = definitionItem(root, "child_1", toc_1);
|
||||
|
||||
Path toc_2 = Paths.get("/fake/path_2/TOC_Source.xml");
|
||||
String root_ID = root.getIDAttribute();
|
||||
String child_1_ID = child_1.getIDAttribute();
|
||||
|
||||
TOCItemReference root_ref = referenceItem(root_ID, toc_2);
|
||||
TOCItemReference child_1_ref = referenceItem(root_ref, child_1_ID, toc_2);
|
||||
TOCItemDefinition child_2 = definitionItem(child_1_ref, "child_2", toc_2);
|
||||
|
||||
TOCItemProviderTestStub tocProvider = new TOCItemProviderTestStub();
|
||||
tocProvider.addDefinition(root);
|
||||
tocProvider.addDefinition(child_1);
|
||||
tocProvider.addDefinition(child_2);// in the second TOC file
|
||||
|
||||
TOCSpyWriter spy = printOverlayTree(tocProvider, toc_2);
|
||||
|
||||
assertNodeCount(spy, 3);
|
||||
assertOrder(spy, 1, root);
|
||||
assertOrder(spy, 2, child_1);
|
||||
assertOrder(spy, 3, child_2);
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// Inner Classes
|
||||
//==================================================================================================
|
||||
|
||||
private TOCSpyWriter printOverlayTree(TOCItemProviderTestStub tocItemProvider, Path tocFile) {
|
||||
|
||||
//
|
||||
// Create a test version of the LinkDatabase for the overlay tree, with test versions of
|
||||
// it's required TOC input file and HelpModuleLocation
|
||||
//
|
||||
GhidraTOCFileDummy toc = new GhidraTOCFileDummy(tocFile);
|
||||
OverlayHelpModuleLocationTestStub location = new OverlayHelpModuleLocationTestStub(toc);
|
||||
LinkDatabaseTestStub db = new LinkDatabaseTestStub(location);
|
||||
|
||||
// This is the class we are testing!!
|
||||
OverlayHelpTree overlayHelpTree = new OverlayHelpTree(tocItemProvider, db);
|
||||
|
||||
TOCSpyWriter spy = new TOCSpyWriter();
|
||||
String TOCID = tocFile.toUri().toString();
|
||||
overlayHelpTree.printTreeForID(spy, TOCID);
|
||||
|
||||
System.out.println(spy.toString());
|
||||
|
||||
return spy;
|
||||
}
|
||||
|
||||
private TOCItemDefinition definitionItem(String ID, Path tocSourceFile) {
|
||||
return definitionItem(null, ID, tocSourceFile);
|
||||
}
|
||||
|
||||
private TOCItemDefinition definitionItem(TOCItem parent, String ID, Path tocSourceFile) {
|
||||
String target = "fake";
|
||||
String sort = "";
|
||||
int line = 1;
|
||||
return new TOCItemDefinition(parent, tocSourceFile, ID, ID, target, sort, line);
|
||||
}
|
||||
|
||||
private TOCItemReference referenceItem(String referenceID, Path tocSourceFile) {
|
||||
return referenceItem(null, referenceID, tocSourceFile);
|
||||
}
|
||||
|
||||
private TOCItemReference referenceItem(TOCItem parent, String referenceID, Path tocSourceFile) {
|
||||
return new TOCItemReference(parent, tocSourceFile, referenceID, 1);
|
||||
}
|
||||
|
||||
private TOCItemExternal externalItem(String ID) {
|
||||
return externalItem(null, ID);
|
||||
}
|
||||
|
||||
private TOCItemExternal externalItem(TOCItem parent, String ID) {
|
||||
Path tocFile = Paths.get("/fake/path_1/PreBuild_TOC.xml");
|
||||
String target = "fake";
|
||||
String sort = "";
|
||||
int line = 1;
|
||||
return new TOCItemExternal(parent, tocFile, ID, ID, target, sort, line);
|
||||
}
|
||||
|
||||
private TOCItemExternal externalItemAlt(TOCItem parent, String ID) {
|
||||
Path tocFile = Paths.get("/fake/path_1/PreBuild_TOC.xml");
|
||||
String target = "fake";
|
||||
String sort = "";
|
||||
int line = 1;
|
||||
return new TOCItemExternal(parent, tocFile, ID, ID, target, sort, line);
|
||||
}
|
||||
|
||||
private void assertOrder(TOCSpyWriter spy, int ordinal, TOCItem item) {
|
||||
String ID = spy.getItem(ordinal - 1 /* make an index */);
|
||||
assertEquals("Did not find TOC item at expected index: " + ordinal, item.getIDAttribute(),
|
||||
ID);
|
||||
}
|
||||
|
||||
private void assertNodeCount(TOCSpyWriter spy, int count) {
|
||||
assertEquals("Did not get exactly one node per TOC item input", count, spy.getItemCount());
|
||||
}
|
||||
|
||||
private class TOCSpyWriter extends PrintWriter {
|
||||
|
||||
private StringWriter stringWriter;
|
||||
|
||||
private List<String> tocItems = new ArrayList<>();
|
||||
|
||||
public TOCSpyWriter() {
|
||||
super(new StringWriter(), true);
|
||||
stringWriter = ((StringWriter) out);
|
||||
}
|
||||
|
||||
String getItem(int position) {
|
||||
return tocItems.get(position);
|
||||
}
|
||||
|
||||
int getItemCount() {
|
||||
return tocItems.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void println(String s) {
|
||||
super.println(s);
|
||||
|
||||
s = s.trim();
|
||||
if (!s.startsWith("<tocitem")) {
|
||||
return;
|
||||
}
|
||||
|
||||
storeDisplayAttribute(s);
|
||||
}
|
||||
|
||||
private void storeDisplayAttribute(String s) {
|
||||
// create a pattern to pull out the display string
|
||||
Pattern p = Pattern.compile(".*display=\"(.*)\" toc_id.*");
|
||||
Matcher matcher = p.matcher(s.trim());
|
||||
|
||||
if (!matcher.matches()) {
|
||||
return;// not a TOC item
|
||||
}
|
||||
|
||||
String value = matcher.group(1);
|
||||
tocItems.add(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return stringWriter.getBuffer().toString();
|
||||
}
|
||||
}
|
||||
|
||||
private class TOCItemProviderTestStub implements TOCItemProvider {
|
||||
|
||||
Map<String, TOCItemExternal> externals = new HashMap<>();
|
||||
Map<String, TOCItemDefinition> definitions = new HashMap<>();
|
||||
|
||||
void addExternal(TOCItemExternal item) {
|
||||
String displayText = item.getIDAttribute();
|
||||
externals.put(displayText, item);
|
||||
}
|
||||
|
||||
void addDefinition(TOCItemDefinition item) {
|
||||
String ID = item.getIDAttribute();
|
||||
definitions.put(ID, item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, TOCItemExternal> getTOCItemExternalsByDisplayMapping() {
|
||||
return externals;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, TOCItemDefinition> getTOCItemDefinitionsByIDMapping() {
|
||||
return definitions;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class LinkDatabaseTestStub extends LinkDatabase {
|
||||
|
||||
public LinkDatabaseTestStub(HelpModuleLocation loc) {
|
||||
super(HelpModuleCollection.fromHelpLocations(Collections.singleton(loc)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getIDForLink(String target) {
|
||||
return "test_ID_" + target;
|
||||
}
|
||||
}
|
||||
|
||||
private class OverlayHelpModuleLocationTestStub extends HelpModuleLocationTestDouble {
|
||||
|
||||
OverlayHelpModuleLocationTestStub(GhidraTOCFileDummy toc) {
|
||||
super(Paths.get("/fake/help"));
|
||||
this.sourceTOCFile = toc;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void loadHelpTopics() {
|
||||
// no! ...don't really go to the filesystem
|
||||
}
|
||||
|
||||
@Override
|
||||
public GhidraTOCFile loadSourceTOCFile() {
|
||||
return null;// we set this in the constructor
|
||||
}
|
||||
|
||||
@Override
|
||||
public HelpSet loadHelpSet() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isHelpInputSource() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class GhidraTOCFileDummy extends GhidraTOCFileTestDouble {
|
||||
|
||||
public GhidraTOCFileDummy(Path path) {
|
||||
super(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
/* ###
|
||||
* 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 help.validator.location;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
||||
import help.validator.location.HelpModuleLocation;
|
||||
|
||||
public abstract class HelpModuleLocationTestDouble extends HelpModuleLocation {
|
||||
|
||||
// this class exists to open up the package-level constructor
|
||||
public HelpModuleLocationTestDouble(Path source) {
|
||||
super(source);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/* ###
|
||||
* 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 help.validator.model;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import generic.test.AbstractGenericTest;
|
||||
import ghidra.util.exception.AssertException;
|
||||
|
||||
public class AnchorDefinitionTest extends AbstractGenericTest {
|
||||
|
||||
public AnchorDefinitionTest() {
|
||||
super();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFileWithoutAnchor() {
|
||||
// this should generate and ID that is the filename only
|
||||
Path fullPath = Paths.get("/fake/full/path/help/topics/TopicName/HelpFilename.html"); // dir case
|
||||
AnchorDefinition def = new AnchorDefinition(fullPath, null, 1);
|
||||
assertEquals("TopicName_HelpFilename", def.getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFileInHelpTopicDir() {
|
||||
|
||||
Path fullPath = Paths.get("/fake/full/path/help/topics/TopicName/HelpFilename.html"); // dir case
|
||||
AnchorDefinition def = new AnchorDefinition(fullPath, "anchor_1", 1);
|
||||
assertEquals("TopicName_anchor_1", def.getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFileInHelpTopicJar() {
|
||||
Path fullPath = Paths.get("/help/topics/TopicName/HelpFilename.html"); // jar case
|
||||
AnchorDefinition def = new AnchorDefinition(fullPath, "anchor_1", 1);
|
||||
assertEquals("TopicName_anchor_1", def.getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFileInHelpDir_NotUnderHelpTopic() {
|
||||
Path fullPath = Paths.get("/fake/full/path/help/HelpFilename.html"); // dir case
|
||||
|
||||
try {
|
||||
new AnchorDefinition(fullPath, "anchor_1", 1);
|
||||
Assert.fail("Did not fail with file not living under a help topic directory");
|
||||
}
|
||||
catch (AssertException e) {
|
||||
// good
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/* ###
|
||||
* 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.
|
||||
* 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 help.validator.model;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
||||
public class GhidraTOCFileTestDouble extends GhidraTOCFile {
|
||||
|
||||
// this class exists to open up constructor access
|
||||
public GhidraTOCFileTestDouble(Path sourceFile) {
|
||||
super(sourceFile);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 help.validator.model;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.*;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import help.AbstractHelpTest;
|
||||
import help.validator.*;
|
||||
import help.validator.location.DirectoryHelpModuleLocation;
|
||||
|
||||
public class HelpFileTest extends AbstractHelpTest {
|
||||
|
||||
@Test
|
||||
public void testGoodHTML() throws IOException {
|
||||
|
||||
Path helpDir = createTempHelpDir();
|
||||
addRequiredHelpDirStructure(helpDir);
|
||||
|
||||
Path topic = createFakeHelpTopic(helpDir);
|
||||
|
||||
DirectoryHelpModuleLocation helpLocation =
|
||||
new DirectoryHelpModuleLocation(helpDir.toFile());
|
||||
|
||||
Path html = createGoodHTMLFile(topic);
|
||||
new HelpFile(helpLocation, html);
|
||||
|
||||
// if we get here, then no exceptions happened
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBadHTML_InvalidStyleSheet() throws Exception {
|
||||
|
||||
Path helpDir = createTempHelpDir();
|
||||
addRequiredHelpDirStructure(helpDir);
|
||||
|
||||
Path topic = createFakeHelpTopic(helpDir);
|
||||
|
||||
DirectoryHelpModuleLocation helpLocation =
|
||||
new DirectoryHelpModuleLocation(helpDir.toFile());
|
||||
|
||||
Path html = createBadHTMLFile_InvalidStyleSheet(topic);
|
||||
|
||||
try {
|
||||
new HelpFile(helpLocation, html);
|
||||
fail("Parsing did not fail for invalid stylesheet");
|
||||
}
|
||||
catch (Exception e) {
|
||||
// good
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBadHTML_InvalidAnchorRef_BadURI() throws Exception {
|
||||
|
||||
Path helpDir = createTempHelpDir();
|
||||
addRequiredHelpDirStructure(helpDir);
|
||||
|
||||
Path topic = createFakeHelpTopic(helpDir);
|
||||
|
||||
DirectoryHelpModuleLocation helpLocation =
|
||||
new DirectoryHelpModuleLocation(helpDir.toFile());
|
||||
|
||||
Path html = createBadHTMLFile_InvalidAnchor_BadURI(topic);
|
||||
|
||||
try {
|
||||
new HelpFile(helpLocation, html);
|
||||
fail("Parsing did not fail for invalid stylesheet");
|
||||
}
|
||||
catch (Exception e) {
|
||||
// good
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBadHTML_InvalidAnchorRef_WrongAttribtues() throws Exception {
|
||||
// no 'name' or 'href' attribute
|
||||
Path helpDir = createTempHelpDir();
|
||||
addRequiredHelpDirStructure(helpDir);
|
||||
|
||||
Path topic = createFakeHelpTopic(helpDir);
|
||||
|
||||
DirectoryHelpModuleLocation helpLocation =
|
||||
new DirectoryHelpModuleLocation(helpDir.toFile());
|
||||
|
||||
Path html = createBadHTMLFile_InvalidAnchor_WrongAttributes(topic);
|
||||
|
||||
try {
|
||||
new HelpFile(helpLocation, html);
|
||||
fail("Parsing did not fail for invalid stylesheet");
|
||||
}
|
||||
catch (Exception e) {
|
||||
// good
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBadHTML_InvalidIMG_WrongAttribtues() throws Exception {
|
||||
// no 'src'
|
||||
Path helpDir = createTempHelpDir();
|
||||
addRequiredHelpDirStructure(helpDir);
|
||||
|
||||
Path topic = createFakeHelpTopic(helpDir);
|
||||
|
||||
DirectoryHelpModuleLocation helpLocation =
|
||||
new DirectoryHelpModuleLocation(helpDir.toFile());
|
||||
|
||||
Path html = createBadHTMLFile_InvalidIMG_WrongAttributes(topic);
|
||||
|
||||
try {
|
||||
new HelpFile(helpLocation, html);
|
||||
fail("Parsing did not fail for invalid stylesheet");
|
||||
}
|
||||
catch (Exception e) {
|
||||
// good
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCommentGetsIgnored() throws Exception {
|
||||
|
||||
Path helpDir = createTempHelpDir();
|
||||
addRequiredHelpDirStructure(helpDir);
|
||||
Path topic = createFakeHelpTopic(helpDir);
|
||||
DirectoryHelpModuleLocation helpLocation =
|
||||
new DirectoryHelpModuleLocation(helpDir.toFile());
|
||||
|
||||
Path html = createGoodHTMLFile_InvalidAnchor_CommentedOut_MultiLineComment(topic);
|
||||
HelpFile helpFile = new HelpFile(helpLocation, html);
|
||||
Collection<HREF> hrefs = helpFile.getAllHREFs();
|
||||
assertTrue(hrefs.isEmpty());
|
||||
}
|
||||
|
||||
// @Test
|
||||
// for debugging a real help file
|
||||
public void test() throws Exception {
|
||||
|
||||
Path path = Paths.get("<home dir>/<git>/ghidra/Ghidra/Features/" +
|
||||
"Base/src/main/help/help/topics/Annotations/Annotations.html");
|
||||
|
||||
Path helpDir = createTempHelpDir();
|
||||
addRequiredHelpDirStructure(helpDir);
|
||||
DirectoryHelpModuleLocation helpLocation =
|
||||
new DirectoryHelpModuleLocation(helpDir.toFile());
|
||||
AnchorManager anchorManager = new AnchorManager();
|
||||
ReferenceTagProcessor tagProcessor = new ReferenceTagProcessor(helpLocation, anchorManager);
|
||||
HTMLFileParser.scanHtmlFile(path, tagProcessor);
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// Private Methods
|
||||
//==================================================================================================
|
||||
|
||||
/** Has valid links */
|
||||
private Path createGoodHTMLFile(Path topic) throws IOException {
|
||||
String anchor = "ManagePluginsDialog";
|
||||
return createHelpContent(topic, anchor);
|
||||
}
|
||||
|
||||
private Path createBadHTMLFile_InvalidAnchor_WrongAttributes(Path topic) throws IOException {
|
||||
Path htmlPath = topic.resolve("FakeHTML_WrongAttributes.html");
|
||||
Path file = Files.createFile(htmlPath);
|
||||
|
||||
String badAttr = "bob=1";
|
||||
|
||||
//@formatter:off
|
||||
String HTML =
|
||||
"<HTML>\n" +
|
||||
"<HEAD>\n" +
|
||||
"<TITLE>Configure Tool</TITLE>\n" +
|
||||
"<LINK rel=\"stylesheet\" type=\"text/css\" href=\"../../shared/Frontpage.css\">\n" +
|
||||
"</HEAD>\n" +
|
||||
"<BODY>\n" +
|
||||
"<H1><A name=\"ManagePluginsDialog\"></A>Configure Tool</H1>\n" +
|
||||
"Some text with reference to shared image <a "+badAttr+">Click me</a>\n" +
|
||||
"\n" +
|
||||
"</BODY>\n" +
|
||||
"</HTML>\n";
|
||||
//@formatter:on
|
||||
|
||||
Files.write(file, HTML.getBytes(), StandardOpenOption.CREATE);
|
||||
return file;
|
||||
}
|
||||
|
||||
private Path createBadHTMLFile_InvalidIMG_WrongAttributes(Path topic) throws IOException {
|
||||
Path htmlPath = topic.resolve("FakeHTML_WrongAttributes.html");
|
||||
Path file = Files.createFile(htmlPath);
|
||||
|
||||
String badAttr = "bob=1";
|
||||
|
||||
//@formatter:off
|
||||
String HTML =
|
||||
"<HTML>\n" +
|
||||
"<HEAD>\n" +
|
||||
"<TITLE>Configure Tool</TITLE>\n" +
|
||||
"<LINK rel=\"stylesheet\" type=\"text/css\" href=\"../../shared/Frontpage.css\">\n" +
|
||||
"</HEAD>\n" +
|
||||
"<BODY>\n" +
|
||||
"<H1><A name=\"ManagePluginsDialog\"></A>Configure Tool</H1>\n" +
|
||||
"Some text with reference to shared image <IMG "+badAttr+"s>\n" +
|
||||
"\n" +
|
||||
"</BODY>\n" +
|
||||
"</HTML>\n";
|
||||
//@formatter:on
|
||||
|
||||
Files.write(file, HTML.getBytes(), StandardOpenOption.CREATE);
|
||||
return file;
|
||||
}
|
||||
|
||||
private Path createBadHTMLFile_InvalidAnchor_BadURI(Path topic) throws IOException {
|
||||
Path htmlPath = topic.resolve("FakeHTML_BadURI.html");
|
||||
Path file = Files.createFile(htmlPath);
|
||||
|
||||
String badURI = ":baduri"; // no scheme name on this URI
|
||||
|
||||
//@formatter:off
|
||||
String HTML =
|
||||
"<HTML>\n" +
|
||||
"<HEAD>\n" +
|
||||
"<TITLE>Configure Tool</TITLE>\n" +
|
||||
"<LINK rel=\"stylesheet\" type=\"text/css\" href=\"../../shared/Frontpage.css\">\n" +
|
||||
"</HEAD>\n" +
|
||||
"<BODY>\n" +
|
||||
"<H1><A name=\"ManagePluginsDialog\"></A>Configure Tool</H1>\n" +
|
||||
"Some text with reference to shared image <a href=\""+badURI+"\">Click me</a>\n" +
|
||||
"\n" +
|
||||
"</BODY>\n" +
|
||||
"</HTML>\n";
|
||||
//@formatter:on
|
||||
|
||||
Files.write(file, HTML.getBytes(), StandardOpenOption.CREATE);
|
||||
return file;
|
||||
}
|
||||
|
||||
private Path createBadHTMLFile_InvalidStyleSheet(Path topic) throws IOException {
|
||||
Path htmlPath = topic.resolve("FakeHTML_InvalidStyleSheet.html");
|
||||
Path file = Files.createFile(htmlPath);
|
||||
|
||||
String badName = "bad_name";
|
||||
|
||||
//@formatter:off
|
||||
String HTML =
|
||||
"<HTML>\n" +
|
||||
"<HEAD>\n" +
|
||||
"<TITLE>Configure Tool</TITLE>\n" +
|
||||
"<LINK rel=\"stylesheet\" type=\"text/css\" href=\"../../shared/"+badName+".css\">\n" +
|
||||
"</HEAD>\n" +
|
||||
"<BODY>\n" +
|
||||
"<H1><A name=\"ManagePluginsDialog\"></A>Configure Tool</H1>\n" +
|
||||
"Some text with reference to shared image <IMG src=\"../../shared/test.png\">\n" +
|
||||
"\n" +
|
||||
"</BODY>\n" +
|
||||
"</HTML>\n";
|
||||
//@formatter:on
|
||||
|
||||
Files.write(file, HTML.getBytes(), StandardOpenOption.CREATE);
|
||||
return file;
|
||||
}
|
||||
|
||||
private Path createGoodHTMLFile_InvalidAnchor_CommentedOut_MultiLineComment(Path topic)
|
||||
throws IOException {
|
||||
Path htmlPath = topic.resolve("HTMLWithComment.html");
|
||||
Path file = Files.createFile(htmlPath);
|
||||
|
||||
String badURI = ":baduri"; // no scheme name on this URI
|
||||
|
||||
//@formatter:off
|
||||
String HTML =
|
||||
"<HTML>\n" +
|
||||
"<HEAD>\n" +
|
||||
"<TITLE>Configure Tool</TITLE>\n" +
|
||||
"<LINK rel=\"stylesheet\" type=\"text/css\" href=\"../../shared/Frontpage.css\">\n" +
|
||||
"</HEAD>\n" +
|
||||
"<BODY>\n" +
|
||||
"<H1><A name=\"ManagePluginsDialog\"></A>Configure Tool</H1>\n" +
|
||||
" <!--" +
|
||||
" Some text with reference to shared image <a href=\""+badURI+"\">Click me</a>\n" +
|
||||
" -->" +
|
||||
"\n" +
|
||||
"</BODY>\n" +
|
||||
"</HTML>\n";
|
||||
//@formatter:on
|
||||
|
||||
Files.write(file, HTML.getBytes(), StandardOpenOption.CREATE);
|
||||
return file;
|
||||
}
|
||||
}
|
||||
@@ -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
-3
@@ -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;
|
||||
|
||||
/**
|
||||
|
||||
+1
-1
@@ -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;
|
||||
|
||||
+1
-1
@@ -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);
|
||||
|
||||
+1
@@ -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;
|
||||
|
||||
+2
-3
@@ -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
|
||||
+1
-1
@@ -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 {
|
||||
@@ -1,2 +1,3 @@
|
||||
##VERSION: 2.0
|
||||
build.gradle||GHIDRA||||END|
|
||||
src/test.slow/java/ghidra/pcodeCPort/slgh_compile/pcode1.xml||GHIDRA||||END|
|
||||
|
||||
+131
@@ -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());
|
||||
}
|
||||
}
|
||||
+1736
File diff suppressed because it is too large
Load Diff
+307
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
+172
@@ -0,0 +1,172 @@
|
||||
/* ###
|
||||
* 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.script;
|
||||
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import org.junit.*;
|
||||
import org.junit.experimental.categories.Category;
|
||||
|
||||
import generic.jar.ResourceFile;
|
||||
import generic.test.category.NightlyCategory;
|
||||
import ghidra.app.services.ProgramManager;
|
||||
import ghidra.framework.Application;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.program.model.symbol.RefType;
|
||||
import ghidra.program.model.symbol.Reference;
|
||||
import ghidra.test.*;
|
||||
|
||||
@Category(NightlyCategory.class)
|
||||
public class WindowsResourceReferenceScriptTest extends AbstractGhidraHeadedIntegrationTest {
|
||||
|
||||
private TestEnv env;
|
||||
private File script;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
env = new TestEnv();
|
||||
|
||||
ResourceFile resourceFile =
|
||||
Application.getModuleFile("Decompiler", "ghidra_scripts/WindowsResourceReference.java");
|
||||
|
||||
script = resourceFile.getFile(true);
|
||||
}
|
||||
|
||||
private void openProgram(Program program) {
|
||||
ProgramManager pm = env.getTool().getService(ProgramManager.class);
|
||||
pm.openProgram(program.getDomainFile());
|
||||
}
|
||||
|
||||
private void closeProgram() {
|
||||
ProgramManager pm = env.getTool().getService(ProgramManager.class);
|
||||
pm.closeProgram();
|
||||
waitForPostedSwingRunnables();
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
env.dispose();
|
||||
}
|
||||
|
||||
/*
|
||||
* Checks against known result set of addresses for use in regression testing.
|
||||
* Checks each address location of created reference and checks
|
||||
* that the correct format of address has been created to the resource
|
||||
*/
|
||||
@Test
|
||||
public void testWinmineNormalCases() throws Exception {
|
||||
Reference[] refs; //Array of mnemonic references
|
||||
RefType type; //Type of reference
|
||||
Instruction inst;
|
||||
Boolean isAddr;
|
||||
|
||||
Program program = env.getProgram("Winmine__XP.exe.gzf");
|
||||
openProgram(program);
|
||||
|
||||
ScriptTaskListener scriptId = env.runScript(script);
|
||||
waitForScriptCompletion(scriptId, 65000);
|
||||
program.flushEvents();
|
||||
waitForPostedSwingRunnables();
|
||||
|
||||
Listing listing = program.getListing();
|
||||
|
||||
//Fill array with addresses of reference results
|
||||
Address[] winmineTestAddrs = propagateWinMineTestAddrs(program);
|
||||
|
||||
for (Address winmineTestAddr : winmineTestAddrs) {
|
||||
inst = listing.getInstructionAt(winmineTestAddr);
|
||||
refs = inst.getMnemonicReferences();
|
||||
//Check a reference exists on the Mnemonic
|
||||
assertNotNull(refs);
|
||||
type = refs[0].getReferenceType();
|
||||
isAddr = refs[0].getToAddress().isMemoryAddress();
|
||||
//Check the reference is a real memory address
|
||||
assertTrue(isAddr);
|
||||
//Check the reference type created is of type DATA
|
||||
assertTrue(type.equals(RefType.DATA));
|
||||
}
|
||||
closeProgram();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMIPNormalCases() throws Exception {
|
||||
Reference[] refs; //Array of mnemonic references
|
||||
RefType type; //Type of reference
|
||||
Boolean isAddr;
|
||||
Instruction inst;
|
||||
|
||||
Program program = env.getProgram("mip.exe.gzf");
|
||||
openProgram(program);
|
||||
|
||||
ScriptTaskListener scriptID = env.runScript(script);
|
||||
waitForScriptCompletion(scriptID, 60000);
|
||||
program.flushEvents();
|
||||
waitForPostedSwingRunnables();
|
||||
|
||||
Listing listing = program.getListing();
|
||||
|
||||
Address[] mipTestAddrs = propagateMIPTestAddrs(program);
|
||||
for (Address mipTestAddr : mipTestAddrs) {
|
||||
inst = listing.getInstructionAt(mipTestAddr);
|
||||
refs = inst.getMnemonicReferences();
|
||||
//Check a reference exists on the mnemonic
|
||||
assertNotNull(refs);
|
||||
type = refs[0].getReferenceType();
|
||||
isAddr = refs[0].getToAddress().isMemoryAddress();
|
||||
//check the reference is a real memory address
|
||||
assertTrue(isAddr);
|
||||
//check the reference type created is of type DATA
|
||||
assertTrue(type.equals(RefType.DATA));
|
||||
}
|
||||
|
||||
closeProgram();
|
||||
}
|
||||
|
||||
private Address addr(long offset, Program program) {
|
||||
return program.getAddressFactory().getDefaultAddressSpace().getAddress(offset);
|
||||
}
|
||||
|
||||
/*
|
||||
* Creates and returns the known result set to check against for use
|
||||
* in regression testing
|
||||
*/
|
||||
protected Address[] propagateWinMineTestAddrs(Program pgm) {
|
||||
Address[] winmineTestAddrs = { addr(0x01001b99, pgm), addr(0x01001bc2, pgm),
|
||||
addr(0x01001b5e, pgm), addr(0x010022c2, pgm), addr(0x01002243, pgm),
|
||||
addr(0x01003d52, pgm), addr(0x010022ac, pgm), addr(0x01002334, pgm),
|
||||
addr(0x01001f3b, pgm), addr(0x0100398f, pgm), addr(0x01003ade, pgm),
|
||||
addr(0x01003aec, pgm), addr(0x01003ad0, pgm), addr(0x010039c5, pgm),
|
||||
addr(0x01003d45, pgm), addr(0x0100385b, pgm), addr(0x01003d36, pgm),
|
||||
addr(0x01003920, pgm), addr(0x0100390e, pgm) };
|
||||
|
||||
return winmineTestAddrs;
|
||||
}
|
||||
|
||||
protected Address[] propagateMIPTestAddrs(Program pgm) {
|
||||
Address[] mipTestAddrs =
|
||||
|
||||
{ addr(0x1400172c7L, pgm), addr(0x14005282dL, pgm), addr(0x14005276cL, pgm),
|
||||
addr(0x1400523baL, pgm), addr(0x14004ca38L, pgm), addr(0x14003d855L, pgm),
|
||||
addr(0x14001a964L, pgm), addr(0x14001846fL, pgm), addr(0x140025c87L, pgm) };
|
||||
|
||||
return mipTestAddrs;
|
||||
}
|
||||
|
||||
}
|
||||
+195
@@ -0,0 +1,195 @@
|
||||
/* ###
|
||||
* 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.util.bin.format.dwarf4.next;
|
||||
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.jdom.JDOMException;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.experimental.categories.Category;
|
||||
|
||||
import generic.jar.ResourceFile;
|
||||
import generic.test.category.NightlyCategory;
|
||||
import ghidra.program.model.lang.*;
|
||||
import ghidra.program.util.DefaultLanguageService;
|
||||
import ghidra.test.AbstractGhidraHeadlessIntegrationTest;
|
||||
import ghidra.util.xml.XmlUtilities;
|
||||
import utilities.util.FileResolutionResult;
|
||||
import utilities.util.FileUtilities;
|
||||
|
||||
@Category(NightlyCategory.class)
|
||||
public class DWARFRegisterMappingsTest extends AbstractGhidraHeadlessIntegrationTest {
|
||||
|
||||
private Language x86Lang;
|
||||
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
x86Lang = DefaultLanguageService.getLanguageService().getLanguage(
|
||||
new LanguageID("x86:LE:32:default"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test reading the DWARF register mappings for every language that has a DWARF register
|
||||
* mapping file specified in its LDEF file.
|
||||
* @throws IOException
|
||||
*/
|
||||
@Test
|
||||
public void testReadMappings() throws IOException {
|
||||
for (LanguageDescription langDesc : DefaultLanguageService.getLanguageService().getLanguageDescriptions(
|
||||
false)) {
|
||||
|
||||
if (!DWARFRegisterMappingsManager.hasDWARFRegisterMapping(langDesc)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Language lang =
|
||||
DefaultLanguageService.getLanguageService().getLanguage(langDesc.getLanguageID());
|
||||
|
||||
ResourceFile mappingFile =
|
||||
DWARFRegisterMappingsManager.getDWARFRegisterMappingFileFor(lang);
|
||||
FileResolutionResult dwarfFileFRR;
|
||||
if ((dwarfFileFRR = FileUtilities.existsAndIsCaseDependent(mappingFile)) != null &&
|
||||
!dwarfFileFRR.isOk()) {
|
||||
throw new IOException(
|
||||
"DWARF register mapping filename case problem: " + dwarfFileFRR.getMessage());
|
||||
}
|
||||
|
||||
DWARFRegisterMappings drm = DWARFRegisterMappingsManager.readMappingForLang(lang);
|
||||
assertNotNull("DWARF mapping read failed for " + langDesc.getLanguageID(), drm);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOkMappings() throws JDOMException, IOException {
|
||||
DWARFRegisterMappingsManager.readMappingFrom(
|
||||
XmlUtilities.fromString(
|
||||
"<dwarf><register_mappings><register_mapping dwarf=\"0\" ghidra=\"EAX\" stackpointer=\"true\"/></register_mappings><call_frame_cfa value=\"4\"/></dwarf>"),
|
||||
x86Lang);
|
||||
}
|
||||
|
||||
@Test(expected = IOException.class)
|
||||
public void testMissingStackPointerMappings() throws JDOMException, IOException {
|
||||
DWARFRegisterMappingsManager.readMappingFrom(
|
||||
XmlUtilities.fromString(
|
||||
"<dwarf><register_mappings><register_mapping dwarf=\"0\" ghidra=\"EAX\"/></register_mappings><call_frame_cfa value=\"4\"/></dwarf>"),
|
||||
x86Lang);
|
||||
}
|
||||
|
||||
@Test(expected = IOException.class)
|
||||
public void testBadStackPointerAttrMappings() throws JDOMException, IOException {
|
||||
DWARFRegisterMappingsManager.readMappingFrom(
|
||||
XmlUtilities.fromString(
|
||||
"<dwarf><register_mappings><register_mapping dwarf=\"0\" ghidra=\"EAX\" stackpointer=\"NOT_A_BOOLEAN_STRING\"/></register_mappings><call_frame_cfa value=\"4\"/></dwarf>"),
|
||||
x86Lang);
|
||||
}
|
||||
|
||||
@Test(expected = IOException.class)
|
||||
public void testMissingMappingsElemMappings() throws JDOMException, IOException {
|
||||
DWARFRegisterMappingsManager.readMappingFrom(XmlUtilities.fromString("<dwarf></dwarf>"),
|
||||
x86Lang);
|
||||
}
|
||||
|
||||
@Test(expected = IOException.class)
|
||||
public void testBadDuplicateMapping() throws JDOMException, IOException {
|
||||
DWARFRegisterMappingsManager.readMappingFrom(
|
||||
XmlUtilities.fromString(
|
||||
"<dwarf><register_mappings><register_mapping dwarf=\"0\" ghidra=\"EAX\" stackpointer=\"true\"/><register_mapping dwarf=\"0\" ghidra=\"EBX\"/></register_mappings></dwarf>"),
|
||||
x86Lang);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGoodDuplicateMapping() throws JDOMException, IOException {
|
||||
DWARFRegisterMappingsManager.readMappingFrom(
|
||||
XmlUtilities.fromString(
|
||||
"<dwarf><register_mappings><register_mapping dwarf=\"0\" ghidra=\"EAX\" stackpointer=\"true\"/><register_mapping dwarf=\"1\" ghidra=\"EAX\"/></register_mappings></dwarf>"),
|
||||
x86Lang);
|
||||
}
|
||||
|
||||
@Test(expected = IOException.class)
|
||||
public void testBadDWARFRegVal() throws JDOMException, IOException {
|
||||
DWARFRegisterMappingsManager.readMappingFrom(
|
||||
XmlUtilities.fromString(
|
||||
"<dwarf><register_mappings><register_mapping missing_dwarf_attrib=\"0\" ghidra=\"EAX\" stackpointer=\"true\"/></register_mappings></dwarf>"),
|
||||
x86Lang);
|
||||
}
|
||||
|
||||
@Test(expected = IOException.class)
|
||||
public void testBadDWARFRegVal2() throws JDOMException, IOException {
|
||||
DWARFRegisterMappingsManager.readMappingFrom(
|
||||
XmlUtilities.fromString(
|
||||
"<dwarf><register_mappings><register_mapping dwarf=\"not_a_number\" ghidra=\"EAX\" stackpointer=\"true\"/></register_mappings></dwarf>"),
|
||||
x86Lang);
|
||||
}
|
||||
|
||||
@Test(expected = IOException.class)
|
||||
public void testMissingGhidraReg() throws JDOMException, IOException {
|
||||
DWARFRegisterMappingsManager.readMappingFrom(
|
||||
XmlUtilities.fromString(
|
||||
"<dwarf><register_mappings><register_mapping dwarf=\"0\" missing_ghidra_attr=\"EAX\" stackpointer=\"true\"/></register_mappings></dwarf>"),
|
||||
x86Lang);
|
||||
}
|
||||
|
||||
@Test(expected = IOException.class)
|
||||
public void testBadGhidraReg() throws JDOMException, IOException {
|
||||
DWARFRegisterMappingsManager.readMappingFrom(
|
||||
XmlUtilities.fromString(
|
||||
"<dwarf><register_mappings><register_mapping dwarf=\"0\" ghidra=\"BLAH\" stackpointer=\"true\"/></register_mappings></dwarf>"),
|
||||
x86Lang);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAutoInc() throws JDOMException, IOException {
|
||||
DWARFRegisterMappingsManager.readMappingFrom(
|
||||
XmlUtilities.fromString(
|
||||
"<dwarf><register_mappings><register_mapping dwarf=\"0\" ghidra=\"EAX\" stackpointer=\"true\"/><register_mapping dwarf=\"1\" ghidra=\"ST0\" auto_count=\"8\"/></register_mappings></dwarf>"),
|
||||
x86Lang);
|
||||
}
|
||||
|
||||
@Test(expected = IOException.class)
|
||||
public void testTooLargeAutoIncCount() throws JDOMException, IOException {
|
||||
DWARFRegisterMappingsManager.readMappingFrom(
|
||||
XmlUtilities.fromString(
|
||||
"<dwarf><register_mappings><register_mapping dwarf=\"0\" ghidra=\"EAX\" stackpointer=\"true\"/><register_mapping dwarf=\"1\" ghidra=\"ST0\" auto_count=\"16\"/></register_mappings></dwarf>"),
|
||||
x86Lang);
|
||||
}
|
||||
|
||||
@Test(expected = IOException.class)
|
||||
public void testBadFormatAutoIncCount() throws JDOMException, IOException {
|
||||
DWARFRegisterMappingsManager.readMappingFrom(
|
||||
XmlUtilities.fromString(
|
||||
"<dwarf><register_mappings><register_mapping dwarf=\"0\" ghidra=\"EAX\" stackpointer=\"true\"/><register_mapping dwarf=\"1\" ghidra=\"ST0\" auto_count=\"foo\"/></register_mappings></dwarf>"),
|
||||
x86Lang);
|
||||
}
|
||||
|
||||
@Test(expected = IOException.class)
|
||||
public void testBadCFAValue() throws JDOMException, IOException {
|
||||
DWARFRegisterMappingsManager.readMappingFrom(
|
||||
XmlUtilities.fromString(
|
||||
"<dwarf><register_mappings><register_mapping dwarf=\"0\" ghidra=\"EAX\" stackpointer=\"true\"/></register_mappings><call_frame_cfa value=\"foo\"/></dwarf>"),
|
||||
x86Lang);
|
||||
}
|
||||
|
||||
@Test(expected = IOException.class)
|
||||
public void testNegCFAValue() throws JDOMException, IOException {
|
||||
DWARFRegisterMappingsManager.readMappingFrom(
|
||||
XmlUtilities.fromString(
|
||||
"<dwarf><register_mappings><register_mapping dwarf=\"0\" ghidra=\"EAX\" stackpointer=\"true\"/></register_mappings><call_frame_cfa value=\"-1\"/></dwarf>"),
|
||||
x86Lang);
|
||||
}
|
||||
}
|
||||
+248
@@ -0,0 +1,248 @@
|
||||
/* ###
|
||||
* 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.util.demangler;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.junit.*;
|
||||
|
||||
import ghidra.program.database.ProgramDB;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.listing.Data;
|
||||
import ghidra.program.model.listing.Listing;
|
||||
import ghidra.program.model.symbol.*;
|
||||
import ghidra.test.AbstractGhidraHeadlessIntegrationTest;
|
||||
import ghidra.test.ToyProgramBuilder;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
public class DemangledAddressTableTest extends AbstractGhidraHeadlessIntegrationTest {
|
||||
|
||||
private ProgramDB program;
|
||||
private int txID;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
ToyProgramBuilder builder = new ToyProgramBuilder("test", true);
|
||||
builder.createMemory(".text", "0x0100", 0x100);
|
||||
|
||||
// Extent of Address Table determined by one of the following in order of precedence:
|
||||
// 1. Size of undefined array at start of table
|
||||
// 2. Next label beyond start which resides within current block
|
||||
// 3. End of block containing table
|
||||
|
||||
// Address Pointers will only be created if extent of table contains only
|
||||
// Undefined types or pointers
|
||||
|
||||
//@formatter:off
|
||||
builder.setBytes("0x0110", new byte[] {
|
||||
0, 0, 1, 0x40,
|
||||
0, 0, 1, 0x41,
|
||||
0, 0, 1, 0x42,
|
||||
0, 0, 1, 0x43,
|
||||
0, 0, 1, 0x44,
|
||||
0, 0, 1, 0x45,
|
||||
0, 0, 1, 0x46,
|
||||
0, 0, 0, 0, // should be skipped
|
||||
0, 0, 1, 0x48,
|
||||
0, 0, 2, 0x49, // should stop table (no memory)
|
||||
0, 0, 1, 0x4a,
|
||||
0, 0, 1, 0x4b
|
||||
});
|
||||
//@formatter:on
|
||||
|
||||
program = builder.getProgram();
|
||||
txID = program.startTransaction("Test");
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
program.endTransaction(txID, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that the DemangledAddressTable will properly create a sequence of data
|
||||
* pointers. This test deals with the simple case where no existing data
|
||||
* is present. End of block considered end of address table.
|
||||
*/
|
||||
@Test
|
||||
public void testApply_NoNextSymbol_NoData() throws Exception {
|
||||
|
||||
// this is: vtable for UnqiueSpace
|
||||
String mangled = "_ZTV11UniqueSpace";
|
||||
Address addr = addr("0x0110");
|
||||
|
||||
SymbolTable symbolTable = program.getSymbolTable();
|
||||
symbolTable.createLabel(addr, mangled, SourceType.IMPORTED);
|
||||
|
||||
DemangledObject demangled = DemanglerUtil.demangle(mangled);
|
||||
assertTrue(demangled instanceof DemangledAddressTable);
|
||||
|
||||
assertTrue(demangled.applyTo(program, addr, new DemanglerOptions(), TaskMonitor.DUMMY));
|
||||
|
||||
// expected: UniqueSpace::vtable
|
||||
Symbol[] symbols = symbolTable.getSymbols(addr);
|
||||
assertEquals(2, symbols.length);
|
||||
assertEquals("vtable", symbols[0].getName());
|
||||
assertEquals(mangled, symbols[1].getName());
|
||||
|
||||
Namespace ns = symbols[0].getParentNamespace();
|
||||
assertEquals("UniqueSpace", ns.getName(false));
|
||||
|
||||
assertPointersAt(0, 0, "0x110", "0x114", "0x118", "0x11c", "0x120", "0x124", "0x128",
|
||||
/* skip 0 */ "0x130");
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that the DemangledAddressTable will not create a sequence of data
|
||||
* pointers due to a data collision. This test deals with the case where primitive types have been
|
||||
* previously created. End of block considered end of address table.
|
||||
*/
|
||||
@Test
|
||||
public void testApply_NoNextSymbol_DataCollision() throws Exception {
|
||||
|
||||
// this is: vtable for UnqiueSpace
|
||||
String mangled = "_ZTV11UniqueSpace";
|
||||
Address addr = addr("0x0110");
|
||||
|
||||
SymbolTable symbolTable = program.getSymbolTable();
|
||||
symbolTable.createLabel(addr, mangled, SourceType.IMPORTED);
|
||||
|
||||
Listing listing = program.getListing();
|
||||
listing.createData(addr("0x114"), Undefined4DataType.dataType);
|
||||
listing.createData(addr("0x118"), Undefined4DataType.dataType);
|
||||
listing.createData(addr("0x120"), DWordDataType.dataType);
|
||||
|
||||
DemangledObject demangled = DemanglerUtil.demangle(mangled);
|
||||
assertTrue(demangled instanceof DemangledAddressTable);
|
||||
|
||||
assertTrue(demangled.applyTo(program, addr, new DemanglerOptions(), TaskMonitor.DUMMY));
|
||||
|
||||
// expected: UniqueSpace::vtable
|
||||
Symbol[] symbols = symbolTable.getSymbols(addr);
|
||||
assertEquals(2, symbols.length);
|
||||
assertEquals("vtable", symbols[0].getName());
|
||||
assertEquals(mangled, symbols[1].getName());
|
||||
|
||||
Namespace ns = symbols[0].getParentNamespace();
|
||||
assertEquals("UniqueSpace", ns.getName(false));
|
||||
|
||||
// should fail to create pointers since table region (to end of block) contains
|
||||
// data other than pointer or undefined
|
||||
assertPointersAt(3, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that the DemangledAddressTable will properly create a sequence of data
|
||||
* pointers. This test deals with the case where primitive types have been
|
||||
* previously created. Next label considered end of address table.
|
||||
*/
|
||||
@Test
|
||||
public void testApply_WithNextSymbol_UndefinedData() throws Exception {
|
||||
|
||||
// this is: vtable for UnqiueSpace
|
||||
String mangled = "_ZTV11UniqueSpace";
|
||||
Address addr = addr("0x0110");
|
||||
|
||||
SymbolTable symbolTable = program.getSymbolTable();
|
||||
symbolTable.createLabel(addr, mangled, SourceType.IMPORTED);
|
||||
|
||||
Listing listing = program.getListing();
|
||||
listing.createData(addr("0x114"), Undefined4DataType.dataType);
|
||||
listing.createData(addr("0x118"), Undefined4DataType.dataType);
|
||||
listing.createData(addr("0x120"), DWordDataType.dataType);
|
||||
|
||||
symbolTable.createLabel(addr("0x120"), "NextLabel", SourceType.IMPORTED);
|
||||
|
||||
DemangledObject demangled = DemanglerUtil.demangle(mangled);
|
||||
assertTrue(demangled instanceof DemangledAddressTable);
|
||||
|
||||
assertTrue(demangled.applyTo(program, addr, new DemanglerOptions(), TaskMonitor.DUMMY));
|
||||
|
||||
// expected: UniqueSpace::vtable
|
||||
Symbol[] symbols = symbolTable.getSymbols(addr);
|
||||
assertEquals(2, symbols.length);
|
||||
assertEquals("vtable", symbols[0].getName());
|
||||
assertEquals(mangled, symbols[1].getName());
|
||||
|
||||
Namespace ns = symbols[0].getParentNamespace();
|
||||
assertEquals("UniqueSpace", ns.getName(false));
|
||||
|
||||
assertPointersAt(1, 0, "0x110", "0x114", "0x118", "0x11c");
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that the DemangledAddressTable will properly create a sequence of data
|
||||
* pointers. This test deals with the case where primitive types have been
|
||||
* previously created where the first is an undefined array which dictates the
|
||||
* extent of the address table. Next label beyond end of address table.
|
||||
*/
|
||||
@Test
|
||||
public void testApply_WithUndefinedArray() throws Exception {
|
||||
|
||||
// this is: vtable for UnqiueSpace
|
||||
String mangled = "_ZTV11UniqueSpace";
|
||||
Address addr = addr("0x0110");
|
||||
|
||||
SymbolTable symbolTable = program.getSymbolTable();
|
||||
symbolTable.createLabel(addr, mangled, SourceType.IMPORTED);
|
||||
|
||||
Listing listing = program.getListing();
|
||||
listing.createData(addr("0x110"), Undefined.getUndefinedDataType(12));
|
||||
listing.createData(addr("0x11c"), Undefined4DataType.dataType);
|
||||
listing.createData(addr("0x120"), DWordDataType.dataType);
|
||||
|
||||
symbolTable.createLabel(addr("0x120"), "NextLabel", SourceType.IMPORTED);
|
||||
|
||||
DemangledObject demangled = DemanglerUtil.demangle(mangled);
|
||||
assertTrue(demangled instanceof DemangledAddressTable);
|
||||
|
||||
assertTrue(demangled.applyTo(program, addr, new DemanglerOptions(), TaskMonitor.DUMMY));
|
||||
|
||||
// expected: UniqueSpace::vtable
|
||||
Symbol[] symbols = symbolTable.getSymbols(addr);
|
||||
assertEquals(2, symbols.length);
|
||||
assertEquals("vtable", symbols[0].getName());
|
||||
assertEquals(mangled, symbols[1].getName());
|
||||
|
||||
Namespace ns = symbols[0].getParentNamespace();
|
||||
assertEquals("UniqueSpace", ns.getName(false));
|
||||
|
||||
assertPointersAt(2, 0, "0x110", "0x114", "0x118");
|
||||
}
|
||||
|
||||
private void assertPointersAt(int totalNonPointerData, int totalInstructions, String... addrs) {
|
||||
Listing listing = program.getListing();
|
||||
int index = 0;
|
||||
for (Data d : listing.getDefinedData(true)) {
|
||||
if (d.isPointer()) {
|
||||
assertTrue("too many pointers found, expected only " + addrs.length,
|
||||
index < addrs.length);
|
||||
assertEquals("unexpected pointer at " + d.getAddress(), addr(addrs[index++]),
|
||||
d.getAddress());
|
||||
}
|
||||
}
|
||||
assertEquals("insufficient pointers created", addrs.length, index);
|
||||
assertEquals("missing expected non-pointer data", totalNonPointerData,
|
||||
listing.getNumDefinedData() - index);
|
||||
assertEquals("missing expected instructions", totalInstructions,
|
||||
listing.getNumInstructions());
|
||||
}
|
||||
|
||||
private Address addr(String addr) {
|
||||
return program.getAddressFactory().getAddress(addr);
|
||||
}
|
||||
}
|
||||
+356
@@ -0,0 +1,356 @@
|
||||
/* ###
|
||||
* 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.util.demangler;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.junit.*;
|
||||
|
||||
import ghidra.program.database.ProgramBuilder;
|
||||
import ghidra.program.database.ProgramDB;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressSet;
|
||||
import ghidra.program.model.data.VoidDataType;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.program.model.symbol.*;
|
||||
import ghidra.test.AbstractGhidraHeadlessIntegrationTest;
|
||||
import ghidra.test.ToyProgramBuilder;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
public class DemangledFunctionTest extends AbstractGhidraHeadlessIntegrationTest {
|
||||
|
||||
private ProgramBuilder programBuilder;
|
||||
private ProgramDB program;
|
||||
private int txID;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
programBuilder = new ToyProgramBuilder("test", true);
|
||||
programBuilder.createMemory(".text", "0x0100", 0x100);
|
||||
program = programBuilder.getProgram();
|
||||
txID = program.startTransaction("Test");
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
program.endTransaction(txID, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that the DemangledFunction will properly create a cascade of namespaces for
|
||||
* functions that live inside of a class that lives inside of a namespace.
|
||||
* This test applies a demangled name where the mangled name does NOT exist.
|
||||
*/
|
||||
@Test
|
||||
public void testApply_FunctionInClassInNamespace() throws Exception {
|
||||
|
||||
// this is: public long __thiscall ATL::CRegKey::Close(void)
|
||||
String mangled = "?CloseM@CRegKeyM@ATL@@QAEJXZ";
|
||||
DemangledObject demangled = DemanglerUtil.demangle(mangled);
|
||||
assertTrue(demangled instanceof DemangledFunction);
|
||||
|
||||
Address addr = addr("0x0101");
|
||||
assertTrue(demangled.applyTo(program, addr, new DemanglerOptions(), TaskMonitor.DUMMY));
|
||||
|
||||
assertFunction("CloseM", addr);
|
||||
|
||||
SymbolTable symbolTable = program.getSymbolTable();
|
||||
|
||||
// expected: ATL::CRegKey::Close
|
||||
Symbol[] symbols = symbolTable.getSymbols(addr);
|
||||
assertEquals(1, symbols.length);
|
||||
assertEquals("CloseM", symbols[0].getName());
|
||||
|
||||
Namespace ns = symbols[0].getParentNamespace();
|
||||
assertEquals("CRegKeyM", ns.getName(false));
|
||||
|
||||
ns = ns.getParentNamespace();
|
||||
assertEquals("ATL", ns.getName(false));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that the DemangledFunction will properly update a thunk function
|
||||
* with its namespace, and ripple through to the underlying default thunked
|
||||
* function. The thunk 'this' parameter should utilize the Class
|
||||
* within which the thunk resides.
|
||||
*/
|
||||
@Test
|
||||
public void testApplyToThunk_FunctionInClassInNamespace() throws Exception {
|
||||
|
||||
// this is: public long __thiscall ATL::CRegKey::Close(void)
|
||||
String mangled = "?Close@CRegKey@ATL@@QAEJXZ";
|
||||
DemangledObject demangled = DemanglerUtil.demangle(mangled);
|
||||
assertTrue(demangled instanceof DemangledFunction);
|
||||
|
||||
FunctionManager functionMgr = program.getFunctionManager();
|
||||
Function f1 = functionMgr.createFunction(null, addr("0x0110"),
|
||||
new AddressSet(addr("0x0110")), SourceType.DEFAULT);
|
||||
Function f2 = functionMgr.createFunction(mangled, addr("0x0100"),
|
||||
new AddressSet(addr("0x0100")), SourceType.IMPORTED);
|
||||
f2.setThunkedFunction(f1);
|
||||
|
||||
Address addr = addr("0x0100");
|
||||
demangled.applyTo(program, addr, new DemanglerOptions(), TaskMonitor.DUMMY);
|
||||
|
||||
assertFunction("Close", addr);
|
||||
assertNoBookmarkAt(addr);
|
||||
|
||||
SymbolTable symbolTable = program.getSymbolTable();
|
||||
|
||||
// expected: ATL::CRegKey::Close
|
||||
Symbol[] symbols = symbolTable.getSymbols(addr);
|
||||
assertEquals(2, symbols.length); // both mangled and demangled
|
||||
assertTrue(symbols[0].isPrimary());
|
||||
assertEquals("Close", symbols[0].getName());
|
||||
|
||||
Namespace ns = symbols[0].getParentNamespace();
|
||||
assertEquals("CRegKey", ns.getName(false));
|
||||
|
||||
ns = ns.getParentNamespace();
|
||||
assertEquals("ATL", ns.getName(false));
|
||||
|
||||
Parameter[] parameters = f2.getParameters();
|
||||
assertEquals("Missing parameter", 1, parameters.length);
|
||||
assertTrue(parameters[0].isAutoParameter());
|
||||
assertEquals("The 'this' param has incorrect type", "CRegKey *",
|
||||
parameters[0].getDataType().getDisplayName());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that the DemangledFunction will properly create a cascade of namespaces for
|
||||
* functions that live inside of a class that lives inside of a namespace.
|
||||
* This test applies a demangled name where the mangled name exists.
|
||||
*/
|
||||
@Test
|
||||
public void testApply_FunctionInClassInNamespace2() throws Exception {
|
||||
|
||||
// this is: public long __thiscall ATL::CRegKey::Close(void)
|
||||
String mangled = "?Close@CRegKey@ATL@@QAEJXZ";
|
||||
Address addr = addr("0x0101");
|
||||
|
||||
SymbolTable symbolTable = program.getSymbolTable();
|
||||
symbolTable.createLabel(addr, mangled, SourceType.IMPORTED);
|
||||
|
||||
DemangledObject demangled = DemanglerUtil.demangle(mangled);
|
||||
assertTrue(demangled instanceof DemangledFunction);
|
||||
|
||||
assertTrue(demangled.applyTo(program, addr, new DemanglerOptions(), TaskMonitor.DUMMY));
|
||||
|
||||
assertFunction("Close", addr);
|
||||
assertNoBookmarkAt(addr);
|
||||
|
||||
// expected: ATL::CRegKey::Close
|
||||
Symbol[] symbols = symbolTable.getSymbols(addr);
|
||||
assertEquals(2, symbols.length);
|
||||
assertEquals("Close", symbols[0].getName());
|
||||
assertEquals(mangled, symbols[1].getName());
|
||||
|
||||
Namespace ns = symbols[0].getParentNamespace();
|
||||
assertEquals("CRegKey", ns.getName(false));
|
||||
|
||||
ns = ns.getParentNamespace();
|
||||
assertEquals("ATL", ns.getName(false));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that the DemangledFunction will properly create a cascade of namespaces for
|
||||
* functions that live inside of a class that lives inside of a namespace.
|
||||
* This test applies a demangled name where the mangled name exists with address suffix.
|
||||
*/
|
||||
@Test
|
||||
public void testApply_FunctionInClassInNamespace3() throws Exception {
|
||||
|
||||
// this is: public long __thiscall ATL::CRegKey::Close(void)
|
||||
String mangled = "?Close@CRegKey@ATL@@QAEJXZ";
|
||||
Address addr = addr("0x0101");
|
||||
|
||||
SymbolTable symbolTable = program.getSymbolTable();
|
||||
String mangledWithAddr = SymbolUtilities.getAddressAppendedName(mangled, addr);
|
||||
symbolTable.createLabel(addr, mangledWithAddr, SourceType.IMPORTED);
|
||||
|
||||
DemangledObject demangled = DemanglerUtil.demangle(mangled);
|
||||
assertTrue(demangled instanceof DemangledFunction);
|
||||
|
||||
assertTrue(demangled.applyTo(program, addr, new DemanglerOptions(), TaskMonitor.DUMMY));
|
||||
|
||||
assertFunction("Close", addr);
|
||||
assertNoBookmarkAt(addr);
|
||||
|
||||
// expected: ATL::CRegKey::Close
|
||||
Symbol[] symbols = symbolTable.getSymbols(addr);
|
||||
assertEquals(2, symbols.length);
|
||||
assertEquals("Close", symbols[0].getName());
|
||||
assertEquals(mangledWithAddr, symbols[1].getName());
|
||||
|
||||
Namespace ns = symbols[0].getParentNamespace();
|
||||
assertEquals("CRegKey", ns.getName(false));
|
||||
|
||||
ns = ns.getParentNamespace();
|
||||
assertEquals("ATL", ns.getName(false));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that the DemangledFunction will properly create a cascade of namespaces for
|
||||
* functions that live inside of a class that lives inside of a namespace.
|
||||
* This test applies a demangled name where both the mangled name exists and
|
||||
* the simple demangled name exists in the global space.
|
||||
*/
|
||||
@Test
|
||||
public void testApply_FunctionInClassInNamespace4() throws Exception {
|
||||
|
||||
// this is: public long __thiscall ATL::CRegKey::Close(void)
|
||||
String mangled = "?Close@CRegKey@ATL@@QAEJXZ";
|
||||
Address addr = addr("0x0101");
|
||||
|
||||
SymbolTable symbolTable = program.getSymbolTable();
|
||||
symbolTable.createLabel(addr, "Close", SourceType.IMPORTED);
|
||||
symbolTable.createLabel(addr, mangled, SourceType.IMPORTED);
|
||||
|
||||
DemangledObject demangled = DemanglerUtil.demangle(mangled);
|
||||
assertTrue(demangled instanceof DemangledFunction);
|
||||
|
||||
assertTrue(demangled.applyTo(program, addr, new DemanglerOptions(), TaskMonitor.DUMMY));
|
||||
|
||||
assertFunction("Close", addr);
|
||||
assertNoBookmarkAt(addr);
|
||||
|
||||
// expected: ATL::CRegKey::Close
|
||||
Symbol[] symbols = symbolTable.getSymbols(addr);
|
||||
assertEquals(2, symbols.length);
|
||||
assertEquals("Close", symbols[0].getName());
|
||||
assertEquals(mangled, symbols[1].getName());
|
||||
|
||||
Namespace ns = symbols[0].getParentNamespace();
|
||||
assertEquals("CRegKey", ns.getName(false));
|
||||
|
||||
ns = ns.getParentNamespace();
|
||||
assertEquals("ATL", ns.getName(false));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that the DemangledFunction will properly create a cascade of namespaces for
|
||||
* functions that live inside of a class that lives inside of a namespace.
|
||||
* This test applies a demangled name where the mangled name exists on an external
|
||||
* location symbol.
|
||||
*/
|
||||
@Test
|
||||
public void testApply_ExtrnalFunctionInClassInNamespace() throws Exception {
|
||||
|
||||
// this is: public long __thiscall ATL::CRegKey::Close(void)
|
||||
String mangled = "?Close@CRegKey@ATL@@QAEJXZ";
|
||||
|
||||
ExternalManager externalManager = program.getExternalManager();
|
||||
Library lib = externalManager.addExternalLibraryName("MY.DLL", SourceType.IMPORTED);
|
||||
ExternalLocation extLoc =
|
||||
externalManager.addExtLocation(lib, mangled, null, SourceType.IMPORTED);
|
||||
|
||||
Address addr = extLoc.getExternalSpaceAddress();
|
||||
|
||||
DemangledObject demangled = DemanglerUtil.demangle(mangled);
|
||||
assertTrue(demangled instanceof DemangledFunction);
|
||||
|
||||
assertTrue(demangled.applyTo(program, addr, new DemanglerOptions(), TaskMonitor.DUMMY));
|
||||
|
||||
assertFunction("Close", addr);
|
||||
assertNoBookmarkAt(addr);
|
||||
|
||||
SymbolTable symbolTable = program.getSymbolTable();
|
||||
|
||||
// expected: ATL::CRegKey::Close
|
||||
Symbol[] symbols = symbolTable.getSymbols(addr);
|
||||
assertEquals(1, symbols.length);
|
||||
assertEquals("Close", symbols[0].getName());
|
||||
|
||||
Namespace ns = symbols[0].getParentNamespace();
|
||||
assertEquals("CRegKey", ns.getName(false));
|
||||
|
||||
ns = ns.getParentNamespace();
|
||||
assertEquals("ATL", ns.getName(false));
|
||||
|
||||
extLoc = externalManager.getExternalLocation(symbols[0]);
|
||||
assertNotNull(extLoc);
|
||||
|
||||
assertEquals("Close", extLoc.getLabel());
|
||||
assertEquals(mangled, extLoc.getOriginalImportedName());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFunctionVariable() throws Exception {
|
||||
|
||||
//
|
||||
// This makes sure that a variable inside of a function namespace prevents a class
|
||||
// namespace object from being created when a function does not exist. Instead it should
|
||||
// create a simple namespace.
|
||||
//
|
||||
|
||||
String mangled = "_ZZ18__gthread_active_pvE20__gthread_active_ptr";
|
||||
String functionName = "__gthread_active_p";
|
||||
programBuilder.createEmptyFunction(functionName, "0x0101", 10, new VoidDataType());
|
||||
|
||||
programBuilder.createLabel("0x0103", mangled);
|
||||
|
||||
DemangledObject demangled = DemanglerUtil.demangle(program, mangled);
|
||||
assertNotNull(demangled);
|
||||
assertTrue(demangled instanceof DemangledVariable);
|
||||
|
||||
assertEquals("__gthread_active_p()::__gthread_active_ptr", demangled.getSignature(false));
|
||||
|
||||
Address addr = addr("0x0103");
|
||||
demangled.applyTo(program, addr, new DemanglerOptions(), TaskMonitor.DUMMY);
|
||||
|
||||
assertSimpleNamespaceExists("__gthread_active_p()");
|
||||
assertNoBookmarkAt(addr);
|
||||
|
||||
SymbolTable symbolTable = program.getSymbolTable();
|
||||
Symbol[] symbols = symbolTable.getSymbols(addr);
|
||||
assertEquals(2, symbols.length);
|
||||
assertEquals("_ZZ18__gthread_active_pvE20__gthread_active_ptr", symbols[1].getName());
|
||||
assertEquals("__gthread_active_ptr", symbols[0].getName());
|
||||
|
||||
Namespace ns = symbols[0].getParentNamespace();
|
||||
assertEquals("__gthread_active_p()", ns.getName(false));
|
||||
}
|
||||
|
||||
private void assertNoBookmarkAt(Address addr) {
|
||||
BookmarkManager bm = program.getBookmarkManager();
|
||||
Bookmark[] bookmarks = bm.getBookmarks(addr);
|
||||
if (bookmarks.length > 0) {
|
||||
fail("Expected no bookmark; found " + Arrays.toString(bookmarks));
|
||||
}
|
||||
}
|
||||
|
||||
private void assertSimpleNamespaceExists(String name) {
|
||||
SymbolTable symbolTable = program.getSymbolTable();
|
||||
Namespace ns = symbolTable.getNamespace(name, program.getGlobalNamespace());
|
||||
assertNotNull(ns);
|
||||
assertEquals(SymbolType.NAMESPACE, ns.getSymbol().getSymbolType());
|
||||
}
|
||||
|
||||
private void assertFunction(String name, Address addr) {
|
||||
FunctionManager fm = program.getFunctionManager();
|
||||
Function function = fm.getFunctionAt(addr);
|
||||
assertNotNull("Expected function to get created at " + addr, function);
|
||||
assertEquals(name, function.getName());
|
||||
}
|
||||
|
||||
private Address addr(String addr) {
|
||||
return program.getAddressFactory().getAddress(addr);
|
||||
}
|
||||
}
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
/* ###
|
||||
* 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.util.demangler.gnu;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import ghidra.app.cmd.label.DemanglerCmd;
|
||||
import ghidra.app.util.demangler.*;
|
||||
import ghidra.program.database.ProgramDB;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.test.AbstractGhidraHeadlessIntegrationTest;
|
||||
import ghidra.test.ToyProgramBuilder;
|
||||
|
||||
public class GnuDemanglerIntegrationTest extends AbstractGhidraHeadlessIntegrationTest {
|
||||
|
||||
private ProgramDB program;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
ToyProgramBuilder builder = new ToyProgramBuilder("test", true);
|
||||
builder.createMemory(".text", "0x01001000", 0x100);
|
||||
program = builder.getProgram();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExceptionOnFailToDemangle() throws Exception {
|
||||
|
||||
String mangled = "?InvokeHelper@CWnd@@QAAXJGGPAXPBEZZ_00912eaf";
|
||||
|
||||
DemanglerCmd cmd = new DemanglerCmd(addr("01001000"), mangled);
|
||||
|
||||
// this used to trigger an exception
|
||||
cmd.applyTo(program);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDemangler_Format_EDG_DemangleOnlyKnownPatterns_False_DollarInNamespace()
|
||||
throws DemangledException {
|
||||
|
||||
String mangled = "MyFunction__11MyNamespacePQ215$ParamNamespace9paramName";
|
||||
|
||||
GnuDemangler demangler = new GnuDemangler();
|
||||
demangler.canDemangle(program);// this perform initialization
|
||||
|
||||
DemangledObject result = demangler.demangle(mangled, false);
|
||||
assertNotNull(result);
|
||||
assertEquals("undefined MyNamespace::MyFunction($ParamNamespace::paramName *)",
|
||||
result.getSignature(false));
|
||||
|
||||
DemanglerOptions options = new DemanglerOptions();
|
||||
options.setDemangleOnlyKnownPatterns(false);
|
||||
DemanglerCmd cmd = new DemanglerCmd(addr("01001000"), mangled, options);
|
||||
|
||||
// this used to trigger an exception
|
||||
boolean success = applyCmd(program, cmd);
|
||||
assertTrue("Demangler command failed: " + cmd.getStatusMsg(), success);
|
||||
|
||||
assertNotNull(cmd.getDemangledObject());
|
||||
}
|
||||
|
||||
private Address addr(String address) {
|
||||
return program.getAddressFactory().getAddress(address);
|
||||
}
|
||||
|
||||
}
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
/* ###
|
||||
* 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.
|
||||
* 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.util.headless;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.util.Locale;
|
||||
|
||||
import sun.java2d.HeadlessGraphicsEnvironment;
|
||||
|
||||
public class MyHeadlessGraphicsEnvironment extends GraphicsEnvironment {
|
||||
|
||||
static volatile boolean swingErrorRegistered = false;
|
||||
private static String preferredGraphicsEnv;
|
||||
|
||||
private GraphicsEnvironment localEnv;
|
||||
|
||||
static void setup() {
|
||||
//System.setProperty("java.awt.headless", "true");
|
||||
preferredGraphicsEnv = System.getProperty("java.awt.graphicsenv");
|
||||
System.setProperty("java.awt.graphicsenv", MyHeadlessGraphicsEnvironment.class.getName());
|
||||
}
|
||||
|
||||
public MyHeadlessGraphicsEnvironment() {
|
||||
swingErrorRegistered = true;
|
||||
try {
|
||||
throw new Exception("Swing invocation detected for Headless Mode");
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
getRealGraphicsEnvironemnt();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Graphics2D createGraphics(BufferedImage img) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Font[] getAllFonts() {
|
||||
return localEnv.getAllFonts();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getAvailableFontFamilyNames() {
|
||||
return localEnv.getAvailableFontFamilyNames();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getAvailableFontFamilyNames(Locale l) {
|
||||
return localEnv.getAvailableFontFamilyNames(l);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GraphicsDevice getDefaultScreenDevice() throws HeadlessException {
|
||||
return localEnv.getDefaultScreenDevice();
|
||||
}
|
||||
|
||||
@Override
|
||||
public GraphicsDevice[] getScreenDevices() throws HeadlessException {
|
||||
return localEnv.getScreenDevices();
|
||||
}
|
||||
|
||||
private void getRealGraphicsEnvironemnt() {
|
||||
try {
|
||||
localEnv = (GraphicsEnvironment) Class.forName(preferredGraphicsEnv).newInstance();
|
||||
if (isHeadless()) {
|
||||
localEnv = new HeadlessGraphicsEnvironment(localEnv);
|
||||
}
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new Error("Could not find class: " + preferredGraphicsEnv);
|
||||
} catch (InstantiationException e) {
|
||||
throw new Error("Could not instantiate Graphics Environment: " + preferredGraphicsEnv);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new Error("Could not access Graphics Environment: " + preferredGraphicsEnv);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+195
@@ -0,0 +1,195 @@
|
||||
/* ###
|
||||
* 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.util.headless;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.Dialog.ModalExclusionType;
|
||||
import java.awt.Dialog.ModalityType;
|
||||
import java.awt.datatransfer.Clipboard;
|
||||
import java.awt.font.TextAttribute;
|
||||
import java.awt.im.InputMethodHighlight;
|
||||
import java.awt.image.*;
|
||||
import java.net.URL;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
import sun.awt.HeadlessToolkit;
|
||||
|
||||
public class MyHeadlessToolkit extends Toolkit {
|
||||
|
||||
static volatile boolean swingErrorRegistered = false;
|
||||
private static String preferredToolkit;
|
||||
|
||||
private Toolkit localToolKit;
|
||||
|
||||
static void setup() {
|
||||
//System.setProperty("java.awt.headless", "true");
|
||||
preferredToolkit = System.getProperty("awt.toolkit", "sun.awt.X11.XToolkit");
|
||||
System.setProperty("awt.toolkit", MyHeadlessToolkit.class.getName());
|
||||
}
|
||||
|
||||
public MyHeadlessToolkit() {
|
||||
swingErrorRegistered = true;
|
||||
try {
|
||||
throw new Exception("Swing invocation detected for Headless Mode");
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
getRealToolkit();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beep() {
|
||||
localToolKit.beep();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int checkImage(Image image, int width, int height,
|
||||
ImageObserver observer) {
|
||||
return localToolKit.checkImage(image, width, height, observer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Image createImage(String filename) {
|
||||
return localToolKit.createImage(filename);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Image createImage(URL url) {
|
||||
return localToolKit.createImage(url);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Image createImage(ImageProducer producer) {
|
||||
return localToolKit.createImage(producer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Image createImage(byte[] imagedata, int imageoffset, int imagelength) {
|
||||
return localToolKit.createImage(imagedata, imageoffset, imagelength);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ColorModel getColorModel() throws HeadlessException {
|
||||
return localToolKit.getColorModel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getFontList() {
|
||||
return localToolKit.getFontList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public FontMetrics getFontMetrics(Font font) {
|
||||
return localToolKit.getFontMetrics(font);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Image getImage(String filename) {
|
||||
return localToolKit.getImage(filename);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Image getImage(URL url) {
|
||||
return localToolKit.getImage(url);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PrintJob getPrintJob(Frame frame, String jobtitle, Properties props) {
|
||||
return localToolKit.getPrintJob(frame, jobtitle, props);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getScreenResolution() throws HeadlessException {
|
||||
return localToolKit.getScreenResolution();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dimension getScreenSize() throws HeadlessException {
|
||||
return localToolKit.getScreenSize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Clipboard getSystemClipboard() throws HeadlessException {
|
||||
return localToolKit.getSystemClipboard();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected EventQueue getSystemEventQueueImpl() {
|
||||
return localToolKit.getSystemEventQueue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isModalExclusionTypeSupported(
|
||||
ModalExclusionType modalExclusionType) {
|
||||
return localToolKit.isModalExclusionTypeSupported(modalExclusionType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isModalityTypeSupported(ModalityType modalityType) {
|
||||
return localToolKit.isModalityTypeSupported(modalityType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<TextAttribute, ?> mapInputMethodHighlight(
|
||||
InputMethodHighlight highlight) throws HeadlessException {
|
||||
return localToolKit.mapInputMethodHighlight(highlight);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean prepareImage(Image image, int width, int height,
|
||||
ImageObserver observer) {
|
||||
return localToolKit.prepareImage(image, width, height, observer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sync() {
|
||||
localToolKit.sync();
|
||||
}
|
||||
|
||||
private void getRealToolkit() {
|
||||
try {
|
||||
// We disable the JIT during toolkit initialization. This
|
||||
// tends to touch lots of classes that aren't needed again
|
||||
// later and therefore JITing is counter-productiive.
|
||||
java.lang.Compiler.disable();
|
||||
|
||||
Class<?> cls = null;
|
||||
try {
|
||||
try {
|
||||
cls = Class.forName(preferredToolkit);
|
||||
} catch (ClassNotFoundException ee) {
|
||||
throw new AWTError("Toolkit not found: " + preferredToolkit);
|
||||
}
|
||||
if (cls != null) {
|
||||
localToolKit = (Toolkit)cls.newInstance();
|
||||
if (GraphicsEnvironment.isHeadless()) {
|
||||
localToolKit = new HeadlessToolkit(localToolKit);
|
||||
}
|
||||
}
|
||||
} catch (InstantiationException e) {
|
||||
throw new AWTError("Could not instantiate Toolkit: " + preferredToolkit);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new AWTError("Could not access Toolkit: " + preferredToolkit);
|
||||
}
|
||||
|
||||
} finally {
|
||||
// Make sure to always re-enable the JIT.
|
||||
java.lang.Compiler.enable();
|
||||
}
|
||||
}
|
||||
}
|
||||
+261
@@ -0,0 +1,261 @@
|
||||
/* ###
|
||||
* 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.framework.analysis;
|
||||
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import org.junit.*;
|
||||
|
||||
import generic.jar.ResourceFile;
|
||||
import ghidra.app.script.GhidraScriptUtil;
|
||||
import ghidra.app.services.*;
|
||||
import ghidra.framework.task.GScheduledTask;
|
||||
import ghidra.framework.task.GTaskListener;
|
||||
import ghidra.program.database.ProgramBuilder;
|
||||
import ghidra.program.database.ProgramDB;
|
||||
import ghidra.program.model.symbol.Symbol;
|
||||
import ghidra.test.AbstractGhidraHeadlessIntegrationTest;
|
||||
import mockit.*;
|
||||
|
||||
public class AnalysisManagerTest extends AbstractGhidraHeadlessIntegrationTest {
|
||||
@Mocked
|
||||
GTaskListener listener;
|
||||
|
||||
private ProgramBuilder programBuilder;
|
||||
private ProgramDB program;
|
||||
private AnalysisManager analysisManager;
|
||||
private ArrayList<Analyzer> analyzers;
|
||||
|
||||
class TaskTypeDelegate implements Delegate<GScheduledTask> {
|
||||
private String name;
|
||||
|
||||
TaskTypeDelegate(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
void validate(GScheduledTask scheduledTask) {
|
||||
Assert.assertEquals(name, scheduledTask.getTask().getClass().getSimpleName());
|
||||
}
|
||||
}
|
||||
|
||||
class TaskNameDelegate implements Delegate<GScheduledTask> {
|
||||
private String name;
|
||||
|
||||
TaskNameDelegate(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
void validate(GScheduledTask scheduledTask) {
|
||||
Assert.assertEquals(name, scheduledTask.getTask().getName());
|
||||
}
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
programBuilder = new ProgramBuilder();
|
||||
programBuilder.createMemory("AAA", "0x100", 0x1000);
|
||||
program = programBuilder.getProgram();
|
||||
analyzers = new ArrayList<>();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTwoAnalyzersWithOnePhases() {
|
||||
|
||||
analyzers.add(new Analyzer1());
|
||||
analyzers.add(new Analyzer2());
|
||||
AnalysisRecipe recipe = new AnalysisRecipe("Test Recipe", analyzers, program);
|
||||
analysisManager = new AnalysisManager(program, recipe);
|
||||
analysisManager.addTaskListener(listener);
|
||||
|
||||
analysisManager.runAnalysis(null);
|
||||
analysisManager.waitForAnalysis(1000);
|
||||
|
||||
new VerificationsInOrder() {
|
||||
{
|
||||
listener.initialize();
|
||||
listener.taskStarted(with(new TaskTypeDelegate("StartPhaseTask")));
|
||||
listener.taskCompleted(with(new TaskTypeDelegate("StartPhaseTask")), null);
|
||||
listener.taskStarted(with(new TaskTypeDelegate("KickStartAnalyzersTask")));
|
||||
listener.taskCompleted(with(new TaskTypeDelegate("KickStartAnalyzersTask")), null);
|
||||
listener.taskStarted(with(new TaskNameDelegate("Analyzer1")));
|
||||
listener.taskCompleted(with(new TaskNameDelegate("Analyzer1")), null);
|
||||
listener.taskStarted(with(new TaskNameDelegate("Analyzer2")));
|
||||
listener.taskCompleted(with(new TaskNameDelegate("Analyzer2")), null);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTwoAnalyzersWithTwoPhases() {
|
||||
analyzers.add(new Analyzer1());
|
||||
analyzers.add(new Analyzer2());
|
||||
AnalysisRecipe recipe = new AnalysisRecipe("Test Recipe", analyzers, program);
|
||||
AnalysisPhase firstPhase = recipe.createPhase();
|
||||
recipe.setAnalyzerStartPhase(analyzers.get(0), firstPhase);
|
||||
|
||||
analysisManager = new AnalysisManager(program, recipe);
|
||||
analysisManager.addTaskListener(listener);
|
||||
|
||||
analysisManager.runAnalysis(null);
|
||||
analysisManager.waitForAnalysis(1000);
|
||||
|
||||
new VerificationsInOrder() {
|
||||
{
|
||||
listener.initialize();
|
||||
listener.taskStarted(with(new TaskTypeDelegate("StartPhaseTask")));
|
||||
listener.taskCompleted(with(new TaskTypeDelegate("StartPhaseTask")), null);
|
||||
listener.taskStarted(with(new TaskTypeDelegate("KickStartAnalyzersTask")));
|
||||
listener.taskCompleted(with(new TaskTypeDelegate("KickStartAnalyzersTask")), null);
|
||||
listener.taskStarted(with(new TaskNameDelegate("Analyzer1")));
|
||||
listener.taskCompleted(with(new TaskNameDelegate("Analyzer1")), null);
|
||||
listener.taskStarted(with(new TaskTypeDelegate("StartPhaseTask")));
|
||||
listener.taskCompleted(with(new TaskTypeDelegate("StartPhaseTask")), null);
|
||||
listener.taskStarted(with(new TaskNameDelegate("Analyzer2")));
|
||||
listener.taskCompleted(with(new TaskNameDelegate("Analyzer2")), null);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTwoAnalyzersWithTwoPhasesAnalyzerInSecondPhaseOff() {
|
||||
analyzers.add(new Analyzer1());
|
||||
analyzers.add(new Analyzer2());
|
||||
AnalysisRecipe recipe = new AnalysisRecipe("Test Recipe", analyzers, program);
|
||||
AnalysisPhase firstPhase = recipe.createPhase();
|
||||
recipe.setAnalyzerStartPhase(analyzers.get(0), firstPhase);
|
||||
recipe.setAnalyzerEnablement(analyzers.get(1), false);
|
||||
|
||||
analysisManager = new AnalysisManager(program, recipe);
|
||||
analysisManager.addTaskListener(listener);
|
||||
|
||||
analysisManager.runAnalysis(null);
|
||||
analysisManager.waitForAnalysis(1000);
|
||||
|
||||
new VerificationsInOrder() {
|
||||
{
|
||||
listener.initialize();
|
||||
listener.taskStarted(with(new TaskTypeDelegate("StartPhaseTask")));
|
||||
listener.taskCompleted(with(new TaskTypeDelegate("StartPhaseTask")), null);
|
||||
listener.taskStarted(with(new TaskTypeDelegate("KickStartAnalyzersTask")));
|
||||
listener.taskCompleted(with(new TaskTypeDelegate("KickStartAnalyzersTask")), null);
|
||||
listener.taskStarted(with(new TaskNameDelegate("Analyzer1")));
|
||||
listener.taskCompleted(with(new TaskNameDelegate("Analyzer1")), null);
|
||||
listener.taskStarted(with(new TaskTypeDelegate("StartPhaseTask")));
|
||||
listener.taskCompleted(with(new TaskTypeDelegate("StartPhaseTask")), null);
|
||||
listener.taskStarted((GScheduledTask) any);
|
||||
times = 0; // make sure no more taskStarted calls are mode
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSciptAnalyzer() throws Exception {
|
||||
final ResourceFile scriptFile = createScriptFile();
|
||||
analyzers.add(new Analyzer1());
|
||||
GhidraScriptAnalyzerAdapter analyzer =
|
||||
new GhidraScriptAnalyzerAdapter(scriptFile, AnalyzerType.BYTE_ANALYZER, 10000);
|
||||
analyzers.add(analyzer);
|
||||
AnalysisRecipe recipe = new AnalysisRecipe("Test Recipe", analyzers, program);
|
||||
analysisManager = new AnalysisManager(program, recipe);
|
||||
analysisManager.addTaskListener(listener);
|
||||
|
||||
analysisManager.runAnalysis(null);
|
||||
analysisManager.waitForAnalysis(10000);
|
||||
|
||||
// verify that the script ran
|
||||
Symbol symbol = getUniqueSymbol(program, "TEST_SYMBOL");
|
||||
assertNotNull(symbol);
|
||||
|
||||
new VerificationsInOrder() {
|
||||
{
|
||||
listener.initialize();
|
||||
listener.taskStarted(with(new TaskTypeDelegate("StartPhaseTask")));
|
||||
listener.taskCompleted(with(new TaskTypeDelegate("StartPhaseTask")), null);
|
||||
listener.taskStarted(with(new TaskTypeDelegate("KickStartAnalyzersTask")));
|
||||
listener.taskCompleted(with(new TaskTypeDelegate("KickStartAnalyzersTask")), null);
|
||||
listener.taskStarted(with(new TaskNameDelegate("Analyzer1")));
|
||||
listener.taskCompleted(with(new TaskNameDelegate("Analyzer1")), null);
|
||||
listener.taskStarted(with(new TaskNameDelegate("Script: " + scriptFile.getName())));
|
||||
listener.taskCompleted(
|
||||
with(new TaskNameDelegate("Script: " + scriptFile.getName())), null);
|
||||
listener.taskStarted((GScheduledTask) any);
|
||||
times = 0; // make sure no more taskStarted calls are mode
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
private ResourceFile createScriptFile() throws Exception {
|
||||
ResourceFile newScriptFile = createTempScriptFile("TestAnalyzerScript");
|
||||
String filename = newScriptFile.getName();
|
||||
String className = filename.replaceAll("\\.java", "");
|
||||
|
||||
//@formatter:off
|
||||
String newScript =
|
||||
"import ghidra.app.script.GhidraScript;\n\n"+
|
||||
"import ghidra.program.model.address.Address;\n"+
|
||||
"import ghidra.program.model.symbol.SourceType;\n"+
|
||||
"public class "+className+" extends GhidraScript {\n\n"+
|
||||
" @Override\n"+
|
||||
" protected void run() throws Exception {\n"+
|
||||
" Address minAddress = currentProgram.getMinAddress();\n"+
|
||||
" currentProgram.getSymbolTable().createLabel(minAddress, \"TEST_SYMBOL\",\n"+
|
||||
" SourceType.USER_DEFINED);\n"+
|
||||
" }\n\n"+
|
||||
"}\n";
|
||||
//@formatter:on
|
||||
|
||||
writeStringToFile(newScriptFile, newScript);
|
||||
|
||||
return newScriptFile;
|
||||
}
|
||||
|
||||
private void writeStringToFile(ResourceFile file, String string) throws IOException {
|
||||
BufferedWriter writer = new BufferedWriter(new FileWriter(file.getFile(false)));
|
||||
writer.write(string);
|
||||
writer.close();
|
||||
}
|
||||
|
||||
private ResourceFile createTempScriptFile(String name) throws IOException {
|
||||
File userScriptsDir = new File(GhidraScriptUtil.USER_SCRIPTS_DIR);
|
||||
if (name.length() > 50) {
|
||||
// too long and the script manager complains
|
||||
name = name.substring(name.length() - 50);
|
||||
}
|
||||
File tempFile = File.createTempFile(name, ".java", userScriptsDir);
|
||||
tempFile.deleteOnExit();
|
||||
return new ResourceFile(tempFile);
|
||||
}
|
||||
|
||||
public static class Analyzer1 extends AnalyzerTestStub {
|
||||
|
||||
Analyzer1() {
|
||||
super("Analyzer1", AnalyzerType.BYTE_ANALYZER, true, new AnalysisPriority("1", 1));
|
||||
}
|
||||
}
|
||||
|
||||
public static class Analyzer2 extends AnalyzerTestStub {
|
||||
|
||||
Analyzer2() {
|
||||
super("Analyzer2", AnalyzerType.BYTE_ANALYZER, true, new AnalysisPriority("2", 2));
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user