# ********************************************************************** # # 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. # # ********************************************************************** import os, sys, runpy, getopt, traceback, types, threading, time, datetime, re, itertools, random, subprocess, shutil, copy isPython2 = sys.version_info[0] == 2 if isPython2: import Queue as queue from StringIO import StringIO else: import queue from io import StringIO from collections import OrderedDict import Expect toplevel = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) def run(cmd, cwd=None, err=False): p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, cwd=cwd) out = p.stdout.read().decode('UTF-8').strip() if(not err and p.wait() != 0) or (err and p.wait() == 0) : raise RuntimeError(cmd + " failed:\n" + out) return out def val(v, escapeQuotes=False, quoteValue=True): if type(v) == bool: return "1" if v else "0" elif type(v) == str: if not quoteValue or v.find(" ") < 0: return v elif escapeQuotes: return "\\\"{0}\\\"".format(v.replace("\\\"", "\\\\\\\"")) else: return "\"{0}\"".format(v) else: return str(v) def getIceSoVersion(): config = open(os.path.join(toplevel, "cpp", "include", "IceUtil", "Config.h"), "r") intVersion = int(re.search("ICE_INT_VERSION ([0-9]*)", config.read()).group(1)) majorVersion = int(intVersion / 10000) minorVersion = int(intVersion / 100) - 100 * majorVersion patchVersion = intVersion % 100 if patchVersion < 50: return '%d' % (majorVersion * 10 + minorVersion) elif patchVersion < 60: return '%da%d' % (majorVersion * 10 + minorVersion, patchVersion - 50) else: return '%db%d' % (majorVersion * 10 + minorVersion, patchVersion - 60) class Platform: def __init__(self): self.parseBuildVariables({ "supported-platforms" : ("supportedPlatforms", lambda s : s.split(" ")), "supported-configs" : ("supportedConfigs", lambda s : s.split(" ")) }) def parseBuildVariables(self, variables): # Run make to get the values of the given variables output = run('make print V="{0}"'.format(" ".join(variables.keys())), cwd = toplevel) for l in output.split("\n"): match = re.match(r'^.*:.*: (.*) = (.*)', l) if match and match.group(1): if match.group(1) in variables: (varname, valuefn) = variables[match.group(1).strip()] value = match.group(2).strip() or "" setattr(self, varname, valuefn(value) if valuefn else value) def getFilters(self, config): return ([], []) def getDefaultBuildPlatform(self): return self.supportedPlatforms[0] def getDefaultBuildConfig(self): return self.supportedConfigs[0] def getBinSubDir(self, mapping, process, current): # Return the bin sub-directory for the given mapping,platform,config, # to be overriden by specializations return "bin" def getLibSubDir(self, mapping, process, current): # Return the bin sub-directory for the given mapping,platform,config, # to be overriden by specializations return "lib" def getBuildSubDir(self, name, current): # Return the build sub-directory, to be overriden by specializations return os.path.join("build", current.config.buildPlatform, current.config.buildConfig) def getLdPathEnvName(self): return "LD_LIBRARY_PATH" def getIceInstallDir(self, mapping, current): return os.environ.get("ICE_HOME", "/usr") def getSliceDir(self, iceDir): if iceDir.startswith("/usr"): return os.path.join(iceDir, "share", "ice", "slice") else: return os.path.join(iceDir, "slice") def getDefaultExe(self, name, config): if name == "icebox" and config.cpp11: name += "++11" return name def hasOpenSSL(self): # This is used by the IceSSL test suite to figure out how to setup certificates return False def canRun(self, mapping, current): return True class Darwin(Platform): def getFilters(self, config): if config.buildPlatform in ["iphoneos", "iphonesimulator"]: return (["Ice/.*", "IceSSL/configuration"], ["Ice/background", "Ice/echo", "Ice/faultTolerance", "Ice/gc", "Ice/library", "Ice/logger", "Ice/properties", "Ice/plugin", "Ice/stringConverter", "Ice/threadPoolPriority", "Ice/udp"]) return Platform.getFilters(self, config) def getDefaultBuildPlatform(self): return "macosx" def getLdPathEnvName(self): return "DYLD_LIBRARY_PATH" def getIceInstallDir(self, mapping, current): return os.environ.get("ICE_HOME", "/usr/local") class AIX(Platform): def hasOpenSSL(self): return True class Linux(Platform): def __init__(self): Platform.__init__(self) self.parseBuildVariables({ "linux_id" : ("linuxId", None), "build-platform" : ("buildPlatform", None), "foreign-platforms" : ("foreignPlatforms", lambda s : s.split(" ") if s else []), }) self.multiArch = {} if self.linuxId in ["ubuntu", "debian"]: for p in [self.buildPlatform] + self.foreignPlatforms: self.multiArch[p] = run("dpkg-architecture -f -a{0} -qDEB_HOST_MULTIARCH 2> /dev/null".format(p)) def hasOpenSSL(self): return True def getBinSubDir(self, mapping, process, current): if self.linuxId in ["ubuntu", "debian"] and current.config.buildPlatform in self.foreignPlatforms: return os.path.join("bin", self.multiArch[current.config.buildPlatform]) return "bin" def getLibSubDir(self, mapping, process, current): # PHP module is always installed in the lib directory for the default build platform if isinstance(mapping, PhpMapping) and current.config.buildPlatform == self.getDefaultBuildPlatform(): return "lib" if self.linuxId in ["centos", "rhel", "fedora"]: return "lib64" if current.config.buildPlatform == "x64" else "lib" elif self.linuxId in ["ubuntu", "debian"]: return os.path.join("lib", self.multiArch[current.config.buildPlatform]) return "lib" def getBuildSubDir(self, name, current): if self.linuxId in ["ubuntu", "debian"]: return os.path.join("build", self.multiArch[current.config.buildPlatform], current.config.buildConfig) else: return os.path.join("build", current.config.buildPlatform, current.config.buildConfig) def getDefaultExe(self, name, config): if name == "icebox": if self.linuxId in ["centos", "rhel", "fedora"] and config.buildPlatform == "x86": name += "32" # Multilib platform if config.cpp11: name += "++11" return name def canRun(self, mapping, current): if self.linuxId in ["centos", "rhel", "fedora"] and current.config.buildPlatform == "x86": # # Don't test Glacier2/IceStorm/IceGrid services with multilib platforms. We only # build services for the native platform. # parent = re.match(r'^([\w]*).*', current.testcase.getTestSuite().getId()).group(1) if parent in ["Glacier2", "IceStorm", "IceGrid"]: return False return True class Windows(Platform): def getFilters(self, config): if config.uwp: return (["Ice/.*", "IceSSL/configuration"], ["Ice/background", # # TODO: Test scripts are killing the Ice/binding test because it takes # too much time to run # "Ice/binding", "Ice/checksum", "Ice/custom", "Ice/defaultServant", "Ice/defaultValue", "Ice/faultTolerance", "Ice/gc", "Ice/interceptor", "Ice/library", "Ice/logger", "Ice/networkProxy", "Ice/properties", "Ice/plugin", "Ice/stringConverter", "Ice/servantLocator", "Ice/services", "Ice/slicing/exceptions", "Ice/slicing/objects", "Ice/threadPoolPriority", # TODO: Only run IceSSL/configuration with remote C++ server. "IceSSL/configuration"]) return Platform.getFilters(self, config) def parseBuildVariables(self, variables): pass # Nothing to do, we don't support the make build system on Windows def getDefaultBuildPlatform(self): return "Win32" def getDefaultBuildConfig(self): return "Debug" def getCompiler(self): out = run("cl") if out.find("Version 16.") != -1: return "v100" elif out.find("Version 17.") != -1: return "v110" elif out.find("Version 18.") != -1: return "v120" elif out.find("Version 19.") != -1: return "v140" def getBinSubDir(self, mapping, process, current): # # Platform/Config taget bin directories. # platform = current.config.buildPlatform config = "Debug" if current.config.buildConfig.find("Debug") >= 0 else "Release" if current.driver.useIceBinDist(mapping): iceHome = os.environ.get("ICE_HOME") v140 = self.getCompiler() == "v140" cpp = isinstance(mapping, CppMapping) csharp = isinstance(mapping, CSharpMapping) if iceHome and ((cpp and v140 and platform == "x64" and config == "Release") or (not csharp and not cpp)): return "bin" elif csharp or isinstance(process, SliceTranslator): return os.path.join("tools") else: # # With Windows binary distribution Glacier2 and IcePatch2 binaries are only included # for Release configuration. # binaries = [Glacier2Router, IcePatch2Calc, IcePatch2Client, IcePatch2Server] config = next(("Release" for p in binaries if isinstance(process, p)), config) return os.path.join("build", "native", "bin", platform, config) else: if isinstance(mapping, CppMapping): return os.path.join("bin", platform, config) elif isinstance(mapping, PhpMapping): return os.path.join("lib", platform, config) return "bin" def getLibSubDir(self, mapping, process, current): if isinstance(mapping, PhpMapping): return "php" if current.driver.useIceBinDist(mapping) else "lib" return self.getBinSubDir(mapping, process, current) def getBuildSubDir(self, name, current): if os.path.exists(os.path.join(current.testcase.getPath(), "msbuild", name)): return os.path.join("msbuild", name, current.config.buildPlatform, current.config.buildConfig) else: return os.path.join("msbuild", current.config.buildPlatform, current.config.buildConfig) def getLdPathEnvName(self): return "PATH" def getIceInstallDir(self, mapping, current): platform = current.config.buildPlatform config = "Debug" if current.config.buildConfig.find("Debug") >= 0 else "Release" with open(os.path.join(toplevel, "config", "icebuilder.props"), "r") as configFile: version = re.search("(.*)", configFile.read()).group(1) comp = self.getCompiler() if isinstance(mapping, CppMapping) else "net" iceHome = os.environ.get("ICE_HOME") v140 = self.getCompiler() == "v140" cpp = isinstance(mapping, CppMapping) csharp = isinstance(mapping, CSharpMapping) # # Use binary distribution from ICE_HOME if building for C++/VC140/x64/Release or # for another mapping than C++ or C#. # if iceHome and ((cpp and v140 and platform == "x64" and config == "Release") or (not csharp and not cpp)): return iceHome # # Otherwise, use the appropriate nuget package # return os.path.join(toplevel, mapping.name, "msbuild", "packages", "zeroc.ice.{0}.{1}".format(comp, version)) def canRun(self, mapping, current): # # On Windows, if testing with a binary distribution, don't test Glacier2/IceStorm services # with the Debug configurations since we don't provide binaries for them. # if current.driver.useIceBinDist(mapping): parent = re.match(r'^([\w]*).*', current.testcase.getTestSuite().getId()).group(1) if parent in ["Glacier2", "IceStorm"] and current.config.buildConfig.find("Debug") >= 0: return False return True platform = None if sys.platform == "darwin": platform = Darwin() elif sys.platform.startswith("aix"): platform = AIX() elif sys.platform.startswith("linux") or sys.platform.startswith("gnukfreebsd"): platform = Linux() elif sys.platform == "win32" or sys.platform[:6] == "cygwin": platform = Windows() if not platform: print("can't run on unknown platform `{0}'".format(sys.platform)) sys.exit(1) def parseOptions(obj, options, mapped={}): # Transform configuration options provided on the command line to # object data members. The data members must be already set on the # object and with the correct type. if not hasattr(obj, "parsedOptions"): obj.parsedOptions=[] remaining = [] for (o, a) in options: if o.startswith("--"): o = o[2:] if o.startswith("-"): o = o[1:] if o in mapped: o = mapped[o] if hasattr(obj, o): if isinstance(getattr(obj, o), bool): setattr(obj, o, a.lower() in ("yes", "true", "1") if a else True) elif isinstance(getattr(obj, o), list): l = getattr(obj, o) l.append(a) else: if not a and not isinstance(a, str): a = "0" setattr(obj, o, type(getattr(obj, o))(a)) if not o in obj.parsedOptions: obj.parsedOptions.append(o) else: remaining.append((o, a)) options[:] = remaining class Mapping: mappings = OrderedDict() class Config: # All option values for Ice/IceBox tests. coreOptions = { "protocol" : ["tcp", "ssl", "wss", "ws"], "compress" : [False, True], "ipv6" : [False, True], "serialize" : [False, True], "mx" : [False, True], } # All option values for IceGrid/IceStorm/Glacier2/IceDiscovery tests. serviceOptions = { "protocol" : ["tcp", "wss"], "compress" : [False, True], "ipv6" : [False, True], "serialize" : [False, True], "mx" : [False, True], } @classmethod def getOptions(self): return ("", ["config=", "platform=", "protocol=", "compress", "ipv6", "serialize", "mx", "cprops=", "sprops=", "uwp"]) @classmethod def usage(self): pass @classmethod def commonUsage(self): print("") print("Mapping options:") print("--protocol= Run with the given protocol.") print("--compress Run the tests with protocol compression.") print("--ipv6 Use IPv6 addresses.") print("--serialize Run with connection serialization.") print("--mx Run with metrics enabled.") print("--cprops= Specifies a list of additional client properties.") print("--sprops= Specifies a list of additional server properties.") print("--config= Build configuration for native executables.") print("--platform= Build platform for native executables.") print("--uwp Run UWP (Universal Windows Platform).") def __init__(self, options=[]): # Build configuration self.parsedOptions = [] self.buildConfig = os.environ.get("CONFIGS", "").split(" ")[0] if self.buildConfig: self.parsedOptions.append("buildConfig") else: self.buildConfig = platform.getDefaultBuildConfig() self.buildPlatform = os.environ.get("PLATFORMS", "").split(" ")[0] if self.buildPlatform: self.parsedOptions.append("buildPlatform") else: self.buildPlatform = platform.getDefaultBuildPlatform() self.protocol = "tcp" self.compress = False self.serialize = False self.ipv6 = False self.mx = False self.cprops = [] self.sprops = [] self.uwp = False parseOptions(self, options, { "config" : "buildConfig", "platform" : "buildPlatform", "uwp" : "uwp" }) def __str__(self): s = [] for o in self.parsedOptions: v = getattr(self, o) if v: s.append(o if type(v) == bool else str(v)) return ",".join(s) def getAll(self, current, testcase, rand=False): # # A generator to generate combinations of options (e.g.: tcp/compress/mx, ssl/ipv6/serialize, etc) # def gen(supportedOptions): if not supportedOptions: yield self return supportedOptions = supportedOptions.copy() 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 if o in supportedOptions: del supportedOptions[o] if len(supportedOptions) == 0: yield self return # Find the option with the longest list of values length = max([len(v) for v in supportedOptions.values()]) # Replace the values with a cycle iterator on the values for (k, v) in supportedOptions.items(): supportedOptions[k] = itertools.cycle(random.sample(v, len(v)) if rand else v) # Now, for the length of the longest array of values, we return # an array with the supported option combinations for i in range(0, length): options = [] for k, v in supportedOptions.items(): v = next(v) if v: if type(v) == bool: options.append(("--{0}".format(k), None)) else: options.append(("--{0}".format(k), v)) # Add parsed options for o in self.parsedOptions: v = getattr(self, o) if type(v) == bool: options.append(("--{0}".format(o), None)) elif type(v) == list: options += [("--{0}".format(o), e) for e in v] else: options.append(("--{0}".format(o), v)) yield self.__class__(options) options = None parent = re.match(r'^([\w]*).*', testcase.getTestSuite().getId()).group(1) if isinstance(testcase, ClientServerTestCase) and parent in ["Ice", "IceBox"]: options = current.driver.filterOptions(testcase, self.coreOptions) elif parent in ["IceGrid", "Glacier2", "IceStorm", "IceDiscovery"]: options = current.driver.filterOptions(testcase, self.serviceOptions) return [c for c in gen(options)] def canRun(self, current): if not platform.canRun(self, current): return False options = {} 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 if not getattr(self, k) in v: return False else: return True def cloneRunnable(self, current): # # Clone this configuration and make sure all the options are supported # options = {} 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]: setattr(clone, o, options[o][0] if len(options[o]) > 0 else None) return clone def cloneAndOverrideWith(self, current): # # Clone this configuration and override options with options from the given configuration # (the parent configuraton). This is usefull when running cross-testing. For example, JS # tests don't support all the options so we clone the C++ configuration and override the # options that are set on the JS configuration. # clone = copy.copy(self) 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 return clone def getArgs(self, process, current): return [] def getProps(self, process, current): props = {} if isinstance(process, IceProcess): props["Ice.Warn.Connections"] = True if self.protocol: props["Ice.Default.Protocol"] = self.protocol if self.compress: props["Ice.Override.Compress"] = "1" if self.serialize: props["Ice.ThreadPool.Server.Serialize"] = "1" props["Ice.IPv6"] = self.ipv6 if self.ipv6: props["Ice.PreferIPv6Address"] = True if self.mx: props["Ice.Admin.Endpoints"] = "default -h localhost" props["Ice.Admin.InstanceName"] = "Server" if isinstance(process, Server) else "Client" props["IceMX.Metrics.Debug.GroupBy"] ="id" props["IceMX.Metrics.Parent.GroupBy"] = "parent" props["IceMX.Metrics.All.GroupBy"] = "none" # Additional properties specified on the command line with --cprops or --sprops additionalProps = [] if self.cprops and isinstance(process, Client): additionalProps = self.cprops elif self.sprops and isinstance(process, Server): additionalProps = self.sprops for pps in additionalProps: for p in pps.split(" "): if p.find("=") > 0: (k , v) = p.split("=") props[k] = v else: props[p] = True return props @classmethod def getByName(self, name): if not name in self.mappings: raise RuntimeError("unknown mapping `{0}'".format(name)) return self.mappings.get(name) @classmethod def getByPath(self, path): path = os.path.abspath(path) for m in self.mappings.values(): if path.startswith(m.getPath() + os.sep): return m @classmethod def add(self, name, mapping): self.mappings[name] = mapping.init(name) @classmethod def getAll(self): languages = os.environ.get("LANGUAGES", None) return [self.getByName(l) for l in languages.split(" ")] if languages else list(self.mappings.values()) def __init__(self, path=None): self.platform = None self.name = None self.path = os.path.abspath(path) if path else None self.testsuites = {} def init(self, name): self.name = name if not self.path: self.path = os.path.join(toplevel, name) return self def __str__(self): return self.name def createConfig(self, options): return self.Config(options) def filterTestSuite(self, testId, config, filters=[], rfilters=[]): (pfilters, prfilters) = platform.getFilters(config) for includes in [filters, [re.compile(pf) for pf in pfilters]]: if len(includes) > 0: for f in includes: if f.search(self.name + "/" + testId): break else: return True for excludes in [rfilters, [re.compile(pf) for pf in prfilters]]: if len(excludes) > 0: for f in excludes: if f.search(self.name + "/" + testId): return True return False def loadTestSuites(self, tests, config, filters=[], rfilters=[]): for test in tests or [""]: for root, dirs, files in os.walk(os.path.join(self.getTestsPath(), test.replace('/', os.sep))): testId = root[len(self.getTestsPath()) + 1:] if os.sep != "/": testId = testId.replace(os.sep, "/") if self.filterTestSuite(testId, config, filters, rfilters): continue # # First check if there's a test.py file in the directory, if there's one use it. # if "test.py" in files: # # WORKAROUND for Python issue 15230 (fixed in 3.2) where run_path doesn't work correctly. # #runpy.run_path(os.path.join(root, "test.py")) origsyspath = sys.path sys.path = [root] + sys.path runpy.run_module("test", init_globals=globals(), run_name=root) origsyspath = sys.path continue # # If there's no test.py file in the test directory, we check if there's a common # script for the test in scripts/tests. If there's on we use it. # script = os.path.join(self.getCommonTestsPath(), testId + ".py") if os.path.isfile(script): runpy.run_module("tests." + testId.replace("/", "."), init_globals=globals(), run_name=root) continue # # Finally, we try to "discover/compute" the test by looking up for well-known # files. # testcases = self.computeTestCases(testId, files) if testcases: TestSuite(root, testcases) def getTestSuites(self, ids=[]): if not ids: return self.testsuites.values() return [self.testsuites[testSuiteId] for testSuiteId in ids if testSuiteId in self.testsuites] def addTestSuite(self, testsuite): assert len(testsuite.path) > len(self.getTestsPath()) + 1 testSuiteId = testsuite.path[len(self.getTestsPath()) + 1:].replace('\\', '/') self.testsuites[testSuiteId] = testsuite return testSuiteId def findTestSuite(self, testsuite): return self.testsuites.get(testsuite if isinstance(testsuite, str) else testsuite.id) def computeTestCases(self, testId, files): # Instantiate a new test suite if the directory contains well-known source files. def checkFile(f, m): try: # If given mapping is same as local mapping, just check the files set, otherwise check # with the mapping return (self.getDefaultSource(f) in files) if m == self else m.hasSource(testId, f) except KeyError: # Expected if the mapping doesn't support the process type (such as clientBidir) return False checkClient = lambda f: checkFile(f, self.getClientMapping()) checkServer = lambda f: checkFile(f, self.getServerMapping()) testcases = [] if checkClient("client") and checkServer("server"): testcases.append(ClientServerTestCase()) if checkClient("client") and checkServer("serveramd") and self.getServerMapping() == self: 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: return testcases def hasSource(self, testId, processType): try: return os.path.exists(os.path.join(self.getTestsPath(), testId, self.getDefaultSource(processType))) except KeyError: return False def getPath(self): return self.path def getTestsPath(self): return os.path.join(self.path, "test") def getCommonTestsPath(self): return os.path.join(self.path, "..", "scripts", "tests") def getTestCwd(self, process, current): return current.testcase.getPath() def getDefaultSource(self, processType): return processType def getDefaultProcess(self, processType, testsuite): # # If no server or client is explicitly set with a testcase, getDefaultProcess is called # to figure out which process class to instantiate. Based on the processType and the testsuite # we instantiate the right default process class. # if processType is None: return None elif testsuite.getId().startswith("IceUtil") or testsuite.getId().startswith("Slice"): return SimpleClient() elif testsuite.getId().startswith("IceGrid"): if processType in ["client", "collocated"]: return IceGridClient() if processType in ["server", "serveramd"]: return IceGridServer() else: return Server() if processType in ["server", "serveramd"] else Client() def getDefaultExe(self, processType, config): return processType def getClientMapping(self): # The client mapping is always the same as this mapping. return self def getServerMapping(self): # Can be overridden for client-only mapping that relies on another mapping for servers return self def getBinDir(self, process, current): return os.path.join(current.driver.getIceDir(self, current), platform.getBinSubDir(self, process, current)) def getLibDir(self, process, current): return os.path.join(current.driver.getIceDir(self, current), platform.getLibSubDir(self, process, current)) def getBuildDir(self, name, current): return platform.getBuildSubDir(name, current) def getCommandLine(self, current, process, exe): name = exe if isinstance(platform, Windows) and not exe.endswith(".exe"): exe += ".exe" if process.isFromBinDir(): # If it's a process from the bin directory, the location is platform specific # so we check with the platform. return os.path.join(self.getBinDir(process, current), exe) elif current.testcase: # If it's a process from a testcase, the binary is in the test build directory. return os.path.join(current.testcase.getPath(), current.getBuildDir(name), exe) else: return exe def getProps(self, process, current): props = {} if isinstance(process, IceProcess): if current.config.protocol in ["bt", "bts"]: props["Ice.Plugin.IceBT"] = self.getPluginEntryPoint("IceBT", process, current) if current.config.protocol in ["ssl", "wss", "bts", "iaps"]: props.update(self.getSSLProps(process, current)) return props def getSSLProps(self, process, current): sslProps = { "Ice.Plugin.IceSSL" : self.getPluginEntryPoint("IceSSL", process, current), "IceSSL.Password": "password", "IceSSL.DefaultDir": os.path.join(toplevel, "certs"), } # # If the client doesn't support client certificates, set IceSSL.VerifyPeer to 0 # if isinstance(process, Server): if isinstance(current.testsuite.getMapping(), JavaScriptMapping): sslProps["IceSSL.VerifyPeer"] = 0 return sslProps def getArgs(self, process, current): return [] def getEnv(self, process, current): return {} def getOptions(self, current): return {} def getRunOrder(self): return ["Slice", "IceUtil", "Ice", "IceSSL", "IceBox", "Glacier2", "IceGrid", "IceStorm"] def getCrossTestSuites(self): return [ "Ice/ami", "Ice/info", "Ice/exceptions", "Ice/enums", "Ice/facets", "Ice/inheritance", "Ice/invoke", "Ice/objects", "Ice/operations", "Ice/proxy", "Ice/servantLocator", "Ice/slicing/exceptions", "Ice/slicing/objects", "Ice/optional" ] # # A Runnable can be used as a "client" for in test cases, it provides # implements run, setup and teardown methods. # class Runnable: def __init__(self, desc=None): self.desc = desc def setup(self, current): ### Only called when ran from testcase pass def teardown(self, current, success): ### Only called when ran from testcase pass def run(self, current): pass # # A Process describes how to run an executable process. # class Process(Runnable): processType = None def __init__(self, exe=None, outfilters=None, quiet=False, args=None, props=None, envs=None, desc=None, mapping=None): Runnable.__init__(self, desc) self.exe = exe self.outfilters = outfilters or [] self.quiet = quiet self.args = args or [] self.props = props or {} self.envs = envs or {} self.mapping = mapping def __str__(self): if not self.exe: return str(self.__class__) return self.exe + (" ({0})".format(self.desc) if self.desc else "") def getOutput(self, current): assert(self in current.processes) def d(s): return s if isPython2 else s.decode("utf-8") if isinstance(s, bytes) else s output = d(current.processes[self].getOutput()) try: # Apply outfilters to the output if len(self.outfilters) > 0: lines = output.split('\n') newLines = [] previous = "" for line in [line + '\n' for line in lines]: for f in self.outfilters: if isinstance(f, types.LambdaType) or isinstance(f, types.FunctionType): line = f(line) elif f.search(line): break else: if line.endswith('\n'): if previous: newLines.append(previous + line) previous = "" else: newLines.append(line) else: previous += line output = "".join(newLines) output = output.strip() return output + '\n' if output else "" except Exception as ex: print("unexpected exception while filtering process output:\n" + str(ex)) raise def run(self, current, args=[], props={}, exitstatus=0, timeout=120): class WatchDog: def __init__(self, timeout): self.lastProgressTime = time.time() self.timeout = timeout self.lock = threading.Lock() def reset(self): with self.lock: self.lastProgressTime = time.time() def timedOut(self): with self.lock: return (time.time() - self.lastProgressTime) >= self.timeout watchDog = WatchDog(timeout) self.start(current, args, props, watchDog=watchDog) if not self.quiet and not current.driver.isWorkerThread(): # Print out the process output to stdout if we're running the client form the main thread. current.processes[self].trace(self.outfilters) try: while True: try: current.processes[self].waitSuccess(exitstatus=exitstatus, timeout=30) break except Expect.TIMEOUT: if watchDog and watchDog.timedOut(): raise finally: self.stop(current, True, exitstatus) def getEffectiveArgs(self, current, args): allArgs = [] allArgs += current.driver.getArgs(self, current) allArgs += current.config.getArgs(self, current) allArgs += self.getMapping(current).getArgs(self, current) allArgs += current.testcase.getArgs(self, current) allArgs += self.getArgs(current) allArgs += self.args(self, current) if callable(self.args) else self.args allArgs += args allArgs = [a.encode("utf-8") if type(a) == "unicode" else str(a) for a in allArgs] return allArgs def getEffectiveProps(self, current, props): allProps = {} allProps.update(current.driver.getProps(self, current)) allProps.update(current.config.getProps(self, current)) allProps.update(self.getMapping(current).getProps(self, current)) allProps.update(current.testcase.getProps(self, current)) allProps.update(self.getProps(current)) allProps.update(self.props(self, current) if callable(self.props) else self.props) allProps.update(props) return allProps def getEffectiveEnv(self, current): allEnvs = {} allEnvs.update(self.getMapping(current).getEnv(self, current)) allEnvs.update(current.testcase.getEnv(self, current)) allEnvs.update(self.getEnv(current)) allEnvs.update(self.envs(self, current) if callable(self.envs) else self.envs) return allEnvs def start(self, current, args=[], props={}, watchDog=None): allArgs = self.getEffectiveArgs(current, args) allProps = self.getEffectiveProps(current, props) allEnvs = self.getEffectiveEnv(current) processController = current.driver.getProcessController(current) current.processes[self] = processController.start(self, current, allArgs, allProps, allEnvs, watchDog) try: self.waitForStart(current) except: self.stop(current) raise def waitForStart(self, current): # To be overridden in specialization to wait for a token indicating the process readiness. pass def stop(self, current, waitSuccess=False, exitstatus=0): if self in current.processes: try: if waitSuccess: # Wait for the process to exit successfully by itself. current.processes[self].waitSuccess(exitstatus=exitstatus, timeout=60) finally: current.processes[self].terminate() if not self.quiet: # Write the output to the test case (but not on stdout) current.write(self.getOutput(current), stdout=False) def expect(self, current, pattern, timeout=60): assert(self in current.processes and isinstance(current.processes[self], Expect.Expect)) return current.processes[self].expect(pattern, timeout) def sendline(self, current, data): assert(self in current.processes and isinstance(current.processes[self], Expect.Expect)) return current.processes[self].sendline(data) def getMatch(self, current): assert(self in current.processes and isinstance(current.processes[self], Expect.Expect)) return current.processes[self].match def isStarted(self, current): return self in current.processes def isFromBinDir(self): return False def getArgs(self, current): return [] def getProps(self, current): return {} def getEnv(self, current): return {} def getMapping(self, current): return self.mapping or current.testcase.getMapping() def getExe(self, current): processType = self.processType or current.testcase.getProcessType(self) return self.exe or self.getMapping(current).getDefaultExe(processType, current.config) def getCommandLine(self, current): return self.getMapping(current).getCommandLine(current, self, self.getExe(current)) # # A simple client (used to run Slice/IceUtil clients for example) # class SimpleClient(Process): pass # # An IceProcess specialization class. This is used by drivers to figure out if # the process accepts Ice configuration properties. # class IceProcess(Process): pass # # An Ice server process. It's possible to configure when the server is considered # ready by setting readyCount or ready. The start method will only return once # the server is considered "ready". It can also be configure to wait (the default) # or not wait for shutdown when the stop method is invoked. # class Server(IceProcess): def __init__(self, exe=None, waitForShutdown=True, readyCount=1, ready=None, startTimeout=120, *args, **kargs): IceProcess.__init__(self, exe, *args, **kargs) self.waitForShutdown = waitForShutdown self.readyCount = readyCount self.ready = ready self.startTimeout = startTimeout def getProps(self, current): props = IceProcess.getProps(self, current) props.update({ "Ice.ThreadPool.Server.Size": 1, "Ice.ThreadPool.Server.SizeMax": 3, "Ice.ThreadPool.Server.SizeWarn": 0, }) props.update(current.driver.getProcessProps(current, self.ready, self.readyCount + (1 if current.config.mx else 0))) return props def waitForStart(self, current): # Wait for the process to be ready current.processes[self].waitReady(self.ready, self.readyCount + (1 if current.config.mx else 0), self.startTimeout) # Filter out remaining ready messages self.outfilters.append(re.compile("[^\n]+ ready")) # If we are not asked to be quiet and running from the main thread, print the server output if not self.quiet and not current.driver.isWorkerThread(): current.processes[self].trace(self.outfilters) def stop(self, current, waitSuccess=False, exitstatus=0): IceProcess.stop(self, current, waitSuccess and self.waitForShutdown, exitstatus) # # An Ice client process. # class Client(IceProcess): pass # # Executables for processes inheriting this marker class are looked up in the # Ice distribution bin directory. # class ProcessFromBinDir: def isFromBinDir(self): return True class SliceTranslator(ProcessFromBinDir, SimpleClient): def __init__(self, translator): SimpleClient.__init__(self, exe=translator, quiet=True, mapping=Mapping.getByName("cpp")) def getCommandLine(self, current): translator = self.getMapping(current).getCommandLine(current, self, self.getExe(current)) # # Look for slice2py installed by Pip if not found in the bin directory # if self.exe == "slice2py" and not os.path.exists(translator): if isinstance(platform, Windows): return os.path.join(os.path.dirname(sys.executable), "Scripts", "slice2py.exe") elif os.path.exists("/usr/local/bin/slice2py"): return "/usr/local/bin/slice2py" else: import slice2py return sys.executable + " " + os.path.normpath( os.path.join(slice2py.__file__, "..", "..", "..", "..", "bin", "slice2py")) return translator # # A test case is composed of servers and clients. When run, all servers are started # sequentially. When the servers are ready, the clients are also ran sequentially. # Once all the clients are terminated, the servers are stopped (which waits for the # successful completion of the server). # # A TestCase is also a "Runnable", like the Process class. In other words, it can be # used a client to allow nested test cases. # class TestCase(Runnable): def __init__(self, name, client=None, clients=None, server=None, servers=None, args=[], props={}, envs={}, options={}, desc=None): Runnable.__init__(self, desc) self.name = name self.parent = None self.mapping = None self.testsuite = None self.options = options self.dirs = [] self.files = [] self.args = args self.props = props self.envs = envs # # Setup client list, "client" can be a string in which case it's assumed to # to the client executable name. # self.clients = clients if client: client = Client(exe=client) if isinstance(client, str) else client self.clients = [client] if not self.clients else self.clients + [client] # # Setup server list, "server" can be a string in which case it's assumed to # to the server executable name. # self.servers = servers if server: server = Server(exe=server) if isinstance(server, str) else server self.servers = [server] if not self.servers else self.servers + [server] def __str__(self): return self.name def init(self, mapping, testsuite): # init is called when the testcase is added to the given testsuite self.mapping = mapping self.testsuite = testsuite # # If no clients are explicitly specified, we instantiate one if getClientType() # returns the type of client to instantiate (client, collocated, etc) # if not self.clients: client = self.mapping.getDefaultProcess(self.getClientType(), testsuite) self.clients = [client] if client else [] # # If no servers are explicitly specified, we instantiate one if getServerType() # returns the type of server to instantiate (server, serveramd, etc) # if not self.servers: server = self.mapping.getDefaultProcess(self.getServerType(), testsuite) self.servers = [server] if server else [] def getOptions(self, current): return self.options def canRun(self, current): # Can be overriden return True def setupServerSide(self, current): # Can be overridden to perform setup activities before the server side is started pass def teardownServerSide(self, current, success): # Can be overridden to perform terddown after the server side is stopped pass def setupClientSide(self, current): # Can be overridden to perform setup activities before the client side is started pass def teardownClientSide(self, current, success): # Can be overridden to perform terddown after the client side is stopped pass def startServerSide(self, current): for server in self.servers: self._startServer(current, server) def stopServerSide(self, current, success): for server in reversed(self.servers): self._stopServer(current, server, success) def runClientSide(self, current): for client in self.clients: self._runClient(current, client) def getTestSuite(self): return self.testsuite def getParent(self): return self.parent def getName(self): return self.name def getPath(self): return self.testsuite.getPath() def getMapping(self): return self.mapping def getArgs(self, process, current): return self.args def getProps(self, process, current): return self.props def getEnv(self, process, current): return self.envs def getProcessType(self, process): if process in self.clients: return self.getClientType() elif process in self.servers: return self.getServerType() elif isinstance(process, Server): return self.getServerType() else: return self.getClientType() def getClientType(self): # Overridden by test case specialization to specify the type of client to instantiate # if no client is explicitly provided return None def getServerType(self): # Overridden by test case specialization to specify the type of client to instantiate # if no server is explicitly provided return None def getServerTestCase(self, cross=None): testsuite = (cross or self.mapping.getServerMapping()).findTestSuite(self.testsuite) return testsuite.findTestCase(self) if testsuite else None def getClientTestCase(self): testsuite = self.mapping.getClientMapping().findTestSuite(self.testsuite) return testsuite.findTestCase(self) if testsuite else None def _startServerSide(self, current): # Set the host to use for the server side current.push(self) current.host = current.driver.getProcessController(current).getHost(current) self.setupServerSide(current) try: self.startServerSide(current) return current.host except: self._stopServerSide(current, False) raise finally: current.pop() def _stopServerSide(self, current, success): current.push(self) try: self.stopServerSide(current, success) finally: for server in reversed(self.servers): if server.isStarted(current): self._stopServer(current, server, False) self.teardownServerSide(current, success) current.pop() def _startServer(self, current, server): if server.desc: current.write("starting {0}... ".format(server.desc)) server.setup(current) server.start(current) if server.desc: current.writeln("ok") def _stopServer(self, current, server, success): try: server.stop(current, success) except: success = False raise finally: server.teardown(current, success) def _runClientSide(self, current, host=None): current.push(self, host) self.setupClientSide(current) success = False try: self.runClientSide(current) success = True finally: self.teardownClientSide(current, success) current.pop() def _runClient(self, current, client): success = False if client.desc: current.writeln("running {0}...".format(client.desc)) client.setup(current) try: client.run(current) success = True finally: client.teardown(current, success) def run(self, current): try: current.push(self) current.result.started(self) self.runWithDriver(current) current.result.succeeded(self) except Exception as ex: current.result.failed(self, traceback.format_exc() if current.driver.debug else str(ex)) raise finally: current.pop() for d in self.dirs: if os.path.exists(d): shutil.rmtree(d) for f in self.files: if os.path.exists(f): os.unlink(f) def createFile(self, path, lines, encoding=None): path = os.path.join(self.getPath(), path.decode("utf-8") if isPython2 else path) with open(path, "w", encoding=encoding) if not isPython2 and encoding else open(path, "w") as file: for l in lines: file.write("%s\n" % l) self.files.append(path) def mkdirs(self, dirs): for d in dirs if isinstance(dirs, list) else [dirs]: d = os.path.join(self.getPath(), d) self.dirs.append(d) if not os.path.exists(d): os.makedirs(d) class ClientTestCase(TestCase): def __init__(self, name="client", *args, **kargs): TestCase.__init__(self, name, *args, **kargs) def runWithDriver(self, current): current.driver.runTestCase(current) def getClientType(self): return "client" class ClientServerTestCase(ClientTestCase): def __init__(self, name="client/server", *args, **kargs): TestCase.__init__(self, name, *args, **kargs) def runWithDriver(self, current): current.driver.runClientServerTestCase(current) 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): ts = Mapping.getByName("cpp").findTestSuite("Ice/echo") if ts: return ts.findTestCase("server") return None def getClientType(self): return "clientBidir" class CollocatedTestCase(ClientTestCase): def __init__(self, name="collocated", *args, **kargs): TestCase.__init__(self, name, *args, **kargs) def getClientType(self): return "collocated" class ClientAMDServerTestCase(ClientServerTestCase): def __init__(self, name="client/amd server", *args, **kargs): ClientServerTestCase.__init__(self, name, *args, **kargs) def getServerType(self): return "serveramd" class Result: def __init__(self, testsuite, writeToStdout): self.testsuite = testsuite self._skipped = [] self._failed = {} self._succeeded = [] self._stdout = StringIO() self._writeToStdout = writeToStdout self._testcases = {} def started(self, testcase): self._start = self._stdout.tell() def failed(self, testcase, exception): self.writeln("\ntest in {0} failed:\n{1}".format(self.testsuite, exception)) self._testcases[testcase] = (self._start, self._stdout.tell()) self._failed[testcase] = exception def succeeded(self, testcase): self._testcases[testcase] = (self._start, self._stdout.tell()) self._succeeded.append(testcase) def isSuccess(self): return len(self._failed) == 0 def getFailed(self): return self._failed def getOutput(self, testcase=None): if testcase: if testcase in self._testcases: (start, end) = self._testcases[testcase] self._stdout.seek(start) try: return self._stdout.read(end - start) finally: self._stdout.seek(os.SEEK_END) return self._stdout.getvalue() def write(self, msg, stdout=True): if self._writeToStdout and stdout: try: sys.stdout.write(msg) except UnicodeEncodeError: # # The console doesn't support the encoding of the message, we convert the message # to an UTF-8 byte sequence and print out the byte sequence. We replace all the # double backslash from the byte sequence string representation to single back # slash. # sys.stdout.write(str(msg.encode("utf-8")).replace("\\\\", "\\")) sys.stdout.flush() self._stdout.write(msg) def writeln(self, msg, stdout=True): if self._writeToStdout and stdout: try: print(msg) except UnicodeEncodeError: # # The console doesn't support the encoding of the message, we convert the message # to an UTF-8 byte sequence and print out the byte sequence. We replace all the # double backslash from the byte sequence string representation to single back # slash. # print(str(msg.encode("utf-8")).replace("\\\\", "\\")) self._stdout.write(msg) self._stdout.write("\n") class TestSuite: def __init__(self, path, testcases=None, options={}, libDirs=[], runOnMainThread=False, chdir=False, multihost=True): self.path = os.path.dirname(path) if os.path.basename(path) == "test.py" else path self.mapping = Mapping.getByPath(self.path) self.id = self.mapping.addTestSuite(self) self.options = options self.libDirs = libDirs self.runOnMainThread = runOnMainThread self.chdir = chdir self.multihost = multihost if self.chdir: # Only tests running on main thread can change the current working directory self.runOnMainThread = True self.files = [] if testcases is None: files = [f for f in os.listdir(self.path) if os.path.isfile(os.path.join(self.path, f))] testcases = self.mapping.computeTestCases(self.id, files) self.testcases = OrderedDict() for testcase in testcases if testcases else []: testcase.init(self.mapping, self) if testcase.name in self.testcases: raise RuntimeError("duplicate testcase {0} in testsuite {1}".format(testcase, self)) self.testcases[testcase.name] = testcase def __str__(self): return self.id def getId(self): return self.id def getOptions(self, current): return self.options def getPath(self): return self.path def getMapping(self): return self.mapping def getLibDirs(self): return self.libDirs def isMainThreadOnly(self): for m in [CppMapping, JavaMapping, CSharpMapping]: if isinstance(self.mapping, m): return self.runOnMainThread else: return True def addTestCase(self, testcase): if testcase.name in self.testcases: raise RuntimeError("duplicate testcase {0} in testsuite {1}".format(testcase, self)) testcase.init(self.mapping, self) self.testcases[testcase.name] = testcase def findTestCase(self, testcase): return self.testcases.get(testcase if isinstance(testcase, str) else testcase.name) def getTestCases(self): return self.testcases.values() def setup(self, current): pass def run(self, current): try: cwd=None if self.chdir: cwd = os.getcwd() os.chdir(self.path) current.driver.runTestSuite(current) finally: if cwd: os.chdir(cwd) for f in self.files: if os.path.exists(f): os.remove(f) def teardown(self, current, success): pass def createFile(self, path, lines, encoding=None): path = os.path.join(self.path, path.decode("utf-8") if isPython2 else path) with open(path, "w", encoding=encoding) if not isPython2 and encoding else open(path, "w") as file: for l in lines: file.write("%s\n" % l) self.files.append(path) def isMultiHost(self): return self.multihost def isCross(self): # Only run the tests that support cross testing --all-cross or --cross return self.id in self.mapping.getCrossTestSuites() 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): class LocalProcess(Expect.Expect): def waitReady(self, ready, readyCount, startTimeout): if ready: self.expect("%s ready\n" % ready, timeout = startTimeout) else: while readyCount > 0: self.expect("[^\n]+ ready\n", timeout = startTimeout) readyCount -= 1 def getHost(self, current): # Depending on the configuration, either use an IPv4, IPv6 or BT address for Ice.Default.Host if current.config.ipv6: return current.driver.hostIPv6 elif current.config.protocol == "bt": if not current.driver.hostBT: raise Test.Common.TestCaseFailedException("no Bluetooth address set with --host-bt") return current.driver.hostBT else: return current.driver.host if current.driver.host else current.driver.interface def start(self, process, current, args, props, envs, watchDog): # # Props and arguments can use the format parameters set below in the kargs # dictionary. It's time to convert them to their values. # kargs = { "process": process, "testcase": current.testcase, "testdir": current.testcase.getPath(), "builddir": current.getBuildDir(process.getExe(current)), "icedir" : current.driver.getIceDir(current.testcase.getMapping(), current), } args = ["--{0}={1}".format(k, val(v)) for k,v in props.items()] + [val(a) for a in args] for k, v in envs.items(): envs[k] = val(v, quoteValue=False) cmd = (process.getCommandLine(current) + (" " + " ".join(args) if len(args) > 0 else "")).format(**kargs) if current.driver.debug: if len(envs) > 0: current.writeln("({0} env={1})".format(cmd, envs)) else: current.writeln("({0})".format(cmd)) env = os.environ.copy() env.update(envs) cwd = process.getMapping(current).getTestCwd(process, current) process = LocalProcessController.LocalProcess(cmd, startReader=False, env=env, cwd=cwd, desc=process.desc) process.startReader(watchDog) return process class RemoteProcessController(ProcessController): class RemoteProcess: def __init__(self, exe, proxy): self.exe = exe self.proxy = proxy self.stdout = False def waitReady(self, ready, readyCount, startTimeout): self.proxy.waitReady(startTimeout) def waitSuccess(self, exitstatus=0, timeout=60): try: result = self.proxy.waitSuccess(timeout) except: raise Except.TIMEOUT("waitSuccess timeout") if exitstatus != result: raise RuntimeError("unexpected exit status: expected: %d, got %d\n" % (exitstatus, result)) def getOutput(self): return self.output def trace(self, outfilters): self.stdout = True def terminate(self): self.output = self.proxy.terminate().strip() if self.stdout and self.output: print(self.output) def __init__(self, current, endpoints=None): self.processControllerProxies = {} self.controllerApps = [] 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" def getHost(self, current): return self.getController(current).getHost(current.config.protocol, current.config.ipv6) def getController(self, current): 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 if current.driver.controllerApp: self.controllerApps.append(ident) self.startControllerApp(current, ident) if not self.adapter: # 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: proxy.ice_ping() except Exception as ex: raise RuntimeError("couldn't reach the remote controller `{0}'".format(proxy)) with self.cond: self.processControllerProxies[ident] = proxy return self.processControllerProxies[ident] else: # Wait 10 seconds for a process controller to be registered with the ProcessControllerRegistry with self.cond: if not ident in self.processControllerProxies: self.cond.wait(10) if ident in self.processControllerProxies: return self.processControllerProxies[ident] raise RuntimeError("couldn't reach the remote controller `{0}'".format(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, ident): pass def start(self, process, current, args, props, envs, watchDog): # Get the process controller processController = self.getController(current) # TODO: support envs? exe = process.getExe(current) 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)) prx = processController.start(str(current.testsuite), exe, args) # Create bi-dir proxy in case we're talking to a bi-bir process controller. if self.adapter: prx = processController.ice_getConnection().createProxy(prx.ice_getIdentity()) import Test return RemoteProcessController.RemoteProcess(exe, Test.Common.ProcessPrx.uncheckedCast(prx)) def destroy(self, driver): if driver.controllerApp: for ident in self.controllerApps: self.stopControllerApp(ident) self.controllerApps = [] if self.adapter: self.adapter.destroy() class iOSSimulatorProcessController(RemoteProcessController): device = "iOSSimulatorProcessController" deviceID = "com.apple.CoreSimulator.SimDeviceType.iPhone-6" runtimeID = "com.apple.CoreSimulator.SimRuntime.iOS-10-2" appPath = "ios/controller/build/Products" def __init__(self, current): RemoteProcessController.__init__(self, current) self.simulatorID = None def __str__(self): return "iOS Simulator" def getControllerIdentity(self, current): if isinstance(current.testcase.getMapping(), ObjCMapping): if current.config.arc: return "iPhoneSimulator/com.zeroc.ObjC-ARC-Test-Controller" else: return "iPhoneSimulator/com.zeroc.ObjC-Test-Controller" else: assert(isinstance(current.testcase.getMapping(), CppMapping)) if current.config.cpp11: return "iPhoneSimulator/com.zeroc.Cpp11-Test-Controller" else: return "iPhoneSimulator/com.zeroc.Cpp98-Test-Controller" 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" else: assert(isinstance(mapping, CppMapping)) appName = "C++11 Test Controller.app" if current.config.cpp11 else "C++98 Test Controller.app" sys.stdout.write("launching simulator... ") sys.stdout.flush() try: run("xcrun simctl boot \"{0}\"".format(self.device)) except Exception as ex: if str(ex).find("Booted") >= 0: pass elif str(ex).find("Invalid device") >= 0: # Create the simulator device if it doesn't exist self.simulatorID = run("xcrun simctl create \"{0}\" {1} {2}".format(self.device, self.deviceID, self.runtimeID)) run("xcrun simctl boot \"{0}\"".format(self.device)) else: raise print("ok") sys.stdout.write("launching {0}... ".format(appName)) sys.stdout.flush() path = os.path.join(mapping.getTestsPath(), self.appPath, "Debug-iphonesimulator", appName) if not os.path.exists(path): path = os.path.join(mapping.getTestsPath(), self.appPath, "Release-iphonesimulator", appName) 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, ident.name)) print("ok") def stopControllerApp(self, ident): try: run("xcrun simctl uninstall \"{0}\" {1}".format(self.device, ident.name)) except: pass def destroy(self, driver): RemoteProcessController.destroy(self, driver) if self.simulatorID: sys.stdout.write("destroying simulator... ") sys.stdout.flush() try: run("xcrun simctl shutdown \"{0}\"".format(self.simulatorID)) except: pass try: run("xcrun simctl delete \"{0}\"".format(self.simulatorID)) except: pass print("ok") class iOSDeviceProcessController(RemoteProcessController): appPath = "cpp/test/ios/controller/build/Products" def __init__(self, current): RemoteProcessController.__init__(self, current) def __str__(self): return "iOS Device" def getControllerIdentity(self, current): if isinstance(current.testcase.getMapping(), ObjCMapping): return "iPhoneOS/com.zeroc.ObjC-Test-Controller" else: assert(isinstance(current.testcase.getMapping(), CppMapping)) if current.config.cpp11: return "iPhoneOS/com.zeroc.Cpp11-Test-Controller" else: return "iPhoneOS/com.zeroc.Cpp98-Test-Controller" def startControllerApp(self, current, ident): # TODO: use ios-deploy to deploy and run the application on an attached device? pass def stopControllerApp(self, ident): pass class UWPProcessController(RemoteProcessController): def __init__(self, current): RemoteProcessController.__init__(self, current, "tcp -h 127.0.0.1 -p 15001") self.name = "ice-uwp-controller" self.appUserModelId = "ice-uwp-controller_3qjctahehqazm" def __str__(self): return "UWP" def getControllerIdentity(self, current): return "UWP/ProcessController" def startControllerApp(self, current, ident): platform = current.config.buildPlatform config = current.config.buildConfig layout = os.path.join(toplevel, "cpp", "test", platform, config, "AppX") self.packageFullName = "{0}_1.0.0.0_{1}__3qjctahehqazm".format( self.name, "x86" if platform == "Win32" else platform) prefix = "controller_1.0.0.0_{0}{1}".format(platform, "{0}_".format(config) if config == "Debug" else "") package = os.path.join(toplevel, "cpp", "msbuild", "AppPackages", "controller", "{0}_Test".format(prefix), "{0}.appx".format(prefix)) # # If the application is already installed remove it, this will also take care # of closing it. # if self.name in run("powershell Get-AppxPackage -Name {0}".format(self.name)): run("powershell Remove-AppxPackage {0}".format(self.packageFullName)) # # Remove any previous package we have extracted to ensure we use a # fresh build # if os.path.exists(layout): shutil.rmtree(layout) os.makedirs(layout) print("Unpackaing package: {0} to {1}....".format(os.path.basename(package), layout)) run("MakeAppx.exe unpack /p \"{0}\" /d \"{1}\" /l".format(package, layout)) print("Registering application to run from layout...") run("powershell Add-AppxPackage -Register \"{0}/AppxManifest.xml\"".format(layout)) run("CheckNetIsolation LoopbackExempt -a -n={0}".format(self.appUserModelId)) # # microsoft.windows.softwarelogo.appxlauncher.exe returns the PID as return code # and 0 on case of failures. We pass err=True to run to handle this. # print("staring UWP controller app...") run('"{0}" {1}!App'.format( "C:/Program Files (x86)/Windows Kits/10/App Certification Kit/microsoft.windows.softwarelogo.appxlauncher.exe", self.appUserModelId), err=True) def stopControllerApp(self, ident): try: run("powershell Remove-AppxPackage {0}".format(self.packageFullName)) run("CheckNetIsolation LoopbackExempt -c -n={0}".format(self.appUserModelId)) except: 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") self.httpServer = None self.safariDriver = None try: 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) elif current.config.browser == "Safari" and os.environ.get("USER", "") == "jenkins": # # When running with Jenkins in headless mode, we need to manually start the webdriver # to fill in the requested password. # import pexpect self.safariDriver = pexpect.spawn("/usr/bin/safaridriver", ["-p", "9987"]) self.safariDriver.expect("Password:") self.safariDriver.sendline("jenkins") self.safariDriver.expect("\n") time.sleep(1) self.driver = webdriver.Remote("http://localhost:9987", webdriver.DesiredCapabilities.SAFARI) 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) self.httpServer.expect("listening on ports") except: self.destroy(current.driver) raise 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) if current.config.protocol == "wss": protocol = "https" port = "9090" cport = "15003" else: protocol = "http" port = "8080" cport = "15002" self.driver.get("{0}://127.0.0.1:{1}/test/{2}/controller.html?port={3}&worker={4}".format(protocol, port, testsuite, cport, current.config.worker)) return "Browser/ProcessController" def destroy(self, driver): if self.httpServer: self.httpServer.terminate() self.httpServer = None if self.safariDriver: self.safariDriver.terminate() self.safariDriver = None try: self.driver.quit() except: pass class Driver: class Current: def __init__(self, driver, testsuite, result): self.driver = driver self.testsuite = testsuite self.config = driver.configs[testsuite.getMapping()] self.result = result self.host = None self.testcase = None self.testcases = [] self.processes = {} def getTestEndpoint(self, *args, **kargs): return self.driver.getTestEndpoint(*args, **kargs) def getBuildDir(self, name): return self.testcase.getMapping().getBuildDir(name, self) def getPluginEntryPoint(self, plugin, process): return self.testcase.getMapping().getPluginEntryPoint(plugin, process, self) def write(self, *args, **kargs): self.result.write(*args, **kargs) def writeln(self, *args, **kargs): self.result.writeln(*args, **kargs) def push(self, testcase, host=None): if not testcase.mapping: assert(not testcase.parent and not testcase.testsuite) testcase.mapping = self.testcase.getMapping() testcase.testsuite = self.testcase.getTestSuite() testcase.parent = self.testcase self.testcases.append((self.testcase, self.config, self.host)) self.testcase = testcase self.config = self.driver.configs[self.testcase.getMapping()].cloneAndOverrideWith(self) self.host = host def pop(self): assert(self.testcase) testcase = self.testcase (self.testcase, self.config, self.host) = self.testcases.pop() if testcase.parent and self.testcase != testcase: testcase.mapping = None testcase.testsuite = None testcase.parent = None drivers = {} driver = "local" @classmethod def add(self, name, driver, default=False): if default: Driver.driver = name self.driver = name self.drivers[name] = driver @classmethod def getAll(self): return list(self.drivers.values()) @classmethod def create(self, options): parseOptions(self, options) driver = self.drivers.get(self.driver) if not driver: raise RuntimeError("unknown driver `{0}'".format(self.driver)) return driver(options) @classmethod def getOptions(self): return ("dlrR", ["debug", "driver=", "filter=", "rfilter=", "host=", "host-ipv6=", "host-bt=", "interface=", "controller-app"]) @classmethod def usage(self): pass @classmethod def commonUsage(self): print("") print("Driver options:") print("-d | --debug Verbose information.") print("--driver= Use the given driver (local, client, server or remote).") print("--filter= Run all the tests that match the given regex.") print("--rfilter= Run all the tests that do not match the given regex.") print("--host= The IPv4 address to use for Ice.Default.Host.") print("--host-ipv6= The IPv6 address to use for Ice.Default.Host.") print("--host-bt= The Bluetooth address to use for Ice.Default.Host.") print("--interface= The multicast interface to use to discover controllers.") print("--controller-app Start the process controller application.") def __init__(self, options): self.debug = False self.filters = [] self.rfilters = [] self.host = "" self.hostIPv6 = "" self.hostBT = "" self.controllerApp = False self.failures = [] parseOptions(self, options, { "d": "debug", "r" : "filters", "R" : "rfilters", "filter" : "filters", "rfilter" : "rfilters", "host-ipv6" : "hostIPv6", "host-bt" : "hostBT", "controller-app" : "controllerApp"}) self.filters = [re.compile(a) for a in self.filters] self.rfilters = [re.compile(a) for a in self.rfilters] self.communicator = None self.interface = "" self.processControllers = {} def setConfigs(self, configs): self.configs = configs def useIceBinDist(self, mapping): env = os.environ.get("ICE_BIN_DIST", "").split() return 'all' in env or mapping in env def getIceDir(self, mapping, current): if self.useIceBinDist(mapping): return platform.getIceInstallDir(mapping, current) elif mapping: return mapping.getPath() else: return toplevel def getSliceDir(self, mapping, current): return platform.getSliceDir(self.getIceDir(mapping, current) if self.useIceBinDist(mapping) else toplevel) def isWorkerThread(self): return False def getTestEndpoint(self, portnum, protocol="default"): return "{0} -p {1}".format(protocol, self.getTestPort(portnum)) def getTestPort(self, portnum): return 12010 + portnum def getArgs(self, process, current): ### Return driver specific arguments return [] def getProps(self, process, current): props = {} if isinstance(process, IceProcess): if not self.host: props["Ice.Default.Host"] = "0:0:0:0:0:0:0:1" if current.config.ipv6 else "127.0.0.1" else: props["Ice.Default.Host"] = self.host return props def getMappings(self): ### Return additional mappings to load required by the driver return [] def getCommunicator(self): self.initCommunicator() return self.communicator def initCommunicator(self): if self.communicator: return try: import Ice except ImportError: # Try to add the local Python build to the sys.path pythonMapping = Mapping.getByName("python") if pythonMapping: for p in pythonMapping.getPythonDirs(pythonMapping.getPath(), self.configs[pythonMapping]): sys.path.append(p) import Ice Ice.loadSlice(os.path.join(toplevel, "scripts", "Controller.ice")) initData = Ice.InitializationData() initData.properties = Ice.createProperties() # Load IceSSL, this is useful to talk with WSS for JavaScript initData.properties.setProperty("Ice.Plugin.IceSSL", "IceSSL:createIceSSL") initData.properties.setProperty("IceSSL.DefaultDir", os.path.join(toplevel, "certs")) initData.properties.setProperty("IceSSL.CertFile", "server.p12") initData.properties.setProperty("IceSSL.Password", "password") initData.properties.setProperty("IceSSL.Keychain", "test.keychain") initData.properties.setProperty("IceSSL.KeychainPassword", "password") initData.properties.setProperty("IceSSL.VerifyPeer", "0"); initData.properties.setProperty("Ice.Plugin.IceDiscovery", "IceDiscovery:createIceDiscovery") initData.properties.setProperty("IceDiscovery.DomainId", "TestController") initData.properties.setProperty("IceDiscovery.Interface", self.interface or "127.0.0.1") initData.properties.setProperty("Ice.Default.Host", self.interface or "127.0.0.1") initData.properties.setProperty("Ice.ThreadPool.Server.Size", "10") #initData.properties.setProperty("Ice.Trace.Protocol", "1") #initData.properties.setProperty("Ice.Trace.Network", "2") initData.properties.setProperty("Ice.Override.Timeout", "10000") self.communicator = Ice.initialize(initData) self.ctrlCHandler = Ice.CtrlCHandler() def signal(sig): self.communicator.destroy() self.ctrlCHandler.setCallback(signal) def getProcessController(self, current): processController = None if current.config.buildPlatform == "iphonesimulator": processController = iOSSimulatorProcessController elif current.config.buildPlatform == "iphoneos": processController = iOSDeviceProcessController elif current.config.uwp: processController = UWPProcessController elif isinstance(current.testcase.getMapping(), JavaScriptMapping) and current.config.browser: processController = BrowserProcessController else: processController = LocalProcessController if processController in self.processControllers: return self.processControllers[processController] # Instantiate the controller self.processControllers[processController] = processController(current) return self.processControllers[processController] def getProcessProps(self, current, ready, readyCount): props = {} if ready or readyCount > 0: if current.config.buildPlatform not in ["iphonesimulator", "iphoneos"]: props["Ice.PrintAdapterReady"] = 1 return props def destroy(self): for controller in self.processControllers.values(): controller.destroy(self) if self.communicator: self.communicator.destroy() self.ctrlCHandler.destroy() class CppMapping(Mapping): class Config(Mapping.Config): @classmethod def getOptions(self): return ("", ["cpp-config=", "cpp-platform="]) @classmethod def usage(self): print("") print("C++ Mapping options:") print("--cpp-config= C++ build configuration for native executables (overrides --config).") print("--cpp-platform= C++ build platform for native executables (overrides --platform).") def __init__(self, options=[]): Mapping.Config.__init__(self, options) # Derive from the build config the cpp11 option. This is used by canRun to allow filtering # tests on the cpp11 value in the testcase options specification self.cpp11 = self.buildConfig.lower().find("cpp11") >= 0 parseOptions(self, options, { "cpp-config" : "buildConfig", "cpp-platform" : "buildPlatform" }) def canRun(self, current): if not Mapping.Config.canRun(self, current): return False # No C++11 tests for IceStorm, IceGrid, etc parent = re.match(r'^([\w]*).*', current.testcase.getTestSuite().getId()).group(1) if self.cpp11 and not parent in ["IceUtil", "Slice", "Ice", "IceSSL", "IceDiscovery", "IceBox"]: return False return True def getDefaultExe(self, processType, config): return platform.getDefaultExe(processType, config) def getProps(self, process, current): props = Mapping.getProps(self, process, current) if isinstance(process, IceProcess): props["Ice.NullHandleAbort"] = True return props def getSSLProps(self, process, current): props = Mapping.getSSLProps(self, process, current) server = isinstance(process, Server) uwp = current.config.buildPlatform == "UWP" props.update({ "IceSSL.CAs": "cacert.pem", "IceSSL.CertFile": "server.p12" if server else "ms-appx:///client.p12" if uwp else "client.p12" }) if isinstance(platform, Darwin): keychainFile = "server.keychain" if server else "client.keychain" props.update({ "IceSSL.KeychainPassword" : "password", "IceSSL.Keychain": keychainFile }) return props def getPluginEntryPoint(self, plugin, process, current): return { "IceSSL" : "IceSSL:createIceSSL", "IceBT" : "IceBT:createIceBT", "IceDiscovery" : "IceDiscovery:createIceDiscovery" }[plugin] def getEnv(self, process, current): # # On Windows, add the testcommon directories to the PATH # libPaths = [] if isinstance(platform, Windows): libPaths.append(self.getLibDir(process, current)) testcommon = os.path.join(self.path, "test", "Common") libPaths.append(os.path.join(testcommon, self.getBuildDir("testcommon", current))) # # Add the test suite library directories to the platform library path environment variable. # if current.testcase: for d in set([current.getBuildDir(d) for d in current.testcase.getTestSuite().getLibDirs()]): libPaths.append(d) env = {} if len(libPaths) > 0: env[platform.getLdPathEnvName()] = os.pathsep.join(libPaths) return env def getDefaultSource(self, processType): return { "client" : "Client.cpp", "server" : "Server.cpp", "serveramd" : "ServerAMD.cpp", "collocated" : "Collocated.cpp", }[processType] class JavaMapping(Mapping): def getCommandLine(self, current, process, exe): javaHome = os.getenv("JAVA_HOME", "") java = os.path.join(javaHome, "bin", "java") if javaHome else "java" if process.isFromBinDir(): return "{0} {1}".format(java, exe) assert(current.testcase.getPath().startswith(self.getTestsPath())) package = "test." + current.testcase.getPath()[len(self.getTestsPath()) + 1:].replace(os.sep, ".") javaArgs = self.getJavaArgs(process, current) if javaArgs: return "{0} {1} {2}.{3}".format(java, " ".join(javaArgs), package, exe) else: return "{0} {1}.{2}".format(java, package, exe) def getJavaArgs(self, process, current): return [] def getSSLProps(self, process, current): props = Mapping.getSSLProps(self, process, current) props.update({ "IceSSL.Keystore": "server.jks" if isinstance(process, Server) else "client.jks", }) return props def getPluginEntryPoint(self, plugin, process, current): return { "IceSSL" : "com.zeroc.IceSSL.PluginFactory", "IceBT" : "com.zeroc.IceBT.PluginFactory", "IceDiscovery" : "com.zeroc.IceDiscovery.PluginFactory" }[plugin] def getEnv(self, process, current): return { "CLASSPATH" : os.path.join(self.path, "lib", "test.jar") } def getTestsPath(self): return os.path.join(self.path, "test/src/main/java/test") def getDefaultSource(self, processType): return self.getDefaultExe(processType) + ".java" def getDefaultExe(self, processType, config=None): return { "client" : "Client", "server" : "Server", "serveramd" : "AMDServer", "collocated" : "Collocated", "icebox": "com.zeroc.IceBox.Server", "iceboxadmin" : "com.zeroc.IceBox.Admin", }[processType] class JavaCompatMapping(JavaMapping): def getPluginEntryPoint(self, plugin, process, current): return { "IceSSL" : "IceSSL.PluginFactory", "IceBT" : "IceBT.PluginFactory", "IceDiscovery" : "IceDiscovery.PluginFactory" }[plugin] def getDefaultExe(self, processType, config=None): return { "client" : "Client", "server" : "Server", "serveramd" : "AMDServer", "collocated" : "Collocated", "icebox": "IceBox.Server", "iceboxadmin" : "IceBox.Admin", }[processType] class CSharpMapping(Mapping): def getBuildDir(self, name, current): return os.path.join("msbuild", name) def getSSLProps(self, process, current): props = Mapping.getSSLProps(self, process, current) props.update({ "IceSSL.Password": "password", "IceSSL.DefaultDir": os.path.join(toplevel, "certs"), "IceSSL.CAs": "cacert.pem", "IceSSL.VerifyPeer": "0" if current.config.protocol == "wss" else "2", "IceSSL.CertFile": "server.p12" if isinstance(process, Server) else "client.p12", }) return props def getPluginEntryPoint(self, plugin, process, current): plugindir = "{0}/{1}".format(current.driver.getIceDir(self, current), "lib" if current.driver.useIceBinDist(self) else "Assemblies") return { "IceSSL" : plugindir + "/IceSSL.dll:IceSSL.PluginFactory", "IceDiscovery" : plugindir + "/IceDiscovery.dll:IceDiscovery.PluginFactory" }[plugin] def getEnv(self, process, current): if current.driver.useIceBinDist(self): bzip2 = os.path.join(platform.getIceInstallDir(self, current), "tools") assembliesDir = os.path.join(platform.getIceInstallDir(self, current), "lib") else: bzip2 = os.path.join(toplevel, "cpp", "msbuild", "packages", "bzip2.{0}.1.0.6.4".format(platform.getCompiler()), "build", "native", "bin", "x64", "Release") assembliesDir = os.path.join(current.driver.getIceDir(self, current), "Assemblies") return { "DEVPATH" : assembliesDir, "PATH" : bzip2 }; def getDefaultSource(self, processType): return { "client" : "Client.cs", "server" : "Server.cs", "serveramd" : "ServerAMD.cs", "collocated" : "Collocated.cs", }[processType] def getDefaultExe(self, processType, config): return "iceboxnet" if processType == "icebox" else processType class CppBasedMapping(Mapping): class Config(Mapping.Config): @classmethod def getOptions(self): return ("", [self.mappingName + "-config=", self.mappingName + "-platform="]) @classmethod def usage(self): print("") print(self.mappingDesc + " mapping options:") print("--{0}-config= {1} build configuration for native executables (overrides --config)." .format(self.mappingName, self.mappingDesc)) print("--{0}-platform= {1} build platform for native executables (overrides --platform)." .format(self.mappingName, self.mappingDesc)) def __init__(self, options=[]): Mapping.Config.__init__(self, options) parseOptions(self, options, { self.mappingName + "-config" : "buildConfig", self.mappingName + "-platform" : "buildPlatform" }) def getSSLProps(self, process, current): return Mapping.getByName("cpp").getSSLProps(process, current) def getPluginEntryPoint(self, plugin, process, current): return Mapping.getByName("cpp").getPluginEntryPoint(plugin, process, current) def getEnv(self, process, current): env = Mapping.getEnv(self, process, current) if current.driver.getIceDir(self, current) != platform.getIceInstallDir(self, current): # If not installed in the default platform installation directory, add # the Ice C++ library directory to the library path env[platform.getLdPathEnvName()] = Mapping.getByName("cpp").getLibDir(process, current) return env class ObjCMapping(CppBasedMapping): class Config(CppBasedMapping.Config): mappingName = "objc" mappingDesc = "Objective-C" def __init__(self, options=[]): Mapping.Config.__init__(self, options) self.arc = self.buildConfig.lower().find("arc") >= 0 def getDefaultSource(self, processType): return { "client" : "Client.m", "server" : "Server.m", "collocated" : "Collocated.m", }[processType] class PythonMapping(CppBasedMapping): class Config(CppBasedMapping.Config): mappingName = "python" mappingDesc = "Python" def getCommandLine(self, current, process, exe): return "\"{0}\" {1}".format(sys.executable, exe) def getEnv(self, process, current): env = CppBasedMapping.getEnv(self, process, current) if current.driver.getIceDir(self, current) != platform.getIceInstallDir(self, current): # If not installed in the default platform installation directory, add # the Ice python directory to PYTHONPATH dirs = self.getPythonDirs(current.driver.getIceDir(self, current), current.config) env["PYTHONPATH"] = os.pathsep.join(dirs) return env def getPythonDirs(self, iceDir, config): dirs = [] if isinstance(platform, Windows): dirs.append(os.path.join(iceDir, "python", config.buildPlatform, config.buildConfig)) dirs.append(os.path.join(iceDir, "python")) return dirs def getDefaultExe(self, processType, config): return self.getDefaultSource(processType) def getDefaultSource(self, processType): return { "client" : "Client.py", "server" : "Server.py", "serveramd" : "ServerAMD.py", "collocated" : "Collocated.py", }[processType] class CppBasedClientMapping(CppBasedMapping): def loadTestSuites(self, tests, config, filters, rfilters): Mapping.loadTestSuites(self, tests, config, filters, rfilters) self.getServerMapping().loadTestSuites(self.testsuites.keys(), config) def getServerMapping(self): return Mapping.getByName("cpp") # By default, run clients against C++ mapping executables def getDefaultExe(self, processType, config): return self.getDefaultSource(processType) class RubyMapping(CppBasedClientMapping): class Config(CppBasedClientMapping.Config): mappingName = "ruby" mappingDesc = "Ruby" def getCommandLine(self, current, process, exe): return "ruby " + exe def getEnv(self, process, current): env = CppBasedMapping.getEnv(self, process, current) if current.driver.getIceDir(self, current) != platform.getIceInstallDir(self, current): # If not installed in the default platform installation directory, add # the Ice ruby directory to RUBYLIB env["RUBYLIB"] = os.path.join(self.path, "ruby") return env def getDefaultSource(self, processType): return { "client" : "Client.rb" }[processType] class PhpMapping(CppBasedClientMapping): class Config(CppBasedClientMapping.Config): mappingName = "php" mappingDesc = "PHP" def getEnv(self, process, current): env = CppBasedMapping.getEnv(self, process, current) if isinstance(platform, Windows) and current.driver.useIceBinDist(self): env[platform.getLdPathEnvName()] = self.getBinDir(process, current) return env def getCommandLine(self, current, process, exe): args = [] if current.driver.getIceDir(self, current) == platform.getIceInstallDir(self, current): # # If installed in the platform system directory and on Linux, we rely # on ice.ini to find the extension. On OS X, we still need to setup # the properties. # if(isinstance(platform, Darwin)): args += ["-n"] # Do not load any php.ini files args += ["-d", "extension_dir=/usr/local/lib/php/extensions"] args += ["-d", "include_path=/usr/local/share/php"] args += ["-d", "extension=IcePHP.so"] else: useBinDist = current.driver.useIceBinDist(self) if isinstance(platform, Windows): extension = "php_ice_nts.dll" if "NTS" in run("php -v") else "php_ice.dll" extensionDir = self.getBinDir(process, current) includePath = self.getLibDir(process, current) else: extension = "IcePHP.so" extensionDir = self.getLibDir(process, current) includePath = "{0}/{1}".format(current.driver.getIceDir(self, current), "php" if useBinDist else "lib") args += ["-n"] # Do not load any php.ini files args += ["-d", "extension_dir='{0}'".format(extensionDir)] args += ["-d", "extension='{0}'".format(extension)] args += ["-d", "include_path='{0}'".format(includePath)] if hasattr(process, "getPhpArgs"): args += process.getPhpArgs(current) return "php {0} -f {1} -- ".format(" ".join(args), exe) def getDefaultSource(self, processType): return { "client" : "Client.php" }[processType] class JavaScriptMapping(Mapping): class Config(Mapping.Config): @classmethod def getOptions(self): return ("", ["es5", "browser=", "worker"]) @classmethod def usage(self): print("") print("JavaScript mapping options:") print("--es5 Use JavaScript ES5 (Babel compiled code).") print("--browser= Run with the given browser.") print("--worker Run with Web workers enabled.") def __init__(self, options=[]): Mapping.Config.__init__(self, options) self.es5 = False self.browser = "" self.worker = False parseOptions(self, options) if self.browser and self.protocol == "tcp": self.protocol = "ws" if self.browser in ["Edge", "Ie"]: self.es5 = True def loadTestSuites(self, tests, config, filters, rfilters): Mapping.loadTestSuites(self, tests, config, filters, rfilters) self.getServerMapping().loadTestSuites(list(self.testsuites.keys()) + ["Ice/echo"], config, filters, rfilters) def getServerMapping(self): return Mapping.getByName("cpp") # By default, run clients against C++ mapping executables def getCommandLine(self, current, process, exe): if current.config.es5: return "node {0}/test/Common/run.js --es5 {1}".format(self.path, exe) else: return "node {0}/test/Common/run.js {1}".format(self.path, exe) def getDefaultSource(self, processType): return { "client" : "Client.js", "clientBidir" : "ClientBidir.js" }[processType] def getDefaultExe(self, processType, config=None): return self.getDefaultSource(processType).replace(".js", "") def getEnv(self, process, current): env = Mapping.getEnv(self, process, current) 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 return os.path.join(self.path, "test", "es5", current.testcase.getTestSuite().getId()) else: return os.path.join(self.path, "test", current.testcase.getTestSuite().getId()) def computeTestCases(self, testId, files): if testId.find("es5") > -1: return # Ignore es5 directories return Mapping.computeTestCases(self, testId, files) def getOptions(self, current): options = { "protocol" : ["ws", "wss"] if current.config.browser else ["tcp"], "compress" : [False], "ipv6" : [False], "serialize" : [False], "mx" : [False], "es5" : [False, True], "worker" : [False, True] if current.config.browser else [False], } # Edge and Ie only support ES5 for now if current.config.browser in ["Edge", "Ie"]: options["es5"] = [True] # TODO: Fix Safari issue where tests hang when ran with --worker if current.config.browser == "Safari": options["worker"] = [False] return options from Glacier2Util import * from IceBoxUtil import * from IcePatch2Util import * from IceGridUtil import * from IceStormUtil import * from LocalDriver import * # # Supported mappings # Mapping.add("cpp", CppMapping()) Mapping.add("java", JavaMapping()) Mapping.add("java-compat", JavaCompatMapping()) Mapping.add("python", PythonMapping()) Mapping.add("ruby", RubyMapping()) Mapping.add("php", PhpMapping()) Mapping.add("js", JavaScriptMapping()) if isinstance(platform, Windows): Mapping.add("csharp", CSharpMapping()) if isinstance(platform, Darwin): Mapping.add("objective-c", ObjCMapping()) def runTestsWithPath(path): runTests([Mapping.getByPath(path)]) def runTests(mappings=None, drivers=None): if not mappings: mappings = Mapping.getAll() if not drivers: drivers = Driver.getAll() # # All mappings contains all the mappings necessary to run the tests from the given mappings. Some # mappings depend on other mappings for running (e.g.: Ruby needs the C++ mapping). # allMappings = list(set([m.getClientMapping() for m in mappings] + [m.getServerMapping() for m in mappings])) def usage(): print("Usage: " + sys.argv[0] + " [options] [tests]") print("") print("Options:") print("-h | --help Show this message") Driver.commonUsage() for driver in drivers: driver.usage() Mapping.Config.commonUsage() for mapping in allMappings: mapping.Config.usage() print("") driver = None try: options = [Driver.getOptions(), Mapping.Config.getOptions()] options += [driver.getOptions() for driver in drivers] options += [mapping.Config.getOptions() for mapping in Mapping.getAll()] shortOptions = "h" longOptions = ["help"] for so, lo in options: shortOptions += so longOptions += lo opts, args = getopt.gnu_getopt(sys.argv[1:], shortOptions, longOptions) for (o, a) in opts: if o in ["-h", "--help"]: usage() sys.exit(0) # # Create the driver # driver = Driver.create(opts) # # Create the configurations for each mapping (we always parse the configuration for the # python mapping because we might use the local IcePy build to initialize a communicator). # configs = {} for mapping in allMappings + driver.getMappings() + [Mapping.getByName("python")]: if mapping not in configs: configs[mapping] = mapping.createConfig(opts[:]) # # Provide the configurations to the driver and load the test suites for each mapping. # driver.setConfigs(configs) for mapping in mappings + driver.getMappings(): mapping.loadTestSuites(args, configs[mapping], driver.filters, driver.rfilters) # # Finally, run the test suites with the driver. # try: sys.exit(driver.run(mappings, args)) except KeyboardInterrupt: pass finally: driver.destroy() except Exception as e: print(sys.argv[0] + ": unexpected exception raised:\n" + traceback.format_exc()) sys.exit(1)