mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-06-02 06:50:52 +08:00
GP-785: Prompting for connection parameters, too.
This commit is contained in:
-11
@@ -20,7 +20,6 @@ import ghidra.app.plugin.core.debug.AbstractDebuggerPlugin;
|
|||||||
import ghidra.app.plugin.core.debug.DebuggerPluginPackage;
|
import ghidra.app.plugin.core.debug.DebuggerPluginPackage;
|
||||||
import ghidra.app.plugin.core.debug.event.ModelActivatedPluginEvent;
|
import ghidra.app.plugin.core.debug.event.ModelActivatedPluginEvent;
|
||||||
import ghidra.app.services.DebuggerModelService;
|
import ghidra.app.services.DebuggerModelService;
|
||||||
import ghidra.framework.options.SaveState;
|
|
||||||
import ghidra.framework.plugintool.*;
|
import ghidra.framework.plugintool.*;
|
||||||
import ghidra.framework.plugintool.annotation.AutoServiceConsumed;
|
import ghidra.framework.plugintool.annotation.AutoServiceConsumed;
|
||||||
import ghidra.framework.plugintool.util.PluginStatus;
|
import ghidra.framework.plugintool.util.PluginStatus;
|
||||||
@@ -68,14 +67,4 @@ public class DebuggerTargetsPlugin extends AbstractDebuggerPlugin {
|
|||||||
provider.modelActivated(evt.getActiveModel());
|
provider.modelActivated(evt.getActiveModel());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeConfigState(SaveState saveState) {
|
|
||||||
provider.writeConfigState(saveState);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void readConfigState(SaveState saveState) {
|
|
||||||
provider.readConfigState(saveState);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-14
@@ -35,7 +35,6 @@ import ghidra.app.plugin.core.debug.gui.DebuggerResources;
|
|||||||
import ghidra.app.plugin.core.debug.gui.DebuggerResources.*;
|
import ghidra.app.plugin.core.debug.gui.DebuggerResources.*;
|
||||||
import ghidra.app.services.DebuggerModelService;
|
import ghidra.app.services.DebuggerModelService;
|
||||||
import ghidra.dbg.DebuggerObjectModel;
|
import ghidra.dbg.DebuggerObjectModel;
|
||||||
import ghidra.framework.options.SaveState;
|
|
||||||
import ghidra.framework.plugintool.AutoService;
|
import ghidra.framework.plugintool.AutoService;
|
||||||
import ghidra.framework.plugintool.ComponentProviderAdapter;
|
import ghidra.framework.plugintool.ComponentProviderAdapter;
|
||||||
import ghidra.framework.plugintool.annotation.AutoServiceConsumed;
|
import ghidra.framework.plugintool.annotation.AutoServiceConsumed;
|
||||||
@@ -106,8 +105,9 @@ public class DebuggerTargetsProvider extends ComponentProviderAdapter {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void actionPerformed(ActionContext context) {
|
public void actionPerformed(ActionContext context) {
|
||||||
connectDialog.reset();
|
// NB. Drop the future on the floor, because the UI will report issues.
|
||||||
tool.showDialog(connectDialog);
|
// Cancellation should be ignored.
|
||||||
|
modelService.showConnectDialog();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -163,8 +163,6 @@ public class DebuggerTargetsProvider extends ComponentProviderAdapter {
|
|||||||
protected GTree tree;
|
protected GTree tree;
|
||||||
protected DebuggerConnectionsNode rootNode;
|
protected DebuggerConnectionsNode rootNode;
|
||||||
|
|
||||||
protected DebuggerConnectDialog connectDialog = new DebuggerConnectDialog();
|
|
||||||
|
|
||||||
ConnectAction actionConnect;
|
ConnectAction actionConnect;
|
||||||
DisconnectAction actionDisconnect;
|
DisconnectAction actionDisconnect;
|
||||||
DockingAction actionDisconnectAll;
|
DockingAction actionDisconnectAll;
|
||||||
@@ -267,7 +265,6 @@ public class DebuggerTargetsProvider extends ComponentProviderAdapter {
|
|||||||
rootNode = new DebuggerConnectionsNode(modelService, this);
|
rootNode = new DebuggerConnectionsNode(modelService, this);
|
||||||
tree.setRootNode(rootNode);
|
tree.setRootNode(rootNode);
|
||||||
}
|
}
|
||||||
connectDialog.setModelService(modelService);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void updateTree(boolean select, Object obj) {
|
protected void updateTree(boolean select, Object obj) {
|
||||||
@@ -304,12 +301,4 @@ public class DebuggerTargetsProvider extends ComponentProviderAdapter {
|
|||||||
model.invalidateAllLocalCaches();
|
model.invalidateAllLocalCaches();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void writeConfigState(SaveState saveState) {
|
|
||||||
connectDialog.writeConfigState(saveState);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void readConfigState(SaveState saveState) {
|
|
||||||
connectDialog.readConfigState(saveState);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-1
@@ -108,12 +108,17 @@ public class GdbDebuggerProgramLaunchOpinion implements DebuggerProgramLaunchOpi
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getMenuTitle() {
|
public String getQuickTitle() {
|
||||||
Map<String, Property<?>> opts = factory.getOptions();
|
Map<String, Property<?>> opts = factory.getOptions();
|
||||||
return String.format("in GDB via ssh:%s@%s",
|
return String.format("in GDB via ssh:%s@%s",
|
||||||
opts.get("SSH username").getValue(),
|
opts.get("SSH username").getValue(),
|
||||||
opts.get("SSH hostname").getValue());
|
opts.get("SSH hostname").getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getMenuTitle() {
|
||||||
|
return "in GDB via ssh";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
+35
-5
@@ -13,7 +13,7 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package ghidra.app.plugin.core.debug.gui.target;
|
package ghidra.app.plugin.core.debug.service.model;
|
||||||
|
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.event.ActionEvent;
|
import java.awt.event.ActionEvent;
|
||||||
@@ -86,6 +86,7 @@ public class DebuggerConnectDialog extends DialogComponentProvider
|
|||||||
|
|
||||||
protected JButton connectButton;
|
protected JButton connectButton;
|
||||||
protected CompletableFuture<? extends DebuggerObjectModel> futureConnect;
|
protected CompletableFuture<? extends DebuggerObjectModel> futureConnect;
|
||||||
|
protected CompletableFuture<DebuggerObjectModel> result;
|
||||||
|
|
||||||
protected static class FactoryEntry {
|
protected static class FactoryEntry {
|
||||||
DebuggerModelFactory factory;
|
DebuggerModelFactory factory;
|
||||||
@@ -236,11 +237,21 @@ public class DebuggerConnectDialog extends DialogComponentProvider
|
|||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
futureConnect = factory.build();
|
futureConnect = factory.build();
|
||||||
}
|
}
|
||||||
futureConnect.thenAcceptAsync(model -> {
|
futureConnect.thenAcceptAsync(m -> {
|
||||||
modelService.addModel(model);
|
modelService.addModel(m);
|
||||||
setStatusText("");
|
setStatusText("");
|
||||||
close();
|
close();
|
||||||
modelService.activateModel(model);
|
modelService.activateModel(m);
|
||||||
|
synchronized (this) {
|
||||||
|
/**
|
||||||
|
* NB. Errors will typically be reported, the dialog stays up, and the user is given
|
||||||
|
* an opportunity to rectify the failure. Thus, errors should not be used to
|
||||||
|
* complete the result exceptionally. Only catastrophic errors and cancellation
|
||||||
|
* should affect the result.
|
||||||
|
*/
|
||||||
|
result.completeAsync(() -> m);
|
||||||
|
result = null;
|
||||||
|
}
|
||||||
}, SwingExecutorService.INSTANCE).exceptionally(e -> {
|
}, SwingExecutorService.INSTANCE).exceptionally(e -> {
|
||||||
e = AsyncUtils.unwrapThrowable(e);
|
e = AsyncUtils.unwrapThrowable(e);
|
||||||
if (!(e instanceof CancellationException)) {
|
if (!(e instanceof CancellationException)) {
|
||||||
@@ -261,12 +272,31 @@ public class DebuggerConnectDialog extends DialogComponentProvider
|
|||||||
if (futureConnect != null) {
|
if (futureConnect != null) {
|
||||||
futureConnect.cancel(false);
|
futureConnect.cancel(false);
|
||||||
}
|
}
|
||||||
|
if (result != null) {
|
||||||
|
result.cancel(false);
|
||||||
|
}
|
||||||
super.cancelCallback();
|
super.cancelCallback();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void reset() {
|
protected synchronized CompletableFuture<DebuggerObjectModel> reset(
|
||||||
|
DebuggerModelFactory factory) {
|
||||||
|
if (factory != null) {
|
||||||
|
synchronized (factories) {
|
||||||
|
dropdownModel.setSelectedItem(factories.get(factory));
|
||||||
|
}
|
||||||
|
dropdown.setEnabled(false);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
dropdown.setEnabled(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result != null) {
|
||||||
|
result.cancel(false);
|
||||||
|
}
|
||||||
|
result = new CompletableFuture<>();
|
||||||
setStatusText("");
|
setStatusText("");
|
||||||
connectButton.setEnabled(true);
|
connectButton.setEnabled(true);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void syncOptionsEnabled() {
|
protected void syncOptionsEnabled() {
|
||||||
+19
-1
@@ -201,7 +201,10 @@ public class DebuggerModelServicePlugin extends Plugin
|
|||||||
protected final ChangeListener classChangeListener = new ChangeListenerForFactoryInstances();
|
protected final ChangeListener classChangeListener = new ChangeListenerForFactoryInstances();
|
||||||
protected final ListenerOnRecorders listenerOnRecorders = new ListenerOnRecorders();
|
protected final ListenerOnRecorders listenerOnRecorders = new ListenerOnRecorders();
|
||||||
|
|
||||||
DebuggerSelectMappingOfferDialog offerDialog = new DebuggerSelectMappingOfferDialog();
|
protected final DebuggerSelectMappingOfferDialog offerDialog =
|
||||||
|
new DebuggerSelectMappingOfferDialog();
|
||||||
|
protected final DebuggerConnectDialog connectDialog = new DebuggerConnectDialog();
|
||||||
|
|
||||||
DockingAction actionDisconnectAll;
|
DockingAction actionDisconnectAll;
|
||||||
|
|
||||||
protected DebuggerObjectModel currentModel;
|
protected DebuggerObjectModel currentModel;
|
||||||
@@ -211,6 +214,7 @@ public class DebuggerModelServicePlugin extends Plugin
|
|||||||
|
|
||||||
ClassSearcher.addChangeListener(classChangeListener);
|
ClassSearcher.addChangeListener(classChangeListener);
|
||||||
refreshFactoryInstances();
|
refreshFactoryInstances();
|
||||||
|
connectDialog.setModelService(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -653,6 +657,7 @@ public class DebuggerModelServicePlugin extends Plugin
|
|||||||
factory.writeConfigState(factoryState);
|
factory.writeConfigState(factoryState);
|
||||||
saveState.putXmlElement(stateName, factoryState.saveToXml());
|
saveState.putXmlElement(stateName, factoryState.saveToXml());
|
||||||
}
|
}
|
||||||
|
connectDialog.writeConfigState(saveState);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -665,6 +670,7 @@ public class DebuggerModelServicePlugin extends Plugin
|
|||||||
factory.readConfigState(factoryState);
|
factory.readConfigState(factoryState);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
connectDialog.readConfigState(saveState);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -673,4 +679,16 @@ public class DebuggerModelServicePlugin extends Plugin
|
|||||||
.stream()
|
.stream()
|
||||||
.flatMap(opinion -> opinion.getOffers(program, tool, this).stream());
|
.flatMap(opinion -> opinion.getOffers(program, tool, this).stream());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected CompletableFuture<DebuggerObjectModel> doShowConnectDialog(PluginTool tool,
|
||||||
|
DebuggerModelFactory factory) {
|
||||||
|
CompletableFuture<DebuggerObjectModel> future = connectDialog.reset(factory);
|
||||||
|
tool.showDialog(connectDialog);
|
||||||
|
return future;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CompletableFuture<DebuggerObjectModel> showConnectDialog(DebuggerModelFactory factory) {
|
||||||
|
return doShowConnectDialog(tool, factory);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+25
-3
@@ -18,10 +18,13 @@ package ghidra.app.plugin.core.debug.service.model;
|
|||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import java.util.concurrent.CancellationException;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.exception.ExceptionUtils;
|
||||||
|
|
||||||
import docking.ActionContext;
|
import docking.ActionContext;
|
||||||
import docking.action.DockingAction;
|
import docking.action.DockingAction;
|
||||||
import docking.action.builder.MultiStateActionBuilder;
|
import docking.action.builder.MultiStateActionBuilder;
|
||||||
@@ -57,6 +60,7 @@ import ghidra.util.Msg;
|
|||||||
import ghidra.util.database.UndoableTransaction;
|
import ghidra.util.database.UndoableTransaction;
|
||||||
import ghidra.util.datastruct.CollectionChangeListener;
|
import ghidra.util.datastruct.CollectionChangeListener;
|
||||||
import ghidra.util.datastruct.ListenerSet;
|
import ghidra.util.datastruct.ListenerSet;
|
||||||
|
import ghidra.util.exception.CancelledException;
|
||||||
import ghidra.util.task.TaskMonitor;
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
@PluginInfo( //
|
@PluginInfo( //
|
||||||
@@ -94,12 +98,17 @@ public class DebuggerModelServiceProxyPlugin extends Plugin
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getMenuParentTitle() {
|
public String getMenuParentTitle() {
|
||||||
return null;
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getMenuTitle() {
|
public String getMenuTitle() {
|
||||||
return null;
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getQuickTitle() {
|
||||||
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -256,6 +265,11 @@ public class DebuggerModelServiceProxyPlugin extends Plugin
|
|||||||
closeAllModels();
|
closeAllModels();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CompletableFuture<DebuggerObjectModel> showConnectDialog(DebuggerModelFactory factory) {
|
||||||
|
return delegate.doShowConnectDialog(tool, factory);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Stream<DebuggerProgramLaunchOffer> getProgramLaunchOffers(Program program) {
|
public Stream<DebuggerProgramLaunchOffer> getProgramLaunchOffers(Program program) {
|
||||||
return orderOffers(delegate.getProgramLaunchOffers(program), program);
|
return orderOffers(delegate.getProgramLaunchOffers(program), program);
|
||||||
@@ -325,7 +339,15 @@ public class DebuggerModelServiceProxyPlugin extends Plugin
|
|||||||
Msg.error(this, "Trouble writing recent launches to program user data");
|
Msg.error(this, "Trouble writing recent launches to program user data");
|
||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
return offer.launchProgram(m, prompt);
|
return offer.launchProgram(m, prompt).exceptionally(ex -> {
|
||||||
|
Throwable t = AsyncUtils.unwrapThrowable(ex);
|
||||||
|
if (t instanceof CancellationException || t instanceof CancelledException) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return ExceptionUtils.rethrow(ex);
|
||||||
|
}).whenCompleteAsync((v, e) -> {
|
||||||
|
updateActionDebugProgram();
|
||||||
|
}, AsyncUtils.SWING_EXECUTOR);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+13
-5
@@ -251,15 +251,23 @@ public abstract class AbstractDebuggerProgramLaunchOffer implements DebuggerProg
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected CompletableFuture<DebuggerObjectModel> connect(boolean prompt) {
|
||||||
|
DebuggerModelService service = tool.getService(DebuggerModelService.class);
|
||||||
|
DebuggerModelFactory factory = getModelFactory();
|
||||||
|
if (prompt) {
|
||||||
|
return service.showConnectDialog(factory);
|
||||||
|
}
|
||||||
|
return factory.build().thenApplyAsync(m -> {
|
||||||
|
service.addModel(m);
|
||||||
|
return m;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CompletableFuture<Void> launchProgram(TaskMonitor monitor, boolean prompt) {
|
public CompletableFuture<Void> launchProgram(TaskMonitor monitor, boolean prompt) {
|
||||||
monitor.initialize(2);
|
monitor.initialize(2);
|
||||||
monitor.setMessage("Connecting");
|
monitor.setMessage("Connecting");
|
||||||
return getModelFactory().build().thenApplyAsync(m -> {
|
return connect(prompt).thenComposeAsync(m -> {
|
||||||
DebuggerModelService service = tool.getService(DebuggerModelService.class);
|
|
||||||
service.addModel(m);
|
|
||||||
return m;
|
|
||||||
}).thenComposeAsync(m -> {
|
|
||||||
List<String> launcherPath = getLauncherPath();
|
List<String> launcherPath = getLauncherPath();
|
||||||
TargetObjectSchema schema = m.getRootSchema().getSuccessorSchema(launcherPath);
|
TargetObjectSchema schema = m.getRootSchema().getSuccessorSchema(launcherPath);
|
||||||
if (!schema.getInterfaces().contains(TargetLauncher.class)) {
|
if (!schema.getInterfaces().contains(TargetLauncher.class)) {
|
||||||
|
|||||||
+14
-1
@@ -85,13 +85,26 @@ public interface DebuggerProgramLaunchOffer {
|
|||||||
*/
|
*/
|
||||||
String getMenuTitle();
|
String getMenuTitle();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the text displayed if the user will not be prompted
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Sometimes when "the last options" are being used without prompting, it's a good idea to
|
||||||
|
* remind the user what those options were.
|
||||||
|
*
|
||||||
|
* @return the title
|
||||||
|
*/
|
||||||
|
default String getQuickTitle() {
|
||||||
|
return getMenuTitle();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the text displayed on buttons for this offer
|
* Get the text displayed on buttons for this offer
|
||||||
*
|
*
|
||||||
* @return the title
|
* @return the title
|
||||||
*/
|
*/
|
||||||
default String getButtonTitle() {
|
default String getButtonTitle() {
|
||||||
return getMenuParentTitle() + " " + getMenuTitle();
|
return getMenuParentTitle() + " " + getQuickTitle();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -266,9 +266,10 @@ public interface DebuggerModelService {
|
|||||||
* <p>
|
* <p>
|
||||||
* Assuming the target object is being actively traced, find the last focused object among those
|
* Assuming the target object is being actively traced, find the last focused object among those
|
||||||
* being traced by the same recorder. Essentially, given that the target likely belongs to a
|
* being traced by the same recorder. Essentially, given that the target likely belongs to a
|
||||||
* process, find the object within that process that last had focus. This is primarily used when
|
* process, find the object within that process that last had focus. This is primarily used
|
||||||
* switching focus between traces. Since the user has not explicitly selected a model object,
|
* wh@Override en switching focus between traces. Since the user has not explicitly selected a
|
||||||
* the UI should choose the one which had focus when the newly-activated trace was last active.
|
* model object, the UI should choose the one which had focus when the newly-activated trace was
|
||||||
|
* last active.
|
||||||
*
|
*
|
||||||
* @param target a source model object being actively traced
|
* @param target a source model object being actively traced
|
||||||
* @return the last focused object being traced by the same recorder
|
* @return the last focused object being traced by the same recorder
|
||||||
@@ -345,4 +346,21 @@ public interface DebuggerModelService {
|
|||||||
* @return the offers
|
* @return the offers
|
||||||
*/
|
*/
|
||||||
Stream<DebuggerProgramLaunchOffer> getProgramLaunchOffers(Program program);
|
Stream<DebuggerProgramLaunchOffer> getProgramLaunchOffers(Program program);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prompt the user to create a new connection
|
||||||
|
*
|
||||||
|
* @return a future which completes with the new connection, possibly cancelled
|
||||||
|
*/
|
||||||
|
default CompletableFuture<DebuggerObjectModel> showConnectDialog() {
|
||||||
|
return showConnectDialog(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prompt the user to create a new connection, optionally fixing the factory
|
||||||
|
*
|
||||||
|
* @param factory the required factory, or null for user selection
|
||||||
|
* @return a future which completes with the new connection, possible cancelled
|
||||||
|
*/
|
||||||
|
CompletableFuture<DebuggerObjectModel> showConnectDialog(DebuggerModelFactory factory);
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-2
@@ -21,8 +21,7 @@ import java.util.concurrent.CompletableFuture;
|
|||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import ghidra.app.plugin.core.debug.service.model.DebuggerModelServiceInternal;
|
import ghidra.app.plugin.core.debug.service.model.*;
|
||||||
import ghidra.app.plugin.core.debug.service.model.DebuggerModelServiceProxyPlugin;
|
|
||||||
import ghidra.dbg.DebuggerModelFactory;
|
import ghidra.dbg.DebuggerModelFactory;
|
||||||
import ghidra.dbg.DebuggerObjectModel;
|
import ghidra.dbg.DebuggerObjectModel;
|
||||||
import ghidra.dbg.agent.AbstractDebuggerObjectModel;
|
import ghidra.dbg.agent.AbstractDebuggerObjectModel;
|
||||||
|
|||||||
+2
-62
@@ -18,14 +18,9 @@ package ghidra.app.plugin.core.debug.gui.target;
|
|||||||
import static ghidra.app.plugin.core.debug.gui.target.DebuggerTargetsProviderFriend.selectNodeForObject;
|
import static ghidra.app.plugin.core.debug.gui.target.DebuggerTargetsProviderFriend.selectNodeForObject;
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
import java.awt.Component;
|
|
||||||
import java.awt.event.MouseEvent;
|
import java.awt.event.MouseEvent;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.CompletableFuture;
|
|
||||||
|
|
||||||
import javax.swing.JLabel;
|
|
||||||
import javax.swing.JTextField;
|
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
@@ -33,11 +28,8 @@ import org.junit.Test;
|
|||||||
import docking.widgets.tree.GTreeNode;
|
import docking.widgets.tree.GTreeNode;
|
||||||
import ghidra.app.plugin.core.debug.gui.AbstractGhidraHeadedDebuggerGUITest;
|
import ghidra.app.plugin.core.debug.gui.AbstractGhidraHeadedDebuggerGUITest;
|
||||||
import ghidra.app.plugin.core.debug.gui.DebuggerResources.*;
|
import ghidra.app.plugin.core.debug.gui.DebuggerResources.*;
|
||||||
import ghidra.app.plugin.core.debug.gui.target.DebuggerConnectDialog.FactoryEntry;
|
import ghidra.app.plugin.core.debug.service.model.DebuggerConnectDialog;
|
||||||
import ghidra.dbg.DebuggerObjectModel;
|
|
||||||
import ghidra.dbg.model.TestDebuggerModelFactory;
|
|
||||||
import ghidra.dbg.model.TestDebuggerObjectModel;
|
import ghidra.dbg.model.TestDebuggerObjectModel;
|
||||||
import ghidra.util.datastruct.CollectionChangeListener;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests of the target provider
|
* Tests of the target provider
|
||||||
@@ -53,68 +45,16 @@ public class DebuggerTargetsProviderTest extends AbstractGhidraHeadedDebuggerGUI
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testConnectDialogPopulates() {
|
public void testConnectActionShowDialog() {
|
||||||
modelServiceInternal.setModelFactories(List.of(mb.testFactory));
|
modelServiceInternal.setModelFactories(List.of(mb.testFactory));
|
||||||
waitForSwing();
|
waitForSwing();
|
||||||
|
|
||||||
performAction(targetsProvider.actionConnect, false);
|
performAction(targetsProvider.actionConnect, false);
|
||||||
DebuggerConnectDialog dialog = waitForDialogComponent(DebuggerConnectDialog.class);
|
DebuggerConnectDialog dialog = waitForDialogComponent(DebuggerConnectDialog.class);
|
||||||
|
|
||||||
FactoryEntry fe = (FactoryEntry) dialog.dropdownModel.getSelectedItem();
|
|
||||||
assertEquals(mb.testFactory, fe.factory);
|
|
||||||
|
|
||||||
assertEquals(TestDebuggerModelFactory.FAKE_DETAILS_HTML, dialog.description.getText());
|
|
||||||
|
|
||||||
Component[] components = dialog.pairPanel.getComponents();
|
|
||||||
|
|
||||||
assertTrue(components[0] instanceof JLabel);
|
|
||||||
JLabel label = (JLabel) components[0];
|
|
||||||
assertEquals(TestDebuggerModelFactory.FAKE_OPTION_NAME, label.getText());
|
|
||||||
|
|
||||||
assertTrue(components[1] instanceof JTextField);
|
|
||||||
JTextField field = (JTextField) components[1];
|
|
||||||
assertEquals(TestDebuggerModelFactory.FAKE_DEFAULT, field.getText());
|
|
||||||
|
|
||||||
pressButtonByText(dialog, "Cancel", true);
|
pressButtonByText(dialog, "Cancel", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testConnectDialogConnectsAndRegistersModelWithService() {
|
|
||||||
modelServiceInternal.setModelFactories(List.of(mb.testFactory));
|
|
||||||
|
|
||||||
CompletableFuture<DebuggerObjectModel> futureModel = new CompletableFuture<>();
|
|
||||||
CollectionChangeListener<DebuggerObjectModel> listener =
|
|
||||||
new CollectionChangeListener<DebuggerObjectModel>() {
|
|
||||||
@Override
|
|
||||||
public void elementAdded(DebuggerObjectModel element) {
|
|
||||||
futureModel.complete(element);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void elementModified(DebuggerObjectModel element) {
|
|
||||||
// Don't care
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void elementRemoved(DebuggerObjectModel element) {
|
|
||||||
fail();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
modelService.addModelsChangedListener(listener);
|
|
||||||
performAction(targetsProvider.actionConnect, false);
|
|
||||||
|
|
||||||
DebuggerConnectDialog connectDialog = waitForDialogComponent(DebuggerConnectDialog.class);
|
|
||||||
|
|
||||||
FactoryEntry fe = (FactoryEntry) connectDialog.dropdownModel.getSelectedItem();
|
|
||||||
assertEquals(mb.testFactory, fe.factory);
|
|
||||||
|
|
||||||
pressButtonByText(connectDialog, AbstractConnectAction.NAME, true);
|
|
||||||
// NOTE: testModel is null. Don't use #createTestModel(), which adds to service
|
|
||||||
TestDebuggerObjectModel model = new TestDebuggerObjectModel();
|
|
||||||
mb.testFactory.pollBuild().complete(model);
|
|
||||||
assertEquals(model, futureModel.getNow(null));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testRegisteredModelsShowInTree() throws Exception {
|
public void testRegisteredModelsShowInTree() throws Exception {
|
||||||
createTestModel();
|
createTestModel();
|
||||||
|
|||||||
+72
@@ -17,16 +17,23 @@ package ghidra.app.plugin.core.debug.service.model;
|
|||||||
|
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
import java.awt.Component;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import javax.swing.JLabel;
|
||||||
|
import javax.swing.JTextField;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import generic.Unique;
|
import generic.Unique;
|
||||||
import ghidra.app.plugin.core.debug.event.ModelObjectFocusedPluginEvent;
|
import ghidra.app.plugin.core.debug.event.ModelObjectFocusedPluginEvent;
|
||||||
import ghidra.app.plugin.core.debug.gui.AbstractGhidraHeadedDebuggerGUITest;
|
import ghidra.app.plugin.core.debug.gui.AbstractGhidraHeadedDebuggerGUITest;
|
||||||
|
import ghidra.app.plugin.core.debug.gui.DebuggerResources.AbstractConnectAction;
|
||||||
|
import ghidra.app.plugin.core.debug.service.model.DebuggerConnectDialog.FactoryEntry;
|
||||||
import ghidra.app.plugin.core.debug.service.model.TestDebuggerProgramLaunchOpinion.TestDebuggerProgramLaunchOffer;
|
import ghidra.app.plugin.core.debug.service.model.TestDebuggerProgramLaunchOpinion.TestDebuggerProgramLaunchOffer;
|
||||||
import ghidra.app.plugin.core.debug.service.model.launch.DebuggerProgramLaunchOffer;
|
import ghidra.app.plugin.core.debug.service.model.launch.DebuggerProgramLaunchOffer;
|
||||||
import ghidra.app.services.TraceRecorder;
|
import ghidra.app.services.TraceRecorder;
|
||||||
@@ -34,9 +41,11 @@ import ghidra.async.AsyncPairingQueue;
|
|||||||
import ghidra.dbg.DebuggerModelFactory;
|
import ghidra.dbg.DebuggerModelFactory;
|
||||||
import ghidra.dbg.DebuggerObjectModel;
|
import ghidra.dbg.DebuggerObjectModel;
|
||||||
import ghidra.dbg.model.TestDebuggerModelFactory;
|
import ghidra.dbg.model.TestDebuggerModelFactory;
|
||||||
|
import ghidra.dbg.model.TestDebuggerObjectModel;
|
||||||
import ghidra.dbg.testutil.DebuggerModelTestUtils;
|
import ghidra.dbg.testutil.DebuggerModelTestUtils;
|
||||||
import ghidra.trace.model.Trace;
|
import ghidra.trace.model.Trace;
|
||||||
import ghidra.trace.model.thread.TraceThread;
|
import ghidra.trace.model.thread.TraceThread;
|
||||||
|
import ghidra.util.Swing;
|
||||||
import ghidra.util.SystemUtilities;
|
import ghidra.util.SystemUtilities;
|
||||||
import ghidra.util.datastruct.CollectionChangeListener;
|
import ghidra.util.datastruct.CollectionChangeListener;
|
||||||
import mockit.Mocked;
|
import mockit.Mocked;
|
||||||
@@ -485,4 +494,67 @@ public class DebuggerModelServiceTest extends AbstractGhidraHeadedDebuggerGUITes
|
|||||||
waitOn(mb.testModel.close());
|
waitOn(mb.testModel.close());
|
||||||
assertNull(modelService.getCurrentModel());
|
assertNull(modelService.getCurrentModel());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testConnectDialogPopulates() {
|
||||||
|
modelServiceInternal.setModelFactories(List.of(mb.testFactory));
|
||||||
|
waitForSwing();
|
||||||
|
|
||||||
|
Swing.runLater(() -> modelService.showConnectDialog());
|
||||||
|
DebuggerConnectDialog dialog = waitForDialogComponent(DebuggerConnectDialog.class);
|
||||||
|
|
||||||
|
FactoryEntry fe = (FactoryEntry) dialog.dropdownModel.getSelectedItem();
|
||||||
|
assertEquals(mb.testFactory, fe.factory);
|
||||||
|
|
||||||
|
assertEquals(TestDebuggerModelFactory.FAKE_DETAILS_HTML, dialog.description.getText());
|
||||||
|
|
||||||
|
Component[] components = dialog.pairPanel.getComponents();
|
||||||
|
|
||||||
|
assertTrue(components[0] instanceof JLabel);
|
||||||
|
JLabel label = (JLabel) components[0];
|
||||||
|
assertEquals(TestDebuggerModelFactory.FAKE_OPTION_NAME, label.getText());
|
||||||
|
|
||||||
|
assertTrue(components[1] instanceof JTextField);
|
||||||
|
JTextField field = (JTextField) components[1];
|
||||||
|
assertEquals(TestDebuggerModelFactory.FAKE_DEFAULT, field.getText());
|
||||||
|
|
||||||
|
pressButtonByText(dialog, "Cancel", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testConnectDialogConnectsAndRegistersModelWithService() throws Throwable {
|
||||||
|
modelServiceInternal.setModelFactories(List.of(mb.testFactory));
|
||||||
|
|
||||||
|
CompletableFuture<DebuggerObjectModel> futureModel = new CompletableFuture<>();
|
||||||
|
CollectionChangeListener<DebuggerObjectModel> listener =
|
||||||
|
new CollectionChangeListener<DebuggerObjectModel>() {
|
||||||
|
@Override
|
||||||
|
public void elementAdded(DebuggerObjectModel element) {
|
||||||
|
futureModel.complete(element);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void elementModified(DebuggerObjectModel element) {
|
||||||
|
// Don't care
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void elementRemoved(DebuggerObjectModel element) {
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
modelService.addModelsChangedListener(listener);
|
||||||
|
Swing.runLater(() -> modelService.showConnectDialog());
|
||||||
|
|
||||||
|
DebuggerConnectDialog connectDialog = waitForDialogComponent(DebuggerConnectDialog.class);
|
||||||
|
|
||||||
|
FactoryEntry fe = (FactoryEntry) connectDialog.dropdownModel.getSelectedItem();
|
||||||
|
assertEquals(mb.testFactory, fe.factory);
|
||||||
|
|
||||||
|
pressButtonByText(connectDialog, AbstractConnectAction.NAME, true);
|
||||||
|
// NOTE: testModel is null. Don't use #createTestModel(), which adds to service
|
||||||
|
TestDebuggerObjectModel model = new TestDebuggerObjectModel();
|
||||||
|
mb.testFactory.pollBuild().complete(model);
|
||||||
|
assertEquals(model, waitOn(futureModel));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user