mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-05 10:49:34 +02:00
GP-829: Removing processContainer concept from tests.
This commit is contained in:
parent
3b058380a3
commit
1a3458ef7c
36 changed files with 218 additions and 330 deletions
|
@ -26,6 +26,7 @@ import ghidra.dbg.target.schema.EnumerableTargetObjectSchema;
|
|||
import ghidra.dbg.target.schema.TargetObjectSchema;
|
||||
import ghidra.dbg.util.PathUtils;
|
||||
import ghidra.lifecycle.Internal;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.datastruct.ListenerSet;
|
||||
|
||||
/**
|
||||
|
@ -245,8 +246,13 @@ public abstract class AbstractTargetObject<P extends TargetObject> implements Sp
|
|||
valid = false;
|
||||
model.objectInvalidated(getProxy());
|
||||
listeners.fire.invalidated(getProxy(), branch, reason);
|
||||
listeners.clear();
|
||||
listeners.clearChained();
|
||||
CompletableFuture.runAsync(() -> {
|
||||
listeners.clear();
|
||||
listeners.clearChained();
|
||||
}, model.clientExecutor).exceptionally(ex -> {
|
||||
Msg.error(this, "Error emptying invalidated object's listener set: ", ex);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
protected void doInvalidateElements(Map<String, ?> elems, String reason) {
|
||||
|
|
|
@ -16,7 +16,8 @@
|
|||
package ghidra.dbg.test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.junit.Assume.*;
|
||||
import static org.junit.Assume.assumeNotNull;
|
||||
import static org.junit.Assume.assumeTrue;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.util.Collection;
|
||||
|
@ -33,7 +34,6 @@ import ghidra.dbg.error.DebuggerIllegalArgumentException;
|
|||
import ghidra.dbg.target.*;
|
||||
import ghidra.dbg.target.TargetExecutionStateful.TargetExecutionState;
|
||||
import ghidra.dbg.target.TargetMethod.TargetParameterMap;
|
||||
import ghidra.dbg.testutil.ElementTrackingListener;
|
||||
import ghidra.program.model.address.AddressFactory;
|
||||
|
||||
public abstract class AbstractDebuggerModelAttacherTest extends AbstractDebuggerModelTest
|
||||
|
@ -47,10 +47,6 @@ public abstract class AbstractDebuggerModelAttacherTest extends AbstractDebugger
|
|||
return null;
|
||||
}
|
||||
|
||||
public List<String> getExpectedProcessesContainerPath() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public abstract TargetParameterMap getExpectedAttachParameters();
|
||||
|
||||
public abstract void assertEnvironment(TargetEnvironment environment);
|
||||
|
@ -65,16 +61,6 @@ public abstract class AbstractDebuggerModelAttacherTest extends AbstractDebugger
|
|||
assertEquals(expectedAttacherPath, attacher.getPath());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testProcessContainerIsWhereExpected() throws Throwable {
|
||||
List<String> expectedProcessContainerPath = getExpectedProcessesContainerPath();
|
||||
assumeNotNull(expectedProcessContainerPath);
|
||||
m.build();
|
||||
|
||||
TargetObject container = findProcessContainer();
|
||||
assertEquals(expectedProcessContainerPath, container.getPath());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAttachableContainerIsWhereExpected() throws Throwable {
|
||||
List<String> expectedAttachableContainerPath = getExpectedAttachableContainerPath();
|
||||
|
@ -195,12 +181,12 @@ public abstract class AbstractDebuggerModelAttacherTest extends AbstractDebugger
|
|||
runTestAttachByObjBogusThrowsException(attacher);
|
||||
}
|
||||
|
||||
protected void runTestAttachByPidThenDetach(TargetAttacher attacher, TargetObject container)
|
||||
protected void runTestAttachByPidThenDetach(TargetAttacher attacher)
|
||||
throws Throwable {
|
||||
DebuggerTestSpecimen specimen = getAttachSpecimen();
|
||||
assertNull(getProcessRunning(container, specimen, this));
|
||||
assertNull(getProcessRunning(specimen, this));
|
||||
runTestAttachByPid(attacher);
|
||||
runTestDetach(container, specimen);
|
||||
runTestDetach(specimen);
|
||||
assertTrue(dummy.process.isAlive());
|
||||
}
|
||||
|
||||
|
@ -212,16 +198,15 @@ public abstract class AbstractDebuggerModelAttacherTest extends AbstractDebugger
|
|||
dummy = specimen.runDummy();
|
||||
|
||||
TargetAttacher attacher = findAttacher();
|
||||
TargetObject container = findProcessContainer();
|
||||
runTestAttachByPidThenDetach(attacher, container);
|
||||
runTestAttachByPidThenDetach(attacher);
|
||||
}
|
||||
|
||||
protected void runTestAttachByPidThenKill(TargetAttacher attacher, TargetObject container)
|
||||
protected void runTestAttachByPidThenKill(TargetAttacher attacher)
|
||||
throws Throwable {
|
||||
DebuggerTestSpecimen specimen = getAttachSpecimen();
|
||||
assertNull(getProcessRunning(container, specimen, this));
|
||||
assertNull(getProcessRunning(specimen, this));
|
||||
runTestAttachByPid(attacher);
|
||||
runTestKill(container, specimen);
|
||||
runTestKill(specimen);
|
||||
retryVoid(() -> assertFalse(dummy.process.isAlive()), List.of(AssertionError.class));
|
||||
}
|
||||
|
||||
|
@ -233,16 +218,14 @@ public abstract class AbstractDebuggerModelAttacherTest extends AbstractDebugger
|
|||
dummy = specimen.runDummy();
|
||||
|
||||
TargetAttacher attacher = findAttacher();
|
||||
TargetObject container = findProcessContainer();
|
||||
runTestAttachByPidThenKill(attacher, container);
|
||||
runTestAttachByPidThenKill(attacher);
|
||||
}
|
||||
|
||||
protected void runTestAttachByPidThenResumeInterrupt(TargetAttacher attacher,
|
||||
TargetObject container) throws Throwable {
|
||||
protected void runTestAttachByPidThenResumeInterrupt(TargetAttacher attacher) throws Throwable {
|
||||
DebuggerTestSpecimen specimen = getAttachSpecimen();
|
||||
assertNull(getProcessRunning(container, specimen, this));
|
||||
assertNull(getProcessRunning(specimen, this));
|
||||
runTestAttachByPid(attacher);
|
||||
runTestResumeInterruptMany(container, specimen, 3);
|
||||
runTestResumeInterruptMany(specimen, 3);
|
||||
assertTrue(dummy.process.isAlive());
|
||||
}
|
||||
|
||||
|
@ -254,16 +237,14 @@ public abstract class AbstractDebuggerModelAttacherTest extends AbstractDebugger
|
|||
dummy = specimen.runDummy();
|
||||
|
||||
TargetAttacher attacher = findAttacher();
|
||||
TargetObject container = findProcessContainer();
|
||||
runTestAttachByPidThenResumeInterrupt(attacher, container);
|
||||
runTestAttachByPidThenResumeInterrupt(attacher);
|
||||
}
|
||||
|
||||
protected void runTestAttachShowsInProcessContainer(TargetAttacher attacher,
|
||||
TargetObject container) throws Throwable {
|
||||
protected void runTestAttachShowsInProcessContainer(TargetAttacher attacher) throws Throwable {
|
||||
DebuggerTestSpecimen specimen = getAttachSpecimen();
|
||||
assertNull(getProcessRunning(container, specimen, this));
|
||||
assertNull(getProcessRunning(specimen, this));
|
||||
runTestAttachByPid(attacher);
|
||||
retryForProcessRunning(container, specimen, this);
|
||||
retryForProcessRunning(specimen, this);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -274,36 +255,6 @@ public abstract class AbstractDebuggerModelAttacherTest extends AbstractDebugger
|
|||
dummy = specimen.runDummy();
|
||||
|
||||
TargetAttacher attacher = findAttacher();
|
||||
TargetObject container = findProcessContainer();
|
||||
runTestAttachShowsInProcessContainer(attacher, container);
|
||||
}
|
||||
|
||||
protected void runTestAttachShowsInProcessContainerViaListener(TargetAttacher attacher,
|
||||
TargetObject container) throws Throwable {
|
||||
DebuggerTestSpecimen specimen = getAttachSpecimen();
|
||||
ElementTrackingListener<? extends TargetProcess> procListener =
|
||||
new ElementTrackingListener<>(TargetProcess.class);
|
||||
container.addListener(procListener);
|
||||
// NB. Have to express interest, otherwise model is not obligated to invoke listener
|
||||
Collection<TargetProcess> procsBefore = fetchProcesses(container);
|
||||
procListener.putAll(container.getCachedElements());
|
||||
assertNull(getProcessRunning(procsBefore, specimen, this));
|
||||
runTestAttachByPid(attacher);
|
||||
retryVoid(() -> {
|
||||
// Cannot fetch elements. rely only on listener.
|
||||
assertNotNull(getProcessRunning(procListener.elements.values(), specimen, this));
|
||||
}, List.of(AssertionError.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAttachShowsInProcessContainerViaListener() throws Throwable {
|
||||
DebuggerTestSpecimen specimen = getAttachSpecimen();
|
||||
assumeTrue(m.hasProcessContainer());
|
||||
m.build();
|
||||
dummy = specimen.runDummy();
|
||||
|
||||
TargetAttacher attacher = findAttacher();
|
||||
TargetObject container = findProcessContainer();
|
||||
runTestAttachShowsInProcessContainerViaListener(attacher, container);
|
||||
runTestAttachShowsInProcessContainer(attacher);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,7 +16,8 @@
|
|||
package ghidra.dbg.test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.junit.Assume.*;
|
||||
import static org.junit.Assume.assumeNotNull;
|
||||
import static org.junit.Assume.assumeTrue;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
|
@ -38,18 +39,16 @@ import ghidra.util.Msg;
|
|||
* Tests the functionality of breakpoints
|
||||
*
|
||||
* <p>
|
||||
* Note that this test does not check for nuances regarding specification vs.
|
||||
* location, as it is meant to generalize across models for interests of the UI
|
||||
* only. As such, we only test that we can set breakpoints at given addresses
|
||||
* and that a location manifests there, regardless of the intervening
|
||||
* mechanisms. We also test some basic operations on the breakpoint (location)
|
||||
* itself. Models which have separate specifications from locations, or for
|
||||
* which you want to test non-address specifications will need to add their own
|
||||
* tests, tailored to the semantics of that model's breakpoint specifications.
|
||||
* Note that this test does not check for nuances regarding specification vs. location, as it is
|
||||
* meant to generalize across models for interests of the UI only. As such, we only test that we can
|
||||
* set breakpoints at given addresses and that a location manifests there, regardless of the
|
||||
* intervening mechanisms. We also test some basic operations on the breakpoint (location) itself.
|
||||
* Models which have separate specifications from locations, or for which you want to test
|
||||
* non-address specifications will need to add their own tests, tailored to the semantics of that
|
||||
* model's breakpoint specifications.
|
||||
*
|
||||
* <p>
|
||||
* TODO: Enable, disable (if supported), delete (if supported), manipulation via
|
||||
* CLI is synced
|
||||
* TODO: Enable, disable (if supported), delete (if supported), manipulation via CLI is synced
|
||||
*/
|
||||
public abstract class AbstractDebuggerModelBreakpointsTest extends AbstractDebuggerModelTest
|
||||
implements RequiresTarget {
|
||||
|
@ -225,7 +224,7 @@ public abstract class AbstractDebuggerModelBreakpointsTest extends AbstractDebug
|
|||
waitOn(container.placeBreakpoint(range, Set.of(kind)));
|
||||
retryVoid(() -> {
|
||||
Collection<? extends TargetBreakpointLocation> found =
|
||||
m.findAll(TargetBreakpointLocation.class, target.getPath()).values();
|
||||
m.findAll(TargetBreakpointLocation.class, target.getPath(), true).values();
|
||||
assertAtLeastOneLocCovers(found, range, kind);
|
||||
}, List.of(AssertionError.class));
|
||||
}
|
||||
|
@ -261,7 +260,7 @@ public abstract class AbstractDebuggerModelBreakpointsTest extends AbstractDebug
|
|||
waitOn(container.placeBreakpoint(range, Set.of(kind)));
|
||||
locs.add(retry(() -> {
|
||||
Collection<? extends TargetBreakpointLocation> found =
|
||||
m.findAll(TargetBreakpointLocation.class, target.getPath()).values();
|
||||
m.findAll(TargetBreakpointLocation.class, target.getPath(), true).values();
|
||||
return assertAtLeastOneLocCovers(found, range, kind);
|
||||
}, List.of(AssertionError.class)));
|
||||
}
|
||||
|
|
|
@ -27,6 +27,10 @@ import ghidra.dbg.target.TargetFocusScope;
|
|||
import ghidra.dbg.target.TargetObject;
|
||||
import ghidra.dbg.util.PathUtils;
|
||||
|
||||
/**
|
||||
* TODO: Since activation and focus are separate concepts, we need to fix the terminology here and
|
||||
* ensure we're testing the right things.
|
||||
*/
|
||||
public abstract class AbstractDebuggerModelFocusTest extends AbstractDebuggerModelTest {
|
||||
|
||||
/**
|
||||
|
|
|
@ -15,9 +15,10 @@
|
|||
*/
|
||||
package ghidra.dbg.test;
|
||||
|
||||
import static ghidra.lifecycle.Unfinished.*;
|
||||
import static ghidra.lifecycle.Unfinished.TODO;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.junit.Assume.*;
|
||||
import static org.junit.Assume.assumeNotNull;
|
||||
import static org.junit.Assume.assumeTrue;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
@ -27,23 +28,39 @@ import org.junit.Ignore;
|
|||
import org.junit.Test;
|
||||
|
||||
import ghidra.async.AsyncReference;
|
||||
import ghidra.dbg.DebugModelConventions;
|
||||
import ghidra.dbg.DebuggerModelListener;
|
||||
import ghidra.dbg.error.DebuggerModelTerminatingException;
|
||||
import ghidra.dbg.target.*;
|
||||
import ghidra.dbg.target.TargetConsole.Channel;
|
||||
import ghidra.dbg.target.TargetInterpreter;
|
||||
import ghidra.dbg.target.TargetObject;
|
||||
import ghidra.dbg.testutil.CatchOffThread;
|
||||
import ghidra.util.Msg;
|
||||
|
||||
public abstract class AbstractDebuggerModelInterpreterTest extends AbstractDebuggerModelTest
|
||||
implements RequiresAttachSpecimen, RequiresLaunchSpecimen {
|
||||
|
||||
/**
|
||||
* Get the path of the expected result of {@link #findInterpreter()} for this test
|
||||
*
|
||||
* @return the expected path
|
||||
*/
|
||||
public List<String> getExpectedInterpreterPath() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the CLI command to echo a string back
|
||||
*
|
||||
* @param msg the message to echo
|
||||
* @return the command
|
||||
*/
|
||||
protected abstract String getEchoCommand(String msg);
|
||||
|
||||
/**
|
||||
* If applicable, get the CLI command to terminate the session / model
|
||||
*
|
||||
* @return the command
|
||||
*/
|
||||
protected abstract String getQuitCommand();
|
||||
|
||||
/**
|
||||
|
@ -53,6 +70,30 @@ public abstract class AbstractDebuggerModelInterpreterTest extends AbstractDebug
|
|||
*/
|
||||
protected abstract String getAttachCommand();
|
||||
|
||||
/**
|
||||
* Get the CLI command to detach from the given process
|
||||
*
|
||||
* <p>
|
||||
* Note that the given process should already be the current/active process of the interpreter,
|
||||
* so the parameter may not be needed.
|
||||
*
|
||||
* @param process the process to detach from, which should already be active
|
||||
* @return the command
|
||||
*/
|
||||
protected abstract String getDetachCommand(TargetProcess process);
|
||||
|
||||
/**
|
||||
* Get the CLI command to kill the given process
|
||||
*
|
||||
* <p>
|
||||
* Note that the given process should already be the current/active process of the interpreter,
|
||||
* so the parameter may not be needed.
|
||||
*
|
||||
* @param process the process to kill, which should already be active
|
||||
* @return the command
|
||||
*/
|
||||
protected abstract String getKillCommand(TargetProcess process);
|
||||
|
||||
@Test
|
||||
public void testInterpreterIsWhereExpected() throws Throwable {
|
||||
List<String> expectedInterpreterPath = getExpectedInterpreterPath();
|
||||
|
@ -126,6 +167,11 @@ public abstract class AbstractDebuggerModelInterpreterTest extends AbstractDebug
|
|||
runTestExecuteCapture(interpreter, cmd);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that the user quitting via the CLI properly terminates the model
|
||||
*
|
||||
* @throws Throwable expected since the model will terminate
|
||||
*/
|
||||
@Test(expected = DebuggerModelTerminatingException.class)
|
||||
public void testExecuteQuit() throws Throwable {
|
||||
String cmd = getQuitCommand();
|
||||
|
@ -151,14 +197,22 @@ public abstract class AbstractDebuggerModelInterpreterTest extends AbstractDebug
|
|||
// TODO: Delete (spec vs. loc?)
|
||||
}
|
||||
|
||||
protected void runTestLaunchViaInterpreterShowsInProcessContainer(TargetInterpreter interpreter,
|
||||
TargetObject container) throws Throwable {
|
||||
protected TargetProcess runTestLaunchViaInterpreterShowsInProcessContainer(
|
||||
TargetInterpreter interpreter) throws Throwable {
|
||||
DebuggerTestSpecimen specimen = getLaunchSpecimen();
|
||||
assertNull(getProcessRunning(container, specimen, this));
|
||||
assertNull(getProcessRunning(specimen, this));
|
||||
for (String line : specimen.getLaunchScript()) {
|
||||
waitOn(interpreter.execute(line));
|
||||
}
|
||||
retryForProcessRunning(container, specimen, this);
|
||||
return retryForProcessRunning(specimen, this);
|
||||
}
|
||||
|
||||
protected void runTestKillViaInterpreter(TargetProcess process, TargetInterpreter interpreter)
|
||||
throws Throwable {
|
||||
waitOn(interpreter.execute(getKillCommand(process)));
|
||||
retryVoid(() -> {
|
||||
assertFalse(DebugModelConventions.isProcessAlive(process));
|
||||
}, List.of(AssertionError.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -167,18 +221,26 @@ public abstract class AbstractDebuggerModelInterpreterTest extends AbstractDebug
|
|||
m.build();
|
||||
|
||||
TargetInterpreter interpreter = findInterpreter();
|
||||
TargetObject container = findProcessContainer();
|
||||
assertNotNull("No process container", container);
|
||||
runTestLaunchViaInterpreterShowsInProcessContainer(interpreter, container);
|
||||
TargetProcess process = runTestLaunchViaInterpreterShowsInProcessContainer(interpreter);
|
||||
|
||||
runTestKillViaInterpreter(process, interpreter);
|
||||
}
|
||||
|
||||
protected void runTestAttachViaInterpreterShowsInProcessContainer(TargetInterpreter interpreter,
|
||||
TargetObject container) throws Throwable {
|
||||
protected TargetProcess runTestAttachViaInterpreterShowsInProcessContainer(
|
||||
TargetInterpreter interpreter) throws Throwable {
|
||||
DebuggerTestSpecimen specimen = getAttachSpecimen();
|
||||
assertNull(getProcessRunning(container, specimen, this));
|
||||
assertNull(getProcessRunning(specimen, this));
|
||||
String cmd = getAttachCommand();
|
||||
waitOn(interpreter.execute(cmd));
|
||||
retryForProcessRunning(container, specimen, this);
|
||||
return retryForProcessRunning(specimen, this);
|
||||
}
|
||||
|
||||
protected void runTestDetachViaInterpreter(TargetProcess process, TargetInterpreter interpreter)
|
||||
throws Throwable {
|
||||
waitOn(interpreter.execute(getDetachCommand(process)));
|
||||
retryVoid(() -> {
|
||||
assertFalse(DebugModelConventions.isProcessAlive(process));
|
||||
}, List.of(AssertionError.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -189,8 +251,8 @@ public abstract class AbstractDebuggerModelInterpreterTest extends AbstractDebug
|
|||
dummy = specimen.runDummy();
|
||||
|
||||
TargetInterpreter interpreter = findInterpreter();
|
||||
TargetObject container = findProcessContainer();
|
||||
assertNotNull("No process container", container);
|
||||
runTestAttachViaInterpreterShowsInProcessContainer(interpreter, container);
|
||||
TargetProcess process = runTestAttachViaInterpreterShowsInProcessContainer(interpreter);
|
||||
|
||||
runTestDetachViaInterpreter(process, interpreter);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,12 +15,12 @@
|
|||
*/
|
||||
package ghidra.dbg.test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assume.assumeNotNull;
|
||||
import static org.junit.Assume.assumeTrue;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
|
@ -31,7 +31,6 @@ import ghidra.dbg.DebugModelConventions;
|
|||
import ghidra.dbg.target.*;
|
||||
import ghidra.dbg.target.TargetExecutionStateful.TargetExecutionState;
|
||||
import ghidra.dbg.target.TargetMethod.TargetParameterMap;
|
||||
import ghidra.dbg.testutil.ElementTrackingListener;
|
||||
|
||||
public abstract class AbstractDebuggerModelLauncherTest extends AbstractDebuggerModelTest
|
||||
implements RequiresLaunchSpecimen {
|
||||
|
@ -40,10 +39,6 @@ public abstract class AbstractDebuggerModelLauncherTest extends AbstractDebugger
|
|||
return null;
|
||||
}
|
||||
|
||||
public List<String> getExpectedProcessesContainerPath() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public abstract TargetParameterMap getExpectedLauncherParameters();
|
||||
|
||||
public abstract void assertEnvironment(TargetEnvironment environment);
|
||||
|
@ -58,16 +53,6 @@ public abstract class AbstractDebuggerModelLauncherTest extends AbstractDebugger
|
|||
assertEquals(expectedLauncherPath, launcher.getPath());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testProcessContainerIsWhereExpected() throws Throwable {
|
||||
List<String> expectedProcessContainerPath = getExpectedProcessesContainerPath();
|
||||
assumeNotNull(expectedProcessContainerPath);
|
||||
m.build();
|
||||
|
||||
TargetObject container = findProcessContainer();
|
||||
assertEquals(expectedProcessContainerPath, container.getPath());
|
||||
}
|
||||
|
||||
protected void runTestLaunchParameters(TargetLauncher launcher,
|
||||
TargetParameterMap expectedParameters) throws Throwable {
|
||||
waitAcc(launcher);
|
||||
|
@ -123,12 +108,11 @@ public abstract class AbstractDebuggerModelLauncherTest extends AbstractDebugger
|
|||
waitOn(listener.observedCreated);
|
||||
}
|
||||
|
||||
protected void runTestLaunchThenDetach(TargetLauncher launcher,
|
||||
TargetObject container) throws Throwable {
|
||||
protected void runTestLaunchThenDetach(TargetLauncher launcher) throws Throwable {
|
||||
DebuggerTestSpecimen specimen = getLaunchSpecimen();
|
||||
assertNull(getProcessRunning(container, specimen, this));
|
||||
assertNull(getProcessRunning(specimen, this));
|
||||
runTestLaunch(launcher);
|
||||
runTestDetach(container, specimen);
|
||||
runTestDetach(specimen);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -137,16 +121,14 @@ public abstract class AbstractDebuggerModelLauncherTest extends AbstractDebugger
|
|||
m.build();
|
||||
|
||||
TargetLauncher launcher = findLauncher();
|
||||
TargetObject container = findProcessContainer();
|
||||
runTestLaunchThenDetach(launcher, container);
|
||||
runTestLaunchThenDetach(launcher);
|
||||
}
|
||||
|
||||
protected void runTestLaunchThenKill(TargetLauncher launcher,
|
||||
TargetObject container) throws Throwable {
|
||||
protected void runTestLaunchThenKill(TargetLauncher launcher) throws Throwable {
|
||||
DebuggerTestSpecimen specimen = getLaunchSpecimen();
|
||||
assertNull(getProcessRunning(container, specimen, this));
|
||||
assertNull(getProcessRunning(specimen, this));
|
||||
runTestLaunch(launcher);
|
||||
runTestKill(container, specimen);
|
||||
runTestKill(specimen);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -155,16 +137,14 @@ public abstract class AbstractDebuggerModelLauncherTest extends AbstractDebugger
|
|||
m.build();
|
||||
|
||||
TargetLauncher launcher = findLauncher();
|
||||
TargetObject container = findProcessContainer();
|
||||
runTestLaunchThenKill(launcher, container);
|
||||
runTestLaunchThenKill(launcher);
|
||||
}
|
||||
|
||||
protected void runTestLaunchThenResume(TargetLauncher launcher,
|
||||
TargetObject container) throws Throwable {
|
||||
protected void runTestLaunchThenResume(TargetLauncher launcher) throws Throwable {
|
||||
DebuggerTestSpecimen specimen = getLaunchSpecimen();
|
||||
assertNull(getProcessRunning(container, specimen, this));
|
||||
assertNull(getProcessRunning(specimen, this));
|
||||
runTestLaunch(launcher);
|
||||
runTestResumeTerminates(container, specimen);
|
||||
runTestResumeTerminates(specimen);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -173,16 +153,14 @@ public abstract class AbstractDebuggerModelLauncherTest extends AbstractDebugger
|
|||
m.build();
|
||||
|
||||
TargetLauncher launcher = findLauncher();
|
||||
TargetObject container = findProcessContainer();
|
||||
runTestLaunchThenResume(launcher, container);
|
||||
runTestLaunchThenResume(launcher);
|
||||
}
|
||||
|
||||
protected void runTestLaunchShowsInProcessContainer(TargetLauncher launcher,
|
||||
TargetObject container) throws Throwable {
|
||||
protected void runTestLaunchShowsInProcessContainer(TargetLauncher launcher) throws Throwable {
|
||||
DebuggerTestSpecimen specimen = getLaunchSpecimen();
|
||||
assertNull(getProcessRunning(container, specimen, this));
|
||||
assertNull(getProcessRunning(specimen, this));
|
||||
runTestLaunch(launcher);
|
||||
retryForProcessRunning(container, specimen, this);
|
||||
retryForProcessRunning(specimen, this);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -191,34 +169,6 @@ public abstract class AbstractDebuggerModelLauncherTest extends AbstractDebugger
|
|||
m.build();
|
||||
|
||||
TargetLauncher launcher = findLauncher();
|
||||
TargetObject container = findProcessContainer();
|
||||
runTestLaunchShowsInProcessContainer(launcher, container);
|
||||
}
|
||||
|
||||
protected void runTestLaunchShowsInProcessContainerViaListener(
|
||||
TargetLauncher launcher, TargetObject container) throws Throwable {
|
||||
DebuggerTestSpecimen specimen = getLaunchSpecimen();
|
||||
ElementTrackingListener<? extends TargetProcess> procListener =
|
||||
new ElementTrackingListener<>(TargetProcess.class);
|
||||
container.addListener(procListener);
|
||||
// NB. Have to express interest, otherwise model is not obligated to invoke listener
|
||||
Collection<TargetProcess> procsBefore = fetchProcesses(container);
|
||||
procListener.putAll(container.getCachedElements());
|
||||
assertNull(getProcessRunning(procsBefore, specimen, this));
|
||||
runTestLaunch(launcher);
|
||||
retryVoid(() -> {
|
||||
// Cannot fetch elements. rely only on listener.
|
||||
assertNotNull(getProcessRunning(procListener.elements.values(), specimen, this));
|
||||
}, List.of(AssertionError.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLaunchShowsInProcessContainerViaListener() throws Throwable {
|
||||
assumeTrue(m.hasProcessContainer());
|
||||
m.build();
|
||||
|
||||
TargetLauncher launcher = findLauncher();
|
||||
TargetObject container = findProcessContainer();
|
||||
runTestLaunchShowsInProcessContainerViaListener(launcher, container);
|
||||
runTestLaunchShowsInProcessContainer(launcher);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,7 +15,8 @@
|
|||
*/
|
||||
package ghidra.dbg.test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.util.*;
|
||||
|
@ -131,14 +132,12 @@ public abstract class AbstractDebuggerModelScenarioCloneExitTest extends Abstrac
|
|||
Msg.debug(this, "Launching " + specimen);
|
||||
waitOn(launcher.launch(specimen.getLauncherArgs()));
|
||||
Msg.debug(this, " Done launching");
|
||||
TargetObject processContainer = findProcessContainer();
|
||||
assertNotNull("No process container", processContainer);
|
||||
TargetProcess process = retryForProcessRunning(processContainer, specimen, this);
|
||||
TargetProcess process = retryForProcessRunning(specimen, this);
|
||||
postLaunch(process);
|
||||
TargetBreakpointSpecContainer breakpointContainer =
|
||||
TargetBreakpointSpecContainer bpContainer =
|
||||
findBreakpointSpecContainer(process.getPath());
|
||||
Msg.debug(this, "Placing breakpoint");
|
||||
waitOn(breakpointContainer.placeBreakpoint(getBreakpointExpression(),
|
||||
waitOn(bpContainer.placeBreakpoint(getBreakpointExpression(),
|
||||
Set.of(TargetBreakpointKind.SW_EXECUTE)));
|
||||
|
||||
assertTrue(DebugModelConventions.isProcessAlive(process));
|
||||
|
|
|
@ -182,8 +182,6 @@ public abstract class AbstractDebuggerModelScenarioForkExitTest extends Abstract
|
|||
Msg.debug(this, "Launching " + specimen);
|
||||
waitOn(launcher.launch(specimen.getLauncherArgs()));
|
||||
Msg.debug(this, " Done launching");
|
||||
TargetObject processContainer = findProcessContainer();
|
||||
assertNotNull("No process container", processContainer);
|
||||
TargetProcess parentProcess = waitOn(stateMonitor.observedParent);
|
||||
Msg.debug(this, "Parent is " + parentProcess.getJoinedPath("."));
|
||||
postLaunch(parentProcess);
|
||||
|
@ -201,7 +199,7 @@ public abstract class AbstractDebuggerModelScenarioForkExitTest extends Abstract
|
|||
Msg.debug(this, " Done " + i);
|
||||
waitAcc(access(parentProcess));
|
||||
try {
|
||||
childProcess = retryForOtherProcessRunning(processContainer, specimen, this,
|
||||
childProcess = retryForOtherProcessRunning(specimen, this,
|
||||
p -> p != parentProcess, WAIT_FOR_CHILD_MS);
|
||||
}
|
||||
catch (AssertionError e) {
|
||||
|
|
|
@ -141,8 +141,7 @@ public abstract class AbstractDebuggerModelScenarioMemoryTest extends AbstractDe
|
|||
Msg.debug(this, "Launching " + specimen);
|
||||
waitOn(launcher.launch(specimen.getLauncherArgs()));
|
||||
Msg.debug(this, " Done launching");
|
||||
TargetObject processContainer = findProcessContainer();
|
||||
TargetProcess process = retryForProcessRunning(processContainer, specimen, this);
|
||||
TargetProcess process = retryForProcessRunning(specimen, this);
|
||||
postLaunch(process);
|
||||
|
||||
Address address = Objects.requireNonNull(getAddressToWrite(process));
|
||||
|
|
|
@ -15,7 +15,6 @@
|
|||
*/
|
||||
package ghidra.dbg.test;
|
||||
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
|
@ -138,9 +137,7 @@ public abstract class AbstractDebuggerModelScenarioRegistersTest extends Abstrac
|
|||
Msg.debug(this, "Launching " + specimen);
|
||||
waitOn(launcher.launch(specimen.getLauncherArgs()));
|
||||
Msg.debug(this, " Done launching");
|
||||
TargetObject processContainer = findProcessContainer();
|
||||
assertNotNull(processContainer);
|
||||
TargetProcess process = retryForProcessRunning(processContainer, specimen, this);
|
||||
TargetProcess process = retryForProcessRunning(specimen, this);
|
||||
postLaunch(process);
|
||||
|
||||
TargetBreakpointSpecContainer breakpointContainer =
|
||||
|
|
|
@ -122,8 +122,7 @@ public abstract class AbstractDebuggerModelScenarioStackTest extends AbstractDeb
|
|||
TargetLauncher launcher = findLauncher();
|
||||
waitOn(launcher.launch(specimen.getLauncherArgs()));
|
||||
Msg.debug(this, " Done launching");
|
||||
TargetObject processContainer = findProcessContainer();
|
||||
TargetProcess process = retryForProcessRunning(processContainer, specimen, this);
|
||||
TargetProcess process = retryForProcessRunning(specimen, this);
|
||||
postLaunch(process);
|
||||
|
||||
TargetBreakpointSpecContainer breakpointContainer =
|
||||
|
@ -157,7 +156,7 @@ public abstract class AbstractDebuggerModelScenarioStackTest extends AbstractDeb
|
|||
// Sort by path should present them innermost to outermost
|
||||
List<TargetStackFrame> frames = retry(() -> {
|
||||
List<TargetStackFrame> result =
|
||||
List.copyOf(m.findAll(TargetStackFrame.class, stack.getPath()).values());
|
||||
List.copyOf(m.findAll(TargetStackFrame.class, stack.getPath(), true).values());
|
||||
assertTrue("Fewer than 4 frames", result.size() > 4);
|
||||
return result;
|
||||
}, List.of(AssertionError.class));
|
||||
|
|
|
@ -85,19 +85,6 @@ public abstract class AbstractDebuggerModelTest extends AbstractGhidraHeadlessIn
|
|||
return m.find(TargetLauncher.class, seedPath());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the process container under test
|
||||
*
|
||||
* <p>
|
||||
* This can be overridden to force a different object under the test.
|
||||
*
|
||||
* @return the process container
|
||||
* @throws Throwable if anything goes wrong
|
||||
*/
|
||||
protected TargetObject findProcessContainer() throws Throwable {
|
||||
return m.findContainer(TargetProcess.class, seedPath());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the breakpoint container of a target under test
|
||||
*
|
||||
|
@ -285,9 +272,9 @@ public abstract class AbstractDebuggerModelTest extends AbstractGhidraHeadlessIn
|
|||
return waitOn(listener.trapped);
|
||||
}
|
||||
|
||||
protected void runTestDetach(TargetObject container, DebuggerTestSpecimen specimen)
|
||||
protected void runTestDetach(DebuggerTestSpecimen specimen)
|
||||
throws Throwable {
|
||||
TargetProcess process = retryForProcessRunning(container, specimen, this);
|
||||
TargetProcess process = retryForProcessRunning(specimen, this);
|
||||
TargetDetachable detachable = m.suitable(TargetDetachable.class, process.getPath());
|
||||
waitAcc(detachable);
|
||||
waitOn(detachable.detach());
|
||||
|
@ -295,9 +282,9 @@ public abstract class AbstractDebuggerModelTest extends AbstractGhidraHeadlessIn
|
|||
List.of(AssertionError.class));
|
||||
}
|
||||
|
||||
protected void runTestKill(TargetObject container, DebuggerTestSpecimen specimen)
|
||||
protected void runTestKill(DebuggerTestSpecimen specimen)
|
||||
throws Throwable {
|
||||
TargetProcess process = retryForProcessRunning(container, specimen, this);
|
||||
TargetProcess process = retryForProcessRunning(specimen, this);
|
||||
TargetKillable killable = m.suitable(TargetKillable.class, process.getPath());
|
||||
waitAcc(killable);
|
||||
waitOn(killable.kill());
|
||||
|
@ -305,9 +292,8 @@ public abstract class AbstractDebuggerModelTest extends AbstractGhidraHeadlessIn
|
|||
List.of(AssertionError.class));
|
||||
}
|
||||
|
||||
protected void runTestResumeTerminates(TargetObject container, DebuggerTestSpecimen specimen)
|
||||
throws Throwable {
|
||||
TargetProcess process = retryForProcessRunning(container, specimen, this);
|
||||
protected void runTestResumeTerminates(DebuggerTestSpecimen specimen) throws Throwable {
|
||||
TargetProcess process = retryForProcessRunning(specimen, this);
|
||||
TargetResumable resumable = m.suitable(TargetResumable.class, process.getPath());
|
||||
AsyncState state =
|
||||
new AsyncState(m.suitable(TargetExecutionStateful.class, process.getPath()));
|
||||
|
@ -318,9 +304,9 @@ public abstract class AbstractDebuggerModelTest extends AbstractGhidraHeadlessIn
|
|||
List.of(AssertionError.class));
|
||||
}
|
||||
|
||||
protected void runTestResumeInterruptMany(TargetObject container, DebuggerTestSpecimen specimen,
|
||||
protected void runTestResumeInterruptMany(DebuggerTestSpecimen specimen,
|
||||
int repetitions) throws Throwable {
|
||||
TargetProcess process = retryForProcessRunning(container, specimen, this);
|
||||
TargetProcess process = retryForProcessRunning(specimen, this);
|
||||
TargetResumable resumable = m.suitable(TargetResumable.class, process.getPath());
|
||||
TargetInterruptible interruptible =
|
||||
m.suitable(TargetInterruptible.class, process.getPath());
|
||||
|
@ -342,6 +328,6 @@ public abstract class AbstractDebuggerModelTest extends AbstractGhidraHeadlessIn
|
|||
}, List.of(AssertionError.class));
|
||||
}
|
||||
}
|
||||
waitOn(container.getModel().ping("Are you still there?"));
|
||||
waitOn(m.getModel().ping("Are you still there?"));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -206,16 +206,20 @@ public abstract class AbstractModelHost implements ModelHost, DebuggerModelTestU
|
|||
|
||||
@Override
|
||||
public <T extends TargetObject> NavigableMap<List<String>, T> findAll(Class<T> cls,
|
||||
List<String> seedPath) throws Throwable {
|
||||
List<String> seedPath, boolean atLeastOne) throws Throwable {
|
||||
PathMatcher matcher =
|
||||
model.getRootSchema().getSuccessorSchema(seedPath).searchFor(cls, seedPath, false);
|
||||
if (matcher.isEmpty()) {
|
||||
return new TreeMap<>();
|
||||
}
|
||||
|
||||
NavigableMap<List<String>, ?> found = atLeastOne
|
||||
? waitOn(waiter.waitAtLeastOne(matcher))
|
||||
: matcher.getCachedValues(model.getModelRoot());
|
||||
// NB. Outside of testing, an "unsafe" cast of the map should be fine.
|
||||
// During testing, we should expend the energy to verify the heap.
|
||||
NavigableMap<List<String>, T> result = new TreeMap<>(PathComparator.KEYED);
|
||||
for (Entry<List<String>, ?> ent : waitOn(waiter.waitAtLeastOne(matcher)).entrySet()) {
|
||||
for (Entry<List<String>, ?> ent : found.entrySet()) {
|
||||
result.put(ent.getKey(), cls.cast(ent.getValue()));
|
||||
}
|
||||
return result;
|
||||
|
|
|
@ -29,12 +29,11 @@ public interface ProvidesTargetViaAttachSpecimen extends RequiresTarget, Require
|
|||
@Override
|
||||
default TargetObject obtainTarget() throws Throwable {
|
||||
TargetAttacher attacher = getTest().findAttacher();
|
||||
TargetObject container = getTest().findProcessContainer();
|
||||
DebuggerTestSpecimen specimen = getAttachSpecimen();
|
||||
waitAcc(attacher);
|
||||
DummyProc dummy = specimen.runDummy();
|
||||
setDummy(dummy);
|
||||
attacher.attach(dummy.pid);
|
||||
return retryForProcessRunning(container, specimen, getTest());
|
||||
return retryForProcessRunning(specimen, getTest());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
*/
|
||||
package ghidra.dbg.test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
import ghidra.dbg.target.TargetLauncher;
|
||||
import ghidra.dbg.target.TargetObject;
|
||||
|
@ -34,11 +34,9 @@ public interface ProvidesTargetViaLaunchSpecimen extends RequiresTarget, Require
|
|||
default TargetObject obtainTarget() throws Throwable {
|
||||
TargetLauncher launcher = getTest().findLauncher();
|
||||
assertNotNull("No launcher found", launcher);
|
||||
TargetObject container = getTest().findProcessContainer();
|
||||
assertNotNull("No process container found", container);
|
||||
DebuggerTestSpecimen specimen = getLaunchSpecimen();
|
||||
waitAcc(launcher);
|
||||
launcher.launch(specimen.getLauncherArgs());
|
||||
return retryForProcessRunning(container, specimen, getTest());
|
||||
return retryForProcessRunning(specimen, getTest());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@ import ghidra.dbg.target.*;
|
|||
import ghidra.dbg.target.TargetSteppable.TargetStepKind;
|
||||
import ghidra.dbg.test.AbstractDebuggerModelTest;
|
||||
import ghidra.dbg.test.AbstractDebuggerModelTest.DebuggerTestSpecimen;
|
||||
import ghidra.dbg.util.PathUtils;
|
||||
import ghidra.util.NumericUtilities;
|
||||
|
||||
public interface DebuggerModelTestUtils extends AsyncTestUtils {
|
||||
|
@ -164,10 +165,9 @@ public interface DebuggerModelTestUtils extends AsyncTestUtils {
|
|||
}).findFirst().orElse(null);
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
default Collection<TargetProcess> fetchProcesses(TargetObject container)
|
||||
default Collection<TargetProcess> fetchProcesses(AbstractDebuggerModelTest test)
|
||||
throws Throwable {
|
||||
return (Collection) waitOn(container.fetchElements(true)).values();
|
||||
return test.m.findAll(TargetProcess.class, PathUtils.parse(""), false).values();
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
|
@ -176,32 +176,30 @@ public interface DebuggerModelTestUtils extends AsyncTestUtils {
|
|||
return (Collection) waitOn(container.fetchElements(true)).values();
|
||||
}
|
||||
|
||||
default TargetProcess getProcessRunning(TargetObject container,
|
||||
DebuggerTestSpecimen specimen, AbstractDebuggerModelTest test) throws Throwable {
|
||||
return getProcessRunning(container, specimen, test, p -> true);
|
||||
default TargetProcess getProcessRunning(DebuggerTestSpecimen specimen,
|
||||
AbstractDebuggerModelTest test) throws Throwable {
|
||||
return getProcessRunning(specimen, test, p -> true);
|
||||
}
|
||||
|
||||
default TargetProcess getProcessRunning(TargetObject container,
|
||||
DebuggerTestSpecimen specimen, AbstractDebuggerModelTest test,
|
||||
Predicate<TargetProcess> predicate) throws Throwable {
|
||||
return getProcessRunning(fetchProcesses(container), specimen, test, predicate);
|
||||
default TargetProcess getProcessRunning(DebuggerTestSpecimen specimen,
|
||||
AbstractDebuggerModelTest test, Predicate<TargetProcess> predicate) throws Throwable {
|
||||
return getProcessRunning(fetchProcesses(test), specimen, test, predicate);
|
||||
}
|
||||
|
||||
default TargetProcess retryForProcessRunning(TargetObject container,
|
||||
default TargetProcess retryForProcessRunning(
|
||||
DebuggerTestSpecimen specimen, AbstractDebuggerModelTest test) throws Throwable {
|
||||
return retry(() -> {
|
||||
TargetProcess process = getProcessRunning(container, specimen, test);
|
||||
TargetProcess process = getProcessRunning(specimen, test);
|
||||
assertNotNull(process);
|
||||
return process;
|
||||
}, List.of(AssertionError.class));
|
||||
}
|
||||
|
||||
default TargetProcess retryForOtherProcessRunning(TargetObject container,
|
||||
DebuggerTestSpecimen specimen, AbstractDebuggerModelTest test,
|
||||
Predicate<TargetProcess> predicate, long timeoutMs)
|
||||
default TargetProcess retryForOtherProcessRunning(DebuggerTestSpecimen specimen,
|
||||
AbstractDebuggerModelTest test, Predicate<TargetProcess> predicate, long timeoutMs)
|
||||
throws Throwable {
|
||||
return retry(timeoutMs, () -> {
|
||||
TargetProcess process = getProcessRunning(container, specimen, test, predicate);
|
||||
TargetProcess process = getProcessRunning(specimen, test, predicate);
|
||||
assertNotNull(process);
|
||||
return process;
|
||||
}, List.of(AssertionError.class));
|
||||
|
|
|
@ -73,8 +73,8 @@ public interface TestDebuggerModelProvider {
|
|||
|
||||
<T extends TargetObject> T findAny(Class<T> cls, List<String> seedPath) throws Throwable;
|
||||
|
||||
<T extends TargetObject> Map<List<String>, T> findAll(Class<T> cls, List<String> seedPath)
|
||||
throws Throwable;
|
||||
<T extends TargetObject> Map<List<String>, T> findAll(Class<T> cls, List<String> seedPath,
|
||||
boolean atLeastOne) throws Throwable;
|
||||
|
||||
TargetObject findContainer(Class<? extends TargetObject> cls, List<String> seedPath)
|
||||
throws Throwable;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue