summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenoit Foucher <benoit@zeroc.com>2016-12-21 11:36:06 +0100
committerBenoit Foucher <benoit@zeroc.com>2016-12-21 11:36:06 +0100
commit973701c846a588d6e683b8b6b94a164dd41e92d6 (patch)
tree5b5415a35f9c42f10370632f5f79c706d9bc4a3a
parentAdd getEngineName & getEngineVersion methods to IceSSL C++ Plugin (diff)
downloadice-973701c846a588d6e683b8b6b94a164dd41e92d6.tar.bz2
ice-973701c846a588d6e683b8b6b94a164dd41e92d6.tar.xz
ice-973701c846a588d6e683b8b6b94a164dd41e92d6.zip
Supported for automated browser testing
-rw-r--r--js/bin/HttpServer.js40
-rw-r--r--js/gulpfile.js2
-rw-r--r--js/src/Ice/IncomingAsync.js2
-rw-r--r--js/test/Common/ControllerI.js229
-rw-r--r--js/test/Common/controller.html47
-rw-r--r--scripts/Controller.ice49
-rwxr-xr-xscripts/Expect.py8
-rw-r--r--scripts/Util.py274
-rw-r--r--scripts/selenium/firefox/cert8.dbbin0 -> 344064 bytes
-rw-r--r--scripts/tests/Ice/location.py11
10 files changed, 572 insertions, 90 deletions
diff --git a/js/bin/HttpServer.js b/js/bin/HttpServer.js
index 0f1cfef2ab5..400577fef5a 100644
--- a/js/bin/HttpServer.js
+++ b/js/bin/HttpServer.js
@@ -44,6 +44,7 @@ function Init()
var TestSuites = JSON.parse(TestData.TestSuites);
TestData.tests = Object.keys(TestSuites);
var template = hogan.compile(fs.readFileSync(path.join(__dirname, "..", "test", "Common", "index.html"), "utf8"));
+ var controller = hogan.compile(fs.readFileSync(path.join(__dirname, "..", "test", "Common", "controller.html"), "utf8"));
var libraryMaps = libraries.map(
function(f)
{
@@ -58,6 +59,7 @@ function Init()
HttpServer.prototype.processRequest = function(req, res)
{
var match = req.url.pathname.match("^\/test/(.*)/index\.html");
+ var matchController = req.url.pathname.match("^\/test/(.*)/controller\.html");
if(match)
{
var es5 = match[1].indexOf("es5/") !== -1;
@@ -163,13 +165,47 @@ function Init()
}
}
}
+ else if(matchController)
+ {
+ var es5 = matchController[1].indexOf("es5/") !== -1;
+ var m = es5 ? matchController[1].replace("es5/", "") : matchController[1];
+ var testpath = path.resolve(path.join(this._basePath, "test", matchController[1]))
+ var scripts = es5 ? [
+ "/lib/es5/Ice.js",
+ "/test/es5/Common/Controller.js",
+ "/test/es5/Common/ControllerI.js",
+ ] : [
+ "/lib/Ice.js",
+ "/test/Common/Controller.js",
+ "/test/Common/ControllerI.js",
+ ];
+ var testSuite = TestSuites[m];
+ if(testSuite)
+ {
+ scripts = scripts.concat(TestSuites[m].files)
+ }
+ else
+ {
+ scripts = scripts.concat(fs.readdirSync(testpath).filter(function(f) { return path.extname(f) === ".js"; }))
+ }
+ res.writeHead(200, {"Content-Type": "text/html"});
+ res.end(controller.render({ "scripts" : scripts }))
+ console.log("HTTP/200 (Ok) " + req.method + " " + req.url.pathname);
+ }
else
{
var iceLib = libraries.indexOf(req.url.pathname) !== -1;
var iceLibMap = libraryMaps.indexOf(req.url.pathname) !== -1;
- var basePath = (process.env.USE_BIN_DIST == "yes" && (iceLib || iceLibMap)) ?
- path.resolve(path.join(require.resolve("ice"), "..", "..")) : this._basePath;
+ var basePath;
+ if(process.env.USE_BIN_DIST == "yes" && (iceLib || iceLibMap))
+ {
+ basePath = path.resolve(path.join(require.resolve("ice"), "..", ".."));
+ }
+ else
+ {
+ basePath = this._basePath;
+ }
var filePath = path.resolve(path.join(basePath, req.url.pathname));
if(filePath.indexOf("es5/") !== -1 && path.extname(filePath) != "js")
diff --git a/js/gulpfile.js b/js/gulpfile.js
index 2beec425e0a..dcdc38bc788 100644
--- a/js/gulpfile.js
+++ b/js/gulpfile.js
@@ -141,7 +141,7 @@ gulp.task("common:slice", [],
gulp.task("common:slice-babel", ["common:slice"],
function(){
- return gulp.src(["test/Common/Controller.js"])
+ return gulp.src(["test/Common/Controller.js", "test/Common/ControllerI.js"])
.pipe(babel({compact: false}))
.pipe(gulp.dest("test/es5/Common"));
});
diff --git a/js/src/Ice/IncomingAsync.js b/js/src/Ice/IncomingAsync.js
index faa9cab178f..0df70501fe7 100644
--- a/js/src/Ice/IncomingAsync.js
+++ b/js/src/Ice/IncomingAsync.js
@@ -360,7 +360,7 @@ class IncomingAsync
this._os.writeInt(this._current.requestId);
this._os.writeByte(Protocol.replyUnknownException);
//this._os.writeString(ex.toString());
- this._os.writeString(ex.stack ? ex.stack : "");
+ this._os.writeString(ex.toString() + (ex.stack ? "\n" + ex.stack : ""));
this._connection.sendResponse(this._os, this._compress);
}
else
diff --git a/js/test/Common/ControllerI.js b/js/test/Common/ControllerI.js
new file mode 100644
index 00000000000..c459c1f3e94
--- /dev/null
+++ b/js/test/Common/ControllerI.js
@@ -0,0 +1,229 @@
+// **********************************************************************
+//
+// Copyright (c) 2003-2016 ZeroC, Inc. All rights reserved.
+//
+// This copy of Ice is licensed to you under the terms described in the
+// ICE_LICENSE file included in this distribution.
+//
+// **********************************************************************
+
+function isSafari()
+{
+ return /^((?!chrome).)*safari/i.test(navigator.userAgent);
+}
+
+function isChrome()
+{
+ //
+ // We need to check for Edge browser as it might include Chrome in its user agent.
+ //
+ return navigator.userAgent.indexOf("Edge/") === -1 &&
+ navigator.userAgent.indexOf("Chrome/") !== -1;
+}
+
+function isWorker()
+{
+ return typeof(WorkerGlobalScope) !== 'undefined' && this instanceof WorkerGlobalScope;
+}
+
+function isWindows()
+{
+ return navigator.userAgent.indexOf("Windows") != -1;
+}
+
+class ProcessI extends Test.Common.Process
+{
+ constructor(promise, output)
+ {
+ super();
+ this._promise = promise;
+ this._output = output;
+ }
+
+ waitReady(timeout, current)
+ {
+ }
+
+ waitSuccess(timeout, current)
+ {
+ return this._promise.then(function() { return 0; }, function(ex) { console.log(ex); return 1; });
+ }
+
+ terminate(current)
+ {
+ current.adapter.remove(current.id);
+ return this._output.get();
+ }
+};
+
+class ProcessControllerI extends Test.Common.ProcessController
+{
+ constructor(output, logger)
+ {
+ super();
+ this._output = output;
+ this._logger = logger;
+ }
+
+ start(testSuite, exe, args, current)
+ {
+ let promise;
+ let initData = new Ice.InitializationData();
+ initData.logger = this._logger;
+ initData.properties = Ice.createProperties(args);
+ if(exe === "ClientBidir")
+ {
+ promise = _testBidir(this._output, initData);
+ }
+ else
+ {
+ promise = _test(this._output, initData);
+ }
+ promise = promise.catch(ex => {
+ this._output.writeLine("unexpected exception while running test: " + ex.toString() + "\nstack = " + ex.stack);
+ throw ex;
+ });
+ return Test.Common.ProcessPrx.uncheckedCast(current.adapter.addWithUUID(new ProcessI(promise, this._output)));
+ }
+
+ getHost(protocol, ipv6, current)
+ {
+ return "127.0.0.1"
+ }
+};
+
+function runController(output)
+{
+ let out =
+ {
+ write: function(msg)
+ {
+ let text = output.val();
+ output.val((text === "") ? msg : (text + msg));
+ },
+ writeLine: function(msg)
+ {
+ out.write(msg + "\n");
+ output.scrollTop(output.get(0).scrollHeight);
+ },
+ get: function()
+ {
+ return output.val()
+ }
+ };
+
+ window.onerror = function(msg, url, line, column, err)
+ {
+ let e = msg + " at " + url + ":" + line + ":" + column;
+ if(err)
+ {
+ e += "\n" + err.stack;
+ }
+ out.writeLine(e);
+ return false;
+ };
+
+ //
+ // This logger is setup to work with Web Workers and normal scripts using
+ // the received out object. With some browser like Safari using console.log
+ // method doesn't work when running inside a web worker.
+ //
+ let logger =
+ {
+ print: function(message)
+ {
+ out.writeLine(message, false);
+ },
+ trace: function(category, message)
+ {
+ let s = [];
+ let d = new Date();
+ s.push("-- ");
+ s.push(this.timestamp());
+ s.push(' ');
+ s.push(this._prefix);
+ s.push(category);
+ s.push(": ");
+ s.push(message);
+ out.writeLine(s.join(""), true);
+ },
+ warning: function(message)
+ {
+ let s = [];
+ let d = new Date();
+ s.push("-! ");
+ s.push(this.timestamp());
+ s.push(' ');
+ s.push(this._prefix);
+ s.push("warning: ");
+ s.push(message);
+ out.writeLine(s.join(""), true);
+ },
+ error: function(message)
+ {
+ let s = [];
+ let d = new Date();
+ s.push("!! ");
+ s.push(this.timestamp());
+ s.push(' ');
+ s.push(this._prefix);
+ s.push("error: ");
+ s.push(message);
+ out.writeLine(s.join(""), true);
+ },
+ getPrefix: function()
+ {
+ return "";
+ },
+ cloneWithPrefix: function(prefix)
+ {
+ return Logger;
+ },
+ timestamp: function()
+ {
+ let d = new Date();
+ return d.toLocaleString("en-US", this._dateformat) + "." + d.getMilliseconds();
+ }
+ };
+
+ let uri = new URI(document.location.href)
+ let initData = new Ice.InitializationData();
+ let protocol = uri.protocol() === "http" ? "ws" : "wss";
+ let port = protocol == "ws" ? 15002 : 15003;
+ initData.logger = logger;
+
+ let registerProcessController = function(adapter, registry, processController) {
+ registry.setProcessController(Test.Common.ProcessControllerPrx.uncheckedCast(processController)).then(
+ () => {
+ let connection = registry.ice_getCachedConnection();
+ connection.setAdapter(adapter)
+ connection.setACM(5, ACMClose.CloseOff, ACMHeartbeat.HeartbeatAlways);
+ connection.setCloseCallback(connection => {
+ logger.print("connection with process controller registry closed");
+ });
+ },
+ ex => {
+ if(ex instanceof Ice.ConnectFailedException)
+ {
+ setTimeout(() => registerProcessController(adapter, registry, processController), 2000);
+ }
+ else
+ {
+ logger.error("unexpected exception while connecting to process controller registry:\n" + ex.toString())
+ }
+ });
+ };
+
+ let comm = Ice.initialize();
+ let str = "Util/ProcessControllerRegistry:" + protocol + " -h 127.0.0.1 -p " + port;
+ let registry = Test.Common.ProcessControllerRegistryPrx.uncheckedCast(comm.stringToProxy(str));
+ comm.createObjectAdapter("").then(adapter => {
+ let ident = new Ice.Identity("ProcessController", "Browser");
+ let processController = adapter.add(new ProcessControllerI(out, logger), ident);
+ adapter.activate();
+ registerProcessController(adapter, registry, processController);
+ }).catch(ex => {
+ logger.error("unexpected exception while creating controller:\n" + ex.toString());
+ comm.destroy();
+ });
+} \ No newline at end of file
diff --git a/js/test/Common/controller.html b/js/test/Common/controller.html
new file mode 100644
index 00000000000..2d2dc526431
--- /dev/null
+++ b/js/test/Common/controller.html
@@ -0,0 +1,47 @@
+<!doctype html>
+<html class="no-js" lang="en">
+ <head>
+ <meta charset="utf-8" />
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+ <title>Test Suite | Ice for JavaScript</title>
+ <link rel="icon" type="image/x-icon" href="/assets/favicon.ico"/>
+ <link rel="stylesheet" type="text/css" href="/assets/common.css"/>
+ <script src="/assets/common.min.js"></script>
+ {{#scripts}}
+ <script src="{{.}}"></script>
+ {{/scripts}}
+ <script>
+ $(document).foundation();
+ $(document).ready(function() {
+ $("#console").height(300);
+ runController($("#console"));
+ });
+ </script>
+ </head>
+ <body>
+ <div id="header">
+ <nav class="top-bar" data-topbar>
+ <ul class="title-area">
+ <li class="name">
+ <h1><a href="#">Ice for JavaScript</a></h1>
+ </li>
+ </ul>
+ </nav>
+ </div>
+ <section role="main" id="body">
+ <div class="row">
+ <div class="large-12 medium-12 columns">
+ <textarea id="console" class="disabled" height="300" readonly></textarea>
+ </div>
+ </div>
+ </section>
+ <div id="footer" class="show-for-medium-up">
+ <div class="logo">
+ <h4><strong>ZeroC</strong></h4>
+ </div>
+ <div class="copyright">
+ <h6>Copyright &copy; 2003-2016 ZeroC, Inc. All rights reserved.</h6>
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/scripts/Controller.ice b/scripts/Controller.ice
index c926ef6f579..433fb7284c3 100644
--- a/scripts/Controller.ice
+++ b/scripts/Controller.ice
@@ -13,21 +13,6 @@ module Test
module Common
{
-exception ProcessFailedException
-{
- string reason;
-};
-
-interface Process
-{
- void waitReady(int timeout)
- throws ProcessFailedException;
-
- int waitSuccess(int timeout);
-
- string terminate();
-};
-
sequence<bool> BoolSeq;
sequence<string> StringSeq;
@@ -75,6 +60,31 @@ interface TestCase
void destroy();
};
+interface Controller
+{
+ TestCase* runTestCase(string mapping, string testsuite, string testcase, string cross)
+ throws TestCaseNotExistException;
+
+ OptionOverrides getOptionOverrides();
+
+ StringSeq getTestSuites(string mapping);
+};
+
+exception ProcessFailedException
+{
+ string reason;
+};
+
+interface Process
+{
+ void waitReady(int timeout)
+ throws ProcessFailedException;
+
+ int waitSuccess(int timeout);
+
+ string terminate();
+};
+
interface ProcessController
{
Process* start(string testsuite, string exe, StringSeq args)
@@ -83,14 +93,9 @@ interface ProcessController
string getHost(string protocol, bool ipv6);
};
-interface Controller
+interface ProcessControllerRegistry
{
- TestCase* runTestCase(string mapping, string testsuite, string testcase, string cross)
- throws TestCaseNotExistException;
-
- OptionOverrides getOptionOverrides();
-
- StringSeq getTestSuites(string mapping);
+ void setProcessController(ProcessController* controller);
};
};
diff --git a/scripts/Expect.py b/scripts/Expect.py
index 2bca42a14af..bf73f3d8850 100755
--- a/scripts/Expect.py
+++ b/scripts/Expect.py
@@ -343,15 +343,15 @@ def cleanup():
except:
pass
processes.clear()
-atexit.register(cleanup)
+#atexit.register(cleanup)
def signal_handler(signal, frame):
cleanup()
sys.exit(0)
-if win32:
- signal.signal(signal.SIGINT, signal_handler)
-signal.signal(signal.SIGTERM, signal_handler)
+#if win32:
+# signal.signal(signal.SIGINT, signal_handler)
+#signal.signal(signal.SIGTERM, signal_handler)
class Expect (object):
def __init__(self, command, startReader = True, timeout=30, logfile=None, mapping = None, desc = None, cwd = None, env = None):
diff --git a/scripts/Util.py b/scripts/Util.py
index 17d928db096..f19a573798e 100644
--- a/scripts/Util.py
+++ b/scripts/Util.py
@@ -444,9 +444,9 @@ class Mapping:
return
supportedOptions = supportedOptions.copy()
- supportedOptions.update(testcase.getMapping().getOptions())
- supportedOptions.update(testcase.getTestSuite().getOptions())
- supportedOptions.update(testcase.getOptions())
+ supportedOptions.update(testcase.getMapping().getOptions(current))
+ supportedOptions.update(testcase.getTestSuite().getOptions(current))
+ supportedOptions.update(testcase.getOptions(current))
for o in self.parsedOptions:
# Remove options which were explicitly set
@@ -502,9 +502,10 @@ class Mapping:
return False
options = {}
- options.update(current.testcase.getMapping().getOptions())
- options.update(current.testcase.getTestSuite().getOptions())
- options.update(current.testcase.getOptions())
+ options.update(current.testcase.getMapping().getOptions(current))
+ options.update(current.testcase.getTestSuite().getOptions(current))
+ options.update(current.testcase.getOptions(current))
+
for (k, v) in options.items():
if not hasattr(self, k):
continue
@@ -518,9 +519,9 @@ class Mapping:
# Clone this configuration and make sure all the options are supported
#
options = {}
- options.update(current.testcase.getMapping().getOptions())
- options.update(current.testcase.getTestSuite().getOptions())
- options.update(current.testcase.getOptions())
+ options.update(current.testcase.getMapping().getOptions(current))
+ options.update(current.testcase.getTestSuite().getOptions(current))
+ options.update(current.testcase.getOptions(current))
clone = copy.copy(self)
for o in self.parsedOptions:
if o in options and getattr(self, o) not in options[o]:
@@ -535,7 +536,7 @@ class Mapping:
# options that are set on the JS configuration.
#
clone = copy.copy(self)
- for o in current.config.parsedOptions:
+ for o in current.config.parsedOptions + ["protocol"]:
if o not in ["buildConfig", "buildPlatform"]:
setattr(clone, o, getattr(current.config, o))
clone.parsedOptions = current.config.parsedOptions
@@ -717,6 +718,8 @@ class Mapping:
testcases.append(ClientAMDServerTestCase())
if checkClient("client") and len(testcases) == 0:
testcases.append(ClientTestCase())
+ if checkClient("clientBidir") and self.getServerMapping().hasSource("Ice/echo", "server"):
+ testcases.append(ClientEchoServerTestCase())
if checkClient("collocated"):
testcases.append(CollocatedTestCase())
if len(testcases) > 0:
@@ -826,7 +829,7 @@ class Mapping:
def getEnv(self, process, current):
return {}
- def getOptions(self):
+ def getOptions(self, current):
return {}
def getRunOrder(self):
@@ -1208,7 +1211,7 @@ class TestCase(Runnable):
server = self.mapping.getDefaultProcess(self.getServerType(), testsuite)
self.servers = [server] if server else []
- def getOptions(self):
+ def getOptions(self, current):
return self.options
def canRun(self, current):
@@ -1297,7 +1300,8 @@ class TestCase(Runnable):
def _startServerSide(self, current):
# Set the host to use for the server side
- current.push(self, current.driver.getProcessController(current).getHost(current))
+ current.push(self)
+ current.host = current.driver.getProcessController(current).getHost(current)
self.setupServerSide(current)
try:
self.startServerSide(current)
@@ -1410,6 +1414,17 @@ class ClientServerTestCase(ClientTestCase):
def getServerType(self):
return "server"
+class ClientEchoServerTestCase(ClientServerTestCase):
+
+ def __init__(self, name="client/echo server", *args, **kargs):
+ ClientServerTestCase.__init__(self, name, *args, **kargs)
+
+ def getServerTestCase(self, cross=None):
+ return Mapping.getByName("cpp").findTestSuite("Ice/echo").findTestCase("server")
+
+ def getClientType(self):
+ return "clientBidir"
+
class CollocatedTestCase(ClientTestCase):
def __init__(self, name="collocated", *args, **kargs):
@@ -1528,7 +1543,7 @@ class TestSuite:
def getId(self):
return self.id
- def getOptions(self):
+ def getOptions(self, current):
return self.options
def getPath(self):
@@ -1593,9 +1608,14 @@ class TestSuite:
class ProcessController:
+ def __init__(self, current):
+ pass
+
def start(self, process, current, args, props, envs, watchDog):
raise NotImplemented()
+ def destroy(self, driver):
+ pass
class LocalProcessController(ProcessController):
@@ -1679,8 +1699,28 @@ class RemoteProcessController(ProcessController):
if self.stdout and self.output:
print(self.output)
- def __init__(self):
+ def __init__(self, current, endpoints=None):
self.processControllerProxies = {}
+ self.cond = threading.Condition()
+ if endpoints:
+ comm = current.driver.getCommunicator()
+ import Test
+
+ class ProcessControllerRegistryI(Test.Common.ProcessControllerRegistry):
+
+ def __init__(self, remoteProcessController):
+ self.remoteProcessController = remoteProcessController
+
+ def setProcessController(self, proxy, current):
+ import Test
+ proxy = Test.Common.ProcessControllerPrx.uncheckedCast(current.con.createProxy(proxy.ice_getIdentity()))
+ self.remoteProcessController.setProcessController(proxy)
+
+ self.adapter = comm.createObjectAdapterWithEndpoints("Adapter", endpoints)
+ self.adapter.add(ProcessControllerRegistryI(self), comm.stringToIdentity("Util/ProcessControllerRegistry"))
+ self.adapter.activate()
+ else:
+ self.adapter = None
def __str__(self):
return "remote controller"
@@ -1689,27 +1729,73 @@ class RemoteProcessController(ProcessController):
return self.getController(current).getHost(current.config.protocol, current.config.ipv6)
def getController(self, current):
- proxy = self.getControllerProxy(current)
- if proxy in self.processControllerProxies:
- return self.processControllerProxies[proxy]
+ ident = self.getControllerIdentity(current)
+ if type(ident) == str:
+ ident = current.driver.getCommunicator().stringToIdentity(ident)
+
+ with self.cond:
+ if ident in self.processControllerProxies:
+ return self.processControllerProxies[ident]
comm = current.driver.getCommunicator()
+ import Ice
import Test
- self.processControllerProxies[proxy] = Test.Common.ProcessControllerPrx.uncheckedCast(comm.stringToProxy(proxy))
if current.driver.controllerApp:
- self.startControllerApp(current, self.processControllerProxies[proxy])
+ self.startControllerApp(current, ident)
+ # Use well-known proxy and IceDiscovery to discover the process controller object from the app.
+ proxy = Test.Common.ProcessControllerPrx.uncheckedCast(comm.stringToProxy(comm.identityToString(ident)))
try:
- self.processControllerProxies[proxy].ice_ping()
- except:
+ try:
+ proxy.ice_ping()
+ except Ice.NoEndpointException as ex:
+ with self.cond:
+ if not ident in self.processControllerProxies:
+ self.cond.wait(5)
+ if ident in self.processControllerProxies:
+ return self.processControllerProxies[ident]
+ raise
+ except Exception as ex:
+ print(ex)
raise RuntimeError("couldn't reach the remote controller `{0}'".format(proxy))
- return self.processControllerProxies[proxy]
- def startControllerApp(self, current, proxy):
+ with self.cond:
+ self.processControllerProxies[ident] = proxy
+ return self.processControllerProxies[ident]
+
+ def setProcessController(self, proxy):
+ with self.cond:
+ self.processControllerProxies[proxy.ice_getIdentity()] = proxy
+ conn = proxy.ice_getConnection()
+ if(hasattr(conn, "setCloseCallback")):
+ proxy.ice_getConnection().setCloseCallback(lambda conn : self.clearProcessController(proxy, conn))
+ else:
+ import Ice
+ class CallbackI(Ice.ConnectionCallback):
+ def __init__(self, registry):
+ self.registry = registry
+
+ def heartbeath(self, conn):
+ pass
+
+ def closed(self, conn):
+ self.registry.clearProcessController(proxy, conn)
+
+ proxy.ice_getConnection().setCallback(CallbackI(self))
+
+ self.cond.notifyAll()
+
+ def clearProcessController(self, proxy, conn):
+ with self.cond:
+ if proxy.ice_getIdentity() in self.processControllerProxies:
+ if conn == self.processControllerProxies[proxy.ice_getIdentity()].ice_getCachedConnection():
+ del self.processControllerProxies[proxy.ice_getIdentity()]
+
+ def startControllerApp(self, current, ident):
pass
- def stopControllerApp(self, proxy):
+ def stopControllerApp(self, ident):
pass
def start(self, process, current, args, props, envs, watchDog):
@@ -1722,12 +1808,19 @@ class RemoteProcessController(ProcessController):
args = ["--{0}={1}".format(k, val(v, quoteValue=False)) for k,v in props.items()] + [val(a) for a in args]
if current.driver.debug:
current.writeln("(executing `{0}/{1}' on `{2}' args = {3})".format(current.testsuite, exe, self, args))
- return RemoteProcessController.Process(exe, processController.start(str(current.testsuite), exe, args))
+ prx = processController.start(str(current.testsuite), exe, args)
+
+ # Create bi-dir proxy in case we're talking to a bi-bir process controller.
+ prx = processController.ice_getConnection().createProxy(prx.ice_getIdentity())
+ import Test
+ return RemoteProcessController.Process(exe, Test.Common.ProcessPrx.uncheckedCast(prx))
def destroy(self, driver):
if driver.controllerApp:
- for p in self.processControllerProxies.values():
- self.stopControllerApp(p)
+ for ident in self.processControllerProxies.keys():
+ self.stopControllerApp(ident)
+ if self.adapter:
+ self.adapter.destroy()
class iOSSimulatorProcessController(RemoteProcessController):
@@ -1736,14 +1829,14 @@ class iOSSimulatorProcessController(RemoteProcessController):
runtimeID = "com.apple.CoreSimulator.SimRuntime.iOS-10-2"
appPath = "ios/controller/build/Products"
- def __init__(self):
- RemoteProcessController.__init__(self)
+ def __init__(self, current):
+ RemoteProcessController.__init__(self, current)
self.simulatorID = None
def __str__(self):
return "iOS Simulator"
- def getControllerProxy(self, current):
+ def getControllerIdentity(self, current):
if isinstance(current.testcase.getMapping(), ObjCMapping):
if current.config.arc:
return "iPhoneSimulator/com.zeroc.ObjC-ARC-Test-Controller"
@@ -1756,7 +1849,7 @@ class iOSSimulatorProcessController(RemoteProcessController):
else:
return "iPhoneSimulator/com.zeroc.Cpp98-Test-Controller"
- def startControllerApp(self, current, proxy):
+ def startControllerApp(self, current, ident):
mapping = current.testcase.getMapping()
if isinstance(mapping, ObjCMapping):
appName = "Objective-C ARC Test Controller.app" if current.config.arc else "Objective-C Test Controller.app"
@@ -1787,12 +1880,12 @@ class iOSSimulatorProcessController(RemoteProcessController):
if not os.path.exists(path):
raise RuntimeError("couldn't find iOS simulator controller application, did you build it?")
run("xcrun simctl install \"{0}\" \"{1}\"".format(self.device, path))
- run("xcrun simctl launch \"{0}\" {1}".format(self.device, proxy.ice_getIdentity().name))
+ run("xcrun simctl launch \"{0}\" {1}".format(self.device, ident.name))
print("ok")
- def stopControllerApp(self, proxy):
+ def stopControllerApp(self, ident):
try:
- run("xcrun simctl uninstall \"{0}\" {1}".format(self.device, proxy.ice_getIdentity().name))
+ run("xcrun simctl uninstall \"{0}\" {1}".format(self.device, ident.name))
except:
pass
@@ -1815,13 +1908,13 @@ class iOSDeviceProcessController(RemoteProcessController):
appPath = "cpp/test/ios/controller/build/Products"
- def __init__(self):
- RemoteProcessController.__init__(self)
+ def __init__(self, current):
+ RemoteProcessController.__init__(self, current)
def __str__(self):
return "iOS Device"
- def getControllerProxy(self, current):
+ def getControllerIdentity(self, current):
if isinstance(current.testcase.getMapping(), ObjCMapping):
return "iPhoneOS/com.zeroc.ObjC-Test-Controller"
else:
@@ -1831,13 +1924,70 @@ class iOSDeviceProcessController(RemoteProcessController):
else:
return "iPhoneOS/com.zeroc.Cpp98-Test-Controller"
- def startControllerApp(self, current, proxy):
+ def startControllerApp(self, current, ident):
# TODO: use ios-deploy to deploy and run the application on an attached device?
pass
- def stopControllerApp(self, proxy):
+ def stopControllerApp(self, ident):
pass
+class BrowserProcessController(RemoteProcessController):
+
+ def __init__(self, current):
+ RemoteProcessController.__init__(self, current, "ws -h 127.0.0.1 -p 15002:wss -h 127.0.0.1 -p 15003")
+
+ from selenium import webdriver
+ if not hasattr(webdriver, current.config.browser):
+ raise RuntimeError("unknown browser `{0}'".format(current.config.browser))
+
+ if current.config.browser == "Firefox":
+ #
+ # We need to specify a profile for Firefox. This profile only provides the cert8.db which
+ # contains our Test CA cert. It should be possible to avoid this by setting the webdriver
+ # acceptInsecureCerts capability but it's only supported by latest Firefox releases.
+ #
+ # capabilities = webdriver.DesiredCapabilities.FIREFOX.copy()
+ # capabilities["marionette"] = True
+ # capabilities["acceptInsecureCerts"] = True
+ # capabilities["moz:firefoxOptions"] = {}
+ # capabilities["moz:firefoxOptions"]["binary"] = "/Applications/FirefoxNightly.app/Contents/MacOS/firefox-bin"
+ profile = webdriver.FirefoxProfile(os.path.join(toplevel, "scripts", "selenium", "firefox"))
+ self.driver = webdriver.Firefox(firefox_profile=profile)
+ else:
+ self.driver = getattr(webdriver, current.config.browser)()
+
+
+ cmd = "node -e \"require('./bin/HttpServer')()\"";
+ cwd = current.testsuite.getMapping().getPath()
+ self.httpServer = Expect.Expect(cmd, cwd=cwd)
+
+ def __str__(self):
+ return str(self.driver)
+
+ def getControllerIdentity(self, current):
+
+ #
+ # Load the controller page each time we're asked for the controller, the controller page
+ # will connect to the process controller registry to register itself with this script.
+ #
+ testsuite = ("es5/" if current.config.es5 else "") + str(current.testsuite)
+ protocol = "https" if current.config.protocol == "wss" else "http"
+ port = 9090 if current.config.protocol == "wss" else 8080
+ self.driver.get("{0}://127.0.0.1:{1}/test/{2}/controller.html".format(protocol, port, testsuite))
+ self.currentTestSuite = current.testsuite
+
+ return "Browser/ProcessController"
+
+ def destroy(self, driver):
+ if self.httpServer:
+ self.httpServer.terminate()
+ self.httpServer = None
+
+ try:
+ self.driver.quit()
+ except:
+ pass
+
class Driver:
class Current:
@@ -1955,7 +2105,6 @@ class Driver:
self.communicator = None
self.interface = ""
- self.localProcessController = LocalProcessController()
self.processControllers = {}
def setConfigs(self, configs):
@@ -2041,25 +2190,22 @@ class Driver:
self.ctrlCHandler.setCallback(signal)
def getProcessController(self, current):
- if current.config.buildPlatform in self.processControllers:
- return self.processControllers[current.config.buildPlatform]
-
processController = None
if current.config.buildPlatform == "iphonesimulator":
- processController = iOSSimulatorProcessController()
+ processController = iOSSimulatorProcessController
elif current.config.buildPlatform == "iphoneos":
- processController = iOSDeviceProcessController()
-
- if processController:
- self.processControllers[current.config.buildPlatform] = processController
+ processController = iOSDeviceProcessController
+ elif isinstance(current.testcase.getMapping(), JavaScriptMapping) and current.config.browser:
+ processController = BrowserProcessController
else:
- #
- # Fallback on the local process controller if no specific
- # controller is needed for the given current
- #
- processController = self.localProcessController
+ processController = LocalProcessController
+
+ if processController in self.processControllers:
+ return self.processControllers[processController]
- return processController
+ # Instantiate the controller
+ self.processControllers[processController] = processController(current)
+ return self.processControllers[processController]
def getProcessProps(self, current, ready, readyCount):
props = {}
@@ -2466,18 +2612,22 @@ class JavaScriptMapping(Mapping):
@classmethod
def getOptions(self):
- return ("", ["es5"])
+ return ("", ["es5", "browser="])
@classmethod
def usage(self):
print("")
print("JavaScript mapping options:")
print("--es5 Use JavaScript ES5 (Babel compiled code).")
+ print("--browser=<name> Run with the given browser.")
def __init__(self, options=[]):
Mapping.Config.__init__(self, options)
self.es5 = False
+ self.browser = ""
parseOptions(self, options)
+ if self.browser and self.protocol == "tcp":
+ self.protocol = "ws"
def loadTestSuites(self, tests, config, filters, rfilters):
Mapping.loadTestSuites(self, tests, config, filters, rfilters)
@@ -2493,7 +2643,7 @@ class JavaScriptMapping(Mapping):
return "node {0}/test/Common/run.js {1}".format(self.path, exe)
def getDefaultSource(self, processType):
- return { "client" : "Client.js" }[processType]
+ return { "client" : "Client.js", "clientBidir" : "ClientBidir.js" }[processType]
def getDefaultExe(self, processType, config=None):
return self.getDefaultSource(processType).replace(".js", "")
@@ -2503,6 +2653,9 @@ class JavaScriptMapping(Mapping):
env["NODE_PATH"] = self.getTestCwd(process, current)
return env
+ def getSSLProps(self, process, current):
+ return {}
+
def getTestCwd(self, process, current):
if current.config.es5:
# Change to the ES5 test directory if testing ES5
@@ -2515,9 +2668,10 @@ class JavaScriptMapping(Mapping):
return # Ignore es5 directories
return Mapping.computeTestCases(self, testId, files)
- def getOptions(self):
- # JavaScript with NodeJS only supports tcp and no other options
- return { "protocol" : ["tcp"], "compress" : [False], "ipv6" : [False], "serialize" : [False], "mx" : [False] }
+ def getOptions(self, current):
+ # JavaScript with NodeJS only supports tcp and no other options, Browsers only support WS and WSS
+ protocols = ["ws", "wss"] if current.config.browser else ["tcp"]
+ return { "protocol" : protocols, "compress" : [False], "ipv6" : [False], "serialize" : [False], "mx" : [False] }
from Glacier2Util import *
from IceBoxUtil import *
diff --git a/scripts/selenium/firefox/cert8.db b/scripts/selenium/firefox/cert8.db
new file mode 100644
index 00000000000..42d6415b042
--- /dev/null
+++ b/scripts/selenium/firefox/cert8.db
Binary files differ
diff --git a/scripts/tests/Ice/location.py b/scripts/tests/Ice/location.py
new file mode 100644
index 00000000000..7d990532bc6
--- /dev/null
+++ b/scripts/tests/Ice/location.py
@@ -0,0 +1,11 @@
+# -*- coding: utf-8 -*-
+# **********************************************************************
+#
+# Copyright (c) 2003-2016 ZeroC, Inc. All rights reserved.
+#
+# This copy of Ice is licensed to you under the terms described in the
+# ICE_LICENSE file included in this distribution.
+#
+# **********************************************************************
+
+TestSuite(__name__, options = { "browser" : [] })