mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-05 10:49:34 +02:00
Tests - cleanup of stack traces in test log file
This commit is contained in:
parent
b7c8056c8a
commit
e5358323d8
24 changed files with 889 additions and 678 deletions
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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()))
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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());
|
||||||
}
|
}
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue