summaryrefslogtreecommitdiff
path: root/scripts/Util.py
diff options
context:
space:
mode:
authorBenoit Foucher <benoit@zeroc.com>2016-11-25 13:13:22 +0100
committerBenoit Foucher <benoit@zeroc.com>2016-11-25 13:13:22 +0100
commitdcdc32af1fced49d80a8ccd93230e15d91ab45d8 (patch)
treeeb69e2555fbd54496fce8a33f4dd610e1473ff51 /scripts/Util.py
parentC# IceSSL/configuration log expired certificate exceptions. (diff)
downloadice-dcdc32af1fced49d80a8ccd93230e15d91ab45d8.tar.bz2
ice-dcdc32af1fced49d80a8ccd93230e15d91ab45d8.tar.xz
ice-dcdc32af1fced49d80a8ccd93230e15d91ab45d8.zip
Refactored test scripts
Diffstat (limited to 'scripts/Util.py')
-rw-r--r--scripts/Util.py2114
1 files changed, 2114 insertions, 0 deletions
diff --git a/scripts/Util.py b/scripts/Util.py
new file mode 100644
index 00000000000..17f88cd3771
--- /dev/null
+++ b/scripts/Util.py
@@ -0,0 +1,2114 @@
+# **********************************************************************
+#
+# 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
+
+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):
+ p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, cwd=cwd)
+ if(p.wait() != 0):
+ raise RuntimeError(cmd + " failed:\n" + p.stdout.read().strip())
+ return p.stdout.read().decode('UTF-8').strip()
+
+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 getDefaultBuildPlatform(self):
+ return os.environ.get("PLATFORMS", "").split(" ")[0] or self.supportedPlatforms[0]
+
+ def getDefaultBuildConfig(self):
+ return os.environ.get("CONFIGS", "").split(" ")[0] or self.supportedConfigs[0]
+
+ def getBinSubDir(self, mapping, current):
+ # Return the bin sub-directory for the given mapping,platform,config,
+ # to be overriden by specializations
+ return "bin"
+
+ def getLibSubDir(self, mapping, 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 getIceDir(self):
+ return "/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, current):
+ return True
+
+class Darwin(Platform):
+
+ def getDefaultBuildPlatform(self):
+ return "macosx"
+
+ def getLdPathEnvName(self):
+ return "DYLD_LIBRARY_PATH"
+
+ def getIceDir(self):
+ return "/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, 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, current):
+ 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, 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 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, current):
+ c = current.config
+ if isinstance(mapping, CppMapping) or isinstance(mapping, PhpMapping):
+ return os.path.join("bin", c.buildPlatform, "Debug" if c.buildConfig.find("Debug") >= 0 else "Release")
+ return "bin"
+
+ def getLibSubDir(self, mapping, current):
+ c = current.config
+ if isinstance(mapping, CppMapping):
+ return os.path.join("bin", c.buildPlatform, "Debug" if c.buildConfig.find("Debug") >= 0 else "Release")
+ elif isinstance(mapping, PhpMapping):
+ return os.path.join("lib", c.buildPlatform, "Debug" if c.buildConfig.find("Debug") >= 0 else "Release")
+ return "lib"
+
+ def getBuildSubDir(self, name, current):
+ return os.path.join("msbuild", name, current.config.buildPlatform, current.config.buildConfig)
+
+ def getLdPathEnvName(self):
+ return "PATH"
+
+ def getIceDir(self):
+ return None
+
+ def canRun(self, 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.useBinDist():
+ parent = re.match(r'^([\w]*).*', current.testcase.getTestSuite().getId()).group(1)
+ if parent in ["Glacier2", "IceStorm"] and current.config.buildConfiguration.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.
+ 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))
+ obj.parsedOptions.append(o)
+ else:
+ remaining.append((o, a))
+ options[:] = remaining
+
+
+class Mapping:
+
+ mappings = OrderedDict()
+
+ class Config:
+
+ supportedOptions = ["protocol", "compress", "ipv6", "serialize", "mx"]
+
+ # 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="])
+
+ @classmethod
+ def usage(self):
+ pass
+
+ @classmethod
+ def commonUsage(self):
+ print("")
+ print("Mapping options:")
+ print("--protocol=<prot> 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=<properties> Specifies a list of additional client properties.")
+ print("--sprops=<properties> Specifies a list of additional server properties.")
+ print("--config=<config> Build configuration for native executables.")
+ print("--platform=<platform> Build plaform for native executables.")
+
+ def __init__(self, options=[]):
+ self.buildConfig = platform.getDefaultBuildConfig()
+ self.buildPlatform = platform.getDefaultBuildPlatform()
+ self.protocol = "tcp"
+ self.compress = False
+ self.serialize = False
+ self.ipv6 = False
+ self.mx = False
+ self.cprops = []
+ self.sprops = []
+ parseOptions(self, options, { "config" : "buildConfig", "platform" : "buildPlatform" })
+
+ 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())
+ supportedOptions.update(testcase.getTestSuite().getOptions())
+ supportedOptions.update(testcase.getOptions())
+
+ 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 longuest 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(current):
+ return False
+
+ options = {}
+ options.update(current.testcase.getTestSuite().getOptions())
+ options.update(current.testcase.getOptions())
+ for (k, v) in options.items():
+ if not hasattr(self, k):
+ print("warning: unknown option `{0}' specified in `{1}'".format(k, current.testcase))
+ continue
+ if not getattr(self, k) in v:
+ return False
+ else:
+ return True
+
+ 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"
+ if self.ipv6:
+ props.update({"Ice.IPv6": True, "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()):
+ 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 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 Mapping.Config(options)
+
+ def filterTestSuite(self, testId, filters, rfilters):
+ for f in filters:
+ if not f.search(self.name + "/" + testId):
+ return True
+ else:
+ for f in rfilters:
+ if f.search(self.name + "/" + testId):
+ return True
+ return False
+
+ def loadTestSuites(self, tests, filters=[], rfilters=[]):
+ for test in tests or [""]:
+ for root, dirs, files in os.walk(os.path.join(self.getTestsPath(), test)):
+
+ testId = root[len(self.getTestsPath()) + 1:]
+ if os.sep != "/":
+ testId = testId.replace(os.sep, "/")
+
+ if self.filterTestSuite(testId, 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):
+ return self.testsuites.values()
+
+ def addTestSuite(self, testsuite):
+ assert len(testsuite.path) > len(self.getTestsPath()) + 1
+ testSuiteId = testsuite.path[len(self.getTestsPath()) + 1:]
+ 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("collocated"):
+ testcases.append(CollocatedTestCase())
+ if checkClient("clientBidir") and self.getServerMapping().hasSource("Ice/echo", "server"):
+ testcases.append(ClientEchoServerTestCase())
+ 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, current):
+ return current.testcase.getPath()
+
+ def getDefaultSource(self, processType):
+ return processType
+
+ def getDefaultProcess(self, processType, testsuite):
+ #
+ # If no server or client is explictily 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 overriden for client-only mapping that relies on another mapping for servers
+ return self
+
+ def getBinDir(self, current):
+ return os.path.join(current.driver.getIceDir(self), platform.getBinSubDir(self, current))
+
+ def getLibDir(self, current):
+ return os.path.join(current.driver.getIceDir(self), platform.getLibSubDir(self, 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(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")
+ if current.config.protocol in ["ssl", "wss", "bts", "iaps"]:
+ props.update(self.getSSLProps(process, current.config.protocol))
+ return props
+
+ def getSSLProps(self, process, protocol="ssl"):
+ return { "Ice.Plugin.IceSSL" : self.getPluginEntryPoint("IceSSL") }
+
+ def getArgs(self, process, current):
+ return []
+
+ def getEnv(self, process, current):
+ return {}
+
+ def getOptions(self):
+ 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=[], quiet=False, args=[], props={}, envs={}, desc=None, mapping=None):
+ Runnable.__init__(self, desc)
+ self.exe = exe
+ self.outfilters = outfilters
+ self.quiet = quiet
+ self.args = args
+ self.props = props
+ self.envs = envs
+ self.process = None
+ 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):
+ assert(self.process)
+
+ def d(s):
+ return s if isPython2 else s.decode("utf-8") if isinstance(s, bytes) else s
+
+ output = d(self.process.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):
+ self.lastProgressTime = time.time()
+ self.lock = threading.Lock()
+
+ def reset(self):
+ with self.lock: self.lastProgressTime = time.time()
+
+ def lastProgress(self):
+ with self.lock: return self.lastProgressTime
+
+ watchDog = WatchDog()
+ 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.
+ self.process.trace(self.outfilters)
+
+ try:
+ while True:
+ try:
+ self.process.waitSuccess(exitstatus=exitstatus, timeout=30)
+ break
+ except Expect.TIMEOUT:
+ if time.time() - watchDog.lastProgress() >= timeout: # If no progress, raise
+ raise
+ finally:
+ self.process.terminate()
+ # Write the output to the test case (but not on stdout)
+ if not self.quiet:
+ current.write(self.getOutput(), stdout=False)
+
+ 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)
+
+ # Evaluate and transform properties into command line arguments
+ allArgs = ["--{0}={1}".format(k, val(v)) for k,v in allProps.items()] + [val(a) for a in allArgs]
+
+ # Evaluate environment values
+ for k, v in allEnvs.items():
+ allEnvs[k] = val(v, quoteValue=False)
+
+ # Get command line from the mapping
+ cmd = self.getCommandLine(current)
+ if len(allArgs) > 0:
+ cmd += " " + " ".join(allArgs)
+
+ #
+ # 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": self,
+ "testcase": current.testcase,
+ "testdir": current.testcase.getPath(),
+ "icedir" : current.driver.getIceDir(current.testcase.getMapping()),
+ }
+ cmd = cmd.format(**kargs)
+
+ if current.driver.debug:
+ if len(allEnvs) > 0:
+ current.writeln("({0} env={1})".format(cmd, allEnvs))
+ else:
+ current.writeln("({0})".format(cmd))
+
+ env = os.environ.copy()
+ env.update(allEnvs)
+
+ cwd = self.getMapping(current).getTestCwd(current)
+
+ self.process = Expect.Expect(cmd, startReader=False, env=env, cwd=cwd, desc=self.desc)
+ self.process.startReader(watchDog)
+ try:
+ self.waitForStart(current)
+ except:
+ self.stop(current)
+ raise
+
+ def waitForStart(self, current):
+ # To be overriden in specialization to wait for a token indiciating the process readyness.
+ pass
+
+ def stop(self, current, waitSuccess=False):
+ if self.process:
+ try:
+ if waitSuccess: # Wait for the process to exit successfully by itself.
+ self.process.waitSuccess(timeout=60)
+ finally:
+ self.process.terminate()
+ if not self.quiet: # Write the output to the test case (but not on stdout)
+ current.write(self.getOutput(), stdout=False)
+ self.process = None
+
+ def expect(self, pattern, timeout=60):
+ assert(self.process)
+ return self.process.expect(pattern, timeout)
+
+ def sendline(self, data):
+ assert(self.process)
+ return self.process.sendline(data)
+
+ def isStarted(self):
+ return self.process is not None
+
+ 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=60, *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,
+ })
+ if self.ready or (self.readyCount + (1 if current.config.mx else 0)) > 0:
+ props["Ice.PrintAdapterReady"] = 1
+ return props
+
+ def waitForStart(self, current):
+ if self.ready:
+ self.process.expect("%s ready\n" % self.ready, timeout = self.startTimeout)
+ else:
+ count = self.readyCount + (1 if current.config.mx else 0)
+ while count > 0:
+ self.process.expect("[^\n]+ ready\n", timeout = self.startTimeout)
+ count -= 1
+
+ # 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():
+ self.process.trace(self.outfilters)
+
+ def stop(self, current, waitSuccess=False):
+ IceProcess.stop(self, current, waitSuccess and self.waitForShutdown)
+
+#
+# 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):
+ return self.options
+
+ def setupServerSide(self, current):
+ # Can be overriden to perform setup activities before the server side is started
+ pass
+
+ def teardownServerSide(self, current, success):
+ # Can be overriden to perform terddown after the server side is stopped
+ pass
+
+ def setupClientSide(self, current):
+ # Can be overriden to perform setup activities before the client side is started
+ pass
+
+ def teardownClientSide(self, current, success):
+ # Can be overriden 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):
+ # Overriden by test case specialization to specify the type of client to instantiate
+ # if no client is explictly provided
+ return None
+
+ def getServerType(self):
+ # Overriden by test case specialization to specify the type of client to instantiate
+ # if no server is explictly 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):
+ current.push(self)
+ self.setupServerSide(current)
+ try:
+ self.startServerSide(current)
+ 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():
+ 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):
+ current.push(self)
+ 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, "wb") 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):
+ return Mapping.getByName("cpp").findTestSuite("Ice/echo").findTestCase("server")
+
+ 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("test 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:
+ sys.stdout.write(msg)
+ sys.stdout.flush()
+ self._stdout.write(msg)
+
+ def writeln(self, msg, stdout=True):
+ if self._writeToStdout and stdout:
+ print(msg)
+ self._stdout.write(msg + "\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):
+ 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, "wb") 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()
+
+ def isAllCross(self):
+ # Only run the Ice/operations test suite with --all-cross
+ return self.id == "Ice/operations"
+
+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 = []
+
+ 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):
+ return self.testcase.getMapping().getPluginEntryPoint(plugin)
+
+ def write(self, *args, **kargs):
+ self.result.write(*args, **kargs)
+
+ def writeln(self, *args, **kargs):
+ self.result.writeln(*args, **kargs)
+
+ def push(self, testcase):
+ 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.testcase = testcase
+
+ def pop(self):
+ assert(self.testcase)
+ testcase = self.testcase
+ self.testcase = 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 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="])
+
+ @classmethod
+ def usage(self):
+ pass
+
+ @classmethod
+ def commonUsage(self):
+ print("")
+ print("Driver options:")
+ print("-d | --debug Verbose information.")
+ print("--driver=<driver> Use the given driver (local, client, server or remote).")
+ print("--filter=<regex> Run all the tests that match the given regex.")
+ print("--rfilter=<regex> Run all the tests that do not match the given regex.")
+ print("--host=<addr> The IPv4 address to use for Ice.Default.Host.")
+ print("--host-ipv6=<addr> The IPv6 address to use for Ice.Default.Host.")
+ print("--host-bt=<addr> The Bluetooth address to use for Ice.Default.Host.")
+
+ def __init__(self, options):
+ self.debug = False
+ self.filters = []
+ self.rfilters = []
+ self.host = ""
+ self.hostIPv6 = ""
+ self.hostBT = ""
+ self.failures = []
+ parseOptions(self, options, { "d": "debug",
+ "r" : "filters",
+ "R" : "rfilters",
+ "filter" : "filters",
+ "rfilter" : "rfilters",
+ "host-ipv6" : "hostIPv6",
+ "host-bt" : "hostBT" })
+
+ self.filters = [re.compile(re.escape(a)) for a in self.filters]
+ self.rfilters = [re.compile(re.escape(a)) for a in self.rfilters]
+
+ def setConfigs(self, configs):
+ self.configs = configs
+
+ def useBinDist(self):
+ return os.environ.get("USE_BIN_DIST", "no") == "yes"
+
+ def getIceDir(self, mapping=None):
+ if self.useBinDist():
+ iceHome = os.environ.get("ICE_HOME", "")
+ return iceHome if iceHome else platform.getIceDir()
+ elif mapping:
+ return mapping.getPath()
+ else:
+ return toplevel
+
+ def getSliceDir(self):
+ return platform.getSliceDir(self.getIceDir())
+
+ 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 destroy(self):
+ pass
+
+
+class CppMapping(Mapping):
+
+ class Config(Mapping.Config):
+
+ 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
+
+ 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 createConfig(self, options):
+ return CppMapping.Config(options)
+
+ 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, protocol="ssl"):
+ props = Mapping.getSSLProps(self, process, protocol)
+ props.update({
+ "IceSSL.Password": "password",
+ "IceSSL.DefaultDir": os.path.join(toplevel, "certs"),
+ "IceSSL.CAs": "cacert.pem",
+ "IceSSL.VerifyPeer": "0" if protocol == "wss" else "2",
+ "IceSSL.CertFile": "server.p12" if isinstance(process, Server) else "client.p12",
+ })
+ if isinstance(platform, Darwin):
+ props.update({
+ "IceSSL.KeychainPassword" : "password",
+ "IceSSL.Keychain": "server.keychain" if isinstance(process, Server) else "client.keychain"
+ })
+ return props
+
+ def getPluginEntryPoint(self, plugin):
+ 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(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(current.getBuildDir(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):
+ if process.isFromBinDir():
+ return "java {0}".format(exe)
+
+ assert(current.testcase.getPath().startswith(self.getTestsPath()))
+ package = "test." + current.testcase.getPath()[len(self.getTestsPath()) + 1:].replace(os.sep, ".")
+ return "java {0}.{1}".format(package, exe)
+
+ def getSSLProps(self, process, protocol="ssl"):
+ props = Mapping.getSSLProps(self, process, protocol)
+ props.update({
+ "IceSSL.Password": "password",
+ "IceSSL.DefaultDir": os.path.join(toplevel, "certs"),
+ "IceSSL.VerifyPeer": "0" if protocol == "wss" else "2",
+ "IceSSL.Keystore": "server.jks" if isinstance(process, Server) else "client.jks",
+ })
+ return props
+
+ def getPluginEntryPoint(self, plugin):
+ 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):
+ 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):
+ # Executables are not produced in build sub-directory with the C# mapping.
+ return ""
+
+ def getSSLProps(self, process, protocol="ssl"):
+ props = Mapping.getSSLProps(self, process, protocol)
+ props.update({
+ "IceSSL.Password": "password",
+ "IceSSL.DefaultDir": os.path.join(toplevel, "certs"),
+ "IceSSL.CAs": "cacert.pem",
+ "IceSSL.VerifyPeer": "0" if protocol == "wss" else "2",
+ "IceSSL.CertFile": "server.p12" if isinstance(process, Server) else "client.p12",
+ })
+ return props
+
+ def getPluginEntryPoint(self, plugin):
+ return {
+ "IceSSL" : "{icedir}/Assemblies/IceSSL.dll:IceSSL.PluginFactory",
+ "IceDiscovery" : "{icedir}/Assemblies/IceDiscovery.dll:IceDiscovery.PluginFactory"
+ }[plugin]
+
+ def getEnv(self, process, current):
+ if current.driver.useBinDist():
+ bzip2 = Mapping.getByName("cpp").getLibDir(current)
+ else:
+ bzip2 = os.path.join(toplevel, "cpp", "msbuild", "packages",
+ "bzip2.{0}.1.0.6.4".format(platform.getCompiler()),
+ "build", "native", "bin", "x64", "Release")
+
+ return { "DEVPATH" : os.path.join(current.driver.getIceDir(self), "Assemblies"), "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):
+
+ def getSSLProps(self, process, protocol="ssl"):
+ return Mapping.getByName("cpp").getSSLProps(process, protocol)
+
+ def getPluginEntryPoint(self, plugin):
+ return Mapping.getByName("cpp").getPluginEntryPoint(plugin)
+
+ def getEnv(self, process, current):
+ env = Mapping.getEnv(self, process, current)
+ if current.driver.getIceDir() != platform.getIceDir():
+ # 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(current)
+ return env
+
+class ObjCMapping(CppBasedMapping):
+
+ def getDefaultSource(self, processType):
+ return {
+ "client" : "Client.m",
+ "server" : "Server.m",
+ "collocated" : "Collocated.m",
+ }[processType]
+
+class PythonMapping(CppBasedMapping):
+
+ def getCommandLine(self, current, process, exe):
+ return sys.executable + " " + exe
+
+ def getEnv(self, process, current):
+ env = CppBasedMapping.getEnv(self, process, current)
+ if current.driver.getIceDir() != platform.getIceDir():
+ # If not installed in the default platform installation directory, add
+ # the Ice python directory to PYTHONPATH
+ env["PYTHONPATH"] = os.path.join(current.driver.getIceDir(self), "python")
+ return env
+
+ 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, filters, rfilters):
+ Mapping.loadTestSuites(self, tests, filters, rfilters)
+ self.getServerMapping().loadTestSuites(self.testsuites.keys())
+
+ 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):
+
+ def getCommandLine(self, current, process, exe):
+ return "ruby " + exe
+
+ def getEnv(self, process, current):
+ env = CppBasedMapping.getEnv(self, process, current)
+ if current.driver.getIceDir() != platform.getIceDir():
+ # 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):
+
+ def getCommandLine(self, current, process, exe):
+ args = []
+ if current.driver.getIceDir() == platform.getIceDir():
+ #
+ # 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 += ["-d", "extension_dir=/usr/local/lib/php/extensions"]
+ args += ["-d", "include_path=/usr/local/share/php"]
+ args += ["-d", "extension=IcePHP.so"]
+ else:
+ args += ["-d", "extension_dir='{0}'".format(self.getLibDir(current))]
+ args += ["-d", "extension='{0}'".format("php_ice.dll" if isinstance(platform, Windows) else "IcePHP.so")]
+ args += ["-d", "include_path='{0}'".format(self.getLibDir(current))]
+ 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"])
+
+ @classmethod
+ def usage(self):
+ print("")
+ print("JavaScript mapping options:")
+ print("--es5 Use JavaScript ES5 (Babel compiled code).")
+
+ def __init__(self, options=[]):
+ Mapping.Config.__init__(self, options)
+ self.es5 = False
+ parseOptions(self, options)
+
+ def createConfig(self, options):
+ return JavaScriptMapping.Config(options)
+
+ def loadTestSuites(self, tests, filters, rfilters):
+ Mapping.loadTestSuites(self, tests, filters, rfilters)
+ self.getServerMapping().loadTestSuites(self.testsuites.keys() + ["Ice/echo"])
+
+ 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(current)
+ return env
+
+ def getTestCwd(self, 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):
+ # JavaScript with NodeJS only supports tcp and no other options
+ return { "protocol" : ["tcp"], "compress" : [False], "ipv6" : [False], "serialize" : [False], "mx" : [False] }
+
+from Glacier2Util import *
+from IceBoxUtil import *
+from IceGridUtil import *
+from IceStormUtil import *
+from IcePatch2Util 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()
+
+ 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 mappings:
+ 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 mappings]
+ 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
+ #
+ configs = {}
+ for mapping in mappings:
+ configs[mapping] = mapping.createConfig(opts)
+
+ if len(opts) > 0:
+ print(sys.argv[0] + ": unknown options {0}".format(opts))
+ usage()
+ sys.exit(1)
+
+ #
+ # 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, driver.filters, driver.rfilters)
+
+ #
+ # Finally, run the test suites with the driver.
+ #
+ try:
+ driver.run(mappings)
+ except KeyboardInterrupt:
+ pass
+ finally:
+ driver.destroy()
+
+ except Exception as e:
+ print(sys.argv[0] + ": unexpected exception raised:\n" + traceback.format_exc())
+ sys.exit(1)