Tests - cleanup of stack traces in test log file

This commit is contained in:
dragonmacher 2020-09-24 18:19:28 -04:00
parent b7c8056c8a
commit e5358323d8
24 changed files with 889 additions and 678 deletions

View file

@ -713,6 +713,7 @@ public class GhidraSourceBundle extends GhidraBundle {
} }
} }
catch (Throwable e) { catch (Throwable e) {
Msg.error(this, "Exception searching ", e);
e.printStackTrace(); e.printStackTrace();
} }
} }
@ -1090,13 +1091,21 @@ public class GhidraSourceBundle extends GhidraBundle {
* @throws IOException if there's a problem listing files * @throws IOException if there's a problem listing files
*/ */
ClassMapper(Path directory) throws IOException { ClassMapper(Path directory) throws IOException {
if (Files.exists(directory)) { if (!Files.exists(directory)) {
try (Stream<Path> pathStream = Files.list(directory)) { classToClassFilesMap = Collections.emptyMap();
classToClassFilesMap = pathStream return;
.filter(f -> Files.isRegularFile(f) && }
f.getFileName().toString().endsWith(".class"))
.collect(groupingBy(f -> { try (Stream<Path> paths = Files.list(directory)) {
String fileName = f.getFileName().toString(); classToClassFilesMap = paths
.filter(p -> Files.isRegularFile(p))
.filter(p -> p.getFileName().toString().endsWith(".class"))
.collect(groupingBy(this::getClassName));
}
}
private String getClassName(Path p) {
String fileName = p.getFileName().toString();
// if f is the class file of an inner class, use the class name // if f is the class file of an inner class, use the class name
int money = fileName.indexOf('$'); int money = fileName.indexOf('$');
if (money >= 0) { if (money >= 0) {
@ -1104,23 +1113,21 @@ public class GhidraSourceBundle extends GhidraBundle {
} }
// drop ".class" // drop ".class"
return fileName.substring(0, fileName.length() - 6); return fileName.substring(0, fileName.length() - 6);
}));
}
}
else {
classToClassFilesMap = Collections.emptyMap();
}
} }
List<Path> findAndRemove(ResourceFile sourceFile) { List<Path> findAndRemove(ResourceFile sourceFile) {
String className = sourceFile.getName(); String className = sourceFile.getName();
if (className.endsWith(".java")) { if (!className.endsWith(".java")) {
return null;
}
className = className.substring(0, className.length() - 5); className = className.substring(0, className.length() - 5);
long lastModifiedSource = sourceFile.lastModified(); long lastModifiedSource = sourceFile.lastModified();
List<Path> classFiles = classToClassFilesMap.remove(className); List<Path> classFiles = classToClassFilesMap.remove(className);
if (classFiles == null) { if (classFiles == null) {
classFiles = Collections.emptyList(); classFiles = Collections.emptyList();
} }
long lastModifiedClassFile = classFiles.isEmpty() ? -1 long lastModifiedClassFile = classFiles.isEmpty() ? -1
: classFiles.stream() : classFiles.stream()
.mapToLong(p -> p.toFile().lastModified()) .mapToLong(p -> p.toFile().lastModified())
@ -1130,7 +1137,6 @@ public class GhidraSourceBundle extends GhidraBundle {
if (lastModifiedSource > lastModifiedClassFile) { if (lastModifiedSource > lastModifiedClassFile) {
return classFiles; return classFiles;
} }
}
return null; return null;
} }

View file

@ -241,6 +241,7 @@ public class GhidraScriptUtil {
*/ */
@Deprecated @Deprecated
public static List<ResourceFile> getExplodedCompiledSourceBundlePaths() { public static List<ResourceFile> getExplodedCompiledSourceBundlePaths() {
try (Stream<Path> pathStream = Files.list(BundleHost.getOsgiDir())) { try (Stream<Path> pathStream = Files.list(BundleHost.getOsgiDir())) {
return pathStream.filter(Files::isDirectory) return pathStream.filter(Files::isDirectory)
.map(x -> new ResourceFile(x.toFile())) .map(x -> new ResourceFile(x.toFile()))

View file

@ -50,11 +50,9 @@ import ghidra.program.model.util.*;
import ghidra.program.util.DefaultLanguageService; import ghidra.program.util.DefaultLanguageService;
import ghidra.program.util.GhidraProgramUtilities; import ghidra.program.util.GhidraProgramUtilities;
import ghidra.test.AbstractGhidraHeadedIntegrationTest; import ghidra.test.AbstractGhidraHeadedIntegrationTest;
import ghidra.util.NumericUtilities; import ghidra.util.*;
import ghidra.util.Saveable;
import ghidra.util.exception.*; import ghidra.util.exception.*;
import ghidra.util.task.TaskMonitor; import ghidra.util.task.TaskMonitor;
import ghidra.util.task.TaskMonitorAdapter;
// TODO: Move this class into a different package (i.e., ghidra.test.program) // TODO: Move this class into a different package (i.e., ghidra.test.program)
public class ProgramBuilder { public class ProgramBuilder {
@ -97,7 +95,7 @@ public class ProgramBuilder {
* Construct program builder using the big-endian Toy language and default compiler spec. * Construct program builder using the big-endian Toy language and default compiler spec.
* This builder object will be the program consumer and must be disposed to properly * This builder object will be the program consumer and must be disposed to properly
* release the program. * release the program.
* @throws Exception * @throws Exception if there is an exception creating the program
*/ */
public ProgramBuilder() throws Exception { public ProgramBuilder() throws Exception {
this("Test Program", _TOY); this("Test Program", _TOY);
@ -109,7 +107,7 @@ public class ProgramBuilder {
* release the program. * release the program.
* @param name program name * @param name program name
* @param languageName supported language ID (includes all Toy language IDs) * @param languageName supported language ID (includes all Toy language IDs)
* @throws Exception * @throws Exception if there is an exception creating the program
*/ */
public ProgramBuilder(String name, String languageName) throws Exception { public ProgramBuilder(String name, String languageName) throws Exception {
this(name, languageName, null, null); this(name, languageName, null, null);
@ -120,7 +118,7 @@ public class ProgramBuilder {
* @param name program name * @param name program name
* @param languageName supported language ID (includes all Toy language IDs) * @param languageName supported language ID (includes all Toy language IDs)
* @param consumer program consumer (if null this builder will be used as consumer and must be disposed to release program) * @param consumer program consumer (if null this builder will be used as consumer and must be disposed to release program)
* @throws Exception * @throws Exception if there is an exception creating the program
*/ */
public ProgramBuilder(String name, String languageName, Object consumer) throws Exception { public ProgramBuilder(String name, String languageName, Object consumer) throws Exception {
this(name, languageName, null, consumer); this(name, languageName, null, consumer);
@ -132,7 +130,7 @@ public class ProgramBuilder {
* @param languageName supported language ID (includes all Toy language IDs) * @param languageName supported language ID (includes all Toy language IDs)
* @param compilerSpecID compiler specification ID (if null default spec will be used) * @param compilerSpecID compiler specification ID (if null default spec will be used)
* @param consumer program consumer (if null this builder will be used as consumer and must be disposed to release program) * @param consumer program consumer (if null this builder will be used as consumer and must be disposed to release program)
* @throws Exception * @throws Exception if there is an exception creating the program
*/ */
public ProgramBuilder(String name, String languageName, String compilerSpecID, Object consumer) public ProgramBuilder(String name, String languageName, String compilerSpecID, Object consumer)
throws Exception { throws Exception {
@ -205,10 +203,22 @@ public class ProgramBuilder {
public void dispose() { public void dispose() {
if (program.isUsedBy(this)) { if (program.isUsedBy(this)) {
// Make sure any buffered events are processed before we release. This fixes a timing
// issue that can happen when the test thread disposes the program while the Swing
// thread is processing events.
flushEvents();
program.release(this); program.release(this);
} }
} }
private void flushEvents() {
program.flushEvents();
if (!SystemUtilities.isInHeadlessMode()) {
AbstractGenericTest.waitForSwing();
}
}
public void setName(String name) { public void setName(String name) {
startTransaction(); startTransaction();
try { try {
@ -286,7 +296,8 @@ public class ProgramBuilder {
LanguageService languageService = DefaultLanguageService.getLanguageService(ldefFile); LanguageService languageService = DefaultLanguageService.getLanguageService(ldefFile);
try { try {
language = languageService.getLanguage(new LanguageID(languageName)); language = languageService.getLanguage(new LanguageID(languageName));
} catch (LanguageNotFoundException e) { }
catch (LanguageNotFoundException e) {
throw new LanguageNotFoundException("Unsupported test language: " + languageName); throw new LanguageNotFoundException("Unsupported test language: " + languageName);
} }
LANGUAGE_CACHE.put(languageName, language); LANGUAGE_CACHE.put(languageName, language);
@ -303,7 +314,10 @@ public class ProgramBuilder {
AbstractGenericTest.setInstanceField("recordChanges", program, Boolean.valueOf(enabled)); AbstractGenericTest.setInstanceField("recordChanges", program, Boolean.valueOf(enabled));
} }
/** Don't show the 'ask to analyze' dialog by default */ /**
* This prevents the 'ask to analyze' dialog from showing when called with {@code true}
* @param analyzed true to mark the program as analyzed
*/
public void setAnalyzed(boolean analyzed) { public void setAnalyzed(boolean analyzed) {
GhidraProgramUtilities.setAnalyzedFlag(program, analyzed); GhidraProgramUtilities.setAnalyzedFlag(program, analyzed);
} }
@ -325,7 +339,7 @@ public class ProgramBuilder {
MemoryBlock block = null; MemoryBlock block = null;
try { try {
block = memory.createInitializedBlock(name, startAddress, size, initialValue, block = memory.createInitializedBlock(name, startAddress, size, initialValue,
TaskMonitorAdapter.DUMMY_MONITOR, false); TaskMonitor.DUMMY, false);
block.setComment(comment); block.setComment(comment);
} }
catch (CancelledException e) { catch (CancelledException e) {
@ -359,7 +373,8 @@ public class ProgramBuilder {
startTransaction(); startTransaction();
try { try {
return program.getMemory().createInitializedBlock(name, addr(address), size, (byte) 0, return program.getMemory()
.createInitializedBlock(name, addr(address), size, (byte) 0,
TaskMonitor.DUMMY, true); TaskMonitor.DUMMY, true);
} }
catch (Exception e) { catch (Exception e) {
@ -378,7 +393,7 @@ public class ProgramBuilder {
* @param address String containing numeric value, preferably hex encoded: "0x1004000" * @param address String containing numeric value, preferably hex encoded: "0x1004000"
* @param byteString String containing 2 digit hex values, separated by ' ' space chars * @param byteString String containing 2 digit hex values, separated by ' ' space chars
* or by comma ',' chars: "12 05 ff". See {@link NumericUtilities#parseHexLong(String)}. * or by comma ',' chars: "12 05 ff". See {@link NumericUtilities#parseHexLong(String)}.
* @throws Exception * @throws Exception if there is an exception applying the bytes
*/ */
public void setBytes(String address, String byteString) throws Exception { public void setBytes(String address, String byteString) throws Exception {
byte[] bytes = NumericUtilities.convertStringToBytes(byteString); byte[] bytes = NumericUtilities.convertStringToBytes(byteString);
@ -395,7 +410,7 @@ public class ProgramBuilder {
* @param byteString String containing 2 digit hex values, separated by ' ' space chars * @param byteString String containing 2 digit hex values, separated by ' ' space chars
* or by comma ',' chars: "12 05 ff". See {@link NumericUtilities#parseHexLong(String)}. * or by comma ',' chars: "12 05 ff". See {@link NumericUtilities#parseHexLong(String)}.
* @param disassemble boolean flag. * @param disassemble boolean flag.
* @throws Exception * @throws Exception if there is an exception applying the bytes
*/ */
public void setBytes(String address, String byteString, boolean disassemble) throws Exception { public void setBytes(String address, String byteString, boolean disassemble) throws Exception {
byte[] bytes = NumericUtilities.convertStringToBytes(byteString); byte[] bytes = NumericUtilities.convertStringToBytes(byteString);
@ -413,7 +428,7 @@ public class ProgramBuilder {
* @param stringAddress String containing numeric value, preferably hex encoded: "0x1004000" * @param stringAddress String containing numeric value, preferably hex encoded: "0x1004000"
* @param bytes array of bytes to copy into the memory buffer at the addresss. * @param bytes array of bytes to copy into the memory buffer at the addresss.
* @param disassemble boolean flag. See {@link #disassemble(String, int)} * @param disassemble boolean flag. See {@link #disassemble(String, int)}
* @throws Exception * @throws Exception if there is an exception applying the bytes
*/ */
public void setBytes(String stringAddress, byte[] bytes, boolean disassemble) throws Exception { public void setBytes(String stringAddress, byte[] bytes, boolean disassemble) throws Exception {
Address address = addr(stringAddress); Address address = addr(stringAddress);
@ -547,9 +562,6 @@ public class ProgramBuilder {
} }
} }
/**
* This creates a function as big as you say.
*/
public Function createEmptyFunction(String name, String address, int size, DataType returnType, public Function createEmptyFunction(String name, String address, int size, DataType returnType,
Parameter... params) throws Exception, OverlappingFunctionException { Parameter... params) throws Exception, OverlappingFunctionException {

View file

@ -688,7 +688,8 @@ public class RefMergerExtTest extends AbstractExternalMergerTest {
int txId = program.startTransaction("Modify Original Program"); int txId = program.startTransaction("Modify Original Program");
boolean commit = false; boolean commit = false;
try { try {
ExternalLocation extLoc = createExternalLabel(program, new String[] { "Library", "Namespace", "Label1" }, ExternalLocation extLoc = createExternalLabel(program,
new String[] { "Library", "Namespace", "Label1" },
addr(program, "77db1020"), SourceType.ANALYSIS); addr(program, "77db1020"), SourceType.ANALYSIS);
ReferenceManager refMgr = program.getReferenceManager(); ReferenceManager refMgr = program.getReferenceManager();
@ -1328,13 +1329,7 @@ public class RefMergerExtTest extends AbstractExternalMergerTest {
finally { finally {
program.endTransaction(txId, commit); program.endTransaction(txId, commit);
} }
ExternalManager externalManager = program.getExternalManager();
ExternalLocationIterator externalLocations =
externalManager.getExternalLocations("ADVAPI32.DLL");
while (externalLocations.hasNext()) {
ExternalLocation next = externalLocations.next();
System.out.println("Location=" + next.getSymbol().getName(true));
}
ExternalLocation externalLocation1 = ExternalLocation externalLocation1 =
getExternalLocation(program, new String[] { "USER32.DLL", "printf" }); getExternalLocation(program, new String[] { "USER32.DLL", "printf" });
assertNotNull(externalLocation1); assertNotNull(externalLocation1);
@ -1452,13 +1447,7 @@ public class RefMergerExtTest extends AbstractExternalMergerTest {
finally { finally {
program.endTransaction(txId, commit); program.endTransaction(txId, commit);
} }
ExternalManager externalManager = program.getExternalManager();
ExternalLocationIterator externalLocations =
externalManager.getExternalLocations("ADVAPI32.DLL");
while (externalLocations.hasNext()) {
ExternalLocation next = externalLocations.next();
System.out.println("Location=" + next.getSymbol().getName(true));
}
ExternalLocation externalLocation1 = ExternalLocation externalLocation1 =
getExternalLocation(program, new String[] { "USER32.DLL", "printf" }); getExternalLocation(program, new String[] { "USER32.DLL", "printf" });
assertNotNull(externalLocation1); assertNotNull(externalLocation1);

View file

@ -94,15 +94,7 @@ public class SymbolMergeManagerNamespace1Test extends AbstractListingMergeManage
// 01005c6f FUN_01005c6f body:[1005c6f-1005fbd][1005ff5-10061e2] // 01005c6f FUN_01005c6f body:[1005c6f-1005fbd][1005ff5-10061e2]
// 01006420 entry body:[1006420-1006581][10065a4-10065cd] // 01006420 entry body:[1006420-1006581][10065a4-10065cd]
/** /*
*
* @param arg0
*/
public SymbolMergeManagerNamespace1Test() {
super();
}
/**
* Test generic Namespace symbols being removed from either the LATEST or * Test generic Namespace symbols being removed from either the LATEST or
* CHECKED OUT program when it doesn't result in a conflict. * CHECKED OUT program when it doesn't result in a conflict.
* @throws Exception * @throws Exception
@ -169,7 +161,7 @@ public class SymbolMergeManagerNamespace1Test extends AbstractListingMergeManage
assertNull(funcMgr.getFunctionAt(addr("0x01004bc0"))); assertNull(funcMgr.getFunctionAt(addr("0x01004bc0")));
} }
/** /*
* Test Class symbols being removed from either the LATEST or * Test Class symbols being removed from either the LATEST or
* CHECKED OUT program when it doesn't result in a conflict. * CHECKED OUT program when it doesn't result in a conflict.
* @throws Exception * @throws Exception
@ -566,16 +558,20 @@ public class SymbolMergeManagerNamespace1Test extends AbstractListingMergeManage
try { try {
Namespace ns; Namespace ns;
GhidraClass gc; GhidraClass gc;
ns = program.getSymbolTable().createNameSpace(program.getGlobalNamespace(), ns = program.getSymbolTable()
.createNameSpace(program.getGlobalNamespace(),
"Blue", SourceType.USER_DEFINED); "Blue", SourceType.USER_DEFINED);
assertNotNull(ns); assertNotNull(ns);
ns = program.getSymbolTable().createNameSpace(program.getGlobalNamespace(), ns = program.getSymbolTable()
.createNameSpace(program.getGlobalNamespace(),
"Green", SourceType.USER_DEFINED); "Green", SourceType.USER_DEFINED);
assertNotNull(ns); assertNotNull(ns);
gc = program.getSymbolTable().createClass(program.getGlobalNamespace(), "Red", gc = program.getSymbolTable()
.createClass(program.getGlobalNamespace(), "Red",
SourceType.USER_DEFINED); SourceType.USER_DEFINED);
assertNotNull(gc); assertNotNull(gc);
gc = program.getSymbolTable().createClass(program.getGlobalNamespace(), gc = program.getSymbolTable()
.createClass(program.getGlobalNamespace(),
"Yellow", SourceType.USER_DEFINED); "Yellow", SourceType.USER_DEFINED);
assertNotNull(gc); assertNotNull(gc);
commit = true; commit = true;
@ -598,16 +594,20 @@ public class SymbolMergeManagerNamespace1Test extends AbstractListingMergeManage
try { try {
Namespace ns; Namespace ns;
GhidraClass gc; GhidraClass gc;
gc = program.getSymbolTable().createClass(program.getGlobalNamespace(), "Blue", gc = program.getSymbolTable()
.createClass(program.getGlobalNamespace(), "Blue",
SourceType.USER_DEFINED); SourceType.USER_DEFINED);
assertNotNull(gc); assertNotNull(gc);
gc = program.getSymbolTable().createClass(program.getGlobalNamespace(), "Green", gc = program.getSymbolTable()
.createClass(program.getGlobalNamespace(), "Green",
SourceType.USER_DEFINED); SourceType.USER_DEFINED);
assertNotNull(gc); assertNotNull(gc);
ns = program.getSymbolTable().createNameSpace(program.getGlobalNamespace(), ns = program.getSymbolTable()
.createNameSpace(program.getGlobalNamespace(),
"Red", SourceType.USER_DEFINED); "Red", SourceType.USER_DEFINED);
assertNotNull(ns); assertNotNull(ns);
ns = program.getSymbolTable().createNameSpace(program.getGlobalNamespace(), ns = program.getSymbolTable()
.createNameSpace(program.getGlobalNamespace(),
"Yellow", SourceType.USER_DEFINED); "Yellow", SourceType.USER_DEFINED);
assertNotNull(ns); assertNotNull(ns);
commit = true; commit = true;
@ -665,16 +665,20 @@ public class SymbolMergeManagerNamespace1Test extends AbstractListingMergeManage
"EmptyNamespace", program.getGlobalNamespace()).getObject(); "EmptyNamespace", program.getGlobalNamespace()).getObject();
Namespace ns; Namespace ns;
GhidraClass gc; GhidraClass gc;
ns = program.getSymbolTable().createNameSpace(emptyNamespace, "Blue", ns = program.getSymbolTable()
.createNameSpace(emptyNamespace, "Blue",
SourceType.USER_DEFINED); SourceType.USER_DEFINED);
assertNotNull(ns); assertNotNull(ns);
ns = program.getSymbolTable().createNameSpace(emptyNamespace, "Green", ns = program.getSymbolTable()
.createNameSpace(emptyNamespace, "Green",
SourceType.USER_DEFINED); SourceType.USER_DEFINED);
assertNotNull(ns); assertNotNull(ns);
gc = program.getSymbolTable().createClass(emptyNamespace, "Red", gc = program.getSymbolTable()
.createClass(emptyNamespace, "Red",
SourceType.USER_DEFINED); SourceType.USER_DEFINED);
assertNotNull(gc); assertNotNull(gc);
gc = program.getSymbolTable().createClass(emptyNamespace, "Yellow", gc = program.getSymbolTable()
.createClass(emptyNamespace, "Yellow",
SourceType.USER_DEFINED); SourceType.USER_DEFINED);
assertNotNull(gc); assertNotNull(gc);
commit = true; commit = true;
@ -699,16 +703,20 @@ public class SymbolMergeManagerNamespace1Test extends AbstractListingMergeManage
"EmptyNamespace", program.getGlobalNamespace()).getObject(); "EmptyNamespace", program.getGlobalNamespace()).getObject();
Namespace ns; Namespace ns;
GhidraClass gc; GhidraClass gc;
gc = program.getSymbolTable().createClass(emptyNamespace, "Blue", gc = program.getSymbolTable()
.createClass(emptyNamespace, "Blue",
SourceType.USER_DEFINED); SourceType.USER_DEFINED);
assertNotNull(gc); assertNotNull(gc);
gc = program.getSymbolTable().createClass(emptyNamespace, "Green", gc = program.getSymbolTable()
.createClass(emptyNamespace, "Green",
SourceType.USER_DEFINED); SourceType.USER_DEFINED);
assertNotNull(gc); assertNotNull(gc);
ns = program.getSymbolTable().createNameSpace(emptyNamespace, "Red", ns = program.getSymbolTable()
.createNameSpace(emptyNamespace, "Red",
SourceType.USER_DEFINED); SourceType.USER_DEFINED);
assertNotNull(ns); assertNotNull(ns);
ns = program.getSymbolTable().createNameSpace(emptyNamespace, "Yellow", ns = program.getSymbolTable()
.createNameSpace(emptyNamespace, "Yellow",
SourceType.USER_DEFINED); SourceType.USER_DEFINED);
assertNotNull(ns); assertNotNull(ns);
commit = true; commit = true;
@ -773,16 +781,20 @@ public class SymbolMergeManagerNamespace1Test extends AbstractListingMergeManage
try { try {
Namespace ns; Namespace ns;
GhidraClass gc; GhidraClass gc;
ns = program.getSymbolTable().createNameSpace(program.getGlobalNamespace(), ns = program.getSymbolTable()
.createNameSpace(program.getGlobalNamespace(),
"Blue", SourceType.USER_DEFINED); "Blue", SourceType.USER_DEFINED);
assertNotNull(ns); assertNotNull(ns);
ns = program.getSymbolTable().createNameSpace(program.getGlobalNamespace(), ns = program.getSymbolTable()
.createNameSpace(program.getGlobalNamespace(),
"Green", SourceType.USER_DEFINED); "Green", SourceType.USER_DEFINED);
assertNotNull(ns); assertNotNull(ns);
gc = program.getSymbolTable().createClass(program.getGlobalNamespace(), "Red", gc = program.getSymbolTable()
.createClass(program.getGlobalNamespace(), "Red",
SourceType.USER_DEFINED); SourceType.USER_DEFINED);
assertNotNull(gc); assertNotNull(gc);
gc = program.getSymbolTable().createClass(program.getGlobalNamespace(), gc = program.getSymbolTable()
.createClass(program.getGlobalNamespace(),
"Yellow", SourceType.USER_DEFINED); "Yellow", SourceType.USER_DEFINED);
assertNotNull(gc); assertNotNull(gc);
commit = true; commit = true;
@ -805,22 +817,28 @@ public class SymbolMergeManagerNamespace1Test extends AbstractListingMergeManage
try { try {
Namespace ns; Namespace ns;
GhidraClass gc; GhidraClass gc;
gc = program.getSymbolTable().createClass(program.getGlobalNamespace(), "Blue", gc = program.getSymbolTable()
.createClass(program.getGlobalNamespace(), "Blue",
SourceType.USER_DEFINED); SourceType.USER_DEFINED);
assertNotNull(gc); assertNotNull(gc);
gc = program.getSymbolTable().createClass(program.getGlobalNamespace(), "Green", gc = program.getSymbolTable()
.createClass(program.getGlobalNamespace(), "Green",
SourceType.USER_DEFINED); SourceType.USER_DEFINED);
assertNotNull(gc); assertNotNull(gc);
ns = program.getSymbolTable().createNameSpace(program.getGlobalNamespace(), ns = program.getSymbolTable()
.createNameSpace(program.getGlobalNamespace(),
"Red", SourceType.USER_DEFINED); "Red", SourceType.USER_DEFINED);
assertNotNull(ns); assertNotNull(ns);
gc = program.getSymbolTable().createClass(ns, "SubRed", gc = program.getSymbolTable()
.createClass(ns, "SubRed",
SourceType.USER_DEFINED); SourceType.USER_DEFINED);
assertNotNull(gc); assertNotNull(gc);
ns = program.getSymbolTable().createNameSpace(program.getGlobalNamespace(), ns = program.getSymbolTable()
.createNameSpace(program.getGlobalNamespace(),
"Yellow", SourceType.USER_DEFINED); "Yellow", SourceType.USER_DEFINED);
assertNotNull(ns); assertNotNull(ns);
ns = program.getSymbolTable().createNameSpace(ns, "SubYellow", ns = program.getSymbolTable()
.createNameSpace(ns, "SubYellow",
SourceType.USER_DEFINED); SourceType.USER_DEFINED);
assertNotNull(ns); assertNotNull(ns);
commit = true; commit = true;
@ -1271,15 +1289,6 @@ public class SymbolMergeManagerNamespace1Test extends AbstractListingMergeManage
"The following namespaces were not removed", 4000); "The following namespaces were not removed", 4000);
waitForMergeCompletion(); waitForMergeCompletion();
SymbolTable symtab = resultProgram.getSymbolTable();
Namespace globalNS = resultProgram.getGlobalNamespace();
SymbolIterator iter = symtab.getSymbols(globalNS);
while (iter.hasNext()) {
Symbol s = iter.next();
if (s.getSymbolType().equals(SymbolType.NAMESPACE)) {
System.out.println("Namespace = " + s.getName(true));
}
}
Symbol firstNsSymbol = Symbol firstNsSymbol =
getUniqueSymbol(resultProgram, "FirstNamespace", resultProgram.getGlobalNamespace()); getUniqueSymbol(resultProgram, "FirstNamespace", resultProgram.getGlobalNamespace());
assertNotNull(firstNsSymbol); assertNotNull(firstNsSymbol);

View file

@ -15,24 +15,14 @@
*/ */
package ghidra.app.plugin.core.analysis; package ghidra.app.plugin.core.analysis;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.*;
import static org.junit.Assert.assertTrue;
import java.util.ArrayList; import java.util.*;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.swing.JComponent; import javax.swing.JComponent;
import javax.swing.table.TableModel; import javax.swing.table.TableModel;
import org.junit.After; import org.junit.*;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import docking.widgets.OptionDialog; import docking.widgets.OptionDialog;
import ghidra.GhidraOptions; import ghidra.GhidraOptions;
@ -71,6 +61,7 @@ public class AnalyzeAllOpenProgramsTaskTest extends AbstractGhidraHeadedIntegrat
for (Program program : openPrograms) { for (Program program : openPrograms) {
env.release(program); env.release(program);
} }
tool.close();
env.dispose(); env.dispose();
} }
@ -236,7 +227,7 @@ public class AnalyzeAllOpenProgramsTaskTest extends AbstractGhidraHeadedIntegrat
openPrograms.toArray(new Program[openPrograms.size()]), spy); openPrograms.toArray(new Program[openPrograms.size()]), spy);
runTask(task); runTask(task);
OptionDialog warningDialog = waitForDialogComponent(null, OptionDialog.class, 2000); OptionDialog warningDialog = waitForDialogComponent(OptionDialog.class);
pressButtonByText(warningDialog, "Cancel"); pressButtonByText(warningDialog, "Cancel");
waitForTasks(); waitForTasks();
@ -307,17 +298,19 @@ public class AnalyzeAllOpenProgramsTaskTest extends AbstractGhidraHeadedIntegrat
findComponent(optionsDialog.getComponent(), AnalysisPanel.class, false); findComponent(optionsDialog.getComponent(), AnalysisPanel.class, false);
invokeInstanceMethod("deselectAll", panel); invokeInstanceMethod("deselectAll", panel);
waitForSwing(); waitForSwing();
close(optionsDialog);
} }
private void enableOption(String optionName, boolean expectWarning) { private void enableOption(String optionName, boolean expectWarning) {
if (expectWarning) { if (expectWarning) {
OptionDialog warningDialog = waitForDialogComponent(null, OptionDialog.class, 2000); OptionDialog warningDialog = waitForDialogComponent(OptionDialog.class);
pressButtonByText(warningDialog, "Continue"); pressButtonByText(warningDialog, "Continue");
} }
AnalysisOptionsDialog optionsDialog = AnalysisOptionsDialog optionsDialog =
waitForDialogComponent(null, AnalysisOptionsDialog.class, DEFAULT_WINDOW_TIMEOUT); waitForDialogComponent(AnalysisOptionsDialog.class);
// select some options // select some options
JComponent root = optionsDialog.getComponent(); JComponent root = optionsDialog.getComponent();
@ -332,7 +325,7 @@ public class AnalyzeAllOpenProgramsTaskTest extends AbstractGhidraHeadedIntegrat
private void cancelAnalysisDialog() { private void cancelAnalysisDialog() {
AnalysisOptionsDialog optionsDialog = AnalysisOptionsDialog optionsDialog =
waitForDialogComponent(null, AnalysisOptionsDialog.class, DEFAULT_WINDOW_TIMEOUT); waitForDialogComponent(AnalysisOptionsDialog.class);
// press Apply // press Apply
pressButtonByText(optionsDialog, "Cancel"); pressButtonByText(optionsDialog, "Cancel");
@ -437,6 +430,11 @@ public class AnalyzeAllOpenProgramsTaskTest extends AbstractGhidraHeadedIntegrat
} }
return super.getOptions(categoryName); return super.getOptions(categoryName);
} }
@Override
public void close() {
runSwing(super::close);
}
} }
private class AnalyzeProgramStrategySpy extends AnalyzeProgramStrategy { private class AnalyzeProgramStrategySpy extends AnalyzeProgramStrategy {

View file

@ -24,6 +24,7 @@ import javax.swing.table.*;
import org.junit.*; import org.junit.*;
import docking.ActionContext;
import docking.ComponentProvider; import docking.ComponentProvider;
import docking.action.DockingActionIf; import docking.action.DockingActionIf;
import docking.tool.ToolConstants; import docking.tool.ToolConstants;
@ -45,6 +46,7 @@ public class FunctionWindowPluginTest extends AbstractGhidraHeadedIntegrationTes
private Program program; private Program program;
private FunctionWindowPlugin plugin; private FunctionWindowPlugin plugin;
private GTable functionTable; private GTable functionTable;
private ComponentProvider provider;
@Before @Before
public void setUp() throws Exception { public void setUp() throws Exception {
@ -56,7 +58,7 @@ public class FunctionWindowPluginTest extends AbstractGhidraHeadedIntegrationTes
plugin.showFunctions(); plugin.showFunctions();
waitForSwing(); waitForSwing();
ComponentProvider provider = tool.getComponentProvider("Functions Window"); provider = tool.getComponentProvider("Functions Window");
functionTable = (GTable) findComponentByName(provider.getComponent(), "FunctionTable"); functionTable = (GTable) findComponentByName(provider.getComponent(), "FunctionTable");
} }
@ -74,6 +76,8 @@ public class FunctionWindowPluginTest extends AbstractGhidraHeadedIntegrationTes
private void closeProgram() { private void closeProgram() {
ProgramManager pm = tool.getService(ProgramManager.class); ProgramManager pm = tool.getService(ProgramManager.class);
pm.closeProgram(program, true); pm.closeProgram(program, true);
waitForSwing();
waitForNotBusy(functionTable);
} }
@Test @Test
@ -96,16 +100,13 @@ public class FunctionWindowPluginTest extends AbstractGhidraHeadedIntegrationTes
waitForNotBusy(functionTable); waitForNotBusy(functionTable);
assertEquals(numData, functionTable.getRowCount()); assertEquals(numData, functionTable.getRowCount());
} }
@Test @Test
public void testProgramClose() throws Exception { public void testProgramClose() throws Exception {
closeProgram(); closeProgram();
waitForNotBusy(functionTable); waitForNotBusy(functionTable);
assertEquals(functionTable.getRowCount(), 0); assertEquals(functionTable.getRowCount(), 0);
loadProgram("notepad");
} }
@Test @Test
@ -147,7 +148,8 @@ public class FunctionWindowPluginTest extends AbstractGhidraHeadedIntegrationTes
String signatureText = getRenderedTableCellValue(functionTable, row, column); String signatureText = getRenderedTableCellValue(functionTable, row, column);
DockingActionIf copyAction = getAction(tool, ToolConstants.SHARED_OWNER, "Table Data Copy"); DockingActionIf copyAction = getAction(tool, ToolConstants.SHARED_OWNER, "Table Data Copy");
performAction(copyAction); ActionContext context = new ActionContext(provider, functionTable);
performAction(copyAction, context, true);
// //
// Note: we cannot make this call: // Note: we cannot make this call:

View file

@ -15,8 +15,7 @@
*/ */
package ghidra.app.plugin.core.label; package ghidra.app.plugin.core.label;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.*;
import static org.junit.Assert.assertTrue;
import javax.swing.JComponent; import javax.swing.JComponent;
import javax.swing.JTable; import javax.swing.JTable;
@ -44,7 +43,8 @@ import ghidra.program.model.symbol.*;
import ghidra.program.util.*; import ghidra.program.util.*;
import ghidra.test.*; import ghidra.test.*;
public class LabelActionTest extends AbstractGhidraHeadedIntegrationTest implements LocationCallback { public class LabelActionTest extends AbstractGhidraHeadedIntegrationTest
implements LocationCallback {
private static final String ADD_LABEL = "Add Label"; private static final String ADD_LABEL = "Add Label";
private static final String EDIT_LABEL = "Edit Label"; private static final String EDIT_LABEL = "Edit Label";
private static final String EDIT_EXTERNAL_LOC = "Edit External Location"; private static final String EDIT_EXTERNAL_LOC = "Edit External Location";
@ -80,6 +80,8 @@ public class LabelActionTest extends AbstractGhidraHeadedIntegrationTest impleme
editExternalLocation = getAction(labelMgrPlugin, EDIT_EXTERNAL_LOC); editExternalLocation = getAction(labelMgrPlugin, EDIT_EXTERNAL_LOC);
removeLabel = getAction(labelMgrPlugin, REMOVE_LABEL); removeLabel = getAction(labelMgrPlugin, REMOVE_LABEL);
setLabel = getAction(labelMgrPlugin, SET_LABEL); setLabel = getAction(labelMgrPlugin, SET_LABEL);
env.showTool();
} }
@After @After
@ -128,6 +130,7 @@ public class LabelActionTest extends AbstractGhidraHeadedIntegrationTest impleme
Object author = model.getValueAt(0, 2); Object author = model.getValueAt(0, 2);
assertTrue(author.toString().startsWith(System.getProperty("user.name"))); assertTrue(author.toString().startsWith(System.getProperty("user.name")));
close(provider);
} }
@Test @Test

View file

@ -27,7 +27,6 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.swing.*; import javax.swing.*;
import javax.swing.table.TableModel; import javax.swing.table.TableModel;
@ -64,6 +63,7 @@ import ghidra.util.exception.CancelledException;
import ghidra.util.table.GhidraTableFilterPanel; import ghidra.util.table.GhidraTableFilterPanel;
import ghidra.util.task.*; import ghidra.util.task.*;
import util.CollectionUtils; import util.CollectionUtils;
import utilities.util.FileUtilities;
public abstract class AbstractGhidraScriptMgrPluginTest public abstract class AbstractGhidraScriptMgrPluginTest
extends AbstractGhidraHeadedIntegrationTest { extends AbstractGhidraHeadedIntegrationTest {
@ -170,32 +170,19 @@ public abstract class AbstractGhidraScriptMgrPluginTest
deleteFile(testScriptFile); deleteFile(testScriptFile);
testScriptFile = null; testScriptFile = null;
} }
wipeUserScripts(); deleteUserScripts();
env.dispose(); env.dispose();
} }
protected static void wipe(ResourceFile path) throws IOException { protected static void delete(Path path) {
wipe(Paths.get(path.getAbsolutePath())); FileUtilities.deleteDir(path);
} }
protected static void wipe(Path path) throws IOException { protected void deleteUserScripts() throws IOException {
if (Files.exists(path)) {
try (Stream<Path> walk = Files.walk(path)) {
for (Path p : (Iterable<Path>) walk.sorted(Comparator.reverseOrder())::iterator) {
Files.deleteIfExists(p);
}
}
}
}
protected void wipeUserScripts() throws IOException { Path userScriptDir = Paths.get(GhidraScriptUtil.USER_SCRIPTS_DIR);
Path userScriptDir = java.nio.file.Paths.get(GhidraScriptUtil.USER_SCRIPTS_DIR); FileUtilities.forEachFile(userScriptDir, paths -> paths.forEach(p -> delete(p)));
try (Stream<Path> pathStream = Files.list(userScriptDir)) {
for (Path p : (Iterable<Path>) pathStream::iterator) {
wipe(p);
}
}
} }
//================================================================================================== //==================================================================================================
@ -983,9 +970,9 @@ public abstract class AbstractGhidraScriptMgrPluginTest
} }
protected void cleanupOldTestFiles() throws IOException { protected void cleanupOldTestFiles() {
// remove the compiled bundles directory so that any scripts we use will be recompiled // remove the compiled bundles directory so that any scripts we use will be recompiled
wipe(GhidraSourceBundle.getCompiledBundlesDir()); delete(GhidraSourceBundle.getCompiledBundlesDir());
String myTestName = super.testName.getMethodName(); String myTestName = super.testName.getMethodName();
@ -1559,12 +1546,12 @@ public abstract class AbstractGhidraScriptMgrPluginTest
@Override @Override
public void taskAdded(Task task) { public void taskAdded(Task task) {
Msg.debug(this, "taskAdded(): " + task.getTaskTitle()); Msg.trace(this, "taskAdded(): " + task.getTaskTitle());
} }
@Override @Override
public void taskRemoved(Task task) { public void taskRemoved(Task task) {
Msg.debug(this, "taskRemoved(): " + task.getTaskTitle()); Msg.trace(this, "taskRemoved(): " + task.getTaskTitle());
if (taskName.equals(task.getTaskTitle())) { if (taskName.equals(task.getTaskTitle())) {
ended = true; ended = true;
} }
@ -1657,13 +1644,13 @@ public abstract class AbstractGhidraScriptMgrPluginTest
@Override @Override
public void println(String msg) { public void println(String msg) {
apiBuffer.append(msg).append('\n'); apiBuffer.append(msg).append('\n');
Msg.debug(this, "Spy Script Console - println(): " + msg); Msg.trace(this, "Spy Script Console - println(): " + msg);
} }
@Override @Override
public void addMessage(String originator, String msg) { public void addMessage(String originator, String msg) {
apiBuffer.append(msg).append('\n'); apiBuffer.append(msg).append('\n');
Msg.debug(this, "Spy Script Console - addMessage(): " + msg); Msg.trace(this, "Spy Script Console - addMessage(): " + msg);
} }
String getApiOutput() { String getApiOutput() {

View file

@ -23,7 +23,6 @@ import java.nio.file.Path;
import java.util.*; import java.util.*;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import java.util.stream.Stream;
import org.junit.*; import org.junit.*;
import org.osgi.framework.Bundle; import org.osgi.framework.Bundle;
@ -31,27 +30,23 @@ import org.osgi.framework.Bundle;
import generic.jar.ResourceFile; import generic.jar.ResourceFile;
import ghidra.app.plugin.core.osgi.*; import ghidra.app.plugin.core.osgi.*;
import ghidra.test.AbstractGhidraHeadlessIntegrationTest; import ghidra.test.AbstractGhidraHeadlessIntegrationTest;
import utilities.util.FileUtilities;
public class BundleHostTest extends AbstractGhidraHeadlessIntegrationTest { public class BundleHostTest extends AbstractGhidraHeadlessIntegrationTest {
BundleHost bundleHost; private static final String TEMP_NAME_PREFIX = "sourcebundle";
CapturingBundleHostListener capturingBundleHostListener; private BundleHost bundleHost;
private CapturingBundleHostListener capturingBundleHostListener;
Set<Path> tempDirs = new HashSet<>(); private Set<Path> tempDirs = new HashSet<>();
LinkedList<GhidraBundle> bundleStack = new LinkedList<>(); private LinkedList<GhidraBundle> bundleStack = new LinkedList<>();
GhidraBundle currentBundle; private GhidraBundle currentBundle;
protected static void wipe(Path path) throws IOException { private static void wipe(Path path) {
if (Files.exists(path)) { FileUtilities.deleteDir(path);
try (Stream<Path> walk = Files.walk(path)) {
for (Path p : (Iterable<Path>) walk.sorted(Comparator.reverseOrder())::iterator) {
Files.deleteIfExists(p);
}
}
}
} }
protected GhidraBundle pushNewBundle() throws IOException { private GhidraBundle pushNewBundle() throws IOException {
String dir = String.format("sourcebundle%03d", tempDirs.size()); String dir = String.format(TEMP_NAME_PREFIX + "%03d", tempDirs.size());
Path tmpDir = new File(getTestDirectoryPath(), dir).toPath(); Path tmpDir = new File(getTestDirectoryPath(), dir).toPath();
Files.createDirectories(tmpDir); Files.createDirectories(tmpDir);
tempDirs.add(tmpDir); tempDirs.add(tmpDir);
@ -62,7 +57,7 @@ public class BundleHostTest extends AbstractGhidraHeadlessIntegrationTest {
return currentBundle; return currentBundle;
} }
static class CapturingBundleHostListener implements BundleHostListener { private static class CapturingBundleHostListener implements BundleHostListener {
String lastBuildSummary; String lastBuildSummary;
@Override @Override
@ -76,6 +71,7 @@ public class BundleHostTest extends AbstractGhidraHeadlessIntegrationTest {
@Before @Before
public void setup() throws OSGiException, IOException { public void setup() throws OSGiException, IOException {
wipe(GhidraSourceBundle.getCompiledBundlesDir()); wipe(GhidraSourceBundle.getCompiledBundlesDir());
deleteSimilarTempFiles(TEMP_NAME_PREFIX);
bundleHost = new BundleHost(); bundleHost = new BundleHost();
bundleHost.startFramework(); bundleHost.startFramework();
@ -86,7 +82,7 @@ public class BundleHostTest extends AbstractGhidraHeadlessIntegrationTest {
} }
@After @After
public void tearDown() throws IOException { public void tearDown() {
bundleHost.dispose(); bundleHost.dispose();
capturingBundleHostListener = null; capturingBundleHostListener = null;
bundleHost = null; bundleHost = null;
@ -96,7 +92,7 @@ public class BundleHostTest extends AbstractGhidraHeadlessIntegrationTest {
} }
} }
protected void buildWithExpectations(String expectedCompilerOutput, String expectedSummary) private void buildWithExpectations(String expectedCompilerOutput, String expectedSummary)
throws Exception { throws Exception {
StringWriter stringWriter = new StringWriter(); StringWriter stringWriter = new StringWriter();
@ -110,32 +106,32 @@ public class BundleHostTest extends AbstractGhidraHeadlessIntegrationTest {
capturingBundleHostListener.lastBuildSummary); capturingBundleHostListener.lastBuildSummary);
} }
protected void activate() throws Exception { private void activate() throws Exception {
Bundle bundle = bundleHost.install(currentBundle); Bundle bundle = bundleHost.install(currentBundle);
assertNotNull("failed to install bundle", bundle); assertNotNull("failed to install bundle", bundle);
bundle.start(); bundle.start();
} }
protected void buildAndActivate() throws Exception { private void buildAndActivate() throws Exception {
buildWithExpectations("", ""); buildWithExpectations("", "");
activate(); activate();
} }
protected Class<?> loadClass(String classname) throws ClassNotFoundException { private Class<?> loadClass(String classname) throws ClassNotFoundException {
Class<?> clazz = currentBundle.getOSGiBundle().loadClass(classname); Class<?> clazz = currentBundle.getOSGiBundle().loadClass(classname);
assertNotNull("failed to load class", clazz); assertNotNull("failed to load class", clazz);
return clazz; return clazz;
} }
protected void addClass(String fullclassname, String body) throws IOException { private void addClass(String fullclassname, String body) throws IOException {
addClass("", fullclassname, body); addClass("", fullclassname, body);
} }
protected void addClass(String imports, String fullclassname, String body) throws IOException { private void addClass(String imports, String fullclassname, String body) throws IOException {
addClass("", imports, fullclassname, body); addClass("", imports, fullclassname, body);
} }
protected void addClass(String meta, String imports, String fullclassname, String body) private void addClass(String meta, String imports, String fullclassname, String body)
throws IOException { throws IOException {
String simplename; String simplename;
Path tmpsource = currentBundle.getFile().getFile(false).toPath(); Path tmpsource = currentBundle.getFile().getFile(false).toPath();
@ -172,7 +168,7 @@ public class BundleHostTest extends AbstractGhidraHeadlessIntegrationTest {
} }
protected Object getInstance(String classname) throws Exception { private Object getInstance(String classname) throws Exception {
Class<?> clazz = loadClass(classname); Class<?> clazz = loadClass(classname);
Object object = clazz.getDeclaredConstructor().newInstance(); Object object = clazz.getDeclaredConstructor().newInstance();
assertNotNull("failed to create instance", object); assertNotNull("failed to create instance", object);

View file

@ -65,155 +65,13 @@ public class BundleStatusManagerTest extends AbstractGhidraScriptMgrPluginTest {
provider.getBundleHost().removeListener(testBundleHostListener); provider.getBundleHost().removeListener(testBundleHostListener);
} }
protected static String translateSeperators(String path) {
if (!File.separator.equals("/")) {
return path.replace("/", File.separator);
}
return path;
}
void enableViaGUI(int viewRow) throws InterruptedException {
testBundleHostListener.reset();
runSwing(() -> {
bundleStatusTable.setValueAt(true, viewRow, 0);
});
waitForSwing();
// we wait for the last event, the activation of the bundle.
testBundleHostListener.awaitActivation();
}
void disableViaGUI(int viewRow) throws InterruptedException {
testBundleHostListener.reset();
runSwing(() -> {
bundleStatusTable.setValueAt(false, viewRow, 0);
});
waitForSwing();
testBundleHostListener.awaitDisablement();
}
List<BundleStatus> selectRows(int... viewRows) {
List<BundleStatus> statuses = Arrays.stream(viewRows)
.mapToObj(bundleStatusTableModel::getRowObject)
.collect(Collectors.toList());
for (BundleStatus status : statuses) {
assertNotNull(status);
}
runSwing(() -> {
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;
do {
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());
}
}
/**
* Find the view row index in the BundleStatusTableModel of the status with the given bundle path, or
* -1 if it's not found.
*
* @param bundlePath bundle path to find
* @return view row index or -1 if not found
*/
protected int getBundleRow(String bundlePath) {
AtomicInteger rowref = new AtomicInteger(-1);
runSwing(() -> {
for (int i = 0; i < bundleStatusTableModel.getRowCount(); i++) {
BundleStatus status = bundleStatusTableModel.getRowObject(i);
if (bundlePath.equals(status.getPathAsString())) {
rowref.set(i);
break;
}
}
});
return rowref.get();
}
@SuppressWarnings("unchecked")
DockingActionIf getActionByName(ComponentProvider componentProvider, String actionName) {
Set<DockingActionIf> actionSet =
(Set<DockingActionIf>) getInstanceField("actionSet", bundleStatusProvider);
for (DockingActionIf action : actionSet) {
if (action.getName().equals(actionName)) {
return action;
}
}
return null;
}
@Test @Test
public void testDisableEnableScriptDirectory() throws Exception { public void testDisableEnableScriptDirectory() throws Exception {
// //
// Tests that the user can disable then enable a script directory // Tests that the user can disable then enable a script directory
// //
int viewRow = getBundleRow(BUNDLE_PATH); int viewRow = getBundleRow(BUNDLE_PATH);
selectRow(viewRow);
assertTrue(viewRow != -1);
runSwing(() -> {
bundleStatusTable.selectRow(viewRow);
bundleStatusTable.scrollToSelectedRow();
});
waitForSwing();
BundleStatus status = bundleStatusTableModel.getRowObject(viewRow); BundleStatus status = bundleStatusTableModel.getRowObject(viewRow);
@ -222,78 +80,22 @@ public class BundleStatusManagerTest extends AbstractGhidraScriptMgrPluginTest {
assertTrue(status.isEnabled()); assertTrue(status.isEnabled());
assertScriptInTable(scriptFile); assertScriptInTable(scriptFile);
// disable it
disableViaGUI(viewRow); disableViaGUI(viewRow);
assertTrue(!status.isEnabled()); assertTrue(!status.isEnabled());
assertScriptNotInTable(scriptFile); assertScriptNotInTable(scriptFile);
// re-enable it
enableViaGUI(viewRow); enableViaGUI(viewRow);
assertTrue(status.isEnabled()); assertTrue(status.isEnabled());
assertScriptInTable(scriptFile); 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 @Test
public void testRunCleanRun() throws Exception { public void testRunCleanRun() throws Exception {
int viewRow = getBundleRow(BUNDLE_PATH); int viewRow = getBundleRow(BUNDLE_PATH);
assertNotEquals(viewRow, -1);
assertTrue(viewRow != -1); selectRows(viewRow);
runSwing(() -> {
bundleStatusTable.selectRow(viewRow);
bundleStatusTable.scrollToSelectedRow();
});
waitForSwing();
BundleStatus status = bundleStatusTableModel.getRowObject(viewRow); BundleStatus status = bundleStatusTableModel.getRowObject(viewRow);
@ -302,18 +104,15 @@ public class BundleStatusManagerTest extends AbstractGhidraScriptMgrPluginTest {
assertTrue(status.isEnabled()); assertTrue(status.isEnabled());
assertScriptInTable(scriptFile); assertScriptInTable(scriptFile);
// run
runScript(SCRIPT_NAME); runScript(SCRIPT_NAME);
// clean
cleanViaGUI(viewRow); cleanViaGUI(viewRow);
// run
runScript(SCRIPT_NAME); runScript(SCRIPT_NAME);
} }
@Test @Test
public void addRunCleanRemoveTwoBundles() throws Exception { public void testAddRunCleanRemoveTwoBundles() throws Exception {
final String TEST_SCRIPT_NAME = testName.getMethodName(); final String TEST_SCRIPT_NAME = testName.getMethodName();
//@formatter:off //@formatter:off
@ -385,30 +184,227 @@ public class BundleStatusManagerTest extends AbstractGhidraScriptMgrPluginTest {
String output = runScript(TEST_SCRIPT_NAME + ".java"); String output = runScript(TEST_SCRIPT_NAME + ".java");
assertEquals(EXPECTED_OUTPUT, output); assertEquals(EXPECTED_OUTPUT, output);
int row1 = getBundleRow(generic.util.Path.toPathString(new ResourceFile(dir1))); int row1 = getBundleRow(dir1);
int row2 = getBundleRow(generic.util.Path.toPathString(new ResourceFile(dir2))); int row2 = getBundleRow(dir2);
assertFalse(row1 == -1); assertNotEquals(row1, -1);
assertFalse(row2 == -1); assertNotEquals(row2, -1);
cleanViaGUI(row1, row2); cleanViaGUI(row1, row2);
removeViaGUI(row1, row2); removeViaGUI(row1, row2);
row1 = getBundleRow(generic.util.Path.toPathString(new ResourceFile(dir1))); row1 = getBundleRow(dir1);
row2 = getBundleRow(generic.util.Path.toPathString(new ResourceFile(dir2))); row2 = getBundleRow(dir2);
assertTrue(row1 == -1); assertEquals(row1, -1);
assertTrue(row2 == -1); assertEquals(row2, -1);
} }
finally { finally {
wipe(dir1.toPath()); delete(dir1.toPath());
wipe(dir2.toPath()); delete(dir2.toPath());
} }
} }
private static String translateSeperators(String path) {
if (!File.separator.equals("/")) {
return path.replace("/", File.separator);
}
return path;
}
private void enableViaGUI(int viewRow) throws InterruptedException {
testBundleHostListener.reset();
runSwing(() -> {
bundleStatusTable.setValueAt(true, viewRow, 0);
});
waitForSwing();
// we wait for the last event, the activation of the bundle.
testBundleHostListener.awaitActivation();
}
private void disableViaGUI(int viewRow) throws InterruptedException {
testBundleHostListener.reset();
runSwing(() -> {
bundleStatusTable.setValueAt(false, viewRow, 0);
});
waitForSwing();
testBundleHostListener.awaitDisablement();
}
private void selectRow(int viewRow) {
assertNotEquals(viewRow, -1);
runSwing(() -> {
bundleStatusTable.selectRow(viewRow);
bundleStatusTable.scrollToSelectedRow();
});
waitForSwing();
}
private List<BundleStatus> selectRows(int... viewRows) {
List<BundleStatus> statuses = Arrays.stream(viewRows)
.mapToObj(bundleStatusTableModel::getRowObject)
.collect(Collectors.toList());
for (BundleStatus status : statuses) {
assertNotNull(status);
}
runSwing(() -> {
bundleStatusTable.clearSelection();
for (int viewRow : viewRows) {
bundleStatusTable.addRowSelectionInterval(viewRow, viewRow);
}
});
return statuses;
}
private 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;
do {
if (statuses.size() <= initialSize - viewRows.length) {
break;
}
Thread.sleep(250);
}
while (++count < 8);
assertTrue("Failure, clean took too long", count < 8);
}
private 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());
}
}
private int getBundleRow(File dir) {
return getBundleRow(generic.util.Path.toPathString(new ResourceFile(dir)));
}
/**
* Find the view row index in the BundleStatusTableModel of the status with the given bundle path, or
* -1 if it's not found.
*
* @param bundlePath bundle path to find
* @return view row index or -1 if not found
*/
private int getBundleRow(String bundlePath) {
AtomicInteger rowref = new AtomicInteger(-1);
runSwing(() -> {
for (int i = 0; i < bundleStatusTableModel.getRowCount(); i++) {
BundleStatus status = bundleStatusTableModel.getRowObject(i);
if (bundlePath.equals(status.getPathAsString())) {
rowref.set(i);
break;
}
}
});
return rowref.get();
}
@SuppressWarnings("unchecked")
private DockingActionIf getActionByName(ComponentProvider componentProvider,
String actionName) {
Set<DockingActionIf> actionSet =
(Set<DockingActionIf>) getInstanceField("actionSet", bundleStatusProvider);
for (DockingActionIf action : actionSet) {
if (action.getName().equals(actionName)) {
return action;
}
}
return null;
}
/**
* 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
*/
private 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]));
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
public 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;
}
/** /**
* A {@link BundleHostListener} to help serialize bundle operations. * A {@link BundleHostListener} to help serialize bundle operations.
*/ */
protected class TestBundleHostListener implements BundleHostListener { private class TestBundleHostListener implements BundleHostListener {
CountDownLatch activationLatch; CountDownLatch activationLatch;
CountDownLatch disablementLatch; CountDownLatch disablementLatch;

View file

@ -631,6 +631,7 @@ public class SearchTextPlugin2Test extends AbstractGhidraHeadedIntegrationTest {
waitForSearchTasks(tempDialog); waitForSearchTasks(tempDialog);
waitForSwing(); waitForSwing();
assertFalse(tempDialog.isVisible()); assertFalse(tempDialog.isVisible());
assertFalse(isEnabled(searchAction)); assertFalse(isEnabled(searchAction));
} }

View file

@ -112,6 +112,8 @@ public class KeyBindingUtilsTest extends AbstractGhidraHeadedIntegrationTest {
tool.addPlugin(DataPlugin.class.getName()); tool.addPlugin(DataPlugin.class.getName());
tool.addPlugin(FunctionPlugin.class.getName()); tool.addPlugin(FunctionPlugin.class.getName());
env.showTool();
debug("two"); debug("two");
} }

View file

@ -15,8 +15,7 @@
*/ */
package ghidra.program.database.map; package ghidra.program.database.map;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.*;
import static org.junit.Assert.assertTrue;
import java.util.Iterator; import java.util.Iterator;
@ -429,7 +428,7 @@ public class NormalizedAddressSetTest extends AbstractGhidraHeadedIntegrationTes
AddressIterator iter = set.getAddresses(false); AddressIterator iter = set.getAddresses(false);
while (iter.hasNext()) { while (iter.hasNext()) {
Address addr = iter.next(); Address addr = iter.next();
System.out.println(addr.toString(true));
assertEquals(checkAddress, addr); assertEquals(checkAddress, addr);
if (checkAddress.equals(addr(0xffffffccL))) { if (checkAddress.equals(addr(0xffffffccL))) {
checkAddress = addr(0xffffffbbL); checkAddress = addr(0xffffffbbL);

View file

@ -15,8 +15,7 @@
*/ */
package ghidra.program.database.util; package ghidra.program.database.util;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.*;
import static org.junit.Assert.assertTrue;
import java.io.IOException; import java.io.IOException;
import java.util.Iterator; import java.util.Iterator;
@ -30,10 +29,12 @@ import ghidra.util.datastruct.IndexRange;
import ghidra.util.datastruct.IndexRangeIterator; import ghidra.util.datastruct.IndexRangeIterator;
@SuppressWarnings("deprecation") // the SharedRangeMapDB is deprecated, but we still need to test it @SuppressWarnings("deprecation") // the SharedRangeMapDB is deprecated, but we still need to test it
public class SharedRangeMapDBTest extends AbstractGhidraHeadedIntegrationTest implements ErrorHandler { public class SharedRangeMapDBTest extends AbstractGhidraHeadedIntegrationTest
implements ErrorHandler {
private DBHandle dbh; private DBHandle dbh;
private long transactionID; private long transactionID;
/** /**
* Constructor for SharedRangeMapDBTest. * Constructor for SharedRangeMapDBTest.
* @param arg0 * @param arg0
@ -65,15 +66,17 @@ public class SharedRangeMapDBTest extends AbstractGhidraHeadedIntegrationTest im
/** /**
* @see db.util.ErrorHandler#dbError(java.io.IOException) * @see db.util.ErrorHandler#dbError(java.io.IOException)
*/ */
@Override
public void dbError(IOException e) { public void dbError(IOException e) {
throw new RuntimeException(e.getMessage()); throw new RuntimeException(e.getMessage());
} }
private int indexOf(Object[] list, Object item) { private int indexOf(Object[] list, Object item) {
for (int i = 0; i < list.length; i++) { for (int i = 0; i < list.length; i++) {
if (list[i].equals(item)) if (list[i].equals(item)) {
return i; return i;
} }
}
return -1; return -1;
} }
@ -85,17 +88,19 @@ public class SharedRangeMapDBTest extends AbstractGhidraHeadedIntegrationTest im
* @param mapRangeToValue (from is rangeKey, to is value) * @param mapRangeToValue (from is rangeKey, to is value)
* @throws IOException * @throws IOException
*/ */
private void inspectRecords(SharedRangeMapDB map, IndexRange[] ranges, IndexRange[] mapRangeToValue) throws IOException { private void inspectRecords(SharedRangeMapDB map, IndexRange[] ranges,
System.out.println("Inspecting---"); IndexRange[] mapRangeToValue) throws IOException {
RecordIterator iter = map.rangeTable.iterator(); RecordIterator iter = map.rangeTable.iterator();
int cnt = 0; int cnt = 0;
while (iter.hasNext()) { while (iter.hasNext()) {
++cnt; ++cnt;
Record rec = iter.next(); Record rec = iter.next();
IndexRange range = new IndexRange(rec.getKey(), rec.getLongValue(SharedRangeMapDB.RANGE_TO_COL)); IndexRange range =
if (indexOf(ranges, range) < 0) new IndexRange(rec.getKey(), rec.getLongValue(SharedRangeMapDB.RANGE_TO_COL));
if (indexOf(ranges, range) < 0) {
Assert.fail("Unexpected range: " + range.getStart() + " - " + range.getEnd()); Assert.fail("Unexpected range: " + range.getStart() + " - " + range.getEnd());
System.out.println(" Range: " + range.getStart() + " - " + range.getEnd()); }
} }
assertEquals(ranges.length, cnt); assertEquals(ranges.length, cnt);
@ -106,14 +111,15 @@ System.out.println(" Range: " + range.getStart() + " - " + range.getEnd());
Record rec = iter.next(); Record rec = iter.next();
IndexRange entry = new IndexRange(rec.getLongValue(SharedRangeMapDB.MAP_RANGE_KEY_COL), IndexRange entry = new IndexRange(rec.getLongValue(SharedRangeMapDB.MAP_RANGE_KEY_COL),
rec.getLongValue(SharedRangeMapDB.MAP_VALUE_COL)); rec.getLongValue(SharedRangeMapDB.MAP_VALUE_COL));
if (indexOf(mapRangeToValue, entry) < 0) if (indexOf(mapRangeToValue, entry) < 0) {
Assert.fail("Unexpected map entry: rangeKey=" + entry.getStart() + ", value=" + entry.getEnd()); Assert.fail("Unexpected map entry: rangeKey=" + entry.getStart() + ", value=" +
System.out.println(" Map entry: rangeKey=" + entry.getStart() + ", value=" + entry.getEnd()); entry.getEnd());
}
} }
assertEquals(mapRangeToValue.length, cnt); assertEquals(mapRangeToValue.length, cnt);
} }
@Test @Test
public void testAdd() throws IOException { public void testAdd() throws IOException {
SharedRangeMapDB map = new SharedRangeMapDB(dbh, "TEST", this, true); SharedRangeMapDB map = new SharedRangeMapDB(dbh, "TEST", this, true);
@ -124,10 +130,10 @@ System.out.println(" Map entry: rangeKey=" + entry.getStart() + ", value=" + en
map.add(70, 80, 1); map.add(70, 80, 1);
IndexRange[] ranges = new IndexRange[] { IndexRange[] ranges = new IndexRange[] {
new IndexRange(10,20), new IndexRange(10, 20),
new IndexRange(30,40), new IndexRange(30, 40),
new IndexRange(50,60), new IndexRange(50, 60),
new IndexRange(70,80) new IndexRange(70, 80)
}; };
IndexRange[] entries = new IndexRange[] { IndexRange[] entries = new IndexRange[] {
@ -150,9 +156,9 @@ System.out.println(" Map entry: rangeKey=" + entry.getStart() + ", value=" + en
map.add(21, 29, 1); map.add(21, 29, 1);
ranges = new IndexRange[] { ranges = new IndexRange[] {
new IndexRange(10,40), new IndexRange(10, 40),
new IndexRange(50,60), new IndexRange(50, 60),
new IndexRange(70,80) new IndexRange(70, 80)
}; };
entries = new IndexRange[] { entries = new IndexRange[] {
@ -167,8 +173,8 @@ System.out.println(" Map entry: rangeKey=" + entry.getStart() + ", value=" + en
map.add(35, 55, 1); map.add(35, 55, 1);
ranges = new IndexRange[] { ranges = new IndexRange[] {
new IndexRange(10,60), new IndexRange(10, 60),
new IndexRange(70,80) new IndexRange(70, 80)
}; };
entries = new IndexRange[] { entries = new IndexRange[] {
@ -182,7 +188,7 @@ System.out.println(" Map entry: rangeKey=" + entry.getStart() + ", value=" + en
map.add(55, 90, 1); map.add(55, 90, 1);
ranges = new IndexRange[] { ranges = new IndexRange[] {
new IndexRange(10,90) new IndexRange(10, 90)
}; };
entries = new IndexRange[] { entries = new IndexRange[] {
@ -296,7 +302,7 @@ System.out.println(" Map entry: rangeKey=" + entry.getStart() + ", value=" + en
inspectRecords(map, ranges, entries); inspectRecords(map, ranges, entries);
} }
@Test @Test
public void testRemove() throws IOException { public void testRemove() throws IOException {
SharedRangeMapDB map = new SharedRangeMapDB(dbh, "TEST", this, true); SharedRangeMapDB map = new SharedRangeMapDB(dbh, "TEST", this, true);
@ -365,7 +371,7 @@ System.out.println(" Map entry: rangeKey=" + entry.getStart() + ", value=" + en
map.remove(2); map.remove(2);
ranges = new IndexRange[] { ranges = new IndexRange[] {
new IndexRange(10,90) new IndexRange(10, 90)
}; };
entries = new IndexRange[] { entries = new IndexRange[] {
@ -381,7 +387,7 @@ System.out.println(" Map entry: rangeKey=" + entry.getStart() + ", value=" + en
} }
@Test @Test
public void testGetValueIterator() { public void testGetValueIterator() {
SharedRangeMapDB map = new SharedRangeMapDB(dbh, "TEST", this, true); SharedRangeMapDB map = new SharedRangeMapDB(dbh, "TEST", this, true);
@ -414,10 +420,11 @@ System.out.println(" Map entry: rangeKey=" + entry.getStart() + ", value=" + en
int cnt = 0; int cnt = 0;
while (iter.hasNext()) { while (iter.hasNext()) {
++cnt; ++cnt;
LongField v = (LongField)iter.next(); LongField v = (LongField) iter.next();
if (indexOf(values, v) < 0) if (indexOf(values, v) < 0) {
Assert.fail("Unexpected value: " + v.getLongValue()); Assert.fail("Unexpected value: " + v.getLongValue());
} }
}
assertEquals(values.length, cnt); assertEquals(values.length, cnt);
// Test 2 // Test 2
@ -429,10 +436,11 @@ System.out.println(" Map entry: rangeKey=" + entry.getStart() + ", value=" + en
cnt = 0; cnt = 0;
while (iter.hasNext()) { while (iter.hasNext()) {
++cnt; ++cnt;
LongField v = (LongField)iter.next(); LongField v = (LongField) iter.next();
if (indexOf(values, v) < 0) if (indexOf(values, v) < 0) {
Assert.fail("Unexpected value: " + v.getLongValue()); Assert.fail("Unexpected value: " + v.getLongValue());
} }
}
assertEquals(values.length, cnt); assertEquals(values.length, cnt);
// Test 3 // Test 3
@ -443,23 +451,23 @@ System.out.println(" Map entry: rangeKey=" + entry.getStart() + ", value=" + en
cnt = 0; cnt = 0;
while (iter.hasNext()) { while (iter.hasNext()) {
++cnt; ++cnt;
LongField v = (LongField)iter.next(); LongField v = (LongField) iter.next();
if (indexOf(values, v) < 0) if (indexOf(values, v) < 0) {
Assert.fail("Unexpected value: " + v.getLongValue()); Assert.fail("Unexpected value: " + v.getLongValue());
} }
}
assertEquals(values.length, cnt); assertEquals(values.length, cnt);
// Test 4 // Test 4
iter = map.getValueIterator(0, 9); iter = map.getValueIterator(0, 9);
assertTrue(!iter.hasNext()); assertTrue(!iter.hasNext());
} }
@Test @Test
public void testGetValueRangeIterator() { public void testGetValueRangeIterator() {
SharedRangeMapDB map = new SharedRangeMapDB(dbh, "TEST", this, true); SharedRangeMapDB map = new SharedRangeMapDB(dbh, "TEST", this, true);
System.out.println("testGetValueRangeIterator ---"); System.out.println("testGetValueRangeIterator ---");
// Add same entries as the testAdd used // Add same entries as the testAdd used
map.add(10, 20, 1); map.add(10, 20, 1);
map.add(30, 40, 1); map.add(30, 40, 1);
@ -490,9 +498,10 @@ System.out.println("testGetValueRangeIterator ---");
while (iter.hasNext()) { while (iter.hasNext()) {
++cnt; ++cnt;
IndexRange range = iter.next(); IndexRange range = iter.next();
if (indexOf(ranges, range) < 0) if (indexOf(ranges, range) < 0) {
Assert.fail("Unexpected range: " + range.getStart() + " - " + range.getEnd()); Assert.fail("Unexpected range: " + range.getStart() + " - " + range.getEnd());
System.out.println(" Range: " + range.getStart() + " - " + range.getEnd()); }
System.out.println(" Range: " + range.getStart() + " - " + range.getEnd());
} }
assertEquals(ranges.length, cnt); assertEquals(ranges.length, cnt);

View file

@ -60,6 +60,7 @@ import ghidra.test.TestEnv;
*/ */
public class OptionsDialogTest extends AbstractGhidraHeadedIntegrationTest { public class OptionsDialogTest extends AbstractGhidraHeadedIntegrationTest {
private static final String MY_PATH_NAME_OPTION_NAME = "My PathName";
private static final String TOOL_NODE_NAME = "Tool"; private static final String TOOL_NODE_NAME = "Tool";
private PluginTool tool; private PluginTool tool;
private TestEnv env; private TestEnv env;
@ -281,7 +282,7 @@ public class OptionsDialogTest extends AbstractGhidraHeadedIntegrationTest {
ScrollableOptionsEditor editor = showOptions(ToolConstants.TOOL_OPTIONS); ScrollableOptionsEditor editor = showOptions(ToolConstants.TOOL_OPTIONS);
pressBrowseButton(editor, "My PathName"); pressBrowseButton(editor, MY_PATH_NAME_OPTION_NAME);
GhidraFileChooser chooser = waitForDialogComponent(GhidraFileChooser.class); GhidraFileChooser chooser = waitForDialogComponent(GhidraFileChooser.class);
assertNotNull(chooser); assertNotNull(chooser);
@ -297,7 +298,7 @@ public class OptionsDialogTest extends AbstractGhidraHeadedIntegrationTest {
pressButton(openButton); pressButton(openButton);
waitForSwing(); waitForSwing();
JTextField pathField = getEditorTextField(editor, "My PathName"); JTextField pathField = getEditorTextField(editor, MY_PATH_NAME_OPTION_NAME);
assertEquals(file.getAbsolutePath(), pathField.getText()); assertEquals(file.getAbsolutePath(), pathField.getText());
} }
@ -305,7 +306,7 @@ public class OptionsDialogTest extends AbstractGhidraHeadedIntegrationTest {
public void testFileChooserEditor_ClearValue() throws Exception { public void testFileChooserEditor_ClearValue() throws Exception {
ScrollableOptionsEditor editor = showOptions(ToolConstants.TOOL_OPTIONS); ScrollableOptionsEditor editor = showOptions(ToolConstants.TOOL_OPTIONS);
JTextField pathField = getEditorTextField(editor, "My PathName"); JTextField pathField = getEditorTextField(editor, MY_PATH_NAME_OPTION_NAME);
setText(pathField, ""); setText(pathField, "");
@ -313,7 +314,7 @@ public class OptionsDialogTest extends AbstractGhidraHeadedIntegrationTest {
showOptionsDialog(tool); showOptionsDialog(tool);
editor = showOptions(ToolConstants.TOOL_OPTIONS); editor = showOptions(ToolConstants.TOOL_OPTIONS);
pathField = getEditorTextField(editor, "My PathName"); pathField = getEditorTextField(editor, MY_PATH_NAME_OPTION_NAME);
assertEquals("", pathField.getText()); assertEquals("", pathField.getText());
} }
@ -1059,10 +1060,13 @@ public class OptionsDialogTest extends AbstractGhidraHeadedIntegrationTest {
// register this options because it is used in a test that saves and restores and // register this options because it is used in a test that saves and restores and
// only registered options are saved. // only registered options are saved.
String myOptionsName = "My Options" + Options.DELIMITER; String myOptionsName = "My Options" + Options.DELIMITER;
options.registerOption(myOptionsName + "my sub group Boolean Value", true, null, null); options.registerOption(myOptionsName + "my sub group Boolean Value", true, null,
"description");
options.registerOption("My PathName", OptionType.FILE_TYPE, null, null, ""); File file = new File(System.getProperty("user.dir"));
options.setFile("My PathName", new File(System.getProperty("user.dir"))); options.registerOption(MY_PATH_NAME_OPTION_NAME, OptionType.FILE_TYPE, file, null,
"description");
options.setFile(MY_PATH_NAME_OPTION_NAME, file);
// the following "get" methods set a value // the following "get" methods set a value
options.getInt(myOptionsName + "my sub group" + Options.DELIMITER + "My Test Value", 10); options.getInt(myOptionsName + "my sub group" + Options.DELIMITER + "My Test Value", 10);
@ -1071,19 +1075,29 @@ public class OptionsDialogTest extends AbstractGhidraHeadedIntegrationTest {
String intOptionName = myOptionsName + "my sub group" + Options.DELIMITER + "Group A" + String intOptionName = myOptionsName + "my sub group" + Options.DELIMITER + "Group A" +
Options.DELIMITER + "Second Int Value"; Options.DELIMITER + "Second Int Value";
options.registerOption(intOptionName, 50, null, "description");
options.setInt(intOptionName, 50); options.setInt(intOptionName, 50);
options.setBoolean(myOptionsName + "my sub group" + Options.DELIMITER + "Group A" +
Options.DELIMITER + "First boolean value", true);
options.setInt( String name = myOptionsName + "my sub group" + Options.DELIMITER + "Group A" +
Options.DELIMITER + "First boolean value";
options.registerOption(name, true, null, "description");
options.setBoolean(name, true);
name =
"New Options" + Options.DELIMITER + " subgroup A" + Options.DELIMITER + " subgroup B" + "New Options" + Options.DELIMITER + " subgroup A" + Options.DELIMITER + " subgroup B" +
Options.DELIMITER + " subgroup C" + Options.DELIMITER + "Another int value", Options.DELIMITER + " subgroup C" + Options.DELIMITER + "Another int value";
300); options.registerOption(name, 300, null, "description");
options.setInt(name, 300);
options.setColor("Favorite Color", Color.RED); name = "Favorite Color";
options.registerOption(name, Color.RED, null, "description");
options.setColor(name, Color.RED);
// select the middle button // select the middle button
options.setEnum("Mouse Buttons" + Options.DELIMITER + "Mouse Button To Activate", name = "Mouse Buttons" + Options.DELIMITER + "Mouse Button To Activate";
options.registerOption(name, GhidraOptions.CURSOR_MOUSE_BUTTON_NAMES.MIDDLE, null,
"description");
options.setEnum(name,
GhidraOptions.CURSOR_MOUSE_BUTTON_NAMES.MIDDLE); GhidraOptions.CURSOR_MOUSE_BUTTON_NAMES.MIDDLE);
} }

View file

@ -28,7 +28,7 @@ import ghidra.program.model.listing.ProgramChangeSet;
import ghidra.test.TestEnv; import ghidra.test.TestEnv;
import ghidra.util.InvalidNameException; import ghidra.util.InvalidNameException;
import ghidra.util.exception.CancelledException; import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitorAdapter; import ghidra.util.task.TaskMonitor;
/** /**
* This model is used by the {@link MergeTestFacilitator} to configure programs needed * This model is used by the {@link MergeTestFacilitator} to configure programs needed
@ -62,8 +62,8 @@ public abstract class AbstractMTFModel {
* This represents the original checked-out version. * This represents the original checked-out version.
* Program returned will be released by the MergeTestFacilitator * Program returned will be released by the MergeTestFacilitator
* when disposed or re-initialized. * when disposed or re-initialized.
* @return the program
*/ */
public ProgramDB getOriginalProgram() { public ProgramDB getOriginalProgram() {
return originalProgram; return originalProgram;
} }
@ -73,8 +73,8 @@ public abstract class AbstractMTFModel {
* This represents the current version. * This represents the current version.
* Program returned will be released by the MergeTestFacilitator * Program returned will be released by the MergeTestFacilitator
* when disposed or re-initialized. * when disposed or re-initialized.
* @return the program
*/ */
public ProgramDB getLatestProgram() { public ProgramDB getLatestProgram() {
return latestProgram; return latestProgram;
} }
@ -84,8 +84,8 @@ public abstract class AbstractMTFModel {
* This represents the local program to be checked-in. * This represents the local program to be checked-in.
* Program returned will be released by the MergeTestFacilitator * Program returned will be released by the MergeTestFacilitator
* when disposed or re-initialized. * when disposed or re-initialized.
* @return the program
*/ */
public ProgramDB getPrivateProgram() { public ProgramDB getPrivateProgram() {
return privateProgram; return privateProgram;
} }
@ -95,8 +95,8 @@ public abstract class AbstractMTFModel {
* This represents the checkin program containing the merged data. * This represents the checkin program containing the merged data.
* Program returned will be released by the MergeTestFacilitator * Program returned will be released by the MergeTestFacilitator
* when disposed or re-initialized. * when disposed or re-initialized.
* @return the program
*/ */
public ProgramDB getResultProgram() { public ProgramDB getResultProgram() {
return resultProgram; return resultProgram;
} }
@ -129,7 +129,7 @@ public abstract class AbstractMTFModel {
BufferFile bufferFile = item.open(); BufferFile bufferFile = item.open();
try { try {
fileSystem.createDatabase(parent.getPathname(), newName, FileIDFactory.createFileID(), fileSystem.createDatabase(parent.getPathname(), newName, FileIDFactory.createFileID(),
bufferFile, null, item.getContentType(), false, TaskMonitorAdapter.DUMMY_MONITOR, bufferFile, null, item.getContentType(), false, TaskMonitor.DUMMY,
null); null);
} }
finally { finally {
@ -141,6 +141,7 @@ public abstract class AbstractMTFModel {
} }
protected void cleanup() { protected void cleanup() {
if (originalProgram != null) { if (originalProgram != null) {
originalProgram.release(this); originalProgram.release(this);
originalProgram = null; originalProgram = null;
@ -154,6 +155,8 @@ public abstract class AbstractMTFModel {
privateProgram = null; privateProgram = null;
} }
if (resultProgram != null) { if (resultProgram != null) {
resultProgram.flushEvents();
AbstractGenericTest.waitForSwing();
resultProgram.release(this); resultProgram.release(this);
resultProgram = null; resultProgram = null;
} }

View file

@ -734,6 +734,8 @@ public class FunctionGraphPlugin1Test extends AbstractFunctionGraphTest {
setNavigationHistoryOption(NavigationHistoryChoices.NAVIGATION_EVENTS); setNavigationHistoryOption(NavigationHistoryChoices.NAVIGATION_EVENTS);
clearHistory();
FGVertex v1 = vertex("01004178"); FGVertex v1 = vertex("01004178");
pickVertex(v1); pickVertex(v1);
@ -743,17 +745,18 @@ public class FunctionGraphPlugin1Test extends AbstractFunctionGraphTest {
FGVertex v3 = vertex("010041a4"); FGVertex v3 = vertex("010041a4");
pickVertex(v3); pickVertex(v3);
// in this navigation mode, merely selecting nodes does *not* put previous nodes in history
assertNotInHistory(v1, v2); assertNotInHistory(v1, v2);
// //
// Now leave the function and verify the old function is in the history // Perform a navigation action (e.g., goTo()) and verify the old function is in the history
// //
Address ghidra = getAddress("0x01002cf5"); Address ghidra = getAddress("0x01002cf5");
goTo(ghidra); goTo(ghidra);
assertInHistory(v3.getVertexAddress());
Address foo = getAddress("0x01002339"); Address foo = getAddress("0x01002339");
goTo(foo); goTo(foo);
assertInHistory(v3.getVertexAddress(), ghidra); assertInHistory(v3.getVertexAddress(), ghidra);
} }
@ -785,6 +788,14 @@ public class FunctionGraphPlugin1Test extends AbstractFunctionGraphTest {
} }
} }
private void clearHistory() {
GoToService goTo = tool.getService(GoToService.class);
Navigatable navigatable = goTo.getDefaultNavigatable();
NavigationHistoryService service = tool.getService(NavigationHistoryService.class);
service.clear(navigatable);
}
private List<LocationMemento> getNavigationHistory() { private List<LocationMemento> getNavigationHistory() {
GoToService goTo = tool.getService(GoToService.class); GoToService goTo = tool.getService(GoToService.class);
@ -823,8 +834,8 @@ public class FunctionGraphPlugin1Test extends AbstractFunctionGraphTest {
for (Address a : expectedAddresses) { for (Address a : expectedAddresses) {
assertTrue("Vertex address should be in the history list: " + a + ".\nHistory: " + assertTrue("Vertex address should be in the history list: " + a + ".\nActual: " +
actualAddresses + "\nNavigated vertices: " + expectedAddresses, actualAddresses + "\nExpected: " + expectedAddresses,
actualAddresses.contains(a)); actualAddresses.contains(a));
} }
} }

View file

@ -29,10 +29,10 @@ import javax.security.auth.spi.LoginModule;
import com.sun.security.auth.UserPrincipal; import com.sun.security.auth.UserPrincipal;
import generic.concurrent.io.ProcessConsumer;
import ghidra.server.RepositoryManager; import ghidra.server.RepositoryManager;
import ghidra.util.DateUtils; import ghidra.util.DateUtils;
import ghidra.util.timer.Watchdog; import ghidra.util.timer.Watchdog;
import utilities.util.FileUtilities;
/** /**
* A JAAS {@link LoginModule} that executes an external program that decides if the username * A JAAS {@link LoginModule} that executes an external program that decides if the username
@ -208,10 +208,11 @@ public class ExternalProgramLoginModule implements LoginModule {
Process p = Runtime.getRuntime().exec(cmdArray); Process p = Runtime.getRuntime().exec(cmdArray);
process.set(p); process.set(p);
FileUtilities.asyncForEachLine(p.getInputStream(), (stdOutStr) -> { ProcessConsumer.consume(p.getInputStream(), stdOutStr -> {
RepositoryManager.log(null, null, extProgramName + " STDOUT: " + stdOutStr, null); RepositoryManager.log(null, null, extProgramName + " STDOUT: " + stdOutStr, null);
}); });
FileUtilities.asyncForEachLine(p.getErrorStream(), (errStr) -> {
ProcessConsumer.consume(p.getErrorStream(), errStr -> {
RepositoryManager.log(null, null, extProgramName + " STDERR: " + errStr, null); RepositoryManager.log(null, null, extProgramName + " STDERR: " + errStr, null);
}); });
@ -266,8 +267,12 @@ public class ExternalProgramLoginModule implements LoginModule {
} }
extProgramName = extProFile.getName(); extProgramName = extProFile.getName();
List<String> argKeys = options.keySet().stream().filter( List<String> argKeys = options.keySet()
key -> key.startsWith(ARG_OPTION_NAME)).sorted().collect(Collectors.toList()); .stream()
.filter(
key -> key.startsWith(ARG_OPTION_NAME))
.sorted()
.collect(Collectors.toList());
List<String> cmdArrayValues = new ArrayList<>(); List<String> cmdArrayValues = new ArrayList<>();
cmdArrayValues.add(externalProgram.toString()); cmdArrayValues.add(externalProgram.toString());
for (String argKey : argKeys) { for (String argKey : argKeys) {

View file

@ -2245,13 +2245,26 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
// Unable to find the manager. This can happen during testing; only report if // Unable to find the manager. This can happen during testing; only report if
// it is unexpected // it is unexpected
if (!instances.isEmpty()) { maybeReportMissingManager();
}
private void maybeReportMissingManager() {
if (instances.isEmpty()) {
return; // not manager means no tool; assume testing
}
if (instances.size() == 1) {
DockingWindowManager dwm = instances.get(0);
if (!dwm.isVisible()) {
return; // not showing; assume testing with tool not shown
}
}
Msg.debug(DockingWindowManager.class, Msg.debug(DockingWindowManager.class,
"Unable to find Docking Window Manager for " + "Unable to find Docking Window Manager for " +
component.getClass().getSimpleName(), component.getClass().getSimpleName(),
ReflectionUtilities.createJavaFilteredThrowable()); ReflectionUtilities.createJavaFilteredThrowable());
} }
}
}); });
} }

View file

@ -2102,40 +2102,34 @@ public abstract class AbstractDockingTest extends AbstractGenericTest {
} }
public static boolean isEnabled(DockingActionIf action) { public static boolean isEnabled(DockingActionIf action) {
AtomicBoolean ref = new AtomicBoolean(); return runSwing(() -> action.isEnabledForContext(new ActionContext()));
runSwing(() -> ref.set(action.isEnabledForContext(new ActionContext())));
return ref.get();
} }
public static boolean isEnabled(AbstractButton button) { public static boolean isEnabled(AbstractButton button) {
AtomicBoolean ref = new AtomicBoolean(); return runSwing(() -> button.isEnabled());
runSwing(() -> ref.set(button.isEnabled()));
return ref.get();
} }
public static boolean isSelected(AbstractButton button) { public static boolean isSelected(AbstractButton button) {
AtomicBoolean ref = new AtomicBoolean(); return runSwing(() -> button.isSelected());
runSwing(() -> ref.set(button.isSelected()));
return ref.get();
} }
/** /**
* Creates a generic action context with no provider, with the given payload * Creates a generic action context with no provider, with the given context object
* @param payload the generic object to put in the context * @param contextObject the generic object to put in the context
* @return the new context * @return the new context
*/ */
public ActionContext createContext(Object payload) { public ActionContext createContext(Object contextObject) {
return new ActionContext().setContextObject(payload); return new ActionContext().setContextObject(contextObject);
} }
/** /**
* Creates a generic action context with the given provider, with the given payload * Creates a generic action context with the given provider, with the given context object
* @param provider the provider * @param provider the provider
* @param payload the generic object to put in the context * @param contextObject the generic object to put in the context
* @return the new context * @return the new context
*/ */
public ActionContext createContext(ComponentProvider provider, Object payload) { public ActionContext createContext(ComponentProvider provider, Object contextObject) {
return new ActionContext(provider).setContextObject(payload); return new ActionContext(provider).setContextObject(contextObject);
} }
//================================================================================================== //==================================================================================================

View file

@ -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 generic.concurrent.io;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import ghidra.util.Msg;
import utilities.util.reflection.ReflectionUtilities;
import utility.function.Dummy;
/**
* Class to pass to a thread pool that will consume all output from an external process. This is
* a {@link Runnable} that get submitted to a thread pool. This class records the data it reads
*/
public class IOResult implements Runnable {
public static final String THREAD_POOL_NAME = "I/O Thread Pool";
private List<String> outputLines = new ArrayList<String>();
private BufferedReader commandOutput;
private final Throwable inception;
private Consumer<String> consumer = Dummy.consumer();
public IOResult(InputStream input) {
this(ReflectionUtilities.createThrowableWithStackOlderThan(IOResult.class), input);
}
public IOResult(Throwable inception, InputStream input) {
this.inception = inception;
commandOutput = new BufferedReader(new InputStreamReader(input));
}
public void setConsumer(Consumer<String> consumer) {
this.consumer = consumer;
}
public String getOutputAsString() {
StringBuilder buffy = new StringBuilder();
for (String line : outputLines) {
buffy.append(line);
}
return buffy.toString();
}
public List<String> getOutput() {
return outputLines;
}
@Override
public void run() {
String line = null;
try {
while ((line = commandOutput.readLine()) != null) {
consumer.accept(line);
outputLines.add(line);
}
}
catch (Exception e) {
String inceptionString = ReflectionUtilities.stackTraceToString(inception);
Msg.debug(IOResult.class,
"Exception reading output from process. Created from:\n" + inceptionString, e);
}
}
}

View file

@ -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 generic.concurrent.io;
import java.io.InputStream;
import java.util.concurrent.Future;
import java.util.function.Consumer;
import generic.concurrent.GThreadPool;
import utilities.util.reflection.ReflectionUtilities;
import utility.function.Dummy;
/**
* A class that allows clients to <b>asynchronously</b> consume the output of a {@link Process}s
* input and error streams. The task is asynchronous to avoid deadlocks when both streams need
* to be read in order for the process to proceed.
*/
public class ProcessConsumer {
/**
* Read the given input stream line-by-line.
*
* <p>To get all output after all reading is done you can call the blocking operation
* {@link Future#get()}.
*
* @param is the input stream
* @return the future that will be complete when all lines are read
*/
public static Future<IOResult> consume(InputStream is) {
return consume(is, Dummy.consumer());
}
/**
* Read the given input stream line-by-line.
*
* <p>If you wish to get all output after all reading is done you can call the blocking
* operation {@link Future#get()}.
*
* @param is the input stream
* @param lineConsumer the line consumer; may be null
* @return the future that will be complete when all lines are read
*/
public static Future<IOResult> consume(InputStream is,
Consumer<String> lineConsumer) {
lineConsumer = Dummy.ifNull(lineConsumer);
Throwable inception = ReflectionUtilities.filterJavaThrowable(
ReflectionUtilities.createThrowableWithStackOlderThan(ProcessConsumer.class));
GThreadPool pool = GThreadPool.getSharedThreadPool(IOResult.THREAD_POOL_NAME);
IOResult runnable = new IOResult(inception, is);
runnable.setConsumer(lineConsumer);
Future<IOResult> future = pool.submit(runnable, runnable);
return future;
}
}

View file

@ -26,6 +26,7 @@ import java.text.DecimalFormat;
import java.text.NumberFormat; import java.text.NumberFormat;
import java.util.*; import java.util.*;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.stream.Stream;
import generic.jar.ResourceFile; import generic.jar.ResourceFile;
import ghidra.util.*; import ghidra.util.*;
@ -95,6 +96,8 @@ public final class FileUtilities {
/** /**
* Return an array of bytes read from the given file. * Return an array of bytes read from the given file.
* @param sourceFile the source file
* @return the bytes
* @throws IOException if the file could not be accessed * @throws IOException if the file could not be accessed
*/ */
public final static byte[] getBytesFromFile(File sourceFile) throws IOException { public final static byte[] getBytesFromFile(File sourceFile) throws IOException {
@ -118,6 +121,8 @@ public final class FileUtilities {
/** /**
* Return an array of bytes read from the given file. * Return an array of bytes read from the given file.
* @param sourceFile the source file
* @return the bytes
* @throws IOException if the file could not be accessed * @throws IOException if the file could not be accessed
*/ */
public final static byte[] getBytesFromFile(ResourceFile sourceFile) throws IOException { public final static byte[] getBytesFromFile(ResourceFile sourceFile) throws IOException {
@ -161,7 +166,6 @@ public final class FileUtilities {
} }
byte[] data = new byte[(int) length]; byte[] data = new byte[(int) length];
try (InputStream fis = sourceFile.getInputStream()) { try (InputStream fis = sourceFile.getInputStream()) {
if (fis.skip(offset) != offset) { if (fis.skip(offset) != offset) {
throw new IOException("Did not skip to the specified offset!"); throw new IOException("Did not skip to the specified offset!");
@ -383,9 +387,20 @@ public final class FileUtilities {
} }
/** /**
* Delete a directory and all of its contents. * Delete a file or directory and all of its contents
* *
* @param dir * @param dir the directory to delete
* @return true if delete was successful. If false is returned, a partial
* delete may have occurred.
*/
public static boolean deleteDir(Path dir) {
return deleteDir(dir.toFile());
}
/**
* Delete a file or directory and all of its contents
*
* @param dir the dir to delete
* @return true if delete was successful. If false is returned, a partial * @return true if delete was successful. If false is returned, a partial
* delete may have occurred. * delete may have occurred.
*/ */
@ -400,11 +415,13 @@ public final class FileUtilities {
} }
/** /**
* Delete a directory and all of its contents. * Delete a directory and all of its contents
* *
* @param dir * @param dir the dir to delete
* @param monitor the task monitor
* @return true if delete was successful. If false is returned, a partial * @return true if delete was successful. If false is returned, a partial
* delete may have occurred. * delete may have occurred.
* @throws CancelledException if the operation is cancelled
*/ */
public final static boolean deleteDir(File dir, TaskMonitor monitor) throws CancelledException { public final static boolean deleteDir(File dir, TaskMonitor monitor) throws CancelledException {
File[] files = dir.listFiles(); File[] files = dir.listFiles();
@ -472,6 +489,12 @@ public final class FileUtilities {
/** /**
* This is the same as calling {@link #copyDir(File, File, FileFilter, TaskMonitor)} with * This is the same as calling {@link #copyDir(File, File, FileFilter, TaskMonitor)} with
* a {@link FileFilter} that accepts all files. * a {@link FileFilter} that accepts all files.
* @param originalDir the source dir
* @param copyDir the destination dir
* @param monitor the task monitor
* @return the number of filed copied
* @throws IOException if there is an issue copying the files
* @throws CancelledException if the operation is cancelled
*/ */
public final static int copyDir(File originalDir, File copyDir, TaskMonitor monitor) public final static int copyDir(File originalDir, File copyDir, TaskMonitor monitor)
throws IOException, CancelledException { throws IOException, CancelledException {
@ -487,6 +510,7 @@ public final class FileUtilities {
* @param copyDir The directory in which the extracted contents will be placed * @param copyDir The directory in which the extracted contents will be placed
* @param filter a filter to apply against the directory's contents * @param filter a filter to apply against the directory's contents
* @param monitor the task monitor * @param monitor the task monitor
* @return the number of filed copied
* @throws IOException if there was a problem accessing the files * @throws IOException if there was a problem accessing the files
* @throws CancelledException if the copy is cancelled * @throws CancelledException if the copy is cancelled
*/ */
@ -572,7 +596,7 @@ public final class FileUtilities {
return;// squash during production mode return;// squash during production mode
} }
Msg.debug(SystemUtilities.class, text); Msg.debug(FileUtilities.class, text);
} }
/** /**
@ -644,10 +668,10 @@ public final class FileUtilities {
} }
/** /**
* Returns all of the lines in the file without any newline characters. * Returns all of the lines in the file without any newline characters
* @param file The file to read in * @param file The file to read in
* @return a list of file lines * @return a list of file lines
* @throws IOException * @throws IOException if an error occurs reading the file
*/ */
public static List<String> getLines(File file) throws IOException { public static List<String> getLines(File file) throws IOException {
return getLines(new ResourceFile(file)); return getLines(new ResourceFile(file));
@ -660,7 +684,7 @@ public final class FileUtilities {
* <p> * <p>
* @param file The text file to read in * @param file The text file to read in
* @return a list of file lines * @return a list of file lines
* @throws IOException if an error occurs trying to read the file. * @throws IOException if an error occurs reading the file
*/ */
public static List<String> getLines(ResourceFile file) throws IOException { public static List<String> getLines(ResourceFile file) throws IOException {
try (InputStream is = file.getInputStream()) { try (InputStream is = file.getInputStream()) {
@ -857,7 +881,7 @@ public final class FileUtilities {
* @param f1 the parent file * @param f1 the parent file
* @param f2 the child file * @param f2 the child file
* @return the portion of the second file that trails the full path of the first file. * @return the portion of the second file that trails the full path of the first file.
* @throws IOException * @throws IOException if there is an error canonicalizing the path
*/ */
public static String relativizePath(File f1, File f2) throws IOException { public static String relativizePath(File f1, File f2) throws IOException {
String parentPath = f1.getCanonicalPath().replace('\\', '/'); String parentPath = f1.getCanonicalPath().replace('\\', '/');
@ -1108,7 +1132,7 @@ public final class FileUtilities {
* <p> * <p>
* TODO: why is the method using 1000 vs. 1024 for K? * TODO: why is the method using 1000 vs. 1024 for K?
* *
* @param length * @param length the length to format
* @return pretty string - "1.1KB", "5.0MB" * @return pretty string - "1.1KB", "5.0MB"
*/ */
public static String formatLength(long length) { public static String formatLength(long length) {
@ -1123,6 +1147,11 @@ public final class FileUtilities {
return formatter.format((length / 1000000f)) + "MB"; return formatter.format((length / 1000000f)) + "MB";
} }
/**
* Creates a temporary directory using the given prefix
* @param prefix the prefix
* @return the temp file
*/
public static File createTempDirectory(String prefix) { public static File createTempDirectory(String prefix) {
try { try {
File temp = File.createTempFile(prefix, Long.toString(System.currentTimeMillis())); File temp = File.createTempFile(prefix, Long.toString(System.currentTimeMillis()));
@ -1175,51 +1204,32 @@ public final class FileUtilities {
public static void openNative(File file) throws IOException { public static void openNative(File file) throws IOException {
if (!Desktop.isDesktopSupported()) { if (!Desktop.isDesktopSupported()) {
Msg.showError(FileUtilities.class, null, "Native Desktop Unsupported", Msg.showError(FileUtilities.class, null, "Native Desktop Unsupported",
"Access to the user's native desktop is not supported in the current environment."); "Access to the user's native desktop is not supported in the current environment." +
"\nUnable to open file: " + file);
return; return;
} }
Desktop.getDesktop().open(file); Desktop.getDesktop().open(file);
} }
/** /**
* Processes each text line in a text file, in a separate thread. * A convenience method to list the contents of the given directory path and pass each to the
* <p> * given consumer. If the given path does not represent a directory, nothing will happen.
* Thread exits when EOF is reached.
* *
* @param is {@link InputStream} to read * <p>This method handles closing resources by using the try-with-resources construct on
* @param consumer code that will process each text of the text file. * {@link Files#list(Path)}
*/
public static void asyncForEachLine(InputStream is, Consumer<String> consumer) {
asyncForEachLine(new BufferedReader(new InputStreamReader(is)), consumer);
}
/**
* Processes each text line in a text file, in a separate thread.
* <p>
* Thread exits when EOF is reached.
* *
* @param reader {@link BufferedReader} to read * @param path the directory
* @param consumer code that will process each text of the text file. * @param consumer the consumer of each child in the given directory
* @throws IOException if there is any problem reading the directory contents
*/ */
public static void asyncForEachLine(BufferedReader reader, Consumer<String> consumer) { public static void forEachFile(Path path, Consumer<Stream<Path>> consumer) throws IOException {
new Thread(() -> {
try { if (!Files.isDirectory(path)) {
while (true) { return;
String line = reader.readLine();
if (line == null) {
break;
}
consumer.accept(line);
}
}
catch (IOException ioe) {
// ignore io errors while reading because thats normal when hitting EOF
}
catch (Exception e) {
Msg.error(FileUtilities.class, "Exception while reading", e);
} }
}, "Threaded Stream Reader Thread").start(); try (Stream<Path> pathStream = Files.list(path)) {
consumer.accept(pathStream);
}
} }
} }