diff options
author | Benoit Foucher <benoit@zeroc.com> | 2016-12-21 11:36:06 +0100 |
---|---|---|
committer | Benoit Foucher <benoit@zeroc.com> | 2016-12-21 11:36:06 +0100 |
commit | 973701c846a588d6e683b8b6b94a164dd41e92d6 (patch) | |
tree | 5b5415a35f9c42f10370632f5f79c706d9bc4a3a | |
parent | Add getEngineName & getEngineVersion methods to IceSSL C++ Plugin (diff) | |
download | ice-973701c846a588d6e683b8b6b94a164dd41e92d6.tar.bz2 ice-973701c846a588d6e683b8b6b94a164dd41e92d6.tar.xz ice-973701c846a588d6e683b8b6b94a164dd41e92d6.zip |
Supported for automated browser testing
-rw-r--r-- | js/bin/HttpServer.js | 40 | ||||
-rw-r--r-- | js/gulpfile.js | 2 | ||||
-rw-r--r-- | js/src/Ice/IncomingAsync.js | 2 | ||||
-rw-r--r-- | js/test/Common/ControllerI.js | 229 | ||||
-rw-r--r-- | js/test/Common/controller.html | 47 | ||||
-rw-r--r-- | scripts/Controller.ice | 49 | ||||
-rwxr-xr-x | scripts/Expect.py | 8 | ||||
-rw-r--r-- | scripts/Util.py | 274 | ||||
-rw-r--r-- | scripts/selenium/firefox/cert8.db | bin | 0 -> 344064 bytes | |||
-rw-r--r-- | scripts/tests/Ice/location.py | 11 |
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 © 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 Binary files differnew file mode 100644 index 00000000000..42d6415b042 --- /dev/null +++ b/scripts/selenium/firefox/cert8.db 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" : [] }) |