GP-829: Removing processContainer concept from tests.

This commit is contained in:
Dan
2021-04-13 13:29:14 -04:00
parent 3b058380a3
commit 1a3458ef7c
36 changed files with 218 additions and 330 deletions
@@ -15,13 +15,14 @@
*/ */
package agent.dbgeng.model; package agent.dbgeng.model;
import static org.junit.Assert.*; import static org.junit.Assert.assertNotNull;
import java.util.List; import java.util.List;
import ghidra.dbg.target.*;
import ghidra.dbg.target.TargetBreakpointSpec.TargetBreakpointKind; import ghidra.dbg.target.TargetBreakpointSpec.TargetBreakpointKind;
import ghidra.dbg.target.TargetBreakpointSpecContainer.TargetBreakpointKindSet; import ghidra.dbg.target.TargetBreakpointSpecContainer.TargetBreakpointKindSet;
import ghidra.dbg.target.TargetObject;
import ghidra.dbg.target.TargetStackFrame;
import ghidra.dbg.test.*; import ghidra.dbg.test.*;
import ghidra.dbg.util.PathUtils; import ghidra.dbg.util.PathUtils;
import ghidra.program.model.address.*; import ghidra.program.model.address.*;
@@ -79,10 +80,4 @@ public abstract class AbstractModelForDbgengBreakpointsTest
throw new AssertionError(); throw new AssertionError();
} }
} }
@Override
protected TargetObject findProcessContainer() throws Throwable {
return m.findContainer(TargetProcess.class, PathUtils.parse("Sessions[0]"));
}
} }
@@ -46,7 +46,7 @@ public abstract class AbstractModelForDbgengFrameFocusTest
return retry(() -> { return retry(() -> {
Map<List<String>, TargetStackFrame> frames = Map<List<String>, TargetStackFrame> frames =
m.findAll(TargetStackFrame.class, seedPath()); m.findAll(TargetStackFrame.class, seedPath(), true);
assertTrue(frames.size() >= 3); assertTrue(frames.size() >= 3);
return Set.copyOf(frames.values()); return Set.copyOf(frames.values());
}, List.of(AssertionError.class)); }, List.of(AssertionError.class));
@@ -17,6 +17,7 @@ package agent.dbgeng.model;
import java.util.List; import java.util.List;
import ghidra.dbg.target.TargetProcess;
import ghidra.dbg.test.AbstractDebuggerModelInterpreterTest; import ghidra.dbg.test.AbstractDebuggerModelInterpreterTest;
import ghidra.dbg.util.PathUtils; import ghidra.dbg.util.PathUtils;
@@ -48,6 +49,16 @@ public abstract class AbstractModelForDbgengInterpreterTest
return ".attach " + Long.toHexString(dummy.pid); return ".attach " + Long.toHexString(dummy.pid);
} }
@Override
protected String getDetachCommand(TargetProcess process) {
return ".detach";
}
@Override
protected String getKillCommand(TargetProcess process) {
return ".kill";
}
@Override @Override
public DebuggerTestSpecimen getAttachSpecimen() { public DebuggerTestSpecimen getAttachSpecimen() {
return WindowsSpecimen.NOTEPAD; return WindowsSpecimen.NOTEPAD;
@@ -44,7 +44,7 @@ public abstract class AbstractModelForDbgengProcessFocusTest
} }
return retry(() -> { return retry(() -> {
Map<List<String>, TargetProcess> found = Map<List<String>, TargetProcess> found =
m.findAll(TargetProcess.class, PathUtils.parse("Sessions[0]")); m.findAll(TargetProcess.class, PathUtils.parse("Sessions[0]"), true);
assertEquals(count, found.size()); assertEquals(count, found.size());
return Set.copyOf(found.values()); return Set.copyOf(found.values());
}, List.of(AssertionError.class)); }, List.of(AssertionError.class));
@@ -20,7 +20,7 @@ import static org.junit.Assert.assertTrue;
import java.util.List; import java.util.List;
import ghidra.dbg.target.*; import ghidra.dbg.target.TargetEnvironment;
import ghidra.dbg.target.TargetMethod.TargetParameterMap; import ghidra.dbg.target.TargetMethod.TargetParameterMap;
import ghidra.dbg.test.AbstractDebuggerModelAttacherTest; import ghidra.dbg.test.AbstractDebuggerModelAttacherTest;
import ghidra.dbg.util.PathUtils; import ghidra.dbg.util.PathUtils;
@@ -28,21 +28,11 @@ import ghidra.dbg.util.PathUtils;
public abstract class AbstractModelForDbgengRootAttacherTest public abstract class AbstractModelForDbgengRootAttacherTest
extends AbstractDebuggerModelAttacherTest { extends AbstractDebuggerModelAttacherTest {
@Override
protected TargetObject findProcessContainer() throws Throwable {
return m.findContainer(TargetProcess.class, PathUtils.parse("Sessions[0]"));
}
@Override @Override
public List<String> getExpectedAttachableContainerPath() { public List<String> getExpectedAttachableContainerPath() {
return List.of("Available"); return List.of("Available");
} }
@Override
public List<String> getExpectedProcessesContainerPath() {
return PathUtils.parse("Sessions[0].Processes");
}
@Override @Override
public List<String> getExpectedAttacherPath() { public List<String> getExpectedAttacherPath() {
return PathUtils.parse(""); return PathUtils.parse("");
@@ -21,7 +21,7 @@ import static org.junit.Assert.assertTrue;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import ghidra.dbg.target.*; import ghidra.dbg.target.TargetEnvironment;
import ghidra.dbg.target.TargetMethod.ParameterDescription; import ghidra.dbg.target.TargetMethod.ParameterDescription;
import ghidra.dbg.target.TargetMethod.TargetParameterMap; import ghidra.dbg.target.TargetMethod.TargetParameterMap;
import ghidra.dbg.test.AbstractDebuggerModelLauncherTest; import ghidra.dbg.test.AbstractDebuggerModelLauncherTest;
@@ -30,16 +30,6 @@ import ghidra.dbg.util.PathUtils;
public abstract class AbstractModelForDbgengRootLauncherTest public abstract class AbstractModelForDbgengRootLauncherTest
extends AbstractDebuggerModelLauncherTest { extends AbstractDebuggerModelLauncherTest {
@Override
protected TargetObject findProcessContainer() throws Throwable {
return m.findContainer(TargetProcess.class, PathUtils.parse("Sessions[0]"));
}
@Override
public List<String> getExpectedProcessesContainerPath() {
return PathUtils.parse("Sessions[0].Processes");
}
@Override @Override
public List<String> getExpectedLauncherPath() { public List<String> getExpectedLauncherPath() {
return PathUtils.parse(""); return PathUtils.parse("");
@@ -15,19 +15,11 @@
*/ */
package agent.dbgeng.model; package agent.dbgeng.model;
import ghidra.dbg.target.TargetObject;
import ghidra.dbg.target.TargetProcess;
import ghidra.dbg.test.AbstractDebuggerModelScenarioCloneExitTest; import ghidra.dbg.test.AbstractDebuggerModelScenarioCloneExitTest;
import ghidra.dbg.util.PathUtils;
public abstract class AbstractModelForDbgengScenarioCloneExitTest public abstract class AbstractModelForDbgengScenarioCloneExitTest
extends AbstractDebuggerModelScenarioCloneExitTest { extends AbstractDebuggerModelScenarioCloneExitTest {
@Override
protected TargetObject findProcessContainer() throws Throwable {
return m.findContainer(TargetProcess.class, PathUtils.parse("Sessions[0]"));
}
@Override @Override
protected DebuggerTestSpecimen getSpecimen() { protected DebuggerTestSpecimen getSpecimen() {
return WindowsSpecimen.CREATE_THREAD_EXIT; return WindowsSpecimen.CREATE_THREAD_EXIT;
@@ -18,18 +18,12 @@ package agent.dbgeng.model;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import ghidra.dbg.target.*; import ghidra.dbg.target.TargetEnvironment;
import ghidra.dbg.test.AbstractDebuggerModelScenarioForkExitTest; import ghidra.dbg.test.AbstractDebuggerModelScenarioForkExitTest;
import ghidra.dbg.util.PathUtils;
public abstract class AbstractModelForDbgengScenarioForkExitTest public abstract class AbstractModelForDbgengScenarioForkExitTest
extends AbstractDebuggerModelScenarioForkExitTest { extends AbstractDebuggerModelScenarioForkExitTest {
@Override
protected TargetObject findProcessContainer() throws Throwable {
return m.findContainer(TargetProcess.class, PathUtils.parse("Sessions[0]"));
}
@Override @Override
protected DebuggerTestSpecimen getSpecimen() { protected DebuggerTestSpecimen getSpecimen() {
return WindowsSpecimen.CREATE_PROCESS; return WindowsSpecimen.CREATE_PROCESS;
@@ -29,11 +29,6 @@ import ghidra.program.model.address.Address;
public abstract class AbstractModelForDbgengScenarioMemoryTest public abstract class AbstractModelForDbgengScenarioMemoryTest
extends AbstractDebuggerModelScenarioMemoryTest { extends AbstractDebuggerModelScenarioMemoryTest {
@Override
protected TargetObject findProcessContainer() throws Throwable {
return m.findContainer(TargetProcess.class, PathUtils.parse("Sessions[0]"));
}
@Override @Override
protected WindowsSpecimen getSpecimen() { protected WindowsSpecimen getSpecimen() {
return WindowsSpecimen.PRINT; return WindowsSpecimen.PRINT;
@@ -31,11 +31,6 @@ public abstract class AbstractModelForDbgengScenarioStackTest
List.of("break_here", "funcC", "funcB", "funcA"); List.of("break_here", "funcC", "funcB", "funcA");
protected NavigableMap<Address, String> symbolsByAddress = new TreeMap<>(); protected NavigableMap<Address, String> symbolsByAddress = new TreeMap<>();
@Override
protected TargetObject findProcessContainer() throws Throwable {
return m.findContainer(TargetProcess.class, PathUtils.parse("Sessions[0]"));
}
@Override @Override
protected WindowsSpecimen getSpecimen() { protected WindowsSpecimen getSpecimen() {
return WindowsSpecimen.STACK; return WindowsSpecimen.STACK;
@@ -20,19 +20,12 @@ import static org.junit.Assert.assertEquals;
import java.util.Map; import java.util.Map;
import agent.dbgeng.model.impl.DbgModelTargetProcessImpl; import agent.dbgeng.model.impl.DbgModelTargetProcessImpl;
import ghidra.dbg.target.TargetObject;
import ghidra.dbg.target.TargetProcess; import ghidra.dbg.target.TargetProcess;
import ghidra.dbg.test.AbstractDebuggerModelScenarioRegistersTest; import ghidra.dbg.test.AbstractDebuggerModelScenarioRegistersTest;
import ghidra.dbg.util.PathUtils;
public abstract class AbstractModelForDbgengScenarioX64RegistersTest public abstract class AbstractModelForDbgengScenarioX64RegistersTest
extends AbstractDebuggerModelScenarioRegistersTest { extends AbstractDebuggerModelScenarioRegistersTest {
@Override
protected TargetObject findProcessContainer() throws Throwable {
return m.findContainer(TargetProcess.class, PathUtils.parse("Sessions[0]"));
}
@Override @Override
protected DebuggerTestSpecimen getSpecimen() { protected DebuggerTestSpecimen getSpecimen() {
return WindowsSpecimen.REGISTERS; return WindowsSpecimen.REGISTERS;
@@ -17,19 +17,11 @@ package agent.dbgeng.model;
import java.util.List; import java.util.List;
import ghidra.dbg.target.TargetObject;
import ghidra.dbg.target.TargetProcess;
import ghidra.dbg.test.*; import ghidra.dbg.test.*;
import ghidra.dbg.util.PathUtils;
public abstract class AbstractModelForDbgengSteppableTest extends AbstractDebuggerModelSteppableTest public abstract class AbstractModelForDbgengSteppableTest extends AbstractDebuggerModelSteppableTest
implements ProvidesTargetViaLaunchSpecimen { implements ProvidesTargetViaLaunchSpecimen {
@Override
protected TargetObject findProcessContainer() throws Throwable {
return m.findContainer(TargetProcess.class, PathUtils.parse("Sessions[0]"));
}
@Override @Override
public AbstractDebuggerModelTest getTest() { public AbstractDebuggerModelTest getTest() {
return this; return this;
@@ -44,7 +44,7 @@ public abstract class AbstractModelForDbgengThreadFocusTest
} }
return retry(() -> { return retry(() -> {
Map<List<String>, TargetThread> found = Map<List<String>, TargetThread> found =
m.findAll(TargetThread.class, PathUtils.parse("Sessions[0]")); m.findAll(TargetThread.class, PathUtils.parse("Sessions[0]"), true);
assertEquals(count, found.size()); assertEquals(count, found.size());
return Set.copyOf(found.values()); return Set.copyOf(found.values());
}, List.of(AssertionError.class)); }, List.of(AssertionError.class));
@@ -18,8 +18,6 @@ package agent.dbgeng.model;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import ghidra.dbg.target.TargetObject;
import ghidra.dbg.target.TargetProcess;
import ghidra.dbg.test.*; import ghidra.dbg.test.*;
import ghidra.dbg.util.PathUtils; import ghidra.dbg.util.PathUtils;
@@ -37,11 +35,6 @@ public abstract class AbstractModelForDbgengX64RegistersTest
return this; return this;
} }
@Override
protected TargetObject findProcessContainer() throws Throwable {
return m.findContainer(TargetProcess.class, PathUtils.parse("Sessions[0]"));
}
@Override @Override
public List<String> getExpectedRegisterBankPath(List<String> threadPath) { public List<String> getExpectedRegisterBankPath(List<String> threadPath) {
return PathUtils.extend(threadPath, PathUtils.parse("Registers")); return PathUtils.extend(threadPath, PathUtils.parse("Registers"));
@@ -100,7 +100,6 @@ public enum WindowsSpecimen implements DebuggerTestSpecimen, DebuggerModelTestUt
public String getBinModuleName() { public String getBinModuleName() {
return getShortName(getCommandLine().split("\\s+")[0]); return getShortName(getCommandLine().split("\\s+")[0]);
} }
@Override @Override
@@ -109,7 +108,7 @@ public enum WindowsSpecimen implements DebuggerTestSpecimen, DebuggerModelTestUt
// NB. ShellUtils.parseArgs removes the \s. Not good. // NB. ShellUtils.parseArgs removes the \s. Not good.
String expected = getBinModuleName(); String expected = getBinModuleName();
Collection<TargetModule> modules = Collection<TargetModule> modules =
test.m.findAll(TargetModule.class, process.getPath()).values(); test.m.findAll(TargetModule.class, process.getPath(), true).values();
return modules.stream() return modules.stream()
.anyMatch(m -> expected.equalsIgnoreCase(getShortName(m.getModuleName()))); .anyMatch(m -> expected.equalsIgnoreCase(getShortName(m.getModuleName())));
} }
@@ -31,11 +31,6 @@ public abstract class AbstractModelForGdbAttacherTest extends AbstractDebuggerMo
return List.of("Available"); return List.of("Available");
} }
@Override
public List<String> getExpectedProcessesContainerPath() {
return List.of("Inferiors");
}
@Override @Override
public DebuggerTestSpecimen getAttachSpecimen() { public DebuggerTestSpecimen getAttachSpecimen() {
return GdbLinuxSpecimen.DD; return GdbLinuxSpecimen.DD;
@@ -17,6 +17,7 @@ package agent.gdb.model;
import java.util.List; import java.util.List;
import ghidra.dbg.target.TargetProcess;
import ghidra.dbg.test.AbstractDebuggerModelInterpreterTest; import ghidra.dbg.test.AbstractDebuggerModelInterpreterTest;
public abstract class AbstractModelForGdbInterpreterTest public abstract class AbstractModelForGdbInterpreterTest
@@ -41,6 +42,16 @@ public abstract class AbstractModelForGdbInterpreterTest
return "attach " + dummy.pid; return "attach " + dummy.pid;
} }
@Override
protected String getDetachCommand(TargetProcess process) {
return "detach";
}
@Override
protected String getKillCommand(TargetProcess process) {
return "kill";
}
@Override @Override
public DebuggerTestSpecimen getAttachSpecimen() { public DebuggerTestSpecimen getAttachSpecimen() {
return GdbLinuxSpecimen.DD; return GdbLinuxSpecimen.DD;
@@ -18,7 +18,6 @@ package agent.gdb.model;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import java.util.List;
import java.util.Map; import java.util.Map;
import ghidra.dbg.target.TargetEnvironment; import ghidra.dbg.target.TargetEnvironment;
@@ -28,11 +27,6 @@ import ghidra.dbg.test.AbstractDebuggerModelLauncherTest;
public abstract class AbstractModelForGdbLauncherTest extends AbstractDebuggerModelLauncherTest { public abstract class AbstractModelForGdbLauncherTest extends AbstractDebuggerModelLauncherTest {
@Override
public List<String> getExpectedProcessesContainerPath() {
return List.of("Inferiors");
}
@Override @Override
public DebuggerTestSpecimen getLaunchSpecimen() { public DebuggerTestSpecimen getLaunchSpecimen() {
return GdbLinuxSpecimen.ECHO_HW; return GdbLinuxSpecimen.ECHO_HW;
@@ -21,8 +21,7 @@ import java.awt.event.MouseEvent;
import java.util.*; import java.util.*;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import org.junit.Before; import org.junit.*;
import org.junit.Test;
import com.google.common.collect.Range; import com.google.common.collect.Range;
@@ -489,6 +488,7 @@ public class DebuggerModulesProviderTest extends AbstractGhidraHeadedDebuggerGUI
} }
@Test @Test
@Ignore("This action is hidden until supported")
public void testActionCaptureTypes() throws Exception { public void testActionCaptureTypes() throws Exception {
assertFalse(modulesProvider.actionCaptureTypes.isEnabled()); assertFalse(modulesProvider.actionCaptureTypes.isEnabled());
createTestModel(); createTestModel();
@@ -26,6 +26,7 @@ import ghidra.dbg.target.schema.EnumerableTargetObjectSchema;
import ghidra.dbg.target.schema.TargetObjectSchema; import ghidra.dbg.target.schema.TargetObjectSchema;
import ghidra.dbg.util.PathUtils; import ghidra.dbg.util.PathUtils;
import ghidra.lifecycle.Internal; import ghidra.lifecycle.Internal;
import ghidra.util.Msg;
import ghidra.util.datastruct.ListenerSet; import ghidra.util.datastruct.ListenerSet;
/** /**
@@ -245,8 +246,13 @@ public abstract class AbstractTargetObject<P extends TargetObject> implements Sp
valid = false; valid = false;
model.objectInvalidated(getProxy()); model.objectInvalidated(getProxy());
listeners.fire.invalidated(getProxy(), branch, reason); listeners.fire.invalidated(getProxy(), branch, reason);
listeners.clear(); CompletableFuture.runAsync(() -> {
listeners.clearChained(); 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) { protected void doInvalidateElements(Map<String, ?> elems, String reason) {
@@ -16,7 +16,8 @@
package ghidra.dbg.test; package ghidra.dbg.test;
import static org.junit.Assert.*; 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.lang.invoke.MethodHandles;
import java.util.Collection; import java.util.Collection;
@@ -33,7 +34,6 @@ import ghidra.dbg.error.DebuggerIllegalArgumentException;
import ghidra.dbg.target.*; import ghidra.dbg.target.*;
import ghidra.dbg.target.TargetExecutionStateful.TargetExecutionState; import ghidra.dbg.target.TargetExecutionStateful.TargetExecutionState;
import ghidra.dbg.target.TargetMethod.TargetParameterMap; import ghidra.dbg.target.TargetMethod.TargetParameterMap;
import ghidra.dbg.testutil.ElementTrackingListener;
import ghidra.program.model.address.AddressFactory; import ghidra.program.model.address.AddressFactory;
public abstract class AbstractDebuggerModelAttacherTest extends AbstractDebuggerModelTest public abstract class AbstractDebuggerModelAttacherTest extends AbstractDebuggerModelTest
@@ -47,10 +47,6 @@ public abstract class AbstractDebuggerModelAttacherTest extends AbstractDebugger
return null; return null;
} }
public List<String> getExpectedProcessesContainerPath() {
return null;
}
public abstract TargetParameterMap getExpectedAttachParameters(); public abstract TargetParameterMap getExpectedAttachParameters();
public abstract void assertEnvironment(TargetEnvironment environment); public abstract void assertEnvironment(TargetEnvironment environment);
@@ -65,16 +61,6 @@ public abstract class AbstractDebuggerModelAttacherTest extends AbstractDebugger
assertEquals(expectedAttacherPath, attacher.getPath()); 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 @Test
public void testAttachableContainerIsWhereExpected() throws Throwable { public void testAttachableContainerIsWhereExpected() throws Throwable {
List<String> expectedAttachableContainerPath = getExpectedAttachableContainerPath(); List<String> expectedAttachableContainerPath = getExpectedAttachableContainerPath();
@@ -195,12 +181,12 @@ public abstract class AbstractDebuggerModelAttacherTest extends AbstractDebugger
runTestAttachByObjBogusThrowsException(attacher); runTestAttachByObjBogusThrowsException(attacher);
} }
protected void runTestAttachByPidThenDetach(TargetAttacher attacher, TargetObject container) protected void runTestAttachByPidThenDetach(TargetAttacher attacher)
throws Throwable { throws Throwable {
DebuggerTestSpecimen specimen = getAttachSpecimen(); DebuggerTestSpecimen specimen = getAttachSpecimen();
assertNull(getProcessRunning(container, specimen, this)); assertNull(getProcessRunning(specimen, this));
runTestAttachByPid(attacher); runTestAttachByPid(attacher);
runTestDetach(container, specimen); runTestDetach(specimen);
assertTrue(dummy.process.isAlive()); assertTrue(dummy.process.isAlive());
} }
@@ -212,16 +198,15 @@ public abstract class AbstractDebuggerModelAttacherTest extends AbstractDebugger
dummy = specimen.runDummy(); dummy = specimen.runDummy();
TargetAttacher attacher = findAttacher(); TargetAttacher attacher = findAttacher();
TargetObject container = findProcessContainer(); runTestAttachByPidThenDetach(attacher);
runTestAttachByPidThenDetach(attacher, container);
} }
protected void runTestAttachByPidThenKill(TargetAttacher attacher, TargetObject container) protected void runTestAttachByPidThenKill(TargetAttacher attacher)
throws Throwable { throws Throwable {
DebuggerTestSpecimen specimen = getAttachSpecimen(); DebuggerTestSpecimen specimen = getAttachSpecimen();
assertNull(getProcessRunning(container, specimen, this)); assertNull(getProcessRunning(specimen, this));
runTestAttachByPid(attacher); runTestAttachByPid(attacher);
runTestKill(container, specimen); runTestKill(specimen);
retryVoid(() -> assertFalse(dummy.process.isAlive()), List.of(AssertionError.class)); retryVoid(() -> assertFalse(dummy.process.isAlive()), List.of(AssertionError.class));
} }
@@ -233,16 +218,14 @@ public abstract class AbstractDebuggerModelAttacherTest extends AbstractDebugger
dummy = specimen.runDummy(); dummy = specimen.runDummy();
TargetAttacher attacher = findAttacher(); TargetAttacher attacher = findAttacher();
TargetObject container = findProcessContainer(); runTestAttachByPidThenKill(attacher);
runTestAttachByPidThenKill(attacher, container);
} }
protected void runTestAttachByPidThenResumeInterrupt(TargetAttacher attacher, protected void runTestAttachByPidThenResumeInterrupt(TargetAttacher attacher) throws Throwable {
TargetObject container) throws Throwable {
DebuggerTestSpecimen specimen = getAttachSpecimen(); DebuggerTestSpecimen specimen = getAttachSpecimen();
assertNull(getProcessRunning(container, specimen, this)); assertNull(getProcessRunning(specimen, this));
runTestAttachByPid(attacher); runTestAttachByPid(attacher);
runTestResumeInterruptMany(container, specimen, 3); runTestResumeInterruptMany(specimen, 3);
assertTrue(dummy.process.isAlive()); assertTrue(dummy.process.isAlive());
} }
@@ -254,16 +237,14 @@ public abstract class AbstractDebuggerModelAttacherTest extends AbstractDebugger
dummy = specimen.runDummy(); dummy = specimen.runDummy();
TargetAttacher attacher = findAttacher(); TargetAttacher attacher = findAttacher();
TargetObject container = findProcessContainer(); runTestAttachByPidThenResumeInterrupt(attacher);
runTestAttachByPidThenResumeInterrupt(attacher, container);
} }
protected void runTestAttachShowsInProcessContainer(TargetAttacher attacher, protected void runTestAttachShowsInProcessContainer(TargetAttacher attacher) throws Throwable {
TargetObject container) throws Throwable {
DebuggerTestSpecimen specimen = getAttachSpecimen(); DebuggerTestSpecimen specimen = getAttachSpecimen();
assertNull(getProcessRunning(container, specimen, this)); assertNull(getProcessRunning(specimen, this));
runTestAttachByPid(attacher); runTestAttachByPid(attacher);
retryForProcessRunning(container, specimen, this); retryForProcessRunning(specimen, this);
} }
@Test @Test
@@ -274,36 +255,6 @@ public abstract class AbstractDebuggerModelAttacherTest extends AbstractDebugger
dummy = specimen.runDummy(); dummy = specimen.runDummy();
TargetAttacher attacher = findAttacher(); TargetAttacher attacher = findAttacher();
TargetObject container = findProcessContainer(); runTestAttachShowsInProcessContainer(attacher);
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);
} }
} }
@@ -16,7 +16,8 @@
package ghidra.dbg.test; package ghidra.dbg.test;
import static org.junit.Assert.*; 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.*;
import java.util.Map.Entry; import java.util.Map.Entry;
@@ -38,18 +39,16 @@ import ghidra.util.Msg;
* Tests the functionality of breakpoints * Tests the functionality of breakpoints
* *
* <p> * <p>
* Note that this test does not check for nuances regarding specification vs. * Note that this test does not check for nuances regarding specification vs. location, as it is
* location, as it is meant to generalize across models for interests of the UI * meant to generalize across models for interests of the UI only. As such, we only test that we can
* only. As such, we only test that we can set breakpoints at given addresses * set breakpoints at given addresses and that a location manifests there, regardless of the
* and that a location manifests there, regardless of the intervening * intervening mechanisms. We also test some basic operations on the breakpoint (location) itself.
* mechanisms. We also test some basic operations on the breakpoint (location) * Models which have separate specifications from locations, or for which you want to test
* itself. Models which have separate specifications from locations, or for * non-address specifications will need to add their own tests, tailored to the semantics of that
* which you want to test non-address specifications will need to add their own * model's breakpoint specifications.
* tests, tailored to the semantics of that model's breakpoint specifications.
* *
* <p> * <p>
* TODO: Enable, disable (if supported), delete (if supported), manipulation via * TODO: Enable, disable (if supported), delete (if supported), manipulation via CLI is synced
* CLI is synced
*/ */
public abstract class AbstractDebuggerModelBreakpointsTest extends AbstractDebuggerModelTest public abstract class AbstractDebuggerModelBreakpointsTest extends AbstractDebuggerModelTest
implements RequiresTarget { implements RequiresTarget {
@@ -225,7 +224,7 @@ public abstract class AbstractDebuggerModelBreakpointsTest extends AbstractDebug
waitOn(container.placeBreakpoint(range, Set.of(kind))); waitOn(container.placeBreakpoint(range, Set.of(kind)));
retryVoid(() -> { retryVoid(() -> {
Collection<? extends TargetBreakpointLocation> found = Collection<? extends TargetBreakpointLocation> found =
m.findAll(TargetBreakpointLocation.class, target.getPath()).values(); m.findAll(TargetBreakpointLocation.class, target.getPath(), true).values();
assertAtLeastOneLocCovers(found, range, kind); assertAtLeastOneLocCovers(found, range, kind);
}, List.of(AssertionError.class)); }, List.of(AssertionError.class));
} }
@@ -261,7 +260,7 @@ public abstract class AbstractDebuggerModelBreakpointsTest extends AbstractDebug
waitOn(container.placeBreakpoint(range, Set.of(kind))); waitOn(container.placeBreakpoint(range, Set.of(kind)));
locs.add(retry(() -> { locs.add(retry(() -> {
Collection<? extends TargetBreakpointLocation> found = Collection<? extends TargetBreakpointLocation> found =
m.findAll(TargetBreakpointLocation.class, target.getPath()).values(); m.findAll(TargetBreakpointLocation.class, target.getPath(), true).values();
return assertAtLeastOneLocCovers(found, range, kind); return assertAtLeastOneLocCovers(found, range, kind);
}, List.of(AssertionError.class))); }, List.of(AssertionError.class)));
} }
@@ -27,6 +27,10 @@ import ghidra.dbg.target.TargetFocusScope;
import ghidra.dbg.target.TargetObject; import ghidra.dbg.target.TargetObject;
import ghidra.dbg.util.PathUtils; 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 { public abstract class AbstractDebuggerModelFocusTest extends AbstractDebuggerModelTest {
/** /**
@@ -15,9 +15,10 @@
*/ */
package ghidra.dbg.test; package ghidra.dbg.test;
import static ghidra.lifecycle.Unfinished.*; import static ghidra.lifecycle.Unfinished.TODO;
import static org.junit.Assert.*; 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.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@@ -27,23 +28,39 @@ import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
import ghidra.async.AsyncReference; import ghidra.async.AsyncReference;
import ghidra.dbg.DebugModelConventions;
import ghidra.dbg.DebuggerModelListener; import ghidra.dbg.DebuggerModelListener;
import ghidra.dbg.error.DebuggerModelTerminatingException; import ghidra.dbg.error.DebuggerModelTerminatingException;
import ghidra.dbg.target.*;
import ghidra.dbg.target.TargetConsole.Channel; import ghidra.dbg.target.TargetConsole.Channel;
import ghidra.dbg.target.TargetInterpreter;
import ghidra.dbg.target.TargetObject;
import ghidra.dbg.testutil.CatchOffThread; import ghidra.dbg.testutil.CatchOffThread;
import ghidra.util.Msg; import ghidra.util.Msg;
public abstract class AbstractDebuggerModelInterpreterTest extends AbstractDebuggerModelTest public abstract class AbstractDebuggerModelInterpreterTest extends AbstractDebuggerModelTest
implements RequiresAttachSpecimen, RequiresLaunchSpecimen { implements RequiresAttachSpecimen, RequiresLaunchSpecimen {
/**
* Get the path of the expected result of {@link #findInterpreter()} for this test
*
* @return the expected path
*/
public List<String> getExpectedInterpreterPath() { public List<String> getExpectedInterpreterPath() {
return null; 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); protected abstract String getEchoCommand(String msg);
/**
* If applicable, get the CLI command to terminate the session / model
*
* @return the command
*/
protected abstract String getQuitCommand(); protected abstract String getQuitCommand();
/** /**
@@ -53,6 +70,30 @@ public abstract class AbstractDebuggerModelInterpreterTest extends AbstractDebug
*/ */
protected abstract String getAttachCommand(); 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 @Test
public void testInterpreterIsWhereExpected() throws Throwable { public void testInterpreterIsWhereExpected() throws Throwable {
List<String> expectedInterpreterPath = getExpectedInterpreterPath(); List<String> expectedInterpreterPath = getExpectedInterpreterPath();
@@ -126,6 +167,11 @@ public abstract class AbstractDebuggerModelInterpreterTest extends AbstractDebug
runTestExecuteCapture(interpreter, cmd); 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) @Test(expected = DebuggerModelTerminatingException.class)
public void testExecuteQuit() throws Throwable { public void testExecuteQuit() throws Throwable {
String cmd = getQuitCommand(); String cmd = getQuitCommand();
@@ -151,14 +197,22 @@ public abstract class AbstractDebuggerModelInterpreterTest extends AbstractDebug
// TODO: Delete (spec vs. loc?) // TODO: Delete (spec vs. loc?)
} }
protected void runTestLaunchViaInterpreterShowsInProcessContainer(TargetInterpreter interpreter, protected TargetProcess runTestLaunchViaInterpreterShowsInProcessContainer(
TargetObject container) throws Throwable { TargetInterpreter interpreter) throws Throwable {
DebuggerTestSpecimen specimen = getLaunchSpecimen(); DebuggerTestSpecimen specimen = getLaunchSpecimen();
assertNull(getProcessRunning(container, specimen, this)); assertNull(getProcessRunning(specimen, this));
for (String line : specimen.getLaunchScript()) { for (String line : specimen.getLaunchScript()) {
waitOn(interpreter.execute(line)); 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 @Test
@@ -167,18 +221,26 @@ public abstract class AbstractDebuggerModelInterpreterTest extends AbstractDebug
m.build(); m.build();
TargetInterpreter interpreter = findInterpreter(); TargetInterpreter interpreter = findInterpreter();
TargetObject container = findProcessContainer(); TargetProcess process = runTestLaunchViaInterpreterShowsInProcessContainer(interpreter);
assertNotNull("No process container", container);
runTestLaunchViaInterpreterShowsInProcessContainer(interpreter, container); runTestKillViaInterpreter(process, interpreter);
} }
protected void runTestAttachViaInterpreterShowsInProcessContainer(TargetInterpreter interpreter, protected TargetProcess runTestAttachViaInterpreterShowsInProcessContainer(
TargetObject container) throws Throwable { TargetInterpreter interpreter) throws Throwable {
DebuggerTestSpecimen specimen = getAttachSpecimen(); DebuggerTestSpecimen specimen = getAttachSpecimen();
assertNull(getProcessRunning(container, specimen, this)); assertNull(getProcessRunning(specimen, this));
String cmd = getAttachCommand(); String cmd = getAttachCommand();
waitOn(interpreter.execute(cmd)); 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 @Test
@@ -189,8 +251,8 @@ public abstract class AbstractDebuggerModelInterpreterTest extends AbstractDebug
dummy = specimen.runDummy(); dummy = specimen.runDummy();
TargetInterpreter interpreter = findInterpreter(); TargetInterpreter interpreter = findInterpreter();
TargetObject container = findProcessContainer(); TargetProcess process = runTestAttachViaInterpreterShowsInProcessContainer(interpreter);
assertNotNull("No process container", container);
runTestAttachViaInterpreterShowsInProcessContainer(interpreter, container); runTestDetachViaInterpreter(process, interpreter);
} }
} }
@@ -15,12 +15,12 @@
*/ */
package ghidra.dbg.test; 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.assumeNotNull;
import static org.junit.Assume.assumeTrue; import static org.junit.Assume.assumeTrue;
import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodHandles;
import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
@@ -31,7 +31,6 @@ import ghidra.dbg.DebugModelConventions;
import ghidra.dbg.target.*; import ghidra.dbg.target.*;
import ghidra.dbg.target.TargetExecutionStateful.TargetExecutionState; import ghidra.dbg.target.TargetExecutionStateful.TargetExecutionState;
import ghidra.dbg.target.TargetMethod.TargetParameterMap; import ghidra.dbg.target.TargetMethod.TargetParameterMap;
import ghidra.dbg.testutil.ElementTrackingListener;
public abstract class AbstractDebuggerModelLauncherTest extends AbstractDebuggerModelTest public abstract class AbstractDebuggerModelLauncherTest extends AbstractDebuggerModelTest
implements RequiresLaunchSpecimen { implements RequiresLaunchSpecimen {
@@ -40,10 +39,6 @@ public abstract class AbstractDebuggerModelLauncherTest extends AbstractDebugger
return null; return null;
} }
public List<String> getExpectedProcessesContainerPath() {
return null;
}
public abstract TargetParameterMap getExpectedLauncherParameters(); public abstract TargetParameterMap getExpectedLauncherParameters();
public abstract void assertEnvironment(TargetEnvironment environment); public abstract void assertEnvironment(TargetEnvironment environment);
@@ -58,16 +53,6 @@ public abstract class AbstractDebuggerModelLauncherTest extends AbstractDebugger
assertEquals(expectedLauncherPath, launcher.getPath()); 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, protected void runTestLaunchParameters(TargetLauncher launcher,
TargetParameterMap expectedParameters) throws Throwable { TargetParameterMap expectedParameters) throws Throwable {
waitAcc(launcher); waitAcc(launcher);
@@ -123,12 +108,11 @@ public abstract class AbstractDebuggerModelLauncherTest extends AbstractDebugger
waitOn(listener.observedCreated); waitOn(listener.observedCreated);
} }
protected void runTestLaunchThenDetach(TargetLauncher launcher, protected void runTestLaunchThenDetach(TargetLauncher launcher) throws Throwable {
TargetObject container) throws Throwable {
DebuggerTestSpecimen specimen = getLaunchSpecimen(); DebuggerTestSpecimen specimen = getLaunchSpecimen();
assertNull(getProcessRunning(container, specimen, this)); assertNull(getProcessRunning(specimen, this));
runTestLaunch(launcher); runTestLaunch(launcher);
runTestDetach(container, specimen); runTestDetach(specimen);
} }
@Test @Test
@@ -137,16 +121,14 @@ public abstract class AbstractDebuggerModelLauncherTest extends AbstractDebugger
m.build(); m.build();
TargetLauncher launcher = findLauncher(); TargetLauncher launcher = findLauncher();
TargetObject container = findProcessContainer(); runTestLaunchThenDetach(launcher);
runTestLaunchThenDetach(launcher, container);
} }
protected void runTestLaunchThenKill(TargetLauncher launcher, protected void runTestLaunchThenKill(TargetLauncher launcher) throws Throwable {
TargetObject container) throws Throwable {
DebuggerTestSpecimen specimen = getLaunchSpecimen(); DebuggerTestSpecimen specimen = getLaunchSpecimen();
assertNull(getProcessRunning(container, specimen, this)); assertNull(getProcessRunning(specimen, this));
runTestLaunch(launcher); runTestLaunch(launcher);
runTestKill(container, specimen); runTestKill(specimen);
} }
@Test @Test
@@ -155,16 +137,14 @@ public abstract class AbstractDebuggerModelLauncherTest extends AbstractDebugger
m.build(); m.build();
TargetLauncher launcher = findLauncher(); TargetLauncher launcher = findLauncher();
TargetObject container = findProcessContainer(); runTestLaunchThenKill(launcher);
runTestLaunchThenKill(launcher, container);
} }
protected void runTestLaunchThenResume(TargetLauncher launcher, protected void runTestLaunchThenResume(TargetLauncher launcher) throws Throwable {
TargetObject container) throws Throwable {
DebuggerTestSpecimen specimen = getLaunchSpecimen(); DebuggerTestSpecimen specimen = getLaunchSpecimen();
assertNull(getProcessRunning(container, specimen, this)); assertNull(getProcessRunning(specimen, this));
runTestLaunch(launcher); runTestLaunch(launcher);
runTestResumeTerminates(container, specimen); runTestResumeTerminates(specimen);
} }
@Test @Test
@@ -173,16 +153,14 @@ public abstract class AbstractDebuggerModelLauncherTest extends AbstractDebugger
m.build(); m.build();
TargetLauncher launcher = findLauncher(); TargetLauncher launcher = findLauncher();
TargetObject container = findProcessContainer(); runTestLaunchThenResume(launcher);
runTestLaunchThenResume(launcher, container);
} }
protected void runTestLaunchShowsInProcessContainer(TargetLauncher launcher, protected void runTestLaunchShowsInProcessContainer(TargetLauncher launcher) throws Throwable {
TargetObject container) throws Throwable {
DebuggerTestSpecimen specimen = getLaunchSpecimen(); DebuggerTestSpecimen specimen = getLaunchSpecimen();
assertNull(getProcessRunning(container, specimen, this)); assertNull(getProcessRunning(specimen, this));
runTestLaunch(launcher); runTestLaunch(launcher);
retryForProcessRunning(container, specimen, this); retryForProcessRunning(specimen, this);
} }
@Test @Test
@@ -191,34 +169,6 @@ public abstract class AbstractDebuggerModelLauncherTest extends AbstractDebugger
m.build(); m.build();
TargetLauncher launcher = findLauncher(); TargetLauncher launcher = findLauncher();
TargetObject container = findProcessContainer(); runTestLaunchShowsInProcessContainer(launcher);
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);
} }
} }
@@ -15,7 +15,8 @@
*/ */
package ghidra.dbg.test; 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.lang.invoke.MethodHandles;
import java.util.*; import java.util.*;
@@ -131,14 +132,12 @@ public abstract class AbstractDebuggerModelScenarioCloneExitTest extends Abstrac
Msg.debug(this, "Launching " + specimen); Msg.debug(this, "Launching " + specimen);
waitOn(launcher.launch(specimen.getLauncherArgs())); waitOn(launcher.launch(specimen.getLauncherArgs()));
Msg.debug(this, " Done launching"); Msg.debug(this, " Done launching");
TargetObject processContainer = findProcessContainer(); TargetProcess process = retryForProcessRunning(specimen, this);
assertNotNull("No process container", processContainer);
TargetProcess process = retryForProcessRunning(processContainer, specimen, this);
postLaunch(process); postLaunch(process);
TargetBreakpointSpecContainer breakpointContainer = TargetBreakpointSpecContainer bpContainer =
findBreakpointSpecContainer(process.getPath()); findBreakpointSpecContainer(process.getPath());
Msg.debug(this, "Placing breakpoint"); Msg.debug(this, "Placing breakpoint");
waitOn(breakpointContainer.placeBreakpoint(getBreakpointExpression(), waitOn(bpContainer.placeBreakpoint(getBreakpointExpression(),
Set.of(TargetBreakpointKind.SW_EXECUTE))); Set.of(TargetBreakpointKind.SW_EXECUTE)));
assertTrue(DebugModelConventions.isProcessAlive(process)); assertTrue(DebugModelConventions.isProcessAlive(process));
@@ -182,8 +182,6 @@ public abstract class AbstractDebuggerModelScenarioForkExitTest extends Abstract
Msg.debug(this, "Launching " + specimen); Msg.debug(this, "Launching " + specimen);
waitOn(launcher.launch(specimen.getLauncherArgs())); waitOn(launcher.launch(specimen.getLauncherArgs()));
Msg.debug(this, " Done launching"); Msg.debug(this, " Done launching");
TargetObject processContainer = findProcessContainer();
assertNotNull("No process container", processContainer);
TargetProcess parentProcess = waitOn(stateMonitor.observedParent); TargetProcess parentProcess = waitOn(stateMonitor.observedParent);
Msg.debug(this, "Parent is " + parentProcess.getJoinedPath(".")); Msg.debug(this, "Parent is " + parentProcess.getJoinedPath("."));
postLaunch(parentProcess); postLaunch(parentProcess);
@@ -201,7 +199,7 @@ public abstract class AbstractDebuggerModelScenarioForkExitTest extends Abstract
Msg.debug(this, " Done " + i); Msg.debug(this, " Done " + i);
waitAcc(access(parentProcess)); waitAcc(access(parentProcess));
try { try {
childProcess = retryForOtherProcessRunning(processContainer, specimen, this, childProcess = retryForOtherProcessRunning(specimen, this,
p -> p != parentProcess, WAIT_FOR_CHILD_MS); p -> p != parentProcess, WAIT_FOR_CHILD_MS);
} }
catch (AssertionError e) { catch (AssertionError e) {
@@ -141,8 +141,7 @@ public abstract class AbstractDebuggerModelScenarioMemoryTest extends AbstractDe
Msg.debug(this, "Launching " + specimen); Msg.debug(this, "Launching " + specimen);
waitOn(launcher.launch(specimen.getLauncherArgs())); waitOn(launcher.launch(specimen.getLauncherArgs()));
Msg.debug(this, " Done launching"); Msg.debug(this, " Done launching");
TargetObject processContainer = findProcessContainer(); TargetProcess process = retryForProcessRunning(specimen, this);
TargetProcess process = retryForProcessRunning(processContainer, specimen, this);
postLaunch(process); postLaunch(process);
Address address = Objects.requireNonNull(getAddressToWrite(process)); Address address = Objects.requireNonNull(getAddressToWrite(process));
@@ -15,7 +15,6 @@
*/ */
package ghidra.dbg.test; package ghidra.dbg.test;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodHandles;
@@ -138,9 +137,7 @@ public abstract class AbstractDebuggerModelScenarioRegistersTest extends Abstrac
Msg.debug(this, "Launching " + specimen); Msg.debug(this, "Launching " + specimen);
waitOn(launcher.launch(specimen.getLauncherArgs())); waitOn(launcher.launch(specimen.getLauncherArgs()));
Msg.debug(this, " Done launching"); Msg.debug(this, " Done launching");
TargetObject processContainer = findProcessContainer(); TargetProcess process = retryForProcessRunning(specimen, this);
assertNotNull(processContainer);
TargetProcess process = retryForProcessRunning(processContainer, specimen, this);
postLaunch(process); postLaunch(process);
TargetBreakpointSpecContainer breakpointContainer = TargetBreakpointSpecContainer breakpointContainer =
@@ -122,8 +122,7 @@ public abstract class AbstractDebuggerModelScenarioStackTest extends AbstractDeb
TargetLauncher launcher = findLauncher(); TargetLauncher launcher = findLauncher();
waitOn(launcher.launch(specimen.getLauncherArgs())); waitOn(launcher.launch(specimen.getLauncherArgs()));
Msg.debug(this, " Done launching"); Msg.debug(this, " Done launching");
TargetObject processContainer = findProcessContainer(); TargetProcess process = retryForProcessRunning(specimen, this);
TargetProcess process = retryForProcessRunning(processContainer, specimen, this);
postLaunch(process); postLaunch(process);
TargetBreakpointSpecContainer breakpointContainer = TargetBreakpointSpecContainer breakpointContainer =
@@ -157,7 +156,7 @@ public abstract class AbstractDebuggerModelScenarioStackTest extends AbstractDeb
// Sort by path should present them innermost to outermost // Sort by path should present them innermost to outermost
List<TargetStackFrame> frames = retry(() -> { List<TargetStackFrame> frames = retry(() -> {
List<TargetStackFrame> result = 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); assertTrue("Fewer than 4 frames", result.size() > 4);
return result; return result;
}, List.of(AssertionError.class)); }, List.of(AssertionError.class));
@@ -85,19 +85,6 @@ public abstract class AbstractDebuggerModelTest extends AbstractGhidraHeadlessIn
return m.find(TargetLauncher.class, seedPath()); 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 * Get the breakpoint container of a target under test
* *
@@ -285,9 +272,9 @@ public abstract class AbstractDebuggerModelTest extends AbstractGhidraHeadlessIn
return waitOn(listener.trapped); return waitOn(listener.trapped);
} }
protected void runTestDetach(TargetObject container, DebuggerTestSpecimen specimen) protected void runTestDetach(DebuggerTestSpecimen specimen)
throws Throwable { throws Throwable {
TargetProcess process = retryForProcessRunning(container, specimen, this); TargetProcess process = retryForProcessRunning(specimen, this);
TargetDetachable detachable = m.suitable(TargetDetachable.class, process.getPath()); TargetDetachable detachable = m.suitable(TargetDetachable.class, process.getPath());
waitAcc(detachable); waitAcc(detachable);
waitOn(detachable.detach()); waitOn(detachable.detach());
@@ -295,9 +282,9 @@ public abstract class AbstractDebuggerModelTest extends AbstractGhidraHeadlessIn
List.of(AssertionError.class)); List.of(AssertionError.class));
} }
protected void runTestKill(TargetObject container, DebuggerTestSpecimen specimen) protected void runTestKill(DebuggerTestSpecimen specimen)
throws Throwable { throws Throwable {
TargetProcess process = retryForProcessRunning(container, specimen, this); TargetProcess process = retryForProcessRunning(specimen, this);
TargetKillable killable = m.suitable(TargetKillable.class, process.getPath()); TargetKillable killable = m.suitable(TargetKillable.class, process.getPath());
waitAcc(killable); waitAcc(killable);
waitOn(killable.kill()); waitOn(killable.kill());
@@ -305,9 +292,8 @@ public abstract class AbstractDebuggerModelTest extends AbstractGhidraHeadlessIn
List.of(AssertionError.class)); List.of(AssertionError.class));
} }
protected void runTestResumeTerminates(TargetObject container, DebuggerTestSpecimen specimen) protected void runTestResumeTerminates(DebuggerTestSpecimen specimen) throws Throwable {
throws Throwable { TargetProcess process = retryForProcessRunning(specimen, this);
TargetProcess process = retryForProcessRunning(container, specimen, this);
TargetResumable resumable = m.suitable(TargetResumable.class, process.getPath()); TargetResumable resumable = m.suitable(TargetResumable.class, process.getPath());
AsyncState state = AsyncState state =
new AsyncState(m.suitable(TargetExecutionStateful.class, process.getPath())); new AsyncState(m.suitable(TargetExecutionStateful.class, process.getPath()));
@@ -318,9 +304,9 @@ public abstract class AbstractDebuggerModelTest extends AbstractGhidraHeadlessIn
List.of(AssertionError.class)); List.of(AssertionError.class));
} }
protected void runTestResumeInterruptMany(TargetObject container, DebuggerTestSpecimen specimen, protected void runTestResumeInterruptMany(DebuggerTestSpecimen specimen,
int repetitions) throws Throwable { int repetitions) throws Throwable {
TargetProcess process = retryForProcessRunning(container, specimen, this); TargetProcess process = retryForProcessRunning(specimen, this);
TargetResumable resumable = m.suitable(TargetResumable.class, process.getPath()); TargetResumable resumable = m.suitable(TargetResumable.class, process.getPath());
TargetInterruptible interruptible = TargetInterruptible interruptible =
m.suitable(TargetInterruptible.class, process.getPath()); m.suitable(TargetInterruptible.class, process.getPath());
@@ -342,6 +328,6 @@ public abstract class AbstractDebuggerModelTest extends AbstractGhidraHeadlessIn
}, List.of(AssertionError.class)); }, 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 @Override
public <T extends TargetObject> NavigableMap<List<String>, T> findAll(Class<T> cls, 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 = PathMatcher matcher =
model.getRootSchema().getSuccessorSchema(seedPath).searchFor(cls, seedPath, false); model.getRootSchema().getSuccessorSchema(seedPath).searchFor(cls, seedPath, false);
if (matcher.isEmpty()) { if (matcher.isEmpty()) {
return new TreeMap<>(); 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. // NB. Outside of testing, an "unsafe" cast of the map should be fine.
// During testing, we should expend the energy to verify the heap. // During testing, we should expend the energy to verify the heap.
NavigableMap<List<String>, T> result = new TreeMap<>(PathComparator.KEYED); 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())); result.put(ent.getKey(), cls.cast(ent.getValue()));
} }
return result; return result;
@@ -29,12 +29,11 @@ public interface ProvidesTargetViaAttachSpecimen extends RequiresTarget, Require
@Override @Override
default TargetObject obtainTarget() throws Throwable { default TargetObject obtainTarget() throws Throwable {
TargetAttacher attacher = getTest().findAttacher(); TargetAttacher attacher = getTest().findAttacher();
TargetObject container = getTest().findProcessContainer();
DebuggerTestSpecimen specimen = getAttachSpecimen(); DebuggerTestSpecimen specimen = getAttachSpecimen();
waitAcc(attacher); waitAcc(attacher);
DummyProc dummy = specimen.runDummy(); DummyProc dummy = specimen.runDummy();
setDummy(dummy); setDummy(dummy);
attacher.attach(dummy.pid); attacher.attach(dummy.pid);
return retryForProcessRunning(container, specimen, getTest()); return retryForProcessRunning(specimen, getTest());
} }
} }
@@ -15,7 +15,7 @@
*/ */
package ghidra.dbg.test; package ghidra.dbg.test;
import static org.junit.Assert.*; import static org.junit.Assert.assertNotNull;
import ghidra.dbg.target.TargetLauncher; import ghidra.dbg.target.TargetLauncher;
import ghidra.dbg.target.TargetObject; import ghidra.dbg.target.TargetObject;
@@ -34,11 +34,9 @@ public interface ProvidesTargetViaLaunchSpecimen extends RequiresTarget, Require
default TargetObject obtainTarget() throws Throwable { default TargetObject obtainTarget() throws Throwable {
TargetLauncher launcher = getTest().findLauncher(); TargetLauncher launcher = getTest().findLauncher();
assertNotNull("No launcher found", launcher); assertNotNull("No launcher found", launcher);
TargetObject container = getTest().findProcessContainer();
assertNotNull("No process container found", container);
DebuggerTestSpecimen specimen = getLaunchSpecimen(); DebuggerTestSpecimen specimen = getLaunchSpecimen();
waitAcc(launcher); waitAcc(launcher);
launcher.launch(specimen.getLauncherArgs()); 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.target.TargetSteppable.TargetStepKind;
import ghidra.dbg.test.AbstractDebuggerModelTest; import ghidra.dbg.test.AbstractDebuggerModelTest;
import ghidra.dbg.test.AbstractDebuggerModelTest.DebuggerTestSpecimen; import ghidra.dbg.test.AbstractDebuggerModelTest.DebuggerTestSpecimen;
import ghidra.dbg.util.PathUtils;
import ghidra.util.NumericUtilities; import ghidra.util.NumericUtilities;
public interface DebuggerModelTestUtils extends AsyncTestUtils { public interface DebuggerModelTestUtils extends AsyncTestUtils {
@@ -164,10 +165,9 @@ public interface DebuggerModelTestUtils extends AsyncTestUtils {
}).findFirst().orElse(null); }).findFirst().orElse(null);
} }
@SuppressWarnings({ "rawtypes", "unchecked" }) default Collection<TargetProcess> fetchProcesses(AbstractDebuggerModelTest test)
default Collection<TargetProcess> fetchProcesses(TargetObject container)
throws Throwable { throws Throwable {
return (Collection) waitOn(container.fetchElements(true)).values(); return test.m.findAll(TargetProcess.class, PathUtils.parse(""), false).values();
} }
@SuppressWarnings({ "rawtypes", "unchecked" }) @SuppressWarnings({ "rawtypes", "unchecked" })
@@ -176,32 +176,30 @@ public interface DebuggerModelTestUtils extends AsyncTestUtils {
return (Collection) waitOn(container.fetchElements(true)).values(); return (Collection) waitOn(container.fetchElements(true)).values();
} }
default TargetProcess getProcessRunning(TargetObject container, default TargetProcess getProcessRunning(DebuggerTestSpecimen specimen,
DebuggerTestSpecimen specimen, AbstractDebuggerModelTest test) throws Throwable { AbstractDebuggerModelTest test) throws Throwable {
return getProcessRunning(container, specimen, test, p -> true); return getProcessRunning(specimen, test, p -> true);
} }
default TargetProcess getProcessRunning(TargetObject container, default TargetProcess getProcessRunning(DebuggerTestSpecimen specimen,
DebuggerTestSpecimen specimen, AbstractDebuggerModelTest test, AbstractDebuggerModelTest test, Predicate<TargetProcess> predicate) throws Throwable {
Predicate<TargetProcess> predicate) throws Throwable { return getProcessRunning(fetchProcesses(test), specimen, test, predicate);
return getProcessRunning(fetchProcesses(container), specimen, test, predicate);
} }
default TargetProcess retryForProcessRunning(TargetObject container, default TargetProcess retryForProcessRunning(
DebuggerTestSpecimen specimen, AbstractDebuggerModelTest test) throws Throwable { DebuggerTestSpecimen specimen, AbstractDebuggerModelTest test) throws Throwable {
return retry(() -> { return retry(() -> {
TargetProcess process = getProcessRunning(container, specimen, test); TargetProcess process = getProcessRunning(specimen, test);
assertNotNull(process); assertNotNull(process);
return process; return process;
}, List.of(AssertionError.class)); }, List.of(AssertionError.class));
} }
default TargetProcess retryForOtherProcessRunning(TargetObject container, default TargetProcess retryForOtherProcessRunning(DebuggerTestSpecimen specimen,
DebuggerTestSpecimen specimen, AbstractDebuggerModelTest test, AbstractDebuggerModelTest test, Predicate<TargetProcess> predicate, long timeoutMs)
Predicate<TargetProcess> predicate, long timeoutMs)
throws Throwable { throws Throwable {
return retry(timeoutMs, () -> { return retry(timeoutMs, () -> {
TargetProcess process = getProcessRunning(container, specimen, test, predicate); TargetProcess process = getProcessRunning(specimen, test, predicate);
assertNotNull(process); assertNotNull(process);
return process; return process;
}, List.of(AssertionError.class)); }, 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> T findAny(Class<T> cls, List<String> seedPath) throws Throwable;
<T extends TargetObject> Map<List<String>, T> findAll(Class<T> cls, List<String> seedPath) <T extends TargetObject> Map<List<String>, T> findAll(Class<T> cls, List<String> seedPath,
throws Throwable; boolean atLeastOne) throws Throwable;
TargetObject findContainer(Class<? extends TargetObject> cls, List<String> seedPath) TargetObject findContainer(Class<? extends TargetObject> cls, List<String> seedPath)
throws Throwable; throws Throwable;