#
# Copyright (c) ZeroC, Inc. All rights reserved.
#
import os, sys, runpy, getopt, traceback, types, threading, time, datetime, re, itertools, random, subprocess, shutil
import copy, inspect, xml.sax.saxutils
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, stdout=False, stdin=None, stdinRepeat=True):
p = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE, stdout=None if stdout else subprocess.PIPE,
stderr=subprocess.STDOUT, cwd=cwd)
try:
if stdin:
try:
while True:
p.stdin.write(stdin)
p.stdin.flush()
if not stdinRepeat:
break
time.sleep(1)
except:
pass
out = (p.stderr if stdout else 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)
finally:
#
# Without this we get warnings when running with python_d on Windows
#
# ResourceWarning: unclosed file <_io.TextIOWrapper name=3 encoding='cp1252'>
#
(p.stderr if stdout else p.stdout).close()
try:
p.stdin.close()
except Exception:
pass
return out
def val(v, 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
v = v.replace("\\", "\\\\").replace("\"", "\\\"")
return "\"{0}\"".format(v)
else:
return str(v)
illegalXMLChars = re.compile(u'[\x00-\x08\x0b\x0c\x0e-\x1F\uD800-\uDFFF\uFFFE\uFFFF]')
def escapeXml(s, attribute=False):
# Remove backspace characters from the output (they aren't accepted by Jenkins XML parser)
if isPython2:
s = "".join(ch for ch in unicode(s.decode("utf-8")) if ch != u"\u0008").encode("utf-8")
else:
s = "".join(ch for ch in s if ch != u"\u0008")
s = illegalXMLChars.sub("?", s) # Strip invalid XML characters
return xml.sax.saxutils.quoteattr(s) if attribute else xml.sax.saxutils.escape(s)
"""
Component abstract class. The driver and mapping classes rely on the component
class to provide component specific information.
"""
class Component(object):
def __init__(self):
self.nugetVersion = None
"""
Returns whether or not to use the binary distribution.
"""
def useBinDist(self, mapping, current):
return True
"""
Returns the component installation directory if using a binary distribution
or the mapping directory if using a source distribution.
"""
def getInstallDir(self, mapping, current):
raise Error("must be overriden")
def getSourceDir(self):
return toplevel
def getTestDir(self, mapping):
if isinstance(mapping, JavaMapping):
return os.path.join(mapping.getPath(), "test", "src", "main", "java", "test")
elif isinstance(mapping, TypeScriptMapping):
return os.path.join(mapping.getPath(), "test", "typescript")
return os.path.join(mapping.getPath(), "test")
def getScriptDir(self):
return os.path.join(self.getSourceDir(), "scripts", "tests")
def getPhpExtension(self, mapping, current):
raise RuntimeError("must be overriden if component provides php mapping")
def getNugetPackage(self, mapping):
return "zeroc.{0}.{1}".format(self.__class__.__name__.lower(),
"net" if isinstance(mapping, CSharpMapping) else platform.getPlatformToolset())
def getNugetPackageVersion(self, mapping):
if not self.nugetVersion:
file = self.getNugetPackageVersionFile(mapping)
if file.endswith(".nuspec"):
expr = "(.*)"
elif file.endswith("packages.config"):
expr = "id=\"{0}\" version=\"(.*)\" target".format(self.getNugetPackage(mapping))
if expr:
with open(file, "r") as config:
m = re.search(expr, config.read())
if m:
self.nugetVersion = m.group(1)
if not self.nugetVersion:
raise RuntimeError("couldn't figure out the nuget version from `{0}'".format(file))
return self.nugetVersion
def getNugetPackageVersionFile(self, mapping):
raise RuntimeError("must be overriden if component provides C++ or C# nuget packages")
def getFilters(self, mapping, config):
return ([], [])
def canRun(self, testId, mapping, current):
return True
def isMainThreadOnly(self, testId):
return True
def getDefaultProcesses(self, mapping, processType, testId):
return None
def getDefaultExe(self, mapping, processType):
return None
def getDefaultSource(self, mapping, processType):
return None
def getOptions(self, testcase, current):
return None
def getRunOrder(self):
return []
def getEnv(self, process, current):
return {}
def getProps(self, process, current):
return {}
def isCross(self, testId):
return False
def getSliceDir(self, mapping, current):
installDir = self.getInstallDir(mapping, current)
if installDir.endswith(mapping.name):
installDir = installDir[0:len(installDir) - len(mapping.name) - 1]
if platform.getInstallDir() and installDir == platform.getInstallDir():
return os.path.join(installDir, "share", "ice", "slice")
else:
return os.path.join(installDir, "slice")
def getBinDir(self, process, mapping, current):
return platform._getBinDir(self, process, mapping, current)
def getLibDir(self, process, mapping, current):
return platform._getLibDir(self, process, mapping, current)
def getPhpIncludePath(self, mapping, current):
return "{0}/{1}".format(self.getInstallDir(mapping, current), "php" if self.useBinDist(mapping, current) else "lib")
def _useBinDist(self, mapping, current, envName):
env = os.environ.get(envName, "").split()
return 'all' in env or mapping.name in env
def _getInstallDir(self, mapping, current, envHomeName):
if self.useBinDist(mapping, current):
# On Windows or for the C# mapping we first look for Nuget packages rather than the binary installation
if isinstance(platform, Windows) or isinstance(mapping, CSharpMapping):
packageDir = platform.getNugetPackageDir(self, mapping, current)
if envHomeName and not os.path.exists(packageDir):
home = os.environ.get(envHomeName, "")
if not home or not os.path.exists(home):
raise RuntimeError("Cannot detect a valid distribution in `" + envHomeName + "'")
return home
else:
return packageDir
else:
return os.environ.get(envHomeName, platform.getInstallDir())
elif mapping:
return mapping.getPath()
else:
return self.getSourceDir()
class Platform(object):
def __init__(self):
try:
run("dotnet --version")
self.nugetPackageCache = re.search("info : global-packages: (.*)",
run("dotnet nuget locals --list global-packages")).groups(1)[0]
except:
self.nugetPackageCache = None
def init(self, component):
self.parseBuildVariables(component, {
"supported-platforms" : ("supportedPlatforms", lambda s : s.split(" ")),
"supported-configs" : ("supportedConfigs", lambda s : s.split(" "))
})
def hasDotNet(self):
return self.nugetPackageCache != None
def parseBuildVariables(self, component, variables):
# Run make to get the values of the given variables
if os.path.exists(os.path.join(component.getSourceDir(), "Makefile")): # Top level makefile
cwd = component.getSourceDir()
elif Mapping.getByName("cpp"):
cwd = Mapping.getByName("cpp").getPath()
output = run('gmake print V="{0}"'.format(" ".join(variables.keys())), cwd=cwd)
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 self.supportedPlatforms[0]
def getDefaultBuildConfig(self):
return self.supportedConfigs[0]
def _getBinDir(self, component, process, mapping, current):
installDir = component.getInstallDir(mapping, current)
if isinstance(mapping, CSharpMapping):
if component.useBinDist(mapping, current):
return os.path.join(installDir, "tools", "netcoreapp2.1")
else:
return os.path.join(installDir, "bin", "netcoreapp2.1")
return os.path.join(installDir, "bin")
def _getLibDir(self, component, process, mapping, current):
installDir = component.getInstallDir(mapping, current)
if isinstance(mapping, CSharpMapping):
return os.path.join(installDir, "lib", "netstandard2.0")
return os.path.join(installDir, "lib")
def getBuildSubDir(self, mapping, name, current):
# Return the build sub-directory, to be overriden by specializations
buildPlatform = current.driver.configs[mapping].buildPlatform
buildConfig = current.driver.configs[mapping].buildConfig
return os.path.join("build", buildPlatform, buildConfig)
def getLdPathEnvName(self):
return "LD_LIBRARY_PATH"
def getInstallDir(self):
return "/usr"
def getNugetPackageDir(self, component, mapping, current):
if not self.nugetPackageCache:
return None
return os.path.join(self.nugetPackageCache,
component.getNugetPackage(mapping),
component.getNugetPackageVersion(mapping))
def hasOpenSSL(self):
# This is used by the IceSSL test suite to figure out how to setup certificates
return False
def getDotNetExe(self):
return "dotnet"
class Darwin(Platform):
def getDefaultBuildPlatform(self):
return "macosx"
def getLdPathEnvName(self):
return "DYLD_LIBRARY_PATH"
def getInstallDir(self):
return "/usr/local"
class AIX(Platform):
def hasOpenSSL(self):
return True
class FreeBSD(Platform):
def __init__(self):
self.nugetPackageCache = None
def hasOpenSSL(self):
return True
def getSliceDir(self, iceDir):
installDir = self.getInstallDir(mapping, current)
return os.path.join(installDir, "slice")
def getDefaultExe(self, name, config):
if name == "icebox":
if config.cpp11:
name += "++11"
return name
def canRun(self, mapping, current):
return Platform.canRun(self, mapping, current)
class Linux(Platform):
def __init__(self):
Platform.__init__(self)
self.multiArch = {}
def init(self, component):
Platform.init(self, component)
self.parseBuildVariables(component, {
"linux_id" : ("linuxId", None),
"build-platform" : ("buildPlatform", None),
"foreign-platforms" : ("foreignPlatforms", lambda s : s.split(" ") if s else []),
})
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 _getBinDir(self, component, process, mapping, current):
installDir = component.getInstallDir(mapping, current)
if isinstance(mapping, CSharpMapping):
return Platform._getBinDir(self, component, process, mapping, current)
if self.linuxId in ["ubuntu", "debian"]:
binDir = os.path.join(installDir, "bin", self.multiArch[current.driver.configs[mapping].buildPlatform])
if os.path.exists(binDir):
return binDir
return os.path.join(installDir, "bin")
def _getLibDir(self, component, process, mapping, current):
installDir = component.getInstallDir(mapping, current)
if isinstance(mapping, CSharpMapping):
return Platform._getLibDir(self, component, process, mapping, current)
buildPlatform = current.driver.configs[mapping].buildPlatform
# PHP module is always installed in the lib directory for the default build platform
if isinstance(mapping, PhpMapping) and buildPlatform == self.getDefaultBuildPlatform():
return os.path.join(installDir, "lib")
if self.linuxId in ["centos", "rhel", "fedora"]:
return os.path.join(installDir, "lib64" if buildPlatform == "x64" else "lib")
elif self.linuxId in ["ubuntu", "debian"]:
return os.path.join(installDir, "lib", self.multiArch[buildPlatform])
return os.path.join(installDir, "lib")
def getBuildSubDir(self, mapping, name, current):
buildPlatform = current.driver.configs[mapping].buildPlatform
buildConfig = current.driver.configs[mapping].buildConfig
if self.linuxId in ["ubuntu", "debian"]:
return os.path.join("build", self.multiArch[buildPlatform], buildConfig)
else:
return os.path.join("build", buildPlatform, buildConfig)
def getLinuxId(self):
return self.linuxId
class Windows(Platform):
def __init__(self):
Platform.__init__(self)
self.compiler = None
def parseBuildVariables(self, component, variables):
pass # Nothing to do, we don't support the make build system on Windows
def getDefaultBuildPlatform(self):
return "x64" if "X64" in os.environ.get("PLATFORM", "") else "Win32"
def getDefaultBuildConfig(self):
return "Release"
def getCompiler(self):
if self.compiler != None:
return self.compiler
if os.environ.get("CPP_COMPILER", "") != "":
self.compiler = os.environ["CPP_COMPILER"]
else:
try:
out = run("cl")
if out.find("Version 16.") != -1:
self.compiler = "v100"
elif out.find("Version 17.") != -1:
self.compiler = "v110"
elif out.find("Version 18.") != -1:
self.compiler = "v120"
elif out.find("Version 19.00.") != -1:
self.compiler = "v140"
elif out.find("Version 19.") != -1:
self.compiler = "v141"
else:
raise RuntimeError("Unknown compiler version:\n{0}".format(out))
except:
self.compiler = ""
return self.compiler
def getPlatformToolset(self):
return self.getCompiler().replace("VC", "v")
def _getBinDir(self, component, process, mapping, current):
installDir = component.getInstallDir(mapping, current)
platform = current.driver.configs[mapping].buildPlatform
config = "Debug" if current.driver.configs[mapping].buildConfig.find("Debug") >= 0 else "Release"
if component.useBinDist(mapping, current):
if installDir != self.getNugetPackageDir(component, mapping, current):
return os.path.join(installDir, "bin")
elif isinstance(process, SliceTranslator):
return os.path.join(installDir, "tools")
elif isinstance(mapping, CSharpMapping):
return os.path.join(installDir, "tools", mapping.getBinTargetFramework(current))
elif process.isReleaseOnly():
# Some services are only available in release mode in the Nuget package
return os.path.join(installDir, "build", "native", "bin", platform, "Release")
else:
return os.path.join(installDir, "build", "native", "bin", platform, config)
else:
if isinstance(mapping, CSharpMapping):
return os.path.join(installDir, "bin", mapping.getBinTargetFramework(current))
elif isinstance(mapping, PhpMapping):
return os.path.join(self.getNugetPackageDir(component, mapping, current),
"build", "native", "bin", platform, config)
else:
return os.path.join(installDir, "bin", platform, config)
def _getLibDir(self, component, process, mapping, current):
installDir = component.getInstallDir(mapping, current)
if isinstance(mapping, CSharpMapping):
return os.path.join(installDir, "lib", mapping.getLibTargetFramework(current))
else:
platform = current.driver.configs[mapping].buildPlatform
config = "Debug" if current.driver.configs[mapping].buildConfig.find("Debug") >= 0 else "Release"
if isinstance(mapping, PhpMapping):
return os.path.join(installDir, "lib", "php-{0}".format(current.config.phpVersion), platform, config)
elif component.useBinDist(mapping, current):
return os.path.join(installDir, "build", "native", "bin", platform, config)
else:
return os.path.join(installDir, "bin", platform, config)
def getBuildSubDir(self, mapping, name, current):
buildPlatform = current.driver.configs[mapping].buildPlatform
buildConfig = current.driver.configs[mapping].buildConfig
if os.path.exists(os.path.join(current.testcase.getPath(current), "msbuild", name)):
return os.path.join("msbuild", name, buildPlatform, buildConfig)
else:
return os.path.join("msbuild", buildPlatform, buildConfig)
def getLdPathEnvName(self):
return "PATH"
def getInstallDir(self):
return None # No default installation directory on Windows
def getNugetPackageDir(self, component, mapping, current):
if isinstance(mapping, CSharpMapping) and current.config.dotnetcore:
return Platform.getNugetPackageDir(self, component, mapping, current)
else:
package = "{0}.{1}".format(component.getNugetPackage(mapping), component.getNugetPackageVersion(mapping))
# The package directory is either under the msbuild directory or in the mapping directory depending
# on where the solution is located.
if os.path.exists(os.path.join(mapping.path, "msbuild", "packages")):
return os.path.join(mapping.path, "msbuild", "packages", package)
else:
return os.path.join(mapping.path, "packages", package)
def getDotNetExe(self):
try:
return run("where dotnet").strip()
except:
return None
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 not a and o.startswith("no-"):
a = "false"
o = o[3:]
if o in mapped:
o = mapped[o]
if hasattr(obj, o):
if isinstance(getattr(obj, o), bool):
setattr(obj, o, True if not a else (a.lower() in ["yes", "true", "1"]))
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
"""
Mapping abstract class. The mapping class provides mapping specific information.
Multiple components can share the same mapping rules as long as the layout is
similar.
"""
class Mapping(object):
mappings = OrderedDict()
class Config(object):
@classmethod
def getSupportedArgs(self):
return ("", ["config=", "platform=", "protocol=", "target=", "compress", "ipv6", "no-ipv6", "serialize",
"mx", "cprops=", "sprops="])
@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.")
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.pathOverride = ""
self.protocol = "tcp"
self.compress = False
self.serialize = False
self.ipv6 = False
self.mx = False
self.cprops = []
self.sprops = []
# Options bellow are not parsed by the base class by still initialized here for convenience (this
# avoid having to check the configuration type)
self.uwp = False
self.openssl = False
self.browser = ""
self.es5 = False
self.worker = False
self.dotnetcore = False
self.framework = ""
self.android = False
self.xamarin = False
self.device = ""
self.avd = ""
self.phpVersion = "7.1"
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(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:
if v:
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:
if v:
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)
return [c for c in gen(current.driver.filterOptions(current.driver.getComponent().getOptions(testcase, current)))]
def canRun(self, testId, current):
if not current.driver.getComponent().canRun(testId, current.testcase.getMapping(), 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 hasattr(self, k):
if not getattr(self, k) in v:
return False
elif hasattr(current.driver, k):
if not getattr(current.driver, 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"] = "tcp -h \"::1\"" if self.ipv6 else "tcp -h 127.0.0.1"
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"
#
# Speed up Windows testing. We override the connect timeout for some tests which are
# establishing connections to inactive ports. It takes around 1s for such connection
# establishment to fail on Windows.
#
# if isinstance(platform, Windows):
# if current.testsuite.getId().startswith("IceGrid") or \
# current.testsuite.getId() in ["Ice/binding",
# "Ice/location",
# "Ice/background",
# "Ice/faultTolerance",
# "Ice/services",
# "IceDiscovery/simple"]:
# props["Ice.Override.ConnectTimeout"] = "400"
# 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.normpath(path)
for m in self.mappings.values():
if path.startswith(os.path.normpath(m.getTestDir())):
return m
@classmethod
def getAllByPath(self, path):
path = os.path.abspath(path)
mappings = []
for m in self.mappings.values():
if path.startswith(m.getPath() + os.sep):
mappings.append(m)
return mappings
@classmethod
def add(self, name, mapping, component, path=None):
name = name.replace("\\", "/")
self.mappings[name] = mapping.init(name, component, path)
@classmethod
def remove(self, name):
del self.mappings[name]
@classmethod
def getAll(self, driver=None):
return [m for m in self.mappings.values() if not driver or driver.matchLanguage(str(m))]
def __init__(self, path=None):
self.name = None
self.path = os.path.abspath(path) if path else None
self.testsuites = {}
def init(self, name, component, path=None):
self.name = name
self.component = component
if not self.path:
self.path = os.path.normpath(os.path.join(self.component.getSourceDir(), path or name))
return self
def __str__(self):
return self.name
def getTestDir(self):
return self.component.getTestDir(self)
def createConfig(self, options):
return self.Config(options)
def filterTestSuite(self, testId, config, filters=[], rfilters=[]):
if len(filters) > 0:
for f in filters:
if f.search(self.name + "/" + testId):
break
else:
return True
if len(rfilters) > 0:
for f in rfilters:
if f.search(self.name + "/" + testId):
return True
return False
def loadTestSuites(self, tests, config, filters=[], rfilters=[]):
global currentMapping
currentMapping = self
try:
origsyspath = sys.path
prefix = os.path.commonprefix([toplevel, self.component.getScriptDir()])
moduleprefix = self.component.getScriptDir()[len(prefix) + 1:].replace(os.sep, ".") + "."
sys.path = [prefix] + sys.path
for test in tests or [""]:
testDir = self.component.getTestDir(self)
for root, dirs, files in os.walk(os.path.join(testDir, test.replace('/', os.sep))):
testId = root[len(testDir) + 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 test 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 one we use it.
#
if os.path.isfile(os.path.join(self.component.getScriptDir(), testId + ".py")):
runpy.run_module(moduleprefix + 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)
finally:
currentMapping = None
sys.path = origsyspath
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.component.getTestDir(self)) + 1
testSuiteId = testsuite.path[len(self.component.getTestDir(self)) + 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
return False
checkClient = lambda f: checkFile(f, self.getClientMapping(testId))
checkServer = lambda f: checkFile(f, self.getServerMapping(testId))
testcases = []
if checkClient("client") and checkServer("server"):
testcases.append(ClientServerTestCase())
if checkClient("client") and checkServer("serveramd") and self.getServerMapping(testId) == self:
testcases.append(ClientAMDServerTestCase())
if checkClient("client") and checkServer("servertie") and self.getServerMapping(testId) == self:
testcases.append(ClientTieServerTestCase())
if checkClient("client") and checkServer("serveramdtie") and self.getServerMapping(testId) == self:
testcases.append(ClientAMDTieServerTestCase())
if checkClient("client") and len(testcases) == 0:
testcases.append(ClientTestCase())
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.component.getTestDir(self), testId, self.getDefaultSource(processType)))
except KeyError:
return False
def getPath(self):
return self.path
def getTestCwd(self, process, current):
return current.testcase.getPath(current)
def getDefaultSource(self, processType):
default = self.component.getDefaultSource(self, processType)
if default:
return default
return self._getDefaultSource(processType)
def getDefaultProcesses(self, processType, testsuite):
default = self.component.getDefaultProcesses(self, processType, testsuite.getId())
if default:
return default
return self._getDefaultProcesses(processType)
def getDefaultExe(self, processType):
default = self.component.getDefaultExe(self, processType)
if default:
return default
return self._getDefaultExe(processType)
def _getDefaultSource(self, processType):
return processType
def _getDefaultProcesses(self, processType):
#
# If no server or client is explicitly set with a testcase, getDefaultProcess is called
# to figure out which process class to instantiate.
#
name, ext = os.path.splitext(self.getDefaultSource(processType))
if name in globals():
return [globals()[name]()]
return [Server()] if processType.startswith("server") else [Client()] if processType else []
def _getDefaultExe(self, processType):
return os.path.splitext(self.getDefaultSource(processType))[0]
def getClientMapping(self, testId=None):
# The client mapping is always the same as this mapping.
return self
def getServerMapping(self, testId=None):
# Can be overridden for client-only mapping that relies on another mapping for servers
return self
def getBuildDir(self, name, current):
return platform.getBuildSubDir(self, name, current)
def getCommandLine(self, current, process, exe, args):
cmd = ""
if process.isFromBinDir():
# If it's a process from the bin directory, the location is platform specific
# so we check with the platform.
cmd = os.path.join(self.component.getBinDir(process, self, current), exe)
elif current.testcase:
# If it's a process from a testcase, the binary is in the test build directory.
cmd = os.path.join(current.testcase.getPath(current), current.getBuildDir(exe), exe)
else:
cmd = exe
if isinstance(platform, Windows) and not exe.endswith(".exe"):
cmd += ".exe"
return cmd + " " + args if args else cmd
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": "" if current.config.buildPlatform == "iphoneos" else os.path.join(self.component.getSourceDir(), "certs"),
}
#
# If the client doesn't support client certificates, set IceSSL.VerifyPeer to 0
#
if isinstance(process, Server):
if isinstance(current.testsuite.getMapping(), JavaScriptMixin):
sslProps["IceSSL.VerifyPeer"] = 0
return sslProps
def getArgs(self, process, current):
return []
def getEnv(self, process, current):
return {}
def getOptions(self, current):
return {}
#
# A Runnable can be used as a "client" for in test cases, it provides
# implements run, setup and teardown methods.
#
class Runnable(object):
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, preexec_fn=None, traceProps=None):
Runnable.__init__(self, desc)
self.exe = exe
self.outfilters = outfilters or []
self.quiet = quiet
self.args = args or []
self.props = props or {}
self.traceProps = traceProps or {}
self.envs = envs or {}
self.mapping = mapping
self.preexec_fn = preexec_fn
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, encoding="utf-8"):
assert(self in current.processes)
def d(s):
return s if isPython2 else s.decode(encoding) 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=None):
class WatchDog:
def __init__(self, timeout):
self.lastProgressTime = time.time()
self.lock = threading.Lock()
def reset(self):
with self.lock: self.lastProgressTime = time.time()
def timedOut(self, timeout):
with self.lock:
return (time.time() - self.lastProgressTime) >= timeout
watchDog = WatchDog(timeout)
self.start(current, args, props, watchDog=watchDog)
process = current.processes[self]
if timeout is None:
# If it's not a local process use a large timeout as the watch dog might not
# get invoked (TODO: improve remote processes to use the watch dog)
timeout = 60 if isinstance(process, Expect.Expect) else 480
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.
process.trace(self.outfilters)
try:
while True:
try:
process.waitSuccess(exitstatus=exitstatus, timeout=30)
break
except KeyboardInterrupt:
current.driver.setInterrupt(True)
raise
except Expect.TIMEOUT:
if watchDog and watchDog.timedOut(timeout):
print("process {0} is hanging - {1}".format(process, time.strftime("%x %X")))
if current.driver.isInterrupted():
self.stop(current, False, exitstatus)
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.driver.getComponent().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):
def merge(envs, newEnvs):
if platform.getLdPathEnvName() in newEnvs and platform.getLdPathEnvName() in envs:
newEnvs[platform.getLdPathEnvName()] += os.pathsep + envs[platform.getLdPathEnvName()]
envs.update(newEnvs)
allEnvs = {}
merge(allEnvs, current.driver.getComponent().getEnv(self, current))
merge(allEnvs, self.getMapping(current).getEnv(self, current))
merge(allEnvs, current.testcase.getEnv(self, current))
merge(allEnvs, self.getEnv(current))
merge(allEnvs, self.envs(self, current) if callable(self.envs) else self.envs)
return allEnvs
def getEffectiveTraceProps(self, current):
traceProps = {}
traceProps.update(current.testcase.getTraceProps(self, current))
traceProps.update(self.traceProps(self, current) if callable(self.traceProps) else self.traceProps)
return traceProps
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, self)
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:
process = current.processes[self]
try:
# Wait for the process to exit successfully by itself.
if not process.isTerminated() and waitSuccess:
while True:
try:
process.waitSuccess(exitstatus=exitstatus, timeout=30)
break
except KeyboardInterrupt:
current.driver.setInterrupt(True)
raise
except Expect.TIMEOUT:
print("process {0} is hanging on shutdown - {1}".format(process, time.strftime("%x %X")))
if current.driver.isInterrupted():
raise
finally:
if not process.isTerminated():
process.terminate()
if not self.quiet: # Write the output to the test case (but not on stdout)
current.write(self.getOutput(current), stdout=False)
def teardown(self, current, success):
if self in current.processes:
current.processes[self].teardown(current, success)
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 expectall(self, current, pattern, timeout=60):
assert(self in current.processes and isinstance(current.processes[self], Expect.Expect))
return current.processes[self].expectall(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 and not current.processes[self].isTerminated()
def isFromBinDir(self):
return False
def isReleaseOnly(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)
def getCommandLine(self, current, args=""):
return self.getMapping(current).getCommandLine(current, self, self.getExe(current), args).strip()
#
# 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=300, *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
#
# Executables for processes inheriting this marker class are only provided
# as a Release executble on Windows
#
class ProcessIsReleaseOnly:
def isReleaseOnly(self):
return True
class SliceTranslator(ProcessFromBinDir, ProcessIsReleaseOnly, SimpleClient):
def __init__(self, translator):
SimpleClient.__init__(self, exe=translator, quiet=True, mapping=Mapping.getByName("cpp"))
def getCommandLine(self, current, args=""):
#
# Look for slice2py installed by Pip if not found in the bin directory
#
if self.exe == "slice2py":
translator = self.getMapping(current).getCommandLine(current, self, self.getExe(current), "")
if os.path.exists(translator):
return translator + " " + args if args else translator
elif isinstance(platform, Windows):
return os.path.join(os.path.dirname(sys.executable), "Scripts", "slice2py.exe") + " " + args if args else translator
elif os.path.exists("/usr/local/bin/slice2py"):
return "/usr/local/bin/slice2py" + " " + args if args else translator
else:
import slice2py
return sys.executable + " " + os.path.normpath(
os.path.join(slice2py.__file__, "..", "..", "..", "..", "bin", "slice2py")) + " " + args if args else translator
else:
return Process.getCommandLine(self, current, args)
class ServerAMD(Server):
pass
class Collocated(Client):
pass
class EchoServer(Server):
def __init__(self):
Server.__init__(self, mapping=Mapping.getByName("cpp"), quiet=True, waitForShutdown=False)
def getProps(self, current):
props = Server.getProps(self, current)
props["Ice.MessageSizeMax"] = 8192 # Don't limit the amount of data to transmit between client/server
return props
def getCommandLine(self, current, args=""):
current.push(self.mapping.findTestSuite("Ice/echo").findTestCase("server"))
try:
return Server.getCommandLine(self, current, args)
finally:
current.pop()
#
# 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=None, props=None, envs=None,
options=None, desc=None, traceProps=None):
Runnable.__init__(self, desc)
self.name = name
self.parent = None
self.mapping = None
self.testsuite = None
self.options = options or {}
self.args = args or []
self.props = props or {}
self.traceProps = traceProps or {}
self.envs = envs or {}
#
# 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)
#
testId = self.testsuite.getId()
if not self.clients:
if self.getClientType():
self.clients = self.mapping.getClientMapping(testId).getDefaultProcesses(self.getClientType(), testsuite)
else:
self.clients = []
#
# 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:
if self.getServerType():
self.servers = self.mapping.getServerMapping(testId).getDefaultProcesses(self.getServerType(), testsuite)
else:
self.servers = []
def getOptions(self, current):
return self.options(current) if callable(self.options) else 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, current):
path = self.testsuite.getPath()
if current.config.pathOverride:
return path.replace(toplevel, current.config.pathOverride)
else:
return path
def getMapping(self):
return self.mapping
def getArgs(self, process, current):
return self.args
def getProps(self, process, current):
return self.props
def getTraceProps(self, process, current):
return self.traceProps
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(self.testsuite.getId()).findTestSuite(self.testsuite)
return testsuite.findTestCase(self) if testsuite else None
def getClientTestCase(self):
testsuite = self.mapping.getClientMapping(self.testsuite.getId()).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)
if not self.parent:
current.result.started(current)
self.setup(current)
self.runWithDriver(current)
self.teardown(current, True)
if not self.parent:
current.result.succeeded(current)
except Exception as ex:
self.teardown(current, False)
if not self.parent:
current.result.failed(current, traceback.format_exc() if current.driver.debug else str(ex))
raise
finally:
current.pop()
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 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 ClientTieServerTestCase(ClientServerTestCase):
def __init__(self, name="client/tie server", *args, **kargs):
ClientServerTestCase.__init__(self, name, *args, **kargs)
def getServerType(self):
return "servertie"
class ClientAMDTieServerTestCase(ClientServerTestCase):
def __init__(self, name="client/amd tie server", *args, **kargs):
ClientServerTestCase.__init__(self, name, *args, **kargs)
def getServerType(self):
return "serveramdtie"
class Result:
getKey = lambda self, current: (current.testcase, current.config) if isinstance(current, Driver.Current) else current
getDesc = lambda self, current: current.desc if isinstance(current, Driver.Current) else ""
def __init__(self, testsuite, writeToStdout):
self.testsuite = testsuite
self._failed = {}
self._skipped = {}
self._stdout = StringIO()
self._writeToStdout = writeToStdout
self._testcases = {}
self._duration = 0
self._testCaseDuration = 0;
def start(self):
self._duration = time.time()
def finished(self):
self._duration = time.time() - self._duration
def started(self, current):
self._testCaseDuration = time.time();
self._start = self._stdout.tell()
def failed(self, current, exception):
print(exception)
key = self.getKey(current)
self._testCaseDuration = time.time() - self._testCaseDuration;
self.writeln("\ntest in {0} failed:\n{1}".format(self.testsuite, exception))
self._testcases[key] = (self._start, self._stdout.tell(), self._testCaseDuration, self.getDesc(current))
self._failed[key] = exception
# If ADDRINUSE, dump the current processes
output = self.getOutput(key)
for s in ["EADDRINUSE", "Address already in use"]:
if output.find(s) >= 0:
if isinstance(platform, Windows):
self.writeln(run("netstat -on"))
self.writeln(run("powershell.exe \"Get-Process | Select id,name,path\""))
else:
self.writeln(run("lsof -n -P -i; ps ax"))
def succeeded(self, current):
key = self.getKey(current)
self._testCaseDuration = time.time() - self._testCaseDuration;
self._testcases[key] = (self._start, self._stdout.tell(), self._testCaseDuration, self.getDesc(current))
def skipped(self, current, reason):
self.writeln("skipped, " + reason)
self._skipped[self.getKey(current)] = reason
def isSuccess(self):
return len(self._failed) == 0
def getFailed(self):
return self._failed
def getDuration(self):
return self._duration
def getOutput(self, key=None):
if key:
if key in self._testcases:
(start, end, duration, desc) = self._testcases[key]
self._stdout.seek(start)
try:
return self._stdout.read(end - start)
finally:
self._stdout.seek(0, 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")
def writeAsXml(self, out, hostname=""):
out.write(' \n'
.format(len(self._testcases) - 2,
len(self._failed),
0,
self._duration,
self.testsuite,
self.testsuite.getMapping()))
for (k, v) in self._testcases.items():
if isinstance(k, str):
# Don't keep track of setup/teardown steps
continue
# Don't write skipped tests, this doesn't really provide useful information and clutters
# the output.
if k in self._skipped:
continue
(tc, cf) = k
(s, e, d, c) = v
if c:
name = "{0} [{1}]".format(tc, c)
else:
name = str(tc)
if hostname:
name += " on " + hostname
out.write(' \n'
.format(name,
d,
self.testsuite.getMapping(),
self.testsuite.getId().replace("/", ".")))
if k in self._failed:
last = self._failed[k].strip().split('\n')
if len(last) > 0:
last = last[len(last) - 1]
if hostname:
last = "Failed on {0}\n{1}".format(hostname, last)
out.write(' {0}\n'.format(escapeXml(self._failed[k]),
escapeXml(last, True)))
# elif k in self._skipped:
# out.write(' \n'.format(escapeXml(self._skipped[k], True)))
out.write(' \n')
if hostname:
out.write('Running on {0}\n'.format(hostname))
out.write(escapeXml(self.getOutput(k)))
out.write(' \n')
out.write(' \n')
out.write( '\n')
class TestSuite(object):
def __init__(self, path, testcases=None, options=None, libDirs=None, runOnMainThread=False, chdir=False,
multihost=True, mapping=None):
global currentMapping
self.path = os.path.dirname(path) if os.path.basename(path) == "test.py" else path
self.mapping = currentMapping or Mapping.getByPath(self.path)
self.id = self.mapping.addTestSuite(self)
self.options = options or {}
self.libDirs = libDirs or []
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
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(current) if callable(self.options) else self.options
def getPath(self):
return self.path
def getMapping(self):
return self.mapping
def getLibDirs(self):
return self.libDirs
def isMainThreadOnly(self, driver):
if self.runOnMainThread or driver.getComponent().isMainThreadOnly(self.id):
return True
for m in [CppMapping, JavaMapping, CSharpMapping, PythonMapping, PhpMapping, RubyMapping, JavaScriptMixin]:
if isinstance(self.mapping, m):
config = driver.configs[self.mapping]
if "iphone" in config.buildPlatform or config.uwp or config.browser or config.android:
return True # Not supported yet for tests that require a remote process controller
return False
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:
current.result.start()
cwd=None
if self.chdir:
cwd = os.getcwd()
os.chdir(self.path)
current.driver.runTestSuite(current)
finally:
if cwd: os.chdir(cwd)
current.result.finished()
def teardown(self, current, success):
pass
def isMultiHost(self):
return self.multihost
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 __init__(self, traceFile, *args, **kargs):
Expect.Expect.__init__(self, *args, **kargs)
self.traceFile = traceFile
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 isTerminated(self):
return self.p is None
def teardown(self, current, success):
if self.traceFile:
if success or current.driver.isInterrupted():
os.remove(self.traceFile)
else:
current.writeln("saved {0}".format(self.traceFile))
def getHost(self, current):
return current.driver.getHost(current.config.protocol, current.config.ipv6)
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.testsuite.getPath(),
"builddir": current.getBuildDir(process.getExe(current)),
}
traceFile = ""
if not isinstance(process.getMapping(current), JavaScriptMixin):
traceProps = process.getEffectiveTraceProps(current)
if traceProps:
if "Ice.ProgramName" in props:
programName = props["Ice.ProgramName"]
else:
programName = process.exe or current.testcase.getProcessType(process)
traceFile = os.path.join(current.testsuite.getPath(),
"{0}-{1}.log".format(programName, time.strftime("%m%d%y-%H%M")))
if isinstance(process.getMapping(current), ObjCMapping):
traceProps["Ice.StdErr"] = traceFile
else:
traceProps["Ice.LogFile"] = traceFile
props.update(traceProps)
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 = ""
if current.driver.valgrind:
cmd += "valgrind -q --child-silent-after-fork=yes --leak-check=full --suppressions=\"{0}\" ".format(
os.path.join(current.driver.getComponent().getSourceDir(), "config", "valgrind.sup"))
exe = process.getCommandLine(current, " ".join(args))
cmd += exe.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)
mapping = process.getMapping(current)
cwd = mapping.getTestCwd(process, current)
process = LocalProcessController.LocalProcess(command=cmd,
startReader=False,
env=env,
cwd=cwd,
desc=process.desc or exe,
preexec_fn=process.preexec_fn,
mapping=str(mapping),
traceFile=traceFile)
process.startReader(watchDog)
return process
class RemoteProcessController(ProcessController):
class RemoteProcess:
def __init__(self, exe, proxy):
self.exe = exe
self.proxy = proxy
self.terminated = False
self.stdout = False
def __str__(self):
return "{0} proxy={1}".format(self.exe, self.proxy)
def waitReady(self, ready, readyCount, startTimeout):
self.proxy.waitReady(startTimeout)
def waitSuccess(self, exitstatus=0, timeout=60):
import Ice
try:
result = self.proxy.waitSuccess(timeout)
except Ice.UserException:
raise Expect.TIMEOUT("waitSuccess timeout")
except Ice.LocalException:
raise
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 isTerminated(self):
return self.terminated
def terminate(self):
self.output = self.proxy.terminate().strip()
self.terminated = True
if self.stdout and self.output:
print(self.output)
def teardown(self, current, success):
pass
def __init__(self, current, endpoints="tcp"):
self.processControllerProxies = {}
self.controllerApps = []
self.driver = current.driver
self.cond = threading.Condition()
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)
import Ice
comm.getProperties().setProperty("Adapter.AdapterId", Ice.generateUUID())
self.adapter = comm.createObjectAdapterWithEndpoints("Adapter", endpoints)
self.adapter.add(ProcessControllerRegistryI(self), comm.stringToIdentity("Util/ProcessControllerRegistry"))
self.adapter.activate()
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)
import Ice
import Test
proxy = None
with self.cond:
if ident in self.processControllerProxies:
proxy = self.processControllerProxies[ident]
if proxy:
try:
proxy.ice_ping()
return proxy
except Ice.NoEndpointException:
self.clearProcessController(proxy)
comm = current.driver.getCommunicator()
if current.driver.controllerApp:
if ident in self.controllerApps:
self.restartControllerApp(current, ident) # Controller must have crashed, restart it
else:
self.controllerApps.append(ident)
self.startControllerApp(current, ident)
# Use well-known proxy and IceDiscovery to discover the process controller object from the app.
proxy = Test.Common.ProcessControllerPrx.uncheckedCast(comm.stringToProxy(comm.identityToString(ident)))
try:
proxy.ice_ping()
with self.cond:
self.processControllerProxies[ident] = proxy
return self.processControllerProxies[ident]
except Exception:
pass
# Wait 30 seconds for a process controller to be registered with the ProcessControllerRegistry
with self.cond:
if not ident in self.processControllerProxies:
self.cond.wait(30)
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=None):
with self.cond:
if proxy.ice_getIdentity() in self.processControllerProxies:
if not conn:
conn = proxy.ice_getCachedConnection()
if conn == self.processControllerProxies[proxy.ice_getIdentity()].ice_getCachedConnection():
del self.processControllerProxies[proxy.ice_getIdentity()]
def startControllerApp(self, current, ident):
pass
def restartControllerApp(self, current, ident):
self.stopControllerApp(ident)
self.startControllerApp(current, ident)
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 AndroidProcessController(RemoteProcessController):
def __init__(self, current):
endpoint = None
if current.config.xamarin or current.config.device:
endpoint = "tcp -h 0.0.0.0 -p 15001"
elif current.config.avd or not current.config.device:
endpoint = "tcp -h 127.0.0.1 -p 15001"
RemoteProcessController.__init__(self, current, endpoint)
self.device = current.config.device
self.avd = current.config.avd
self.emulator = None # Keep a reference to the android emulator process
def __str__(self):
return "Android"
def getControllerIdentity(self, current):
if isinstance(current.testcase.getMapping(), CSharpMapping):
return "AndroidXamarin/ProcessController"
elif isinstance(current.testcase.getMapping(), JavaCompatMapping):
return "AndroidCompat/ProcessController"
else:
return "Android/ProcessController"
def adb(self):
return "adb -d" if self.device == "usb" else "adb"
def startEmulator(self, avd):
#
# First check if the AVD image is available
#
print("starting the emulator... ")
out = run("emulator -list-avds")
if avd not in out:
raise RuntimeError("couldn't find AVD `{}'".format(avd))
#
# Find and unused port to run android emulator, between 5554 and 5584
#
port = -1
out = run("adb devices -l")
for p in range(5554, 5586, 2):
if not "emulator-{}".format(p) in out:
port = p
if port == -1:
raise RuntimeError("cannot find free port in range 5554-5584, to run android emulator")
self.device = "emulator-{}".format(port)
cmd = "emulator -avd {0} -port {1} -noaudio -partition-size 768 -no-snapshot".format(avd, port)
self.emulator = subprocess.Popen(cmd, shell=True)
if self.emulator.poll():
raise RuntimeError("failed to start the Android emulator `{}' on port {}".format(avd, port))
self.avd = avd
#
# Wait for the device to be ready
#
t = time.time()
while True:
try:
lines = run("{} shell getprop sys.boot_completed".format(self.adb()))
if len(lines) > 0 and lines[0].strip() == "1":
break
except RuntimeError:
pass # expected if device is offline
#
# If the emulator doesn't complete boot in 300 seconds give up
#
if (time.time() - t) > 300:
raise RuntimeError("couldn't start the Android emulator `{}'".format(avd))
time.sleep(2)
def startControllerApp(self, current, ident):
# Stop previous controller app before starting new one
for ident in self.controllerApps:
self.stopControllerApp(ident)
if current.config.avd:
self.startEmulator(current.config.avd)
elif not current.config.device:
# Create Android Virtual Device
sdk = current.testcase.getMapping().getSDKPackage()
print("creating virtual device ({0})... ".format(sdk))
try:
run("avdmanager delete avd -n IceTests") # Delete the created device
except:
pass
# The SDK is downloaded by test VMs instead of here.
#run("sdkmanager \"{0}\"".format(sdk), stdout=True, stdin="yes", stdinRepeat=True) # yes to accept licenses
run("avdmanager create avd -k \"{0}\" -d \"Nexus 6\" -n IceTests".format(sdk))
self.startEmulator("IceTests")
elif current.config.device != "usb":
run("adb connect {}".format(current.config.device))
run("{} install -t -r {}".format(self.adb(), current.testcase.getMapping().getApk(current)))
run("{} shell am start -n \"{}\" -a android.intent.action.MAIN -c android.intent.category.LAUNCHER".format(
self.adb(), current.testcase.getMapping().getActivityName()))
def stopControllerApp(self, ident):
try:
run("{} shell pm uninstall com.zeroc.testcontroller".format(self.adb()))
except:
pass
if self.avd:
try:
run("{} emu kill".format(self.adb()))
except:
pass
if self.avd == "IceTests":
try:
run("avdmanager delete avd -n IceTests") # Delete the created device
except:
pass
#
# Wait for the emulator to shutdown
#
if self.emulator:
sys.stdout.write("Waiting for the emulator to shutdown..")
sys.stdout.flush()
while True:
if self.emulator.poll() != None:
print(" ok")
break
sys.stdout.write(".")
sys.stdout.flush()
time.sleep(0.5)
try:
run("adb kill-server")
except:
pass
class iOSSimulatorProcessController(RemoteProcessController):
device = "iOSSimulatorProcessController"
deviceID = "com.apple.CoreSimulator.SimDeviceType.iPhone-X"
def __init__(self, current):
RemoteProcessController.__init__(self, current, "tcp -h 0.0.0.0 -p 15001" if current.config.xamarin else None)
self.simulatorID = None
self.runtimeID = None
# Pick the last iOS simulator runtime ID in the list of iOS simulators (assumed to be the latest).
try:
for r in run("xcrun simctl list runtimes").split('\n'):
m = re.search("iOS .* \\(.*\\) - (.*)", r)
if m:
self.runtimeID = m.group(1)
except:
pass
if not self.runtimeID:
self.runtimeID = "com.apple.CoreSimulator.SimRuntime.iOS-12-0" # Default value
def __str__(self):
return "iOS Simulator"
def getControllerIdentity(self, current):
return current.testcase.getMapping().getIOSControllerIdentity(current)
def startControllerApp(self, current, ident):
mapping = current.testcase.getMapping()
appFullPath = mapping.getIOSAppFullPath(current)
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(os.path.basename(appFullPath)))
sys.stdout.flush()
if not os.path.exists(appFullPath):
raise RuntimeError("couldn't find iOS simulator controller application, did you build it?")
run("xcrun simctl install \"{0}\" \"{1}\"".format(self.device, appFullPath))
run("xcrun simctl launch \"{0}\" {1}".format(self.device, ident.name))
print("ok")
def restartControllerApp(self, current, ident):
try:
run("xcrun simctl terminate \"{0}\" {1}".format(self.device, ident.name))
except:
pass
run("xcrun simctl launch \"{0}\" {1}".format(self.device, ident.name))
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)
sys.stdout.write("shutting down simulator... ")
sys.stdout.flush()
try:
run("xcrun simctl shutdown \"{0}\"".format(self.simulatorID))
except:
pass
print("ok")
if self.simulatorID:
sys.stdout.write("destroying simulator... ")
sys.stdout.flush()
try:
run("xcrun simctl delete \"{0}\"".format(self.simulatorID))
except:
pass
print("ok")
class iOSDeviceProcessController(RemoteProcessController):
appPath = "cpp/test/ios/controller/build"
def __init__(self, current):
RemoteProcessController.__init__(self, current, "tcp -h 0.0.0.0 -p 15001" if current.config.xamarin else None)
def __str__(self):
return "iOS Device"
def getControllerIdentity(self, current):
return current.testcase.getMapping().getIOSControllerIdentity(current)
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 = current.testcase.getMapping().getUWPPackageName()
self.appUserModelId = current.testcase.getMapping().getUWPUserModelId()
def __str__(self):
return "UWP"
def getControllerIdentity(self, current):
if isinstance(current.testcase.getMapping(), CSharpMapping):
return "UWPXamarin/ProcessController"
else:
return "UWP/ProcessController"
def startControllerApp(self, current, ident):
platform = current.config.buildPlatform
config = current.config.buildConfig
arch = "X86" if platform == "Win32" else "X64"
self.packageFullName = current.testcase.getMapping().getUWPPackageFullName(platform)
packageFullPath = current.testcase.getMapping().getUWPPackageFullPath(platform, config)
#
# 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
#
layout = os.path.join(current.testcase.getMapping().getPath(), "AppX")
if os.path.exists(layout):
shutil.rmtree(layout)
os.makedirs(layout)
print("Unpacking package: {0} to {1}....".format(os.path.basename(packageFullPath), layout))
run("MakeAppx.exe unpack /p \"{0}\" /d \"{1}\" /l".format(packageFullPath, layout))
print("Registering application to run from layout...")
for root, dirs, files in os.walk(os.path.join(os.path.dirname(packageFullPath), "Dependencies", arch)):
for f in files:
self.installPackage(os.path.join(root, f), arch)
run("powershell Add-AppxPackage -Register \"{0}/AppxManifest.xml\" -ForceApplicationShutdown".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("starting 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
def getPackageVersion(self, package):
import zipfile
import xml.etree.ElementTree as ElementTree
with zipfile.ZipFile(package) as zipfile:
with zipfile.open('AppxManifest.xml') as file:
xml = ElementTree.fromstring(file.read())
identity = xml.find("{http://schemas.microsoft.com/appx/manifest/foundation/windows10}Identity")
return tuple(map(int, identity.attrib['Version'].split(".")))
def installPackage(self, package, arch):
packages = {
"Microsoft.VCLibs.x64.14.00.appx" : "Microsoft.VCLibs.140.00",
"Microsoft.VCLibs.x86.14.00.appx" : "Microsoft.VCLibs.140.00",
"Microsoft.VCLibs.x64.Debug.14.00.appx" : "Microsoft.VCLibs.140.00.Debug",
"Microsoft.VCLibs.x86.Debug.14.00.appx" : "Microsoft.VCLibs.140.00.Debug",
"Microsoft.NET.CoreRuntime.2.1.appx" : "Microsoft.NET.CoreRuntime.2.1"
}
packageName = packages[os.path.basename(package)]
output = run("powershell Get-AppxPackage -Name {0}".format(packageName))
m = re.search("Architecture.*: {0}\\r\\nResourceId.*:.*\\r\\nVersion.*: (.*)\\r\\n".format(arch), output)
installedVersion = tuple(map(int, m.group(1).split("."))) if m else (0, 0, 0, 0)
version = self.getPackageVersion(package)
if installedVersion <= version:
run("powershell Add-AppxPackage -Path \"{0}\" -ForceApplicationShutdown".format(package))
class BrowserProcessController(RemoteProcessController):
def __init__(self, current):
self.host = current.driver.host or "127.0.0.1"
RemoteProcessController.__init__(self, current, "ws -h {0} -p 15002:wss -h {0} -p 15003".format(self.host))
self.httpServer = None
self.url = None
self.driver = None
try:
cmd = "node -e \"require('./bin/HttpServer')()\"";
cwd = current.testcase.getMapping().getPath()
self.httpServer = Expect.Expect(cmd, cwd=cwd)
self.httpServer.expect("listening on ports")
if current.config.browser.startswith("Remote:"):
from selenium import webdriver
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
(driver, capabilities, port) = current.config.browser.split(":")
self.driver = webdriver.Remote("http://localhost:{0}".format(port),
desired_capabilities=getattr(DesiredCapabilities, capabilities),
keep_alive=True)
elif current.config.browser != "Manual":
from selenium import webdriver
if current.config.browser.find(":") > 0:
(driver, port) = current.config.browser.split(":")
else:
(driver, port) = (current.config.browser, 0)
if not hasattr(webdriver, driver):
raise RuntimeError("unknown browser `{0}'".format(driver))
if driver == "Firefox":
if isinstance(platform, Linux) and os.environ.get("DISPLAY", "") != ":1" and os.environ.get("USER", "") == "ubuntu":
current.writeln("error: DISPLAY is unset, setting it to :1")
os.environ["DISPLAY"] = ":1"
#
# 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.
#
profilepath = os.path.join(current.driver.getComponent().getSourceDir(), "scripts", "selenium", "firefox")
profile = webdriver.FirefoxProfile(profilepath)
self.driver = webdriver.Firefox(firefox_profile=profile)
elif driver == "Ie":
# Make sure we start with a clean cache
capabilities = webdriver.DesiredCapabilities.INTERNETEXPLORER.copy()
capabilities["ie.ensureCleanSession"] = True
self.driver = webdriver.Ie(capabilities=capabilities)
elif driver == "Safari" and port > 0:
self.driver = webdriver.Safari(port=int(port), reuse_service=True)
else:
self.driver = getattr(webdriver, driver)()
except:
self.destroy(current.driver)
raise
def __str__(self):
return str(self.driver) if self.driver else "Manual"
def getControllerIdentity(self, current):
#
# Load the controller page each time we're asked for the controller and if we're running
# another testcase, 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"
url = "{0}://{5}:{1}/test/{2}/controller.html?port={3}&worker={4}".format(protocol,
port,
testsuite,
cport,
current.config.worker,
self.host)
if url != self.url:
self.url = url
if self.driver:
self.driver.get(url)
else:
# If no process controller is registered, we request the user to load the controller
# page in the browser. Once loaded, the controller will register and we'll redirect to
# the correct testsuite page.
ident = current.driver.getCommunicator().stringToIdentity("Browser/ProcessController")
prx = None
with self.cond:
while True:
if ident in self.processControllerProxies:
prx = self.processControllerProxies[ident]
break
print("Please load http://{0}:8080/{1}".format(self.host,
"es5/start" if current.config.es5 else "start"))
self.cond.wait(5)
try:
import Test
Test.Common.BrowserProcessControllerPrx.uncheckedCast(prx).redirect(url)
except:
pass
finally:
self.clearProcessController(prx, prx.ice_getCachedConnection())
return "Browser/ProcessController"
def getController(self, current):
try:
return RemoteProcessController.getController(self, current)
except RuntimeError as ex:
if self.driver:
# Print out the client & server console element values
for element in ["clientConsole", "serverConsole"]:
try:
console = self.driver.find_element_by_id(element).get_attribute('value')
if len(console) > 0:
print("controller {0} value:\n{1}".format(element, console))
except Exception as exc:
print("couldn't get controller {0} value:\n{1}".format(element, exc))
pass
# Print out the browser log
try:
print("browser log:\n{0}".format(self.driver.get_log("browser")))
except:
pass # Not all browsers support retrieving the browser console log
raise ex
def destroy(self, driver):
if self.httpServer:
self.httpServer.terminate()
self.httpServer = 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.desc = ""
self.result = result
self.host = None
self.testcase = None
self.testcases = []
self.processes = {}
self.dirs = []
self.files = []
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
def createFile(self, path, lines, encoding=None):
path = os.path.join(self.testsuite.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.testsuite.getPath(), d)
self.dirs.append(d)
if not os.path.exists(d):
os.makedirs(d)
def destroy(self):
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)
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, component):
parseOptions(self, options)
driver = self.drivers.get(self.driver)
if not driver:
raise RuntimeError("unknown driver `{0}'".format(self.driver))
return driver(options, component)
@classmethod
def getSupportedArgs(self):
return ("dlrR", ["debug", "driver=", "filter=", "rfilter=", "host=", "host-ipv6=", "host-bt=", "interface=",
"controller-app", "valgrind", "languages=", "rlanguages="])
@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("--languages=l1,l2,... List of comma-separated language mappings to test.")
print("--rlanguages=l1,l2,.. List of comma-separated language mappings to not test.")
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.")
print("--valgrind Start executables with valgrind.")
def __init__(self, options, component):
self.component = component
self.debug = False
self.filters = []
self.rfilters = []
self.host = ""
self.hostIPv6 = ""
self.hostBT = ""
self.controllerApp = False
self.valgrind = False
self.languages = ",".join(os.environ.get("LANGUAGES", "").split(" "))
self.languages = [self.languages] if self.languages else []
self.rlanguages = []
self.failures = []
parseOptions(self, options, { "d": "debug",
"r" : "filters",
"R" : "rfilters",
"filter" : "filters",
"rfilter" : "rfilters",
"host-ipv6" : "hostIPv6",
"host-bt" : "hostBT",
"controller-app" : "controllerApp"})
if self.languages:
self.languages = [i for sublist in [l.split(",") for l in self.languages] for i in sublist]
if self.rlanguages:
self.rlanguages = [i for sublist in [l.split(",") for l in self.rlanguages] for i in sublist]
(self.filters, self.rfilters) = ([re.compile(a) for a in self.filters], [re.compile(a) for a in self.rfilters])
self.communicator = None
self.interface = ""
self.processControllers = {}
def setConfigs(self, configs):
self.configs = configs
def getFilters(self, mapping, config):
# Return the driver and component filters
(filters, rfilters) = self.component.getFilters(mapping, config)
(filters, rfilters) = ([re.compile(a) for a in filters], [re.compile(a) for a in rfilters])
return (self.filters + filters, self.rfilters + rfilters)
def getHost(self, protocol, ipv6):
if protocol == "bt":
if not self.hostBT:
raise RuntimeError("no Bluetooth address set with --host-bt")
return self.hostBT
elif ipv6:
return self.hostIPv6 or "::1"
else:
return self.host or "127.0.0.1"
def getComponent(self):
return self.component
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 matchLanguage(self, language):
if self.languages and language not in self.languages:
return False
if self.rlanguages and language in self.rlanguages:
return False
return True
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
try:
pythonMapping = Mapping.getByName("python")
if pythonMapping:
for p in pythonMapping.getPythonDirs(pythonMapping.getPath(), self.configs[pythonMapping]):
sys.path.append(p)
except RuntimeError:
print("couldn't find IcePy, running these tests require it to be installed or built")
import Ice
Ice.loadSlice(os.path.join(self.component.getSourceDir(), "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(self.component.getSourceDir(), "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)
initData.properties.setProperty("Ice.Default.Host", self.interface)
initData.properties.setProperty("Ice.ThreadPool.Server.Size", "10")
# initData.properties.setProperty("Ice.Trace.Protocol", "1")
# initData.properties.setProperty("Ice.Trace.Network", "3")
# initData.properties.setProperty("Ice.StdErr", "allTests.log")
initData.properties.setProperty("Ice.Override.Timeout", "10000")
initData.properties.setProperty("Ice.Override.ConnectTimeout", "1000")
self.communicator = Ice.initialize(initData)
def getProcessController(self, current, process=None):
processController = None
if current.config.buildPlatform == "iphonesimulator":
processController = iOSSimulatorProcessController
elif current.config.buildPlatform == "iphoneos":
processController = iOSDeviceProcessController
elif current.config.uwp:
# No SSL server-side support in UWP.
if current.config.protocol in ["ssl", "wss"] and not isinstance(process, Client):
processController = LocalProcessController
else:
processController = UWPProcessController
elif process and current.config.browser and isinstance(process.getMapping(current), JavaScriptMixin):
processController = BrowserProcessController
elif process and current.config.android:
processController = AndroidProcessController
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()
class CppMapping(Mapping):
class Config(Mapping.Config):
@classmethod
def getSupportedArgs(self):
return ("", ["cpp-config=", "cpp-platform=", "cpp-path=", "uwp", "openssl"])
@classmethod
def usage(self):
print("")
print("C++ Mapping options:")
print("--cpp-path= Path of alternate source tree for the C++ mapping.")
print("--cpp-config= C++ build configuration for native executables (overrides --config).")
print("--cpp-platform= C++ build platform for native executables (overrides --platform).")
print("--uwp Run UWP (Universal Windows Platform).")
print("--openssl Run SSL tests with OpenSSL instead of the default platform SSL engine.")
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",
"cpp-path" : "pathOverride" })
if self.pathOverride:
self.pathOverride = os.path.abspath(self.pathOverride)
def getOptions(self, current):
return { "compress" : [False] } if current.config.uwp else {}
def getProps(self, process, current):
props = Mapping.getProps(self, process, current)
if isinstance(process, IceProcess):
props["Ice.NullHandleAbort"] = True
props["Ice.PrintStackTraces"] = "1"
return props
def getSSLProps(self, process, current):
props = Mapping.getSSLProps(self, process, current)
server = isinstance(process, Server)
uwp = current.config.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):
props.update({
"IceSSL.KeychainPassword" : "password",
"IceSSL.Keychain": "server.keychain" if server else "client.keychain"
})
return props
def getPluginEntryPoint(self, plugin, process, current):
return {
"IceSSL" : "IceSSLOpenSSL:createIceSSLOpenSSL" if current.config.openssl else "IceSSL:createIceSSL",
"IceBT" : "IceBT:createIceBT",
"IceDiscovery" : "IceDiscovery:createIceDiscovery",
"IceLocatorDiscovery" : "IceLocatorDiscovery:createIceLocatorDiscovery"
}[plugin]
def getEnv(self, process, current):
#
# On Windows, add the testcommon directories to the PATH
#
libPaths = []
if isinstance(platform, Windows):
testcommon = os.path.join(self.path, "test", "Common")
if os.path.exists(testcommon):
libPaths.append(os.path.join(testcommon, self.getBuildDir("testcommon", current)))
#
# On most platforms, we also need to add the library directory to the library path environment variable.
#
if not isinstance(platform, Darwin):
libPaths.append(self.component.getLibDir(process, self, 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",
"subscriber" : "Subscriber.cpp",
"publisher" : "Publisher.cpp",
}[processType]
def _getDefaultExe(self, processType):
return Mapping._getDefaultExe(self, processType).lower()
def getUWPPackageName(self):
return "ice-uwp-controller.cpp"
def getUWPUserModelId(self):
return "ice-uwp-controller.cpp_3qjctahehqazm"
def getUWPPackageFullName(self, platform):
return "{0}_1.0.0.0_{1}__3qjctahehqazm".format(self.getUWPPackageName(),
"x86" if platform == "Win32" else platform)
def getUWPPackageFullPath(self, platform, config):
prefix = "controller_1.0.0.0_{0}{1}".format(platform, "_{0}".format(config) if config == "Debug" else "")
return os.path.join(self.component.getSourceDir(), "cpp", "msbuild", "AppPackages", "controller",
"{0}_Test".format(prefix), "{0}.appx".format(prefix))
def getIOSControllerIdentity(self, current):
category = "iPhoneSimulator" if current.config.buildPlatform == "iphonesimulator" else "iPhoneOS"
mapping = "Cpp11" if current.config.cpp11 else "Cpp98"
return "{0}/com.zeroc.{1}-Test-Controller".format(category, mapping)
def getIOSAppFullPath(self, current):
appName = "C++11 Test Controller.app" if current.config.cpp11 else "C++98 Test Controller.app"
path = os.path.join(self.component.getTestDir(self), "ios", "controller")
path = os.path.join(path, "build-{0}-{1}".format(current.config.buildPlatform, current.config.buildConfig))
build = "Debug" if os.path.exists(os.path.join(path, "Debug-{0}".format(current.config.buildPlatform))) else "Release"
return os.path.join(path, "{0}-{1}".format(build, current.config.buildPlatform), appName)
class JavaMapping(Mapping):
class Config(Mapping.Config):
@classmethod
def getSupportedArgs(self):
return ("", ["device=", "avd=", "android"])
@classmethod
def usage(self):
print("")
print("Java Mapping options:")
print("--android Run the Android tests.")
print("--device= ID of the Android emulator or device used to run the tests.")
print("--avd= Start specific Android Virtual Device.")
def getCommandLine(self, current, process, exe, args):
javaHome = os.getenv("JAVA_HOME", "")
java = os.path.join(javaHome, "bin", "java") if javaHome else "java"
javaArgs = self.getJavaArgs(process, current)
if process.isFromBinDir():
if javaArgs:
return "{0} -ea {1} {2} {3}".format(java, " ".join(javaArgs), exe, args)
else:
return "{0} -ea {1} {2}".format(java, exe, args)
testdir = self.component.getTestDir(self)
assert(current.testcase.getPath(current).startswith(testdir))
package = "test." + current.testcase.getPath(current)[len(testdir) + 1:].replace(os.sep, ".")
javaArgs = self.getJavaArgs(process, current)
if javaArgs:
return "{0} -ea {1} -Dtest.class={2}.{3} test.TestDriver {4}".format(java, " ".join(javaArgs), package, exe, args)
else:
return "{0} -ea -Dtest.class={1}.{2} test.TestDriver {3}".format(java, package, exe, args)
def getJavaArgs(self, process, current):
# TODO: WORKAROUND for https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=911925
if isinstance(platform, Linux) and platform.getLinuxId() in ["debian", "ubuntu"]:
return ["-Djdk.net.URLClassPath.disableClassPathURLCheck=true"]
return []
def getSSLProps(self, process, current):
props = Mapping.getSSLProps(self, process, current)
if current.config.android:
props.update({
"IceSSL.KeystoreType" : "BKS",
"IceSSL.TruststoreType" : "BKS",
"Ice.InitPlugins" : "0",
"IceSSL.Keystore": "server.bks" if isinstance(process, Server) else "client.bks"
})
else:
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",
"IceLocatorDiscovery" : "com.zeroc.IceLocatorDiscovery.PluginFactory"
}[plugin]
def getEnv(self, process, current):
return { "CLASSPATH" : os.path.join(self.path, "lib", "test.jar") }
def _getDefaultSource(self, processType):
return {
"client" : "Client.java",
"server" : "Server.java",
"serveramd" : "AMDServer.java",
"servertie" : "TieServer.java",
"serveramdtie" : "AMDTieServer.java",
"collocated" : "Collocated.java",
}[processType]
def getSDKPackage(self):
return "system-images;android-27;google_apis;x86"
def getApk(self, current):
return os.path.join(self.getPath(), "test", "android", "controller", "build", "outputs", "apk", "debug",
"controller-debug.apk")
def getActivityName(self):
return "com.zeroc.testcontroller/.ControllerActivity"
class JavaCompatMapping(JavaMapping):
def getPluginEntryPoint(self, plugin, process, current):
return {
"IceSSL" : "IceSSL.PluginFactory",
"IceBT" : "IceBT.PluginFactory",
"IceDiscovery" : "IceDiscovery.PluginFactory",
"IceLocatorDiscovery" : "IceLocatorDiscovery.PluginFactory"
}[plugin]
def getEnv(self, process, current):
classPath = [os.path.join(self.path, "lib", "test.jar")]
if os.path.exists(os.path.join(self.path, "lib", "IceTestLambda.jar")):
classPath += [os.path.join(self.path, "lib", "IceTestLambda.jar")]
return { "CLASSPATH" : os.pathsep.join(classPath) }
def getSDKPackage(self):
return "system-images;android-21;google_apis;x86_64"
class CSharpMapping(Mapping):
class Config(Mapping.Config):
@classmethod
def getSupportedArgs(self):
return ("", ["dotnetcore", "framework="])
@classmethod
def usage(self):
print("")
print("--dotnetcore Run C# tests using .NET Core")
print("--framework= Choose the framework used to run .NET tests")
def __init__(self, options=[]):
Mapping.Config.__init__(self, options)
if not self.dotnetcore and not isinstance(platform, Windows):
self.dotnetcore = True
if self.dotnetcore:
self.libTargetFramework = "netstandard2.0"
self.binTargetFramework = "netcoreapp2.1" if self.framework == "" else self.framework
self.testTargetFramework = "netcoreapp2.1" if self.framework == "" else self.framework
else:
self.libTargetFramework = "net45" if self.framework == "" else "netstandard2.0"
self.binTargetFramework = "net45" if self.framework == "" else self.framework
self.testTargetFramework = "net45" if self.framework == "" else self.framework
# Set Xamarin flag if UWP/iOS or Android testing flag is also specified
if self.uwp or self.android or "iphone" in self.buildPlatform:
self.xamarin = True
def getBinTargetFramework(self, current):
return current.config.binTargetFramework
def getLibTargetFramework(self, current):
return current.config.libTargetFramework
def getTargetFramework(self, current):
return current.config.testTargetFramework
def getBuildDir(self, name, current):
if current.config.dotnetcore or current.config.framework != "":
return os.path.join("msbuild", name, "netstandard2.0", self.getTargetFramework(current))
else:
return os.path.join("msbuild", name, self.getTargetFramework(current))
def getProps(self, process, current):
props = Mapping.getProps(self, process, current)
if current.config.xamarin:
#
# With SSL we need to delay the creation of the admin adapter until the plug-in has
# been initialized.
#
if current.config.protocol in ["ssl", "wss"] and current.config.mx:
props["Ice.Admin.DelayCreation"] = "1"
return props
def getOptions(self, current):
if current.config.xamarin and current.config.uwp:
#
# Do not run MX tests with SSL it cause problems with Xamarin UWP implementation
#
return {"mx" : ["False"]} if current.config.protocol in ["ssl", "wss"] else {}
else:
return {}
def getSSLProps(self, process, current):
props = Mapping.getSSLProps(self, process, current)
props.update({
"IceSSL.Password": "password",
"IceSSL.DefaultDir": os.path.join(self.component.getSourceDir(), "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",
})
if current.config.xamarin:
props["Ice.InitPlugins"] = 0
props["IceSSL.CAs"] = "cacert.der";
return props
def getPluginEntryPoint(self, plugin, process, current):
if current.config.xamarin:
plugindir = ""
else:
plugindir = self.component.getLibDir(process, self, current)
#
# If the plug-in assembly exists in the test directory, this is a good indication that the
# test include a reference to the plug-in, in this case we must use the test dir as the
# plug-in base directory to avoid loading two instances of the same assembly.
#
proccessType = current.testcase.getProcessType(process)
if proccessType:
testdir = os.path.join(current.testcase.getPath(current), self.getBuildDir(proccessType, current))
if os.path.isfile(os.path.join(testdir, plugin + ".dll")):
plugindir = testdir
plugindir += os.sep
return {
"IceSSL" : plugindir + "IceSSL.dll:IceSSL.PluginFactory",
"IceDiscovery" : plugindir + "IceDiscovery.dll:IceDiscovery.PluginFactory",
"IceLocatorDiscovery" : plugindir + "IceLocatorDiscovery.dll:IceLocatorDiscovery.PluginFactory"
}[plugin]
def getEnv(self, process, current):
env = {}
if isinstance(platform, Windows):
if self.component.useBinDist(self, current):
env['PATH'] = self.component.getBinDir(process, self, current)
else:
env['PATH'] = os.path.join(self.component.getSourceDir(), "cpp", "msbuild", "packages",
"bzip2.{0}.1.0.6.10".format(platform.getPlatformToolset()),
"build", "native", "bin", "x64", "Release")
if not current.config.dotnetcore:
env['DEVPATH'] = self.component.getLibDir(process, self, current)
return env
def _getDefaultSource(self, processType):
return {
"client" : "Client.cs",
"server" : "Server.cs",
"serveramd" : "ServerAMD.cs",
"servertie" : "ServerTie.cs",
"serveramdtie" : "ServerAMDTie.cs",
"collocated" : "Collocated.cs",
}[processType]
def _getDefaultExe(self, processType):
return Mapping._getDefaultExe(self, processType).lower()
def getCommandLine(self, current, process, exe, args):
if process.isFromBinDir():
path = self.component.getBinDir(process, self, current)
else:
path = os.path.join(current.testcase.getPath(current), current.getBuildDir(exe))
if current.config.dotnetcore:
return "dotnet " + os.path.join(path, exe) + ".dll " + args
else:
return os.path.join(path, exe) + ".exe " + args
def getSDKPackage(self):
return "system-images;android-27;google_apis;x86"
def getApk(self, current):
return os.path.join(self.getPath(), "test", "xamarin", "controller.Android", "bin", current.config.buildConfig,
"com.zeroc.testcontroller-Signed.apk")
def getActivityName(self):
return "com.zeroc.testcontroller/controller.MainActivity"
def getUWPPackageName(self):
return "ice-uwp-controller.xamarin"
def getUWPUserModelId(self):
return "ice-uwp-controller.xamarin_3qjctahehqazm"
def getUWPPackageFullName(self, platform):
return "{0}_1.0.0.0_{1}__3qjctahehqazm".format(self.getUWPPackageName(), platform)
def getUWPPackageFullPath(self, platform, config):
prefix = "controller.UWP_1.0.0.0_{0}{1}".format(platform, "_{0}".format(config) if config == "Debug" else "")
return os.path.join(self.getPath(), "test", "xamarin", "controller.UWP", "AppPackages",
"{0}_Test".format(prefix), "{0}.appx".format(prefix))
def getIOSControllerIdentity(self, current):
if current.config.buildPlatform == "iphonesimulator":
return "iPhoneSimulator/com.zeroc.Xamarin-Test-Controller"
else:
return "iPhoneOS/com.zeroc.Xamarin-Test-Controller"
def getIOSAppFullPath(self, current):
return os.path.join(self.getPath(), "test", "xamarin", "controller.iOS", "bin", "iPhoneSimulator",
current.config.buildConfig, "controller.iOS.app")
class CppBasedMapping(Mapping):
class Config(Mapping.Config):
@classmethod
def getSupportedArgs(self):
return ("", [self.mappingName + "-config=", self.mappingName + "-platform=", "openssl"])
@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))
print("--openssl Run SSL tests with OpenSSL instead of the default platform SSL engine.")
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 self.component.getInstallDir(self, current) != platform.getInstallDir():
# If not installed in the default platform installation directory, add
# the C++ library directory to the library path
env[platform.getLdPathEnvName()] = self.component.getLibDir(process, Mapping.getByName("cpp"), current)
return env
class ObjCMapping(CppBasedMapping):
def getTestSuites(self, ids=[]):
return Mapping.getTestSuites(self, ids) if isinstance(platform, Darwin) else []
def findTestSuite(self, testsuite):
return Mapping.findTestSuite(self, testsuite) if isinstance(platform, Darwin) else None
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]
def _getDefaultExe(self, processType):
return Mapping._getDefaultExe(self, processType).lower()
def getIOSControllerIdentity(self, current):
category = "iPhoneSimulator" if current.config.buildPlatform == "iphonesimulator" else "iPhoneOS"
mapping = "ObjC-ARC" if current.config.arc else "ObjC"
return "{0}/com.zeroc.{1}-Test-Controller".format(category, mapping)
def getIOSAppFullPath(self, current):
appName = "Objective-C ARC Test Controller.app" if current.config.arc else "Objective-C Test Controller.app"
path = os.path.join(self.component.getTestDir(self), "ios", "controller")
path = os.path.join(path, "build-{0}-{1}".format(current.config.buildPlatform, current.config.buildConfig))
build = "Debug" if os.path.exists(os.path.join(path, "Debug-{0}".format(current.config.buildPlatform))) else "Release"
return os.path.join(path, "{0}-{1}".format(build, current.config.buildPlatform), appName)
class PythonMapping(CppBasedMapping):
class Config(CppBasedMapping.Config):
mappingName = "python"
mappingDesc = "Python"
def getCommandLine(self, current, process, exe, args):
return "\"{0}\" {1} {2} {3}".format(sys.executable,
os.path.join(self.path, "test", "TestHelper.py"),
exe,
args)
def getEnv(self, process, current):
env = CppBasedMapping.getEnv(self, process, current)
dirs = []
if self.component.getInstallDir(self, current) != platform.getInstallDir():
# If not installed in the default platform installation directory, add
# the Ice python directory to PYTHONPATH
dirs += self.getPythonDirs(self.component.getInstallDir(self, current), current.config)
dirs += [current.testcase.getPath(current)]
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 _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, testId=None):
return Mapping.getByName("cpp") # By default, run clients against C++ mapping executables
class RubyMapping(CppBasedClientMapping):
class Config(CppBasedClientMapping.Config):
mappingName = "ruby"
mappingDesc = "Ruby"
def getCommandLine(self, current, process, exe, args):
return "ruby {0} {1} {2}".format(os.path.join(self.path, "test", "TestHelper.rb"), exe, args)
def getEnv(self, process, current):
env = CppBasedMapping.getEnv(self, process, current)
dirs = []
if self.component.getInstallDir(self, current) != platform.getInstallDir():
# If not installed in the default platform installation directory, add
# the Ice ruby directory to RUBYLIB
dirs += [os.path.join(self.path, "ruby")]
dirs += [current.testcase.getPath(current)]
env["RUBYLIB"] = os.pathsep.join(dirs)
return env
def _getDefaultSource(self, processType):
return { "client" : "Client.rb" }[processType]
class PhpMapping(CppBasedClientMapping):
class Config(CppBasedClientMapping.Config):
mappingName = "php"
mappingDesc = "PHP"
@classmethod
def getSupportedArgs(self):
return ("", ["php-version="])
@classmethod
def usage(self):
print("")
print("PHP Mapping options:")
print("--php-version=[7.1|7.2|7.3] PHP Version used for Windows builds")
def __init__(self, options=[]):
CppBasedClientMapping.Config.__init__(self, options)
parseOptions(self, options, { "php-version" : "phpVersion" })
def getCommandLine(self, current, process, exe, args):
phpArgs = []
php = "php"
#
# On Windows, when using a source distribution use the php executable from
# the Nuget PHP dependency.
#
if isinstance(platform, Windows) and not self.component.useBinDist(self, current):
nugetVersions = {
"7.1": "7.1.17",
"7.2": "7.2.8",
"7.3": "7.3.0"
}
nugetVersion = nugetVersions[current.config.phpVersion]
threadSafe = current.driver.configs[self].buildConfig in ["Debug", "Release"]
buildPlatform = current.driver.configs[self].buildPlatform
buildConfig = "Debug" if current.driver.configs[self].buildConfig.find("Debug") >= 0 else "Release"
packageName = "php-{0}-{1}.{2}".format(current.config.phpVersion, "ts" if threadSafe else "nts", nugetVersion)
php = os.path.join(self.path, "msbuild", "packages", packageName, "build", "native", "bin",
buildPlatform, buildConfig, "php.exe")
#
# If Ice is not installed in the system directory, specify its location with PHP
# configuration arguments.
#
if isinstance(platform, Windows) and not self.component.useBinDist(self, current) or \
platform.getInstallDir() and self.component.getInstallDir(self, current) != platform.getInstallDir():
phpArgs += ["-n"] # Do not load any php.ini files
phpArgs += ["-d", "extension_dir='{0}'".format(self.component.getLibDir(process, self, current))]
phpArgs += ["-d", "extension='{0}'".format(self.component.getPhpExtension(self, current))]
phpArgs += ["-d", "include_path='{0}'".format(self.component.getPhpIncludePath(self, current))]
if hasattr(process, "getPhpArgs"):
phpArgs += process.getPhpArgs(current)
return "{0} {1} -f {2} -- {3} {4}".format(php,
" ".join(phpArgs),
os.path.join(self.path, "test", "TestHelper.php"),
exe,
args)
def _getDefaultSource(self, processType):
return { "client" : "Client.php" }[processType]
class MatlabMapping(CppBasedClientMapping):
class Config(CppBasedClientMapping.Config):
mappingName = "matlab"
mappingDesc = "MATLAB"
def getCommandLine(self, current, process, exe, args):
return "matlab -nodesktop -nosplash -wait -log -minimize -r \"cd '{0}', runTest {1} {2} {3}\"".format(
os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "matlab", "test", "lib")),
self.getTestCwd(process, current),
os.path.join(current.config.buildPlatform, current.config.buildConfig),
args)
def getServerMapping(self, testId=None):
return Mapping.getByName("python") # Run clients against Python mapping servers
def _getDefaultSource(self, processType):
return { "client" : "client.m" }[processType]
def getOptions(self, current):
#
# Metrics tests configuration not supported with MATLAB they use the Admin adapter.
#
options = CppBasedClientMapping.getOptions(self, current)
options["mx"] = [ False ]
return options
class JavaScriptMixin():
def loadTestSuites(self, tests, config, filters, rfilters):
Mapping.loadTestSuites(self, tests, config, filters, rfilters)
self.getServerMapping().loadTestSuites(list(self.testsuites.keys()) + ["Ice/echo"], config)
def getServerMapping(self, testId=None):
if testId and self.hasSource(testId, "server"):
return self
else:
return Mapping.getByName("cpp") # Run clients against C++ mapping servers if no JS server provided
def _getDefaultProcesses(self, processType):
if processType.startswith("server"):
return [EchoServer(), Server()]
return Mapping._getDefaultProcesses(self, processType)
def getCommonDir(self, current):
return os.path.join(self.getPath(), "test", "Common")
def getCommandLine(self, current, process, exe, args):
return "node {0}/run.js {1} {2}".format(self.getCommonDir(current), exe, args)
def getEnv(self, process, current):
env = Mapping.getEnv(self, process, current)
env["NODE_PATH"] = os.pathsep.join([self.getCommonDir(current), self.getTestCwd(process, current)])
return env
def getSSLProps(self, process, current):
return {}
def getOptions(self, current):
options = {
"protocol" : ["ws", "wss"] if current.config.browser else ["tcp"],
"compress" : [False],
"ipv6" : [False],
"serialize" : [False],
"mx" : [False],
}
return options
class JavaScriptMapping(JavaScriptMixin,Mapping):
class Config(Mapping.Config):
@classmethod
def getSupportedArgs(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)
if self.browser and self.protocol == "tcp":
self.protocol = "ws"
# Ie only support ES5 for now
if self.browser in ["Ie"]:
self.es5 = True
def getCommonDir(self, current):
if current.config.es5:
return os.path.join(self.getPath(), "test", "es5", "Common")
else:
return os.path.join(self.getPath(), "test", "Common")
def _getDefaultSource(self, processType):
return { "client" : "Client.js", "serveramd" : "ServerAMD.js", "server" : "Server.js" }[processType]
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 getOptions(self, current):
options = JavaScriptMixin.getOptions(self, current)
options.update({
"es5" : [True] if current.config.es5 else [False, True],
"worker" : [False, True] if current.config.browser and current.config.browser != "Ie" else [False],
})
return options
class TypeScriptMapping(JavaScriptMixin,Mapping):
class Config(Mapping.Config):
def canRun(self, testId, current):
return Mapping.Config.canRun(self, testId, current) and self.browser != "Ie" # IE doesn't support ES6
def _getDefaultSource(self, processType):
return { "client" : "Client.ts", "serveramd" : "ServerAMD.ts", "server" : "Server.ts" }[processType]
#
# Instantiate platform global variable
#
platform = None
if sys.platform == "darwin":
platform = Darwin()
elif sys.platform.startswith("aix"):
platform = AIX()
elif sys.platform.startswith("freebsd") or sys.platform.startswith("dragonfly"):
platform = FreeBSD()
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)
#
# Import component classes and instantiate the default component
#
from Component import *
#
# Initialize the platform with component
#
platform.init(component)
#
# Import local driver
#
from LocalDriver import *
def runTestsWithPath(path):
mappings = Mapping.getAllByPath(path)
if not mappings:
print("couldn't find mapping for `{0}' (is this mapping supported on this platform?)".format(path))
sys.exit(0)
runTests(mappings)
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.getSupportedArgs(), Mapping.Config.getSupportedArgs()]
options += [driver.getSupportedArgs() for driver in drivers]
options += [mapping.Config.getSupportedArgs() 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, component)
#
# Create the configurations for each mapping.
#
configs = {}
for mapping in Mapping.getAll():
if mapping not in configs:
configs[mapping] = mapping.createConfig(opts[:])
#
# If the user specified --languages/rlanguages, only run matching mappings.
#
mappings = [m for m in mappings if driver.matchLanguage(str(m))]
#
# Provide the configurations to the driver and load the test suites for each mapping.
#
driver.setConfigs(configs)
for mapping in mappings + driver.getMappings():
(filters, rfilters) = driver.getFilters(mapping, configs[mapping])
mapping.loadTestSuites(args, configs[mapping], filters, 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)