add bundle status manager tests

This commit is contained in:
Jason P. Leasure
2020-07-16 19:03:35 -04:00
parent 93b1d3f697
commit 72ecf05ab2
2 changed files with 223 additions and 29 deletions

View File

@@ -248,7 +248,7 @@ public class BundleStatusComponentProvider extends ComponentProviderAdapter {
private void showAddBundlesFileChooser() {
if (fileChooser == null) {
fileChooser = new GhidraFileChooser(panel);
fileChooser = new GhidraFileChooser(getComponent());
fileChooser.setMultiSelectionEnabled(true);
fileChooser.setFileSelectionMode(GhidraFileChooserMode.FILES_AND_DIRECTORIES);
fileChooser.setTitle("Select Bundle(s)");

View File

@@ -18,22 +18,25 @@ package ghidra.app.plugin.core.script;
import static org.junit.Assert.*;
import java.io.File;
import java.util.Set;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import org.junit.*;
import docking.ComponentProvider;
import docking.action.DockingActionIf;
import docking.widgets.filechooser.GhidraFileChooser;
import docking.widgets.table.GTable;
import generic.jar.ResourceFile;
import generic.util.Path;
import ghidra.app.plugin.core.osgi.*;
public class BundleStatusManagerTest extends AbstractGhidraScriptMgrPluginTest {
protected static String BUNDLE_PATH ="$GHIDRA_HOME/Features/Base/ghidra_scripts";
protected static String BUNDLE_PATH = "$GHIDRA_HOME/Features/Base/ghidra_scripts";
protected static String SCRIPT_NAME = "HelloWorldScript.java";
protected static String SCRIPT_PATH =
translateSeperators("$GHIDRA_HOME/Features/Base/ghidra_scripts/" + SCRIPT_NAME);
@@ -88,30 +91,80 @@ public class BundleStatusManagerTest extends AbstractGhidraScriptMgrPluginTest {
testBundleHostListener.awaitDisablement();
}
void cleanViaGUI(int viewRow) throws InterruptedException {
BundleStatus status = bundleStatusTableModel.getRowObject(viewRow);
bundleStatusTable.selectRow(viewRow);
assertNotNull(status);
status.setSummary("not clean");
DockingActionIf cleanBundlesAction = getActionByName(bundleStatusProvider, "CleanBundles");
List<BundleStatus> selectRows(int... viewRows) {
List<BundleStatus> statuses = Arrays.stream(viewRows)
.mapToObj(bundleStatusTableModel::getRowObject)
.collect(Collectors.toList());
for (BundleStatus status : statuses) {
assertNotNull(status);
}
runSwing(() -> {
cleanBundlesAction.actionPerformed(null);
bundleStatusTable.clearSelection();
for (int viewRow : viewRows) {
bundleStatusTable.addRowSelectionInterval(viewRow, viewRow);
}
});
return statuses;
}
void removeViaGUI(int... viewRows) throws InterruptedException {
assertTrue("removeViaGUI called with no arguments", viewRows.length > 0);
selectRows(viewRows);
List<BundleStatus> statuses = bundleStatusTableModel.getModelData();
int initialSize = statuses.size();
DockingActionIf removeBundlesAction =
getActionByName(bundleStatusProvider, "RemoveBundles");
performAction(removeBundlesAction);
waitForSwing();
int count = 0;
// after cleaning, status is cleared.
do {
if (status.getSummary().isEmpty()) {
if (statuses.size() <= initialSize - viewRows.length) {
break;
}
Thread.sleep(250);
}
while (++count < 8);
assertTrue("Failure, clean took too long", count < 8);
}
void cleanViaGUI(int... viewRows) throws InterruptedException {
assertTrue("cleanViaGUI called with no arguments", viewRows.length > 0);
List<BundleStatus> statuses = selectRows(viewRows);
List<File> binaryDirs = statuses.stream().map((status) -> {
status.setSummary("no summary"); // we use the summary later to test that the bundle's been cleaned
GhidraSourceBundle bundle = (GhidraSourceBundle) provider.getBundleHost()
.getExistingGhidraBundle(status.getFile());
assertNotNull(bundle);
File binaryDir = ((Path) getInstanceField("binaryDir", bundle)).toFile();
assertTrue("Clean of bundle that doesn't exist", binaryDir.exists());
return binaryDir;
}).collect(Collectors.toList());
DockingActionIf cleanBundlesAction = getActionByName(bundleStatusProvider, "CleanBundles");
performAction(cleanBundlesAction);
waitForSwing();
// after cleaning, status is cleared, test for a clear status to know we're done cleaning.
int count = 0;
do {
if (statuses.stream().allMatch(status -> status.getSummary().isEmpty())) {
break;
}
Thread.sleep(250);
}
while (++count < 8);
assertTrue("Failure, clean took too long", count < 8);
for (File binaryDir : binaryDirs) {
assertFalse("Clean of bundle didn't remove directory", binaryDir.exists());
}
}
/**
@@ -165,7 +218,7 @@ public class BundleStatusManagerTest extends AbstractGhidraScriptMgrPluginTest {
BundleStatus status = bundleStatusTableModel.getRowObject(viewRow);
// check that it is currently enabled, and our script exists
ResourceFile scriptFile = Path.fromPathString(SCRIPT_PATH);
ResourceFile scriptFile = generic.util.Path.fromPathString(SCRIPT_PATH);
assertTrue(status.isEnabled());
assertScriptInTable(scriptFile);
@@ -180,6 +233,56 @@ public class BundleStatusManagerTest extends AbstractGhidraScriptMgrPluginTest {
assertScriptInTable(scriptFile);
}
/**
* Add a list of bundles with the addBundles dialogue.
*
* <p>All bundles should reside in a common directory.
*
* @param bundleFiles the bundle files
* @throws Exception if waitForUpdateOnChooser fails
*/
void addBundlesViaGUI(File... bundleFiles) throws Exception {
assertTrue("addBundlesViaGUI called with no arguments", bundleFiles.length > 0);
DockingActionIf addBundlesAction = getActionByName(bundleStatusProvider, "AddBundles");
performAction(addBundlesAction, false);
waitForSwing();
List<File> files = List.of(bundleFiles);
GhidraFileChooser chooser = waitForDialogComponent(GhidraFileChooser.class);
assertNotNull(chooser);
runSwing(() -> {
chooser.setCurrentDirectory(bundleFiles[0]);
}, true);
waitForUpdateOnChooser(chooser);
runSwing(() -> {
// there is no setFiles method of GhidraFileChooser
Object selectedFiles = getInstanceField("selectedFiles", chooser);
invokeInstanceMethod("setFiles", selectedFiles, new Class[] { List.class },
new Object[] { files });
Object validatedFiles = getInstanceField("validatedFiles", chooser);
invokeInstanceMethod("setFiles", validatedFiles, new Class[] { List.class },
new Object[] { files });
});
waitForUpdateOnChooser(chooser);
testBundleHostListener.reset(bundleFiles.length);
pressButtonByText(chooser, "OK");
waitForSwing();
testBundleHostListener.awaitActivation();
}
@Override
protected String runScript(String scriptName) throws Exception {
env.getTool().showComponentProvider(provider, true);
selectScript(scriptName);
String output = super.runScript(scriptName);
env.getTool().showComponentProvider(bundleStatusProvider, true);
return output;
}
@Test
public void testRunCleanRun() throws Exception {
int viewRow = getBundleRow(BUNDLE_PATH);
@@ -195,40 +298,131 @@ public class BundleStatusManagerTest extends AbstractGhidraScriptMgrPluginTest {
BundleStatus status = bundleStatusTableModel.getRowObject(viewRow);
// check that it is currently enabled, and our script exists
ResourceFile scriptFile = Path.fromPathString(SCRIPT_PATH);
ResourceFile scriptFile = generic.util.Path.fromPathString(SCRIPT_PATH);
assertTrue(status.isEnabled());
assertScriptInTable(scriptFile);
// run
env.getTool().showComponentProvider(provider, true);
selectScript(SCRIPT_NAME);
runScript(SCRIPT_NAME);
env.getTool().showComponentProvider(bundleStatusProvider, true);
// clean
cleanViaGUI(viewRow);
// run
env.getTool().showComponentProvider(provider, true);
selectScript(SCRIPT_NAME);
runScript(SCRIPT_NAME);
env.getTool().showComponentProvider(bundleStatusProvider, true);
}
@Test
public void addRunCleanRemoveTwoBundles() throws Exception {
final String TEST_SCRIPT_NAME = testName.getMethodName();
//@formatter:off
final String EXPECTED_OUTPUT =
TEST_SCRIPT_NAME+".java> Running...\n" +
TEST_SCRIPT_NAME+".java> Hello from pack2.Klass2\n" +
TEST_SCRIPT_NAME+".java> Finished!\n";
//@formatter:on
final File dir1 = new File(getTestDirectoryPath() + "/test_scripts1");
final File dir2 = new File(getTestDirectoryPath() + "/test_scripts2");
try {
dir1.mkdirs();
//@formatter:off
Files.writeString(new File(dir1, TEST_SCRIPT_NAME+".java").toPath(),
"//@importpackage pack2\n" +
"\n" +
"import pack1.Klass1;\n" +
"import ghidra.app.script.GhidraScript;\n" +
"public class "+TEST_SCRIPT_NAME+" extends GhidraScript {\n" +
" @Override\n" +
" protected void run() throws Exception {\n" +
" new Klass1(this).hello();\n" +
" }\n" +
"}\n"
);
File pack1 = new File(dir1,"pack1");
pack1.mkdirs();
Files.writeString(new File(pack1, "Klass1.java").toPath(),
"package pack1;\n" +
"\n" +
"import ghidra.app.script.GhidraScript;\n" +
"import pack2.Klass2;\n" +
"\n" +
"public class Klass1 {\n" +
" GhidraScript script;\n" +
" public Klass1(GhidraScript script) {\n" +
" this.script = script;\n" +
" }\n" +
"\n" +
" public void hello() {\n" +
" new Klass2(script).hello();\n" +
" }\n" +
"}\n"
);
dir2.mkdirs();
File pack2 = new File(dir2,"pack2");
pack2.mkdirs();
Files.writeString(new File(pack2, "Klass2.java").toPath(),
"package pack2;\n" +
"\n" +
"import ghidra.app.script.GhidraScript;\n" +
"import pack2.Klass2;\n" +
"\n" +
"public class Klass2 {\n" +
" GhidraScript script;\n" +
" public Klass2(GhidraScript script) {\n" +
" this.script = script;\n" +
" }\n" +
"\n" +
" public void hello() {\n" +
" script.println(\"Hello from pack2.Klass2\");\n" +
" }\n" +
"}\n"
);
//@formatter:on
addBundlesViaGUI(dir1, dir2);
String output = runScript(TEST_SCRIPT_NAME + ".java");
assertEquals(EXPECTED_OUTPUT, output);
int row1 = getBundleRow(generic.util.Path.toPathString(new ResourceFile(dir1)));
int row2 = getBundleRow(generic.util.Path.toPathString(new ResourceFile(dir2)));
assertFalse(row1 == -1);
assertFalse(row2 == -1);
cleanViaGUI(row1, row2);
removeViaGUI(row1, row2);
row1 = getBundleRow(generic.util.Path.toPathString(new ResourceFile(dir1)));
row2 = getBundleRow(generic.util.Path.toPathString(new ResourceFile(dir2)));
assertTrue(row1 == -1);
assertTrue(row2 == -1);
}
finally {
wipe(dir1.toPath());
wipe(dir2.toPath());
}
}
/**
* A {@link BundleHostListener} to help serialize bundle operations.
*/
protected class TestBundleHostListener implements BundleHostListener {
CountDownLatch activationLatch = new CountDownLatch(1);
CountDownLatch disablementLatch = new CountDownLatch(1);
CountDownLatch activationLatch;
CountDownLatch disablementLatch;
TestBundleHostListener() {
reset();
}
void reset() {
activationLatch = new CountDownLatch(1);
disablementLatch = new CountDownLatch(1);
reset(1);
}
void reset(int count) {
activationLatch = new CountDownLatch(count);
disablementLatch = new CountDownLatch(count);
}
@Override