/* MonkeyTalk - a cross-platform functional testing tool
Copyright (C) 2012 Gorilla Logic, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package com.gorillalogic.monkeyconsole.editors.utils;
import java.io.File;
import java.io.FileWriter;
import java.io.FilenameFilter;
import java.io.IOException;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jdt.internal.junit.model.JUnitModel;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.dialogs.IInputValidator;
import org.eclipse.jface.dialogs.InputDialog;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.SWT;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.editors.text.TextEditor;
import org.eclipse.ui.part.FileEditorInput;
import org.eclipse.ui.texteditor.AbstractDecoratedTextEditor;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.mortbay.log.Log;
import com.gorillalogic.monkeyconsole.ADBHelper;
import com.gorillalogic.monkeyconsole.componentview.ui.UIContainerView;
import com.gorillalogic.monkeyconsole.plugin.FoneMonkeyPlugin;
import com.gorillalogic.monkeyconsole.preferences.PreferenceConstants;
import com.gorillalogic.monkeyconsole.server.RecordServer;
import com.gorillalogic.monkeyconsole.tableview.MonkeyTalkTabularEditor;
import com.gorillalogic.monkeytalk.BuildStamp;
import com.gorillalogic.monkeytalk.Command;
import com.gorillalogic.monkeytalk.agents.AgentManager;
import com.gorillalogic.monkeytalk.agents.IAgent;
import com.gorillalogic.monkeytalk.automators.ActionFilter;
import com.gorillalogic.monkeytalk.automators.AutomatorConstants;
import com.gorillalogic.monkeytalk.processor.Globals;
import com.gorillalogic.monkeytalk.processor.PlaybackListener;
import com.gorillalogic.monkeytalk.processor.PlaybackResult;
import com.gorillalogic.monkeytalk.processor.PlaybackStatus;
import com.gorillalogic.monkeytalk.processor.Runner;
import com.gorillalogic.monkeytalk.processor.Scope;
import com.gorillalogic.monkeytalk.processor.ScriptProcessor;
import com.gorillalogic.monkeytalk.processor.SuiteListener;
import com.gorillalogic.monkeytalk.processor.report.Report;
import com.gorillalogic.monkeytalk.processor.report.detail.DetailReportHtml;
import com.gorillalogic.monkeytalk.processor.report.detail.ScriptReportHelper;
import com.gorillalogic.monkeytalk.sender.CommandSender;
import com.gorillalogic.monkeytalk.sender.Response;
import com.gorillalogic.monkeytalk.sender.Response.ResponseStatus;
import com.gorillalogic.monkeytalk.server.ServerConfig;
import com.gorillalogic.monkeytalk.utils.FileUtils;
public class MonkeyTalkController {
private static final int MAX_CONNECTION_RETRIES = 30;
private static final long CONNECTION_RETRY_DELAY = 1000;
public IPreferenceStore preferenceStore;
private ActionFilter recordFilter = new ActionFilter();
private MonkeyTalkTabularEditor tabularEditor;
private TextEditor textEditor;
private FoneMonkeyConsoleHelper fmch;
private boolean currentlyConnected = false;
private Timer timer;
private final int DEFAULT_CONNECTION_TIMEOUT = 5000;
private Runner runner;
private ScriptProcessor processor;
private CommandSender sender;
private long replayCommandStartTime;
Thread commandProcessorThread;
public final RecordServer recordServer;
private boolean replayON = false;
// final String LICENSE =
// "Copyright 2011 Gorilla Logic, Inc. - www.gorillalogic.com\n\nFoneMonkey is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.\n\nFoneMonkey is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.";
public MonkeyTalkController() {
preferenceStore = FoneMonkeyPlugin.getDefault().getPreferenceStore();
getRecordFilter().set(AutomatorConstants.TOUCH_DOWN, false);
getRecordFilter().set(AutomatorConstants.TOUCH_UP, false);
getRecordFilter().set(AutomatorConstants.TOUCH_MOVE, false);
RecordServer temp;
try {
temp = new RecordServer();
} catch (IOException e) {
Log.warn("could not create RecordServer: " + e.getMessage());
temp = null;
}
recordServer = temp;
}
public void stopRecordServer() {
if (recordServer != null) {
recordServer.stop();
}
}
public void startReplayAll() {
startReplayRange(0, tabularEditor.getCommands().size());
}
// public void playOnWeb(){
// com.gorillalogic.agents.html.SeliniumCommandProcessor ssss = new
// com.gorillalogic.agents.html.SeliniumCommandProcessor();
// for(Command c : tabularEditor.getCommands()){
// ssss.processACommand(c);
// }
// }
public void startReplayRange(final int from3, final int to3) {
final File scriptFile = ((FileEditorInput) tabularEditor.getEditorInput()).getPath()
.toFile();
Globals.clear();
processor = new ScriptProcessor(scriptFile.getParentFile(), getAgent(getConnectionType()));
processor.setGlobalThinktime(Integer.parseInt(preferenceStore
.getString(PreferenceConstants.P_THINKTIME)));
processor.setGlobalTimeout(Integer.parseInt(preferenceStore
.getString(PreferenceConstants.P_DEFAULTTIMEOUT)));
processor.setGlobalScreenshotOnError(FoneMonkeyPlugin.getDefault().getPreferenceStore()
.getBoolean(PreferenceConstants.P_TAKEERRORSCREENSHOTS));
processor.setTakeAfterScreenshot(preferenceStore
.getBoolean(PreferenceConstants.P_TAKEAFTERSCREENSHOTS));
processor.setTakeAfterMetrics(preferenceStore
.getBoolean(PreferenceConstants.P_TAKEAFTERSCREENSHOTS));
runner = new Runner(getAgent(getConnectionType()));
runner.setGlobalThinktime(Integer.parseInt(preferenceStore
.getString(PreferenceConstants.P_THINKTIME)));
runner.setGlobalTimeout(Integer.parseInt(preferenceStore
.getString(PreferenceConstants.P_DEFAULTTIMEOUT)));
runner.setGlobalScreenshotOnError(FoneMonkeyPlugin.getDefault().getPreferenceStore()
.getBoolean(PreferenceConstants.P_TAKEERRORSCREENSHOTS));
runner.setTakeAfterScreenshot(preferenceStore
.getBoolean(PreferenceConstants.P_TAKEAFTERSCREENSHOTS));
runner.setTakeAfterMetrics(preferenceStore
.getBoolean(PreferenceConstants.P_TAKEAFTERSCREENSHOTS));
final int from2 = from3 - tabularEditor.getBlankCommandOffset(from3);
final int to2 = to3 - tabularEditor.getBlankCommandOffset(to3);
tabularEditor.deleteBlankRows();
PlaybackListener listener = new PlaybackListener() {
@Override
public void onStart(final Scope scope) {
replayCommandStartTime = System.currentTimeMillis();
fmch.writeToConsole(scope.getCurrentCommand().toString());
MonkeyTalkUtils.runOnGUI(new Runnable() {
public void run() {
if (from2 != to2) {
if (scope.getFilename() == null) {
tabularEditor.setSelection(from2 + scope.getCurrentIndex() - 1);
}
}
}
}, tabularEditor.getSite().getShell().getDisplay());
}
@Override
public void onScriptStart(Scope scope) {
fmch.writeToConsole("Started Script Playback");
fmch.bringToFront();
setReplayON(true);
setPlaybackControlsState();
}
@Override
public void onScriptComplete(Scope scope, PlaybackResult r) {
String msg = "Completed Script Playback" + " - " + r.getStatus().toString();
if (r.getMessage() != null) {
msg = msg + " " + r.getMessage();
}
fmch.writeToConsole(msg);
setReplayON(false);
setPlaybackControlsState();
}
@Override
public void onComplete(final Scope scope, final Response response) {
// long elapsed = System.currentTimeMillis() -
// replayCommandStartTime;
if (response.getStatus() == ResponseStatus.FAILURE) {
MonkeyTalkUtils.runOnGUI(new Runnable() {
public void run() {
if (scope.getFilename() == null) {
tabularEditor.setSelection(from2 + scope.getCurrentIndex() - 1);
tabularEditor.markRowAsError(from2 + scope.getCurrentIndex() - 1);
}
fmch.writeToConsole("FAILURE: " + response.getMessage(), true);
}
}, tabularEditor.getSite().getShell().getDisplay());
} else if (response.getStatus() == ResponseStatus.ERROR) {
MonkeyTalkUtils.runOnGUI(new Runnable() {
public void run() {
if (scope.getFilename() == null) {
tabularEditor.setSelection(from2 + scope.getCurrentIndex() - 1);
tabularEditor.markRowAsError(from2 + scope.getCurrentIndex() - 1);
}
fmch.writeToConsole("ERROR: " + response.getMessage(), true);
}
}, tabularEditor.getSite().getShell().getDisplay());
} else {
String m = response.getStatus().toString();
String msg = response.getMessage();
if (msg != null && msg.length() > 0) {
m = m + " - " + msg;
fmch.writeToConsole(m);
}
}
}
@Override
public void onPrint(String message) {
fmch.writeToConsole(message, SWT.COLOR_DARK_BLUE);
}
};
processor.setPlaybackListener(listener);
runner.setScriptListener(listener);
commandProcessorThread = new Thread(new Runnable() {
public void run() {
if (tabularEditor.getCommands().size() >= to2
&& tabularEditor.getCommands().size() > 0) {
PlaybackResult result = null;
if (from2 == to2) {
// play just a single row
result = processor.runScript(tabularEditor.getCommands().get(from2),
new Scope());
} else if (from2 == 0 && to2 == tabularEditor.getCommands().size()) {
// play all!
result = runner.run(scriptFile, null);
} else if (from2 >= 0 && to2 >= 0) {
// play range of rows
result = processor.runScript(tabularEditor.getCommands()
.subList(from2, to2), new Scope());
}
writeDetailReport(result, new Scope("ide"));
}
}
});
commandProcessorThread.start();
}
public void startJScriptReplay() {
Job job = new Job("MonkeyTalk JSTest") {
protected IStatus run(final IProgressMonitor monitor) {
// monitor.worked(50);
File jsFile = ((FileEditorInput) jsEditor.getEditorInput()).getPath().toFile();
Runner runner = new Runner(getAgent(getConnectionType()));
runner.setGlobalThinktime(Integer.parseInt(preferenceStore
.getString(PreferenceConstants.P_THINKTIME)));
runner.setGlobalTimeout(Integer.parseInt(preferenceStore
.getString(PreferenceConstants.P_DEFAULTTIMEOUT)));
runner.setGlobalScreenshotOnError(FoneMonkeyPlugin.getDefault()
.getPreferenceStore()
.getBoolean(PreferenceConstants.P_TAKEERRORSCREENSHOTS));
runner.setTakeAfterScreenshot(preferenceStore
.getBoolean(PreferenceConstants.P_TAKEAFTERSCREENSHOTS));
runner.setTakeAfterMetrics(preferenceStore
.getBoolean(PreferenceConstants.P_TAKEAFTERSCREENSHOTS));
runner.setScriptListener(new PlaybackListener() {
@Override
public void onStart(Scope scope) {
monitor.subTask(scope.getCurrentCommand().getCommand());
fmch.writeToConsole(scope.getCurrentCommand().getCommand());
}
@Override
public void onScriptStart(Scope scope) {
}
@Override
public void onScriptComplete(Scope scope, PlaybackResult result) {
}
@Override
public void onComplete(Scope scope, Response response) {
fmch.writeToConsole(response.getStatus().toString(),
response.getStatus() == ResponseStatus.ERROR);
if (monitor.isCanceled()) {
throw new OperationCanceledException("Run cancelled");
}
}
@Override
public void onPrint(String message) {
fmch.writeToConsole(message, SWT.COLOR_DARK_BLUE);
}
});
PlaybackResult p = runner.run(jsFile, null);
try {
writeDetailReport(p, new Scope("ide"));
} catch (Exception e) {
e.printStackTrace();
}
return Status.OK_STATUS;
}
};
job.setUser(true);
job.schedule();
}
protected void writeDetailReport(PlaybackResult result, Scope scope) {
File reportdir = getReportDir();
if (reportdir == null) {
return;
}
if (result == null) {
result = new PlaybackResult(PlaybackStatus.ERROR, "no results returned", scope);
}
try {
// Save the xml detail report
saveXmlDetailReport(reportdir, result, scope);
// Save the hmtl detail report
saveHtmlDetailReport(reportdir, result, getDetailReportXml(result, scope, reportdir));
} catch (Exception e) {
System.out.println("error writing the detail report: " + e.getMessage());
e.printStackTrace();
}
// last, we refresh the workspace to pick everything up
try {
IWorkspace workspace = ResourcesPlugin.getWorkspace();
if (workspace != null && workspace.getRoot() != null) {
workspace.getRoot().refreshLocal(IResource.DEPTH_INFINITE, null);
} else {
System.err
.println("cannot refresh workspace after detail report creation: unable to obtain workspace root");
}
} catch (CoreException e) {
// Could not refresh workspace, no biggie
System.out.println("error refreshing workspace after detail report creation: "
+ e.getMessage());
}
}
protected void saveXmlDetailReport(File reportdir, PlaybackResult result, Scope scope)
throws Exception {
File detailReportFile = null;
detailReportFile = new File(reportdir, getXMLDetailReportFilename(result.getScope()
.getFilename()));
if (!reportdir.exists()) {
if (!reportdir.mkdirs()) {
System.err.println("error writing detail report to file '"
+ detailReportFile.getPath() + "': directory '" + reportdir.getPath()
+ "' does not exist and cannot be created");
return;
}
}
String report = getDetailReportXml(result, scope, reportdir);
FileWriter fw = null;
try {
fw = new FileWriter(detailReportFile);
fw.write(report);
} catch (IOException e) {
System.err.println("error writing detail report to file '" + detailReportFile.getPath()
+ "': " + e.getMessage());
} finally {
if (fw != null) {
try {
fw.close();
} catch (IOException e) {
}
}
}
}
protected void saveHtmlDetailReport(File reportdir, PlaybackResult result, String xmlReport)
throws Exception {
File detailReportFile = null;
if (result.getScope().getFilename() != null) {
detailReportFile = new File(reportdir, getHTMLDetailReportFilename(result.getScope()
.getFilename()));
} else {
detailReportFile = new File(reportdir, getHTMLDetailReportFilename(result.getScope()
.getFilename()));
result.setScope(new Scope("DETAIL"));
}
if (!reportdir.exists()) {
if (!reportdir.mkdirs()) {
System.err.println("error writing detail report to file '"
+ detailReportFile.getPath() + "': directory '" + reportdir.getPath()
+ "' does not exist and cannot be created");
return;
}
}
String detailHtml = getDetailReportHtml(result, xmlReport);
FileWriter fw = null;
try {
fw = new FileWriter(detailReportFile);
fw.write(detailHtml);
} catch (IOException e) {
System.err.println("error writing detail report to file '" + detailReportFile.getPath()
+ "': " + e.getMessage());
} finally {
if (fw != null) {
try {
fw.close();
} catch (IOException e) {
}
}
}
}
protected String getDetailReportXml(PlaybackResult result, Scope scope, File reportDir) {
String report = null;
try {
String agentVersion = "";
if (runner != null && runner.getAgent() != null) {
IAgent agent = runner.getAgent();
agentVersion = agent.getName() + " v" + agent.getAgentVersion();
}
report = new ScriptReportHelper().createDetailReport(result, scope, getProjectDir(),
reportDir, getRunnerVersion(), agentVersion).toXMLDocument();
} catch (Exception e) {
e.printStackTrace();
report = "<detail result=\"ERROR\"><msg><![CDATA[" + "REPORTING ERROR : "
+ e.getMessage() + "]]></msg></detail>";
}
return report;
}
protected String getDetailReportHtml(PlaybackResult result, String detailXml) {
String report = null;
try {
report = new DetailReportHtml().createDetailReportHtml(result, detailXml);
} catch (Exception ex) {
ex.printStackTrace();
report = "<html><head><title>ERROR</title></head>" + "<body><h1>REPORTING ERROR</h1>"
+ "<p>" + ex.getMessage() + "</p></body></html>";
}
return report;
}
protected String getRunnerVersion() {
String runner = "MonkeyTalk IDE"
+ " v"
+ BuildStamp.VERSION
+ (BuildStamp.BUILD_NUMBER != null && BuildStamp.BUILD_NUMBER.length() > 0 ? "_"
+ BuildStamp.BUILD_NUMBER : "") + " - " + BuildStamp.TIMESTAMP;
;
return runner;
}
// public static String DETAIL_REPORT_FILENAME="detail.xml";
// protected String getDetailReportFilename() {
// return DETAIL_REPORT_FILENAME;
// }
public static String getXMLDetailReportFilename(String filename) {
String name;
if (filename != null) {
name = "DETAIL-" + filename + ".xml";
} else {
name = "DETAIL.xml";
}
return name;
}
public static String getHTMLDetailReportFilename(String filename) {
String name;
if (filename != null) {
name = "DETAIL-" + filename + ".html";
} else {
name = "DETAIL.html";
}
return name;
}
/**
* Stop the replay
*/
public void stopReplay() {
try {
if (null != commandProcessorThread && commandProcessorThread.isAlive()) {
runner.abort();
processor.abort();
}
} finally {
this.setReplayON(false);
setPlaybackControlsState();
}
}
private File getReportDir() {
return new File(getProjectDir(), "reports");
}
private File getProjectDir() {
return ((FileEditorInput) tabularEditor.getEditorInput()).getPath().toFile()
.getParentFile();
}
public void startSuiteReplay() {
Job job = new Job("MonkeyTalk TestSuite") {
protected IStatus run(final IProgressMonitor monitor) {
monitor.worked(50);
final File f = getProjectDir();
final File reportdir = getReportDir();
if (!reportdir.exists()) {
reportdir.mkdir();
}
// System.out.println(f.getAbsolutePath());
Runner runner = new Runner(getAgent(getConnectionType()));
runner.setReportdir(reportdir);
runner.setGlobalThinktime(Integer.parseInt(preferenceStore
.getString(PreferenceConstants.P_THINKTIME)));
runner.setGlobalTimeout(Integer.parseInt(preferenceStore
.getString(PreferenceConstants.P_DEFAULTTIMEOUT)));
runner.setGlobalScreenshotOnError(FoneMonkeyPlugin.getDefault()
.getPreferenceStore()
.getBoolean(PreferenceConstants.P_TAKEERRORSCREENSHOTS));
runner.setTakeAfterScreenshot(preferenceStore
.getBoolean(PreferenceConstants.P_TAKEAFTERSCREENSHOTS));
runner.setTakeAfterMetrics(preferenceStore
.getBoolean(PreferenceConstants.P_TAKEAFTERSCREENSHOTS));
runner.setSuiteListener(new SuiteListener() {
@Override
public void onSuiteStart(int total) {
monitor.beginTask("Running TestSuite", total);
}
@Override
public void onSuiteComplete(PlaybackResult result, Report report) {
monitor.worked(1);
try {
FileUtils.writeFile(getReportFile(reportdir), report.toString());
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if (result != null)
updateJUnitView(getReportFile(reportdir), result.getMessage());
}
@Override
public void onTestStart(String name, int num, int total) {
monitor.setTaskName(name);
}
@Override
public void onTestComplete(PlaybackResult result, Report report) {
monitor.worked(1);
try {
FileUtils.writeFile(getReportFile(reportdir), report.toString());
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
updateJUnitView(getReportFile(reportdir), result.getMessage());
}
@Override
public void onRunStart(int total) {
}
@Override
public void onRunComplete(PlaybackResult result, Report report) {
}
});
runner.setScriptListener(new PlaybackListener() {
@Override
public void onStart(Scope scope) {
monitor.subTask(scope.getCurrentCommand().getCommand());
}
@Override
public void onScriptStart(Scope scope) {
}
@Override
public void onScriptComplete(Scope scope, PlaybackResult result) {
}
@Override
public void onComplete(Scope scope, Response response) {
if (monitor.isCanceled()) {
throw new OperationCanceledException("Run cancelled");
}
}
@Override
public void onPrint(String message) {
}
});
File suiteFile = ((FileEditorInput) tabularEditor.getEditorInput()).getPath()
.toFile();
String suiteName = suiteFile.getName();
final PlaybackResult p = runner.run(suiteFile, null);
updateJUnitView(f, p.getMessage());
writeDetailReport(p, new Scope(suiteName));
return Status.OK_STATUS;
}
public void updateJUnitView(final File reportfile, final String msg) {
MonkeyTalkUtils.runOnGUI(new Runnable() {
public void run() {
try {
((FileEditorInput) tabularEditor.getEditorInput()).getFile()
.getProject().refreshLocal(IResource.DEPTH_INFINITE, null);
IWorkbenchWindow window = PlatformUI.getWorkbench()
.getActiveWorkbenchWindow();
IWorkbenchPage page = window.getActivePage();
org.eclipse.jdt.internal.junit.ui.TestRunnerViewPart v = (org.eclipse.jdt.internal.junit.ui.TestRunnerViewPart) page
.showView("org.eclipse.jdt.junit.ResultView");
JUnitModel.importTestRunSession(reportfile);
if (msg != null && !msg.equalsIgnoreCase("null"))
fmch.writeToConsole(msg);
} catch (CoreException e) {
}
}
}, tabularEditor.getSite().getShell().getDisplay());
}
private File getReportFile(File reportdir) {
String fileName = ((FileEditorInput) tabularEditor.getEditorInput()).getPath()
.toFile().getName();
return new File(reportdir.getAbsolutePath() + "/TEST-"
+ fileName.substring(0, fileName.lastIndexOf(".")) + ".xml");
}
};
job.setUser(true);
job.schedule();
}
/**
* Clear all rows
*/
public void clear() {
// TODO add dialog
tabularEditor.clear();
textEditor.getDocumentProvider().getDocument(textEditor.getEditorInput())
.set(tabularEditor.getCommandsAsString());
}
public void addARow(final Command command, final boolean b) {
MonkeyTalkUtils.runOnGUI(new Runnable() {
public void run() {
tabularEditor.appendRow(command, b);
textEditor.getDocumentProvider().getDocument(textEditor.getEditorInput())
.set(tabularEditor.getCommandsAsString());
}
}, tabularEditor.getSite().getShell().getDisplay());
}
public ActionFilter getRecordFilter() {
return recordFilter;
}
public void toggleRecordFilter(String action) {
ActionFilter f = getRecordFilter();
f.set(action, !f.get(action));
}
private Action clearButton;
private Action componentTreeButton;
private Action stopbutton;
private Action playbutton;
private Action recordbutton;
private Action playOnCloudButton;
private String extention;
public void setContextualData(MonkeyTalkTabularEditor tabularEditor, TextEditor textEditor,
Action clearButton, Action componentTreeButton, Action playbutton, Action stopbutton,
Action recordbutton, Action playOnCloudButton) {
// Editor
fmch = new FoneMonkeyConsoleHelper(tabularEditor.getEditorSite());
this.textEditor = textEditor;
this.tabularEditor = tabularEditor;
// buttons
this.stopbutton = stopbutton;
this.playbutton = playbutton;
this.recordbutton = recordbutton;
this.clearButton = clearButton;
this.componentTreeButton = componentTreeButton;
this.playOnCloudButton = playOnCloudButton;
}
public void setPlaybackControlsState() {
try {
clearButton.setEnabled(true);
if (cloudIsConfigured()) {
playOnCloudButton.setEnabled(true);
}
if (extention.equalsIgnoreCase("mt")) {
if (isCurrentlyConnected()) {
componentTreeButton.setEnabled(true);
if (isRecordingON() || isReplayON()) {
stopbutton.setEnabled(true);
recordbutton.setEnabled(false);
playbutton.setEnabled(false);
} else {
stopbutton.setEnabled(false);
recordbutton.setEnabled(true);
playbutton.setEnabled(true);
}
} else {
componentTreeButton.setEnabled(false);
stopbutton.setEnabled(false);
recordbutton.setEnabled(false);
playbutton.setEnabled(false);
}
} else if (extention.equalsIgnoreCase("mts")) {
stopbutton.setEnabled(false);
recordbutton.setEnabled(false);
if (isCurrentlyConnected()) {
componentTreeButton.setEnabled(true);
playbutton.setEnabled(true);
} else {
componentTreeButton.setEnabled(false);
playbutton.setEnabled(false);
}
} else if (extention.equalsIgnoreCase("js")) {
stopbutton.setEnabled(false);
recordbutton.setEnabled(false);
playOnCloudButton.setEnabled(true);
clearButton.setEnabled(false);
if (isCurrentlyConnected()) {
componentTreeButton.setEnabled(true);
playbutton.setEnabled(true);
} else {
componentTreeButton.setEnabled(false);
playbutton.setEnabled(false);
}
} else {
componentTreeButton.setEnabled(false);
playbutton.setEnabled(false);
stopbutton.setEnabled(false);
recordbutton.setEnabled(false);
clearButton.setEnabled(false);
}
} catch (Exception e) {
e.printStackTrace();
}
}
public void resetbuttons(Action stopbutton, Action playbutton, Action recordbutton) {
this.stopbutton = stopbutton;
this.playbutton = playbutton;
this.recordbutton = recordbutton;
}
public void setExtention(String extention) {
this.extention = extention;
}
public boolean isCurrentlyConnected() {
return currentlyConnected;
}
public void setCurrentlyConnected(boolean c) {
currentlyConnected = c;
}
public void setHost(String host) {
preferenceStore.setValue(PreferenceConstants.C_HOST, host);
}
public void setCloudHost(String host) {
preferenceStore.setValue(PreferenceConstants.C_CLOUD_HOST, host);
}
private boolean cloudIsConfigured() {
return (preferenceStore.getString(PreferenceConstants.C_CLOUD_HOST) != null)
|| !(preferenceStore.getString(PreferenceConstants.C_CLOUD_HOST).isEmpty()) ? true
: false;
}
/**
* @return
*/
public ConnectionTypesEnum getConnectionType() {
String value = preferenceStore.getString(PreferenceConstants.C_CONNECTION_TYPE);
if (value.equalsIgnoreCase(ConnectionTypesEnum.EMULATOR.toString())) {
return ConnectionTypesEnum.EMULATOR;
} else if (value.equalsIgnoreCase(ConnectionTypesEnum.SIMULATOR.toString())) {
return ConnectionTypesEnum.SIMULATOR;
} else if (value.equalsIgnoreCase(ConnectionTypesEnum.NETWORKED_ANDROID.toString())) {
return ConnectionTypesEnum.NETWORKED_ANDROID;
} else if (value.equalsIgnoreCase(ConnectionTypesEnum.NETWORKED_IOS.toString())) {
return ConnectionTypesEnum.NETWORKED_IOS;
} else if (value.equalsIgnoreCase(ConnectionTypesEnum.FLEX.toString())) {
return ConnectionTypesEnum.FLEX;
} else if (value.equalsIgnoreCase(ConnectionTypesEnum.WEB.toString())) {
return ConnectionTypesEnum.WEB;
} else if (value.equalsIgnoreCase(ConnectionTypesEnum.CHROME.toString())) {
return ConnectionTypesEnum.CHROME;
} else if (value.equalsIgnoreCase(ConnectionTypesEnum.SAFARI.toString())) {
return ConnectionTypesEnum.SAFARI;
} else if (value.equalsIgnoreCase(ConnectionTypesEnum.IE.toString())) {
return ConnectionTypesEnum.IE;
} else if (value.equalsIgnoreCase(ConnectionTypesEnum.CLOUD_ANDROID.toString())) {
return ConnectionTypesEnum.CLOUD_ANDROID;
} else {
return ConnectionTypesEnum.NO_DEVICE;
}
}
/**
* This new code defaults the address to the Android loopback address, it might be more
* acceptable to use something else, but this is most convenient now. We will go through all the
* network devices and look for an IP that is not a loopback address. This could fail if the
* recording computer has more than one NIC and we choose the NIC that is not serving up
* monkeytalk. If we find this to be an issue it will be worthwhile to ping the record service
* at this point and be sure it is listening. We explicitly exclude VirtualBox or VMware
* interfaces since they were giving us problems on Windows 7. Lastly, we just return the first
* valid match.
*/
public String getIpAddress() {
// short circuit if we are connecting to iOS simulator -- they are
// always on localhost
ConnectionTypesEnum type = getConnectionType();
if (ConnectionTypesEnum.SIMULATOR == type) {
return "localhost";
}
List<String> rejectedHostAddresses = new ArrayList<String>();
try {
Enumeration<NetworkInterface> ifaces = NetworkInterface.getNetworkInterfaces();
while (ifaces.hasMoreElements()) {
NetworkInterface iface = ifaces.nextElement();
if (iface.isUp() && !iface.isLoopback() && !iface.isVirtual()
&& !iface.getDisplayName().startsWith("VirtualBox")
&& !iface.getDisplayName().startsWith("VMware")) {
Enumeration<InetAddress> addresses = iface.getInetAddresses();
while (addresses.hasMoreElements()) {
InetAddress addr = addresses.nextElement();
String hostAddress = addr.getHostAddress();
if (!hostAddress.equalsIgnoreCase("127.0.0.1")
&& !hostAddress.substring(0, 7).equalsIgnoreCase("169.254")
&& hostAddress.matches("\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}")
&& addr instanceof Inet4Address) {
return hostAddress; // return the first good match!
} else {
rejectedHostAddresses.add(hostAddress);
}
}
}
}
} catch (SocketException ex) {
ex.printStackTrace();
}
// Log.warn("could not find acceptable IP address for recording in: "
// + rejectedHostAddresses.toString() + ", will use default of "
// + ServerConfig.DEFAULT_RECORD_HOST);
return ServerConfig.DEFAULT_RECORD_HOST; // return 10.0.2.2 if no match
// found
}
private String getHost() {
return getHost(false);
}
public String getHost(boolean prompt) {
if (preferenceStore.getString(PreferenceConstants.C_HOST) == null || prompt) {
String host = enterText(
"Please enter IP or hostname of networked iOS or Android device",
preferenceStore.getString(PreferenceConstants.C_HOST) == null ? ""
: preferenceStore.getString(PreferenceConstants.C_HOST));
if (host != null)
preferenceStore.setValue(PreferenceConstants.C_HOST, host);
}
// System.out.println(preferenceStore.getString(PreferenceConstants.C_HOST));
return preferenceStore.getString(PreferenceConstants.C_HOST);
}
public String getCloudHost() {
return preferenceStore.getString(PreferenceConstants.C_CLOUD_HOST);
}
String siteToTest = null;
public String getWebsiteToTest(boolean prompt) {
if (siteToTest == null || siteToTest.trim().length() == 0 || prompt) {
String host = enterText("Please enter the url of the website to test",
siteToTest == null ? "" : siteToTest);
if (host != null)
siteToTest = host;
}
return siteToTest;
}
// Need to provide default via IAgent
private String getConnectedHost() {
if (getConnectionType() == ConnectionTypesEnum.EMULATOR) {
return "localhost";
}
if (getConnectionType() == ConnectionTypesEnum.SIMULATOR) {
return "localhost";
}
if (getConnectionType() == ConnectionTypesEnum.FLEX) {
return "localhost";
}
if (getConnectionType() == ConnectionTypesEnum.NO_DEVICE) {
return "nodevice";
}
if (getConnectionType() == ConnectionTypesEnum.WEB) {
// return getWebsiteToTest(false);
return "localhost";
}
if (getConnectionType() == ConnectionTypesEnum.CHROME) {
// return getWebsiteToTest(false);
return "localhost";
}
if (getConnectionType() == ConnectionTypesEnum.SAFARI) {
// return getWebsiteToTest(false);
return "localhost";
}
if (getConnectionType() == ConnectionTypesEnum.IE) {
// return getWebsiteToTest(false);
return "localhost";
}
return getHost();
}
class AndroidHomePathValidator implements IInputValidator {
public String isValid(String newText) {
return ADBHelper.validateAndroidSdkPath(newText);
}
}
String enterText(String msg, String value) {
InputDialog dlg = new InputDialog(tabularEditor.getEditorSite().getShell(), "", msg, value,
null);
if (dlg.open() == Window.OK)
return dlg.getValue();
return null;
}
private String getConnectionStatus() {
if (getConnectionType() == null) {
return "Please select a device";
}
String type = getConnectionType().humanReadableFormat;
// If we are connected to a network device, print out the IP address of the device.
if (getConnectionType() == ConnectionTypesEnum.NETWORKED_ANDROID
|| getConnectionType() == ConnectionTypesEnum.NETWORKED_IOS) {
type = getHost(false);
}
if (type != null && !type.contains("unknown")) {
return "Connected to Device: " + type;
}
return "Please select a connection type";
}
AbstractDecoratedTextEditor jsEditor;
public void setJSContextualData(AbstractDecoratedTextEditor jsEditor) {
this.jsEditor = jsEditor;
fmch = new FoneMonkeyConsoleHelper(jsEditor.getEditorSite());
}
ConnectionTypesEnum connectionType;
public void connect(ConnectionTypesEnum type) {
connectionType = type;
preferenceStore.setValue(PreferenceConstants.C_CONNECTION_TYPE, type.toString());
String message = _connect(type);
if (message != null) {
if (message.contains("device not found")) {
message = "Device could not be found!";
} else if (message.length() == 0) {
message = "unspecified";
}
fmch.writeToConsole(type.humanReadableFormat + " connection error: " + message, true);
} else {
setStatus(getConnectionStatus());
}
this.setPlaybackControlsState();
}
CommandSender commandSender;
private String _connect(ConnectionTypesEnum type) {
// we must validate ADB first (before getting the AndroidEmulator agent)
if (type == ConnectionTypesEnum.EMULATOR) {
String msg = ADBHelper.validate();
if (msg != null) {
return msg;
}
}
IAgent agent = getAgent(type);
File f = new File(((FileEditorInput) tabularEditor.getEditorInput()).getPath().toString());
f = f.getParentFile();
runner = new Runner(agent);
commandSender = runner.getAgent().getCommandSender();
this.getRecordServer().setCurrentAgent(agent);
doHeartbeat();
if (type != ConnectionTypesEnum.NO_DEVICE) {
if (waitForConnection(DEFAULT_CONNECTION_TIMEOUT) == false) {
return "connection to agent timed out";
}
}
return null;
}
private IAgent getAgent(ConnectionTypesEnum type) {
IAgent agent;
if (type == ConnectionTypesEnum.WEB) {
agent = AgentManager.getAgent("WebDriver");
} else if (type == ConnectionTypesEnum.CHROME) {
agent = AgentManager.getAgent("Chrome");
} else if (type == ConnectionTypesEnum.SAFARI) {
agent = AgentManager.getAgent("Safari");
} else if (type == ConnectionTypesEnum.IE) {
agent = AgentManager.getAgent("IE");
} else if (type == ConnectionTypesEnum.EMULATOR) {
agent = AgentManager.getAgent("AndroidEmulator");
agent.setProperty("adb", ADBHelper.getAdbPath());
} else if (connectionType == ConnectionTypesEnum.SIMULATOR
|| connectionType == ConnectionTypesEnum.NETWORKED_IOS) {
agent = AgentManager.getAgent("iOS", getConnectedHost());
} else if (connectionType == ConnectionTypesEnum.FLEX) {
agent = AgentManager.getAgent("Flex", getConnectedHost());
} else if (connectionType == ConnectionTypesEnum.CLOUD_ANDROID) {
agent = AgentManager.getAgent("CloudAndroid", getConnectedHost());
} else {
agent = AgentManager.getAgent("Android", getConnectedHost());
}
final IAgent agt = agent;
Job job = new Job("MonkeyTalk") {
protected IStatus run(final IProgressMonitor monitor) {
monitor.beginTask("Starting browser", IProgressMonitor.UNKNOWN);
agt.start();
return Status.OK_STATUS;
}
};
job.setUser(true);
job.schedule();
return agent;
}
private boolean waitForConnection(int timeout) {
long t = System.currentTimeMillis() + timeout;
while (System.currentTimeMillis() < t) {
if (this.isCurrentlyConnected()) {
return true;
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
return false;
}
}
return true;
}
public static File[] fileFinder(String dirName, final String extNamer) {
File dir = new File(dirName);
return dir.listFiles(new FilenameFilter() {
public boolean accept(File dir, String filename) {
return filename.endsWith(extNamer);
}
});
}
void doHeartbeat() {
// if (getConnectionType() == ConnectionTypesEnum.WEB) {
// currentlyConnected = true;
// MonkeyTalkUtils.runOnGUI(new Runnable() {
// public void run() {
// setPlaybackControlsState();
// }
// }, false, tabularEditor.getSite().getShell().getDisplay());
// return;
// }
final int[] attempts = { 0 };
if (timer != null) {
timer.cancel();
}
timer = new Timer();
timer.schedule(new TimerTask() {
private String prevPathToApp = "";
private void checkForNewApp(String os, String pathToApp) {
if (pathToApp != null && pathToApp.length() > 0) {
if (!pathToApp.equals(prevPathToApp)) {
prevPathToApp = pathToApp;
notifyNewAppConnect(os, pathToApp);
}
}
}
private void notifyNewAppConnect(String os, String pathToApp) {
String data = "username="
+ FoneMonkeyPlugin.getDefault().getPreferenceStore()
.getString(PreferenceConstants.P_CLOUDUSR);
data += ",os=" + os;
data += ",isDemoApp=" + MonkeyTalkUtils.isDemoApp(pathToApp);
CloudServices.logEventAsync("IDE_CONNECT_TO_NEW_APP", data);
}
public void run() {
boolean wasConnected = currentlyConnected;
JSONObject body = null;
// CommandSender cs = new CommandSender(getConnectedHost(),
// getPlaybackPort());
CommandSender cs = runner.getAgent().getCommandSender();
Response r = cs.ping(isRecordingON(), getIpAddress(),
ServerConfig.DEFAULT_RECORD_PORT);
if (r.getStatus() == ResponseStatus.OK) {
currentlyConnected = true;
String pathToApp;
try {
currentlyConnected = true;
body = r.getBodyAsJSON();
if (body != null && body.has("message")) {
Object msg = body.get("message");
if (msg instanceof JSONObject) {
String os = body.getJSONObject("message").getString("os");
if (os != null) {
if (os.equalsIgnoreCase("ios")) {
pathToApp = body.getJSONObject("message").getString(
"pathToApp");
File[] list = MonkeyTalkController.fileFinder(pathToApp,
".app");
if (list != null && list.length > 0) {
pathToApp = list[0].getAbsolutePath();
preferenceStore.setValue(PreferenceConstants.C_APPNAME,
pathToApp);
if (pathToApp != null && pathToApp.length() > 0) {
checkForNewApp(os, pathToApp);
}
}
}
}
} else {
try {
if (MonkeyTalkController.this.isRecordingON()) {
JSONArray list = body.getJSONArray("message");
for (int i = 0; i < list.length(); i++) {
JSONObject ob = list.getJSONObject(i);
getRecordServer().getRecordListener().onRecord(
new Command(ob), ob);
}
}
} catch (JSONException e) {
// if (!msg.contains("end of file")) {
System.out.println("Received invalid recording msg " + msg);
// }
}
}
}
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} else {
currentlyConnected = false;
if (attempts[0]++ == MAX_CONNECTION_RETRIES) {
timer.cancel();
}
try {
Thread.sleep(CONNECTION_RETRY_DELAY);
} catch (InterruptedException e) {
// OK
}
}
if (wasConnected != currentlyConnected) {
final JSONObject jsonBody = body;
MonkeyTalkUtils.runOnGUI(new Runnable() {
public void run() {
setPlaybackControlsState();
if (currentlyConnected) {
// we have just successfully connected
String msg = getConnectMessage(jsonBody);
if (msg != null) {
fmch.writeToConsole(msg, false);
} else {
fmch.writeToConsole(
"invalid response connecting to MonkeyTalk agent", true);
}
} else {
// we have just disconnected
fmch.writeToConsole("disconnected from MonkeyTalk agent", false);
}
}
private String getConnectMessage(JSONObject jsonBody) {
String msg = null;
if (jsonBody != null) {
try {
if (jsonBody.has("message")) {
JSONObject o = jsonBody.getJSONObject("message");
String os = null;
if (o.has("os")) {
os = o.getString("os");
}
String mtversion = null;
if (o.has("mtversion")) {
mtversion = o.getString("mtversion");
}
if (os != null) {
msg = "Connected to MonkeyTalk: " + os + " agent";
if (mtversion != null) {
msg += "(" + mtversion + ")";
}
}
}
} catch (JSONException e) {
System.err.println("error formatting connect message:");
e.printStackTrace();
}
}
return msg;
}
}, false, tabularEditor.getSite().getShell().getDisplay());
}
}
}, 0, // initial delay
1 * 2500); // subsequent rate
}
public void setStatus(String msg) {
setStatus(msg, false);
}
void setStatus(final String msg, final boolean isError) {
MonkeyTalkUtils.runOnGUI(new Runnable() {
public void run() {
fmch.writeToConsole(msg, isError);
}
}, tabularEditor.getSite().getShell().getDisplay());
}
/**
* Get the running RecordServer, or run a new server.
*
* @return the record server
*/
public RecordServer getRecordServer() {
return recordServer;
}
// Need to use IAgent
public int getPlaybackPort() {
ConnectionTypesEnum type = getConnectionType();
if (ConnectionTypesEnum.SIMULATOR == type || ConnectionTypesEnum.NETWORKED_IOS == type) {
return ServerConfig.DEFAULT_PLAYBACK_PORT_IOS;
}
if (ConnectionTypesEnum.FLEX == type) {
return ServerConfig.DEFAULT_PLAYBACK_PORT_FLEX;
}
if (ConnectionTypesEnum.WEB == type) {
return ServerConfig.DEFAULT_PLAYBACK_PORT_WEB;
}
if (ConnectionTypesEnum.CHROME == type) {
return ServerConfig.DEFAULT_PLAYBACK_PORT_WEB;
}
if (ConnectionTypesEnum.SAFARI == type) {
return ServerConfig.DEFAULT_PLAYBACK_PORT_HTML5;
}
if (ConnectionTypesEnum.IE == type) {
return ServerConfig.DEFAULT_PLAYBACK_PORT_WEB;
}
return ServerConfig.DEFAULT_PLAYBACK_PORT_ANDROID;
}
public void startRecording() {
RecordServer server = getRecordServer();
if (server != null) {
server.record(true, getConnectedHost(), getPlaybackPort(), getIpAddress(),
ServerConfig.DEFAULT_RECORD_PORT);
}
setPlaybackControlsState();
fmch.bringToFront();
}
public void stopRecording() {
try {
RecordServer server = getRecordServer();
if (server != null) {
server.record(false, getConnectedHost(), getPlaybackPort(), getIpAddress(),
ServerConfig.DEFAULT_RECORD_PORT);
}
} finally {
setPlaybackControlsState();
}
}
public boolean isRecordingON() {
if (null == getRecordServer()) {
return false;
}
return getRecordServer().isRecording();
}
public boolean isReplayON() {
return replayON;
}
private void setReplayON(boolean replayON) {
this.replayON = replayON;
}
public void fetchAndShowComponentTree() {
if (commandSender == null) {
return;
}
Response r = commandSender.dumpTree();
JSONObject jo = null;
try {
jo = new JSONObject(r.getBody());
} catch (JSONException e) {
// Unparsable JSON
e.printStackTrace();
}
try {
tabularEditor.getSite().getPage()
.showView("com.gorillalogic.monkeyconsole.componentview.ui.UIContainerView");
UIContainerView mbv = (UIContainerView) PlatformUI.getWorkbench()
.getActiveWorkbenchWindow().getActivePage()
.findView("com.gorillalogic.monkeyconsole.componentview.ui.UIContainerView");
mbv.setInput(jo);
} catch (JSONException e) {
System.out.println("DANGME: " + e.getMessage());
// e.printStackTrace();
} catch (PartInitException e1) {
System.out.println("HANGME: " + e1.getMessage());
// e1.printStackTrace();
}
}
public void connectToCloudHost(String host) {
if (!(ConnectionTypesEnum.CLOUD_ANDROID.equals(connectionType))) {
return;
}
this.stopReplay();
setCloudHost(host);
setHost(host);
connect(ConnectionTypesEnum.CLOUD_ANDROID);
}
}