# **********************************************************************
#
# Copyright (c) 2003-2018 ZeroC, Inc. All rights reserved.
#
# This copy of Ice is licensed to you under the terms described in the
# ICE_LICENSE file included in this distribution.
#
# **********************************************************************
import os, sys, runpy, getopt, traceback, types, threading, time, datetime, re, itertools, random, subprocess, shutil, copy, inspect
import 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):
p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, cwd=cwd)
out = p.stdout.read().decode('UTF-8').strip()
if(not err and p.wait() != 0) or (err and p.wait() == 0) :
raise RuntimeError(cmd + " failed:\n" + out)
#
# Without this we get warnings when running with python_d on Windows
#
# ResourceWarning: unclosed file <_io.TextIOWrapper name=3 encoding='cp1252'>
#
p.stdout.close()
return out
def val(v, escapeQuotes=False, quoteValue=True):
if type(v) == bool:
return "1" if v else "0"
elif type(v) == str:
if not quoteValue or v.find(" ") < 0:
return v
elif escapeQuotes:
return "\\\"{0}\\\"".format(v.replace("\\\"", "\\\\\\\""))
else:
return "\"{0}\"".format(v)
else:
return str(v)
def escapeXml(s):
# Remove backspace characters from the output (they aren't accepted by Jenkins XML parser)
if isPython2:
return xml.sax.saxutils.escape("".join(ch for ch in unicode(s.decode("utf-8")) if ch != u"\u0008").encode("utf-8"))
else:
return xml.sax.saxutils.escape("".join(ch for ch in s if ch != u"\u0008"))
def getIceSoVersion():
config = open(os.path.join(toplevel, "cpp", "include", "IceUtil", "Config.h"), "r")
intVersion = int(re.search("ICE_INT_VERSION ([0-9]*)", config.read()).group(1))
majorVersion = int(intVersion / 10000)
minorVersion = int(intVersion / 100) - 100 * majorVersion
patchVersion = intVersion % 100
if patchVersion < 50:
return '%d' % (majorVersion * 10 + minorVersion)
elif patchVersion < 60:
return '%da%d' % (majorVersion * 10 + minorVersion, patchVersion - 50)
else:
return '%db%d' % (majorVersion * 10 + minorVersion, patchVersion - 60)
class Platform:
def __init__(self):
self.parseBuildVariables({
"supported-platforms" : ("supportedPlatforms", lambda s : s.split(" ")),
"supported-configs" : ("supportedConfigs", lambda s : s.split(" "))
})
self.nugetPackageVersion = None
self.nugetPackageCache = None
def parseBuildVariables(self, variables):
# Run make to get the values of the given variables
output = run('make print V="{0}"'.format(" ".join(variables.keys())), cwd = toplevel)
for l in output.split("\n"):
match = re.match(r'^.*:.*: (.*) = (.*)', l)
if match and match.group(1):
if match.group(1) in variables:
(varname, valuefn) = variables[match.group(1).strip()]
value = match.group(2).strip() or ""
setattr(self, varname, valuefn(value) if valuefn else value)
def getFilters(self, config):
if config.buildConfig in ["static", "cpp11-static"]:
return (["Ice/.*", "IceSSL/configuration", "IceDiscovery/simple", "IceGrid/simple", "Glacier2/application"],
["Ice/library", "Ice/plugin"])
return ([], [])
def getDefaultBuildPlatform(self):
return self.supportedPlatforms[0]
def getDefaultBuildConfig(self):
return self.supportedConfigs[0]
def getBinSubDir(self, mapping, process, current):
# Return the bin sub-directory for the given mapping,platform,config,
# to be overriden by specializations
return "bin"
def getLibSubDir(self, mapping, process, current):
# Return the bin sub-directory for the given mapping,platform,config,
# to be overriden by specializations
return "lib"
def getBuildSubDir(self, name, current):
# Return the build sub-directory, to be overriden by specializations
return os.path.join("build", current.config.buildPlatform, current.config.buildConfig)
def getLdPathEnvName(self):
return "LD_LIBRARY_PATH"
def getInstallDir(self, mapping, current, envName):
return os.environ.get(envName, "/usr")
def getIceInstallDir(self, mapping, current):
#
# For .NET Core with Ice binary distribution the NuGet packages is
# installed in the global packages package cache
#
if isinstance(mapping, CSharpMapping) and current.driver.useIceBinDist(mapping) and current.config.netframework:
if not self.nugetPackageCache:
self.nugetPackageCache = re.search("info : global-packages: (.*)",
run("dotnet nuget locals --list global-packages")).groups(1)[0]
return os.path.join(self.nugetPackageCache, "zeroc.ice.net", self.getNugetPackageVersion(mapping))
else:
return self.getInstallDir(mapping, current, "ICE_HOME")
def getSliceDir(self, iceDir):
if iceDir.startswith("/usr"):
return os.path.join(iceDir, "share", "ice", "slice")
else:
return os.path.join(iceDir, "slice")
def getDefaultExe(self, name, config):
if name == "icebox" and config.cpp11:
name += "++11"
return name
def hasOpenSSL(self):
# This is used by the IceSSL test suite to figure out how to setup certificates
return False
def canRun(self, mapping, current):
return True
def getDotnetExe(self):
return "dotnet"
def getNugetPackageVersion(self, mapping):
version = None
if isinstance(mapping, CSharpMapping):
with open(os.path.join(toplevel, "csharp", "msbuild", "zeroc.ice.net.nuspec"), "r") as configFile:
version = re.search("(.*)", configFile.read()).group(1)
else:
with open(os.path.join(toplevel, "config", "icebuilder.props"), "r") as configFile:
version = re.search("(.*)", configFile.read()).group(1)
return version
class Darwin(Platform):
def getFilters(self, config):
if config.buildPlatform in ["iphoneos", "iphonesimulator", "macosx"] and "xcodesdk" in config.buildConfig:
return (["Ice/.*", "IceSSL/configuration"],
["Ice/background",
"Ice/echo",
"Ice/faultTolerance",
"Ice/gc",
"Ice/library",
"Ice/logger",
"Ice/properties",
"Ice/plugin",
"Ice/stringConverter",
"Ice/threadPoolPriority",
"Ice/udp"])
return Platform.getFilters(self, config)
def getDefaultBuildPlatform(self):
return "macosx"
def getLdPathEnvName(self):
return "DYLD_LIBRARY_PATH"
def getInstallDir(self, mapping, current, envName):
return os.environ.get(envName, "/usr/local")
class AIX(Platform):
def hasOpenSSL(self):
return True
class Linux(Platform):
def __init__(self):
Platform.__init__(self)
self.parseBuildVariables({
"linux_id" : ("linuxId", None),
"build-platform" : ("buildPlatform", None),
"foreign-platforms" : ("foreignPlatforms", lambda s : s.split(" ") if s else []),
})
self.multiArch = {}
if self.linuxId in ["ubuntu", "debian"]:
for p in [self.buildPlatform] + self.foreignPlatforms:
self.multiArch[p] = run("dpkg-architecture -f -a{0} -qDEB_HOST_MULTIARCH 2> /dev/null".format(p))
def hasOpenSSL(self):
return True
def getBinSubDir(self, mapping, process, current):
buildPlatform = current.driver.configs[mapping].buildPlatform
if self.linuxId in ["ubuntu", "debian"] and buildPlatform in self.foreignPlatforms:
return os.path.join("bin", self.multiArch[buildPlatform])
return "bin"
def getLibSubDir(self, mapping, process, 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 "lib"
if self.linuxId in ["centos", "rhel", "fedora"]:
return "lib64" if buildPlatform == "x64" else "lib"
elif self.linuxId in ["ubuntu", "debian"]:
return os.path.join("lib", self.multiArch[buildPlatform])
return "lib"
def getBuildSubDir(self, name, current):
if self.linuxId in ["ubuntu", "debian"]:
return os.path.join("build", self.multiArch[current.config.buildPlatform], current.config.buildConfig)
else:
return os.path.join("build", current.config.buildPlatform, current.config.buildConfig)
def getDefaultExe(self, name, config):
if name == "icebox":
if self.linuxId in ["centos", "rhel", "fedora"] and config.buildPlatform == "x86":
name += "32" # Multilib platform
if config.cpp11:
name += "++11"
return name
def canRun(self, mapping, current):
if self.linuxId in ["centos", "rhel", "fedora"] and current.config.buildPlatform == "x86":
#
# Don't test Glacier2/IceStorm/IceGrid services with multilib platforms. We only
# build services for the native platform.
#
parent = re.match(r'^([\w]*).*', current.testcase.getTestSuite().getId()).group(1)
if parent in ["Glacier2", "IceStorm", "IceGrid"]:
return False
return Platform.canRun(self, mapping, current)
class Windows(Platform):
def __init__(self):
Platform.__init__(self)
self.compiler = None
def getFilters(self, config):
if config.uwp:
return (["cpp/Ice/.*", "cpp/IceSSL/configuration"],
["Ice/background",
"Ice/echo",
"Ice/faultTolerance",
"Ice/gc",
"Ice/library",
"Ice/logger",
"Ice/networkProxy", # SOCKS proxy not supported with UWP
"Ice/properties", # Property files are not supported with UWP
"Ice/plugin",
"Ice/threadPoolPriority"])
elif self.getCompiler() in ["VC100"]:
return (["cpp/Ice/.*",
"cpp/IceSSL/.*",
"cpp/IceBox/.*",
"cpp/IceDiscovery/.*",
"cpp/IceUtil/.*",
"cpp/Slice/.*"], [])
elif self.getCompiler() not in ["VC140"]:
return ([], ["python", "php", "ruby"])
else:
return ([], ["ruby"])
def parseBuildVariables(self, 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 = "VC100"
elif out.find("Version 17.") != -1:
self.compiler = "VC110"
elif out.find("Version 18.") != -1:
self.compiler = "VC120"
elif out.find("Version 19.00.") != -1:
self.compiler = "VC140"
elif out.find("Version 19.1") != -1:
self.compiler = "VC141"
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 getBinSubDir(self, mapping, process, current):
#
# Platform/Config taget bin directories.
#
platform = current.driver.configs[mapping].buildPlatform
buildConfig = current.driver.configs[mapping].buildConfig
config = "Debug" if buildConfig.find("Debug") >= 0 else "Release"
if current.config.uwp and not current.config.protocol in ["ssl", "wss"]:
return ""
elif current.driver.useIceBinDist(mapping):
version = self.getNugetPackageVersion(mapping)
packageSuffix = self.getPlatformToolset() if isinstance(mapping, CppMapping) else "net"
package = os.path.join(mapping.path, "msbuild", "packages", "{0}".format(
mapping.getNugetPackage(packageSuffix, version))) if hasattr(mapping, "getNugetPackage") else None
nuget = package and os.path.exists(package)
iceHome = os.environ.get("ICE_HOME", "")
if isinstance(mapping, CppMapping) and not nuget and not os.path.exists(iceHome):
raise RuntimeError("Cannot detect a valid C++ distribution")
if not nuget:
return "bin"
elif isinstance(mapping, CSharpMapping) or isinstance(process, SliceTranslator):
return os.path.join("tools")
else:
#
# With Windows binary distribution some binaries are only included for Release configuration.
#
binaries = [Glacier2Router, IcePatch2Calc, IcePatch2Client, IcePatch2Server, IceBoxAdmin, IceBridge,
IceStormAdmin, IceGridAdmin]
config = next(("Release" for p in binaries if isinstance(process, p)), config)
return os.path.join("build", "native", "bin", platform, config)
else:
if isinstance(mapping, CppMapping):
return os.path.join("bin", platform, config)
elif isinstance(mapping, PhpMapping):
return os.path.join("msbuild", "packages", "zeroc.ice.v140.{0}".format(self.getNugetPackageVersion(mapping)),
"build", "native", "bin", platform, config)
return "bin"
def getLibSubDir(self, mapping, process, current):
if isinstance(mapping, PhpMapping):
return "php" if current.driver.useIceBinDist(mapping) else "lib"
return self.getBinSubDir(mapping, process, current)
def getBuildSubDir(self, name, current):
if os.path.exists(os.path.join(current.testcase.getPath(), "msbuild", name)):
return os.path.join("msbuild", name, current.config.buildPlatform, current.config.buildConfig)
else:
return os.path.join("msbuild", current.config.buildPlatform, current.config.buildConfig)
def getLdPathEnvName(self):
return "PATH"
def getInstallDir(self, mapping, current, envName):
platform = current.config.buildPlatform
config = "Debug" if current.config.buildConfig.find("Debug") >= 0 else "Release"
version = self.getNugetPackageVersion(mapping)
packageSuffix = self.getPlatformToolset() if isinstance(mapping, CppMapping) else "net"
package = None
if isinstance(mapping, CSharpMapping) and current.config.netframework:
#
# Use NuGet package from nuget locals
#
if not self.nugetPackageCache:
self.nugetPackageCache = re.search("info : global-packages: (.*)",
run("dotnet nuget locals --list global-packages")).groups(1)[0]
package = os.path.join(self.nugetPackageCache, "zeroc.ice.net", version)
elif hasattr(mapping, "getNugetPackage"):
package = os.path.join(mapping.path, "msbuild", "packages", mapping.getNugetPackage(packageSuffix, version))
home = os.environ.get(envName, "")
if isinstance(mapping, CppMapping) and not package and not os.path.exists(home):
raise RuntimeError("Cannot detect a valid C++ distribution")
return package if package and os.path.exists(package) else home
def canRun(self, mapping, current):
#
# On Windows, if testing with a binary distribution, don't test Glacier2/IceBridge services
# with the Debug configurations since we don't provide binaries for them.
#
if current.driver.useIceBinDist(mapping):
parent = re.match(r'^([\w]*).*', current.testcase.getTestSuite().getId()).group(1)
if parent in ["Glacier2", "IceBridge"] and current.config.buildConfig.find("Debug") >= 0:
return False
return Platform.canRun(self, mapping, current)
def getDotnetExe(self):
try:
return run("where dotnet").strip()
except:
return None
platform = None
if sys.platform == "darwin":
platform = Darwin()
elif sys.platform.startswith("aix"):
platform = AIX()
elif sys.platform.startswith("linux") or sys.platform.startswith("gnukfreebsd"):
platform = Linux()
elif sys.platform == "win32" or sys.platform[:6] == "cygwin":
platform = Windows()
if not platform:
print("can't run on unknown platform `{0}'".format(sys.platform))
sys.exit(1)
def parseOptions(obj, options, mapped={}):
# Transform configuration options provided on the command line to
# object data members. The data members must be already set on the
# object and with the correct type.
if not hasattr(obj, "parsedOptions"):
obj.parsedOptions=[]
remaining = []
for (o, a) in options:
if o.startswith("--"): o = o[2:]
if o.startswith("-"): o = o[1:]
if 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
class Mapping:
mappings = OrderedDict()
class Config:
# All option values for Ice/IceBox tests.
coreOptions = {
"protocol" : ["tcp", "ssl", "wss", "ws"],
"compress" : [False, True],
"ipv6" : [False, True],
"serialize" : [False, True],
"mx" : [False, True],
}
# All option values for IceGrid/IceStorm/Glacier2/IceDiscovery tests.
serviceOptions = {
"protocol" : ["tcp", "wss"],
"compress" : [False, True],
"ipv6" : [False, True],
"serialize" : [False, True],
"mx" : [False, True],
}
@classmethod
def getSupportedArgs(self):
return ("", ["config=", "platform=", "protocol=", "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.protocol = "tcp"
self.compress = False
self.serialize = False
self.ipv6 = False
self.mx = False
self.cprops = []
self.sprops = []
parseOptions(self, options, { "config" : "buildConfig",
"platform" : "buildPlatform" })
# 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.device = ""
self.avd = ""
self.androidemulator = False
self.netframework = ""
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)
options = None
parent = re.match(r'^([\w]*).*', testcase.getTestSuite().getId()).group(1)
if isinstance(testcase, ClientServerTestCase) and parent in ["Ice", "IceBox"]:
options = current.driver.filterOptions(testcase, self.coreOptions)
elif parent in ["IceGrid", "Glacier2", "IceStorm", "IceDiscovery", "IceBridge"]:
options = current.driver.filterOptions(testcase, self.serviceOptions)
return [c for c in gen(options)]
def canRun(self, current):
if not platform.canRun(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"] = "default -h localhost"
props["Ice.Admin.InstanceName"] = "Server" if isinstance(process, Server) else "Client"
props["IceMX.Metrics.Debug.GroupBy"] ="id"
props["IceMX.Metrics.Parent.GroupBy"] = "parent"
props["IceMX.Metrics.All.GroupBy"] = "none"
#
# 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.abspath(path)
mapping = None
for m in self.mappings.values():
if path.startswith(m.getPath() + os.sep) and (not mapping or len(mapping.getPath()) < len(m.getPath())):
mapping=m
return mapping
@classmethod
def add(self, name, mapping):
name = name.replace("\\", "/")
self.mappings[name] = mapping.init(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.platform = None
self.name = None
self.path = os.path.abspath(path) if path else None
self.testsuites = {}
def init(self, name):
self.name = name
if not self.path:
self.path = os.path.normpath(os.path.join(toplevel, name))
return self
def __str__(self):
return self.name
def createConfig(self, options):
return self.Config(options)
def filterTestSuite(self, testId, config, filters=[], rfilters=[]):
(pfilters, prfilters) = platform.getFilters(config)
for includes in [filters, [re.compile(pf) for pf in pfilters]]:
if len(includes) > 0:
for f in includes:
if f.search(self.name + "/" + testId):
break
else:
return True
for excludes in [rfilters, [re.compile(pf) for pf in prfilters]]:
if len(excludes) > 0:
for f in excludes:
if f.search(self.name + "/" + testId):
return True
return False
def loadTestSuites(self, tests, config, filters=[], rfilters=[]):
global currentMapping
currentMapping = self
for test in tests or [""]:
for root, dirs, files in os.walk(os.path.join(self.getTestsPath(), test.replace('/', os.sep))):
testId = root[len(self.getTestsPath()) + 1:]
if os.sep != "/":
testId = testId.replace(os.sep, "/")
if self.filterTestSuite(testId, config, filters, rfilters):
continue
#
# First check if there's a test.py file in the directory, if there's one use it.
#
if "test.py" in files:
#
# WORKAROUND for Python issue 15230 (fixed in 3.2) where run_path doesn't work correctly.
#
#runpy.run_path(os.path.join(root, "test.py"))
origsyspath = sys.path
sys.path = [root] + sys.path
runpy.run_module("test", init_globals=globals(), run_name=root)
origsyspath = sys.path
continue
#
# If there's no test.py file in the test directory, we check if there's a common
# script for the test in scripts/tests. If there's on we use it.
#
script = os.path.join(self.getCommonTestsPath(), testId + ".py")
if os.path.isfile(script):
runpy.run_module("tests." + testId.replace("/", "."), init_globals=globals(), run_name=root)
continue
#
# Finally, we try to "discover/compute" the test by looking up for well-known
# files.
#
testcases = self.computeTestCases(testId, files)
if testcases:
TestSuite(root, testcases)
currentMapping = None
def getTestSuites(self, ids=[]):
if not ids:
return self.testsuites.values()
return [self.testsuites[testSuiteId] for testSuiteId in ids if testSuiteId in self.testsuites]
def addTestSuite(self, testsuite):
assert len(testsuite.path) > len(self.getTestsPath()) + 1
testSuiteId = testsuite.path[len(self.getTestsPath()) + 1:].replace('\\', '/')
self.testsuites[testSuiteId] = testsuite
return testSuiteId
def findTestSuite(self, testsuite):
return self.testsuites.get(testsuite if isinstance(testsuite, str) else testsuite.id)
def computeTestCases(self, testId, files):
# Instantiate a new test suite if the directory contains well-known source files.
def checkFile(f, m):
try:
# If given mapping is same as local mapping, just check the files set, otherwise check
# with the mapping
return (self.getDefaultSource(f) in files) if m == self else m.hasSource(testId, f)
except KeyError:
# Expected if the mapping doesn't support the process type
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 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.getTestsPath(), testId, self.getDefaultSource(processType)))
except KeyError:
return False
def getPath(self):
return self.path
def getTestsPath(self):
return os.path.join(self.path, "test")
def getCommonTestsPath(self):
return os.path.join(self.path, "..", "scripts", "tests")
def getTestCwd(self, process, current):
return current.testcase.getPath()
def getDefaultSource(self, processType):
return processType
def getDefaultProcesses(self, processType, testsuite):
#
# If no server or client is explicitly set with a testcase, getDefaultProcess is called
# to figure out which process class to instantiate. Based on the processType and the testsuite
# we instantiate the right default process class.
#
if processType is None:
return []
elif testsuite.getId().startswith("IceUtil") or testsuite.getId().startswith("Slice"):
return [SimpleClient()]
elif testsuite.getId().startswith("IceGrid"):
if processType in ["client", "collocated"]:
return [IceGridClient()]
if processType in ["server", "serveramd"]:
return [IceGridServer()]
else:
return [Server()] if processType in ["server", "serveramd"] else [Client()]
def getDefaultExe(self, processType, config):
return processType
def getClientMapping(self, 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 getBinDir(self, process, current):
return os.path.join(current.driver.getIceDir(self, current), platform.getBinSubDir(self, process, current))
def getLibDir(self, process, current):
return os.path.join(current.driver.getIceDir(self, current), platform.getLibSubDir(self, process, current))
def getBuildDir(self, name, current):
return platform.getBuildSubDir(name, current)
def getCommandLine(self, current, process, exe, 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.getBinDir(process, 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.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(toplevel, "certs"),
}
#
# If the client doesn't support client certificates, set IceSSL.VerifyPeer to 0
#
if isinstance(process, Server):
if isinstance(current.testsuite.getMapping(), JavaScriptMapping):
sslProps["IceSSL.VerifyPeer"] = 0
return sslProps
def getArgs(self, process, current):
return []
def getEnv(self, process, current):
return {}
def getOptions(self, current):
return {}
def getRunOrder(self):
return ["Slice", "IceUtil", "Ice", "IceSSL", "IceBox", "Glacier2", "IceGrid", "IceStorm"]
def getCrossTestSuites(self):
return [
"Ice/ami",
"Ice/info",
"Ice/exceptions",
"Ice/enums",
"Ice/facets",
"Ice/inheritance",
"Ice/invoke",
"Ice/objects",
"Ice/operations",
"Ice/proxy",
"Ice/servantLocator",
"Ice/slicing/exceptions",
"Ice/slicing/objects",
"Ice/optional"
]
#
# A Runnable can be used as a "client" for in test cases, it provides
# implements run, setup and teardown methods.
#
class Runnable:
def __init__(self, desc=None):
self.desc = desc
def setup(self, current):
### Only called when ran from testcase
pass
def teardown(self, current, success):
### Only called when ran from testcase
pass
def run(self, current):
pass
#
# A Process describes how to run an executable process.
#
class Process(Runnable):
processType = None
def __init__(self, exe=None, outfilters=None, quiet=False, args=None, props=None, envs=None, desc=None,
mapping=None, 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.config.getProps(self, current))
allProps.update(self.getMapping(current).getProps(self, current))
allProps.update(current.testcase.getProps(self, current))
allProps.update(self.getProps(current))
allProps.update(self.props(self, current) if callable(self.props) else self.props)
allProps.update(props)
return allProps
def getEffectiveEnv(self, current):
allEnvs = {}
allEnvs.update(self.getMapping(current).getEnv(self, current))
allEnvs.update(current.testcase.getEnv(self, current))
allEnvs.update(self.getEnv(current))
allEnvs.update(self.envs(self, current) if callable(self.envs) else self.envs)
return allEnvs
def 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 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 getArgs(self, current):
return []
def getProps(self, current):
return {}
def getEnv(self, current):
return {}
def getMapping(self, current):
return self.mapping or current.testcase.getMapping()
def getExe(self, current):
processType = self.processType or current.testcase.getProcessType(self)
return self.exe or self.getMapping(current).getDefaultExe(processType, current.config)
def getCommandLine(self, current, 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=120, *args, **kargs):
IceProcess.__init__(self, exe, *args, **kargs)
self.waitForShutdown = waitForShutdown
self.readyCount = readyCount
self.ready = ready
self.startTimeout = startTimeout
def getProps(self, current):
props = IceProcess.getProps(self, current)
props.update({
"Ice.ThreadPool.Server.Size": 1,
"Ice.ThreadPool.Server.SizeMax": 3,
"Ice.ThreadPool.Server.SizeWarn": 0,
})
props.update(current.driver.getProcessProps(current, self.ready, self.readyCount + (1 if current.config.mx else 0)))
return props
def waitForStart(self, current):
# Wait for the process to be ready
current.processes[self].waitReady(self.ready, self.readyCount + (1 if current.config.mx else 0), self.startTimeout)
# Filter out remaining ready messages
self.outfilters.append(re.compile("[^\n]+ ready"))
# If we are not asked to be quiet and running from the main thread, print the server output
if not self.quiet and not current.driver.isWorkerThread():
current.processes[self].trace(self.outfilters)
def stop(self, current, waitSuccess=False, exitstatus=0):
IceProcess.stop(self, current, waitSuccess and self.waitForShutdown, exitstatus)
#
# An Ice client process.
#
class Client(IceProcess):
pass
#
# Executables for processes inheriting this marker class are looked up in the
# Ice distribution bin directory.
#
class ProcessFromBinDir:
def isFromBinDir(self):
return True
class SliceTranslator(ProcessFromBinDir, SimpleClient):
def __init__(self, translator):
SimpleClient.__init__(self, exe=translator, quiet=True, mapping=Mapping.getByName("cpp"))
def getCommandLine(self, current, 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")
elif os.path.exists("/usr/local/bin/slice2py"):
return "/usr/local/bin/slice2py"
else:
import slice2py
return sys.executable + " " + os.path.normpath(
os.path.join(slice2py.__file__, "..", "..", "..", "..", "bin", "slice2py"))
else:
return Process.getCommandLine(self, current, args)
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)
#
if not self.clients:
self.clients = self.mapping.getDefaultProcesses(self.getClientType(), testsuite)
#
# 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:
self.servers = self.mapping.getDefaultProcesses(self.getServerType(), testsuite)
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):
return self.testsuite.getPath()
def getMapping(self):
return self.mapping
def getArgs(self, process, current):
return self.args
def getProps(self, process, current):
return self.props
def 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 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)))
# elif k in self._skipped:
# out.write(' \n'.format(escapeXml(self._skipped[k])))
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:
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):
for m in [CppMapping, JavaMapping, CSharpMapping]:
config = driver.configs[self.mapping]
# TODO: WORKAROUND for ICE-8175
if self.id.startswith("IceStorm"):
return True
elif isinstance(self.mapping, m):
if "iphone" in config.buildPlatform or config.uwp:
return True # Not supported yet for tests that require a remote process controller
return self.runOnMainThread
else:
return True
def addTestCase(self, testcase):
if testcase.name in self.testcases:
raise RuntimeError("duplicate testcase {0} in testsuite {1}".format(testcase, self))
testcase.init(self.mapping, self)
self.testcases[testcase.name] = testcase
def findTestCase(self, testcase):
return self.testcases.get(testcase if isinstance(testcase, str) else testcase.name)
def getTestCases(self):
return self.testcases.values()
def setup(self, current):
pass
def run(self, current):
try:
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
def isCross(self):
# Only run the tests that support cross testing --all-cross or --cross
return self.id in self.mapping.getCrossTestSuites()
class ProcessController:
def __init__(self, current):
pass
def start(self, process, current, args, props, envs, watchDog):
raise NotImplemented()
def destroy(self, driver):
pass
class LocalProcessController(ProcessController):
class LocalProcess(Expect.Expect):
def __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):
# Depending on the configuration, either use an IPv4, IPv6 or BT address for Ice.Default.Host
if current.config.protocol == "bt":
if not current.driver.hostBT:
raise Test.Common.TestCaseFailedException("no Bluetooth address set with --host-bt")
return current.driver.hostBT
elif current.config.ipv6:
return current.driver.hostIPv6 or "::1"
else:
return current.driver.host or "127.0.0.1"
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)),
"icedir" : current.driver.getIceDir(current.testcase.getMapping(), current),
"iceboxconfigext": "" if not current.config.netframework else ".{0}".format(current.config.netframework)
}
traceFile = ""
if not isinstance(process.getMapping(current), JavaScriptMapping):
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")))
traceProps["Ice.StdErr"] = 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(toplevel, "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=None):
self.processControllerProxies = {}
self.controllerApps = []
self.cond = threading.Condition()
if endpoints:
comm = current.driver.getCommunicator()
import Test
class ProcessControllerRegistryI(Test.Common.ProcessControllerRegistry):
def __init__(self, remoteProcessController):
self.remoteProcessController = remoteProcessController
def setProcessController(self, proxy, current):
import Test
proxy = Test.Common.ProcessControllerPrx.uncheckedCast(current.con.createProxy(proxy.ice_getIdentity()))
self.remoteProcessController.setProcessController(proxy)
self.adapter = comm.createObjectAdapterWithEndpoints("Adapter", endpoints)
self.adapter.add(ProcessControllerRegistryI(self), comm.stringToIdentity("Util/ProcessControllerRegistry"))
self.adapter.activate()
else:
self.adapter = None
def __str__(self):
return "remote controller"
def getHost(self, current):
return self.getController(current).getHost(current.config.protocol, current.config.ipv6)
def getController(self, current):
ident = self.getControllerIdentity(current)
if type(ident) == str:
ident = current.driver.getCommunicator().stringToIdentity(ident)
with self.cond:
if ident in self.processControllerProxies:
return self.processControllerProxies[ident]
comm = current.driver.getCommunicator()
import Ice
import Test
if current.driver.controllerApp:
self.controllerApps.append(ident)
self.startControllerApp(current, ident)
if not self.adapter:
# Use well-known proxy and IceDiscovery to discover the process controller object from the app.
proxy = Test.Common.ProcessControllerPrx.uncheckedCast(comm.stringToProxy(comm.identityToString(ident)))
try:
proxy.ice_ping()
except Exception as ex:
raise RuntimeError("couldn't reach the remote controller `{0}'".format(proxy))
with self.cond:
self.processControllerProxies[ident] = proxy
return self.processControllerProxies[ident]
else:
# Wait 10 seconds for a process controller to be registered with the ProcessControllerRegistry
with self.cond:
if not ident in self.processControllerProxies:
self.cond.wait(10)
if ident in self.processControllerProxies:
return self.processControllerProxies[ident]
raise RuntimeError("couldn't reach the remote controller `{0}'".format(ident))
def setProcessController(self, proxy):
with self.cond:
self.processControllerProxies[proxy.ice_getIdentity()] = proxy
conn = proxy.ice_getConnection()
if(hasattr(conn, "setCloseCallback")):
proxy.ice_getConnection().setCloseCallback(lambda conn : self.clearProcessController(proxy, conn))
else:
import Ice
class CallbackI(Ice.ConnectionCallback):
def __init__(self, registry):
self.registry = registry
def heartbeath(self, conn):
pass
def closed(self, conn):
self.registry.clearProcessController(proxy, conn)
proxy.ice_getConnection().setCallback(CallbackI(self))
self.cond.notifyAll()
def clearProcessController(self, proxy, conn):
with self.cond:
if proxy.ice_getIdentity() in self.processControllerProxies:
if conn == self.processControllerProxies[proxy.ice_getIdentity()].ice_getCachedConnection():
del self.processControllerProxies[proxy.ice_getIdentity()]
def startControllerApp(self, current, ident):
pass
def stopControllerApp(self, ident):
pass
def start(self, process, current, args, props, envs, watchDog):
# Get the process controller
processController = self.getController(current)
# TODO: support envs?
exe = process.getExe(current)
args = ["--{0}={1}".format(k, val(v, quoteValue=False)) for k,v in props.items()] + [val(a) for a in args]
if current.driver.debug:
current.writeln("(executing `{0}/{1}' on `{2}' args = {3})".format(current.testsuite, exe, self, args))
prx = processController.start(str(current.testsuite), exe, args)
# Create bi-dir proxy in case we're talking to a bi-bir process controller.
if self.adapter:
prx = processController.ice_getConnection().createProxy(prx.ice_getIdentity())
import Test
return RemoteProcessController.RemoteProcess(exe, Test.Common.ProcessPrx.uncheckedCast(prx))
def destroy(self, driver):
if driver.controllerApp:
for ident in self.controllerApps:
self.stopControllerApp(ident)
self.controllerApps = []
if self.adapter:
self.adapter.destroy()
class AndroidProcessController(RemoteProcessController):
def __init__(self, current):
run("adb kill-server")
RemoteProcessController.__init__(self, current, "tcp -h 127.0.0.1 -p 15001" if current.config.androidemulator else None)
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):
return "Android/ProcessController"
def adb(self):
return "adb -s {}".format(self.device) if self.device 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 -no-window -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 60 seconds give up
#
if (time.time() - t) > 60:
raise RuntimeError("couldn't start the Android emulator `{}'".format(avd))
time.sleep(2)
print(" ok")
def startControllerApp(self, current, ident):
if current.config.avd:
self.startEmulator(current.config.avd)
elif current.config.androidemulator:
# Create Android Virtual Device
sdk = current.testcase.getMapping().getSDKPackage()
try:
run("avdmanager delete avd -n IceTests") # Delete the created device
except:
pass
run("sdkmanager \"{0}\"".format(sdk))
run("avdmanager create avd -k \"{0}\" -d \"Nexus 6\" -n IceTests".format(sdk))
self.startEmulator("IceTests")
elif not self.device:
raise RuntimeError("no Android device specified to run the controller application")
run("{} install -t -r {}".format(self.adb(), current.config.apk))
run("{} shell am start -n com.zeroc.testcontroller/.ControllerActivity".format(self.adb()))
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
try:
run("adb kill-server")
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)
class iOSSimulatorProcessController(RemoteProcessController):
device = "iOSSimulatorProcessController"
deviceID = "com.apple.CoreSimulator.SimDeviceType.iPhone-6"
appPath = "ios/controller/build"
def __init__(self, current):
RemoteProcessController.__init__(self, current)
self.simulatorID = None
self.runtimeID = None
# Pick the last iOS simulator runtime ID in the list of iOS simulators (assumed to be the latest).
for r in run("xcrun simctl list runtimes").split('\n'):
m = re.search("iOS .* \(.*\) - (.*)", r)
if m:
self.runtimeID = m.group(1)
if not self.runtimeID:
self.runtimeID = "com.apple.CoreSimulator.SimRuntime.iOS-11-0" # Default value
def __str__(self):
return "iOS Simulator"
def getControllerIdentity(self, current):
if isinstance(current.testcase.getMapping(), ObjCMapping):
if current.config.arc:
return "iPhoneSimulator/com.zeroc.ObjC-ARC-Test-Controller"
else:
return "iPhoneSimulator/com.zeroc.ObjC-Test-Controller"
elif isinstance(current.testcase.getMapping(), CppMapping):
if current.config.cpp11:
return "iPhoneSimulator/com.zeroc.Cpp11-Test-Controller"
else:
return "iPhoneSimulator/com.zeroc.Cpp98-Test-Controller"
else:
raise RuntimeError("can't run tests from the `{0}' mapping on iOS".format(current.testcase.getMapping()))
def startControllerApp(self, current, ident):
mapping = current.testcase.getMapping()
if isinstance(mapping, ObjCMapping):
appName = "Objective-C ARC Test Controller.app" if current.config.arc else "Objective-C Test Controller.app"
else:
assert(isinstance(mapping, CppMapping))
appName = "C++11 Test Controller.app" if current.config.cpp11 else "C++98 Test Controller.app"
sys.stdout.write("launching simulator... ")
sys.stdout.flush()
try:
run("xcrun simctl boot \"{0}\"".format(self.device))
except Exception as ex:
if str(ex).find("Booted") >= 0:
pass
elif str(ex).find("Invalid device") >= 0:
# Create the simulator device if it doesn't exist
self.simulatorID = run("xcrun simctl create \"{0}\" {1} {2}".format(self.device, self.deviceID, self.runtimeID))
run("xcrun simctl boot \"{0}\"".format(self.device))
else:
raise
print("ok")
sys.stdout.write("launching {0}... ".format(appName))
sys.stdout.flush()
path = os.path.join(mapping.getTestsPath(), self.appPath, "Debug-iphonesimulator", appName)
if not os.path.exists(path):
path = os.path.join(mapping.getTestsPath(), self.appPath, "Release-iphonesimulator", appName)
if not os.path.exists(path):
raise RuntimeError("couldn't find iOS simulator controller application, did you build it?")
run("xcrun simctl install \"{0}\" \"{1}\"".format(self.device, path))
run("xcrun simctl launch \"{0}\" {1}".format(self.device, ident.name))
print("ok")
def stopControllerApp(self, ident):
try:
run("xcrun simctl uninstall \"{0}\" {1}".format(self.device, ident.name))
except:
pass
def destroy(self, driver):
RemoteProcessController.destroy(self, driver)
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)
def __str__(self):
return "iOS Device"
def getControllerIdentity(self, current):
if isinstance(current.testcase.getMapping(), ObjCMapping):
if current.config.arc:
return "iPhoneOS/com.zeroc.ObjC-ARC-Test-Controller"
else:
return "iPhoneOS/com.zeroc.ObjC-Test-Controller"
else:
assert(isinstance(current.testcase.getMapping(), CppMapping))
if current.config.cpp11:
return "iPhoneOS/com.zeroc.Cpp11-Test-Controller"
else:
return "iPhoneOS/com.zeroc.Cpp98-Test-Controller"
def startControllerApp(self, current, ident):
# TODO: use ios-deploy to deploy and run the application on an attached device?
pass
def stopControllerApp(self, ident):
pass
class UWPProcessController(RemoteProcessController):
def __init__(self, current):
RemoteProcessController.__init__(self, current, "tcp -h 127.0.0.1 -p 15001")
self.name = "ice-uwp-controller"
self.appUserModelId = "ice-uwp-controller_3qjctahehqazm"
def __str__(self):
return "UWP"
def getControllerIdentity(self, current):
return "UWP/ProcessController"
def startControllerApp(self, current, ident):
platform = current.config.buildPlatform
config = current.config.buildConfig
layout = os.path.join(toplevel, "cpp", "test", platform, config, "AppX")
arch = "x86" if platform == "Win32" else platform
self.packageFullName = "{0}_1.0.0.0_{1}__3qjctahehqazm".format(self.name, arch)
prefix = "controller_1.0.0.0_{0}{1}".format(platform, "_{0}".format(config) if config == "Debug" else "")
package = os.path.join(toplevel, "cpp", "msbuild", "AppPackages", "controller",
"{0}_Test".format(prefix), "{0}.appx".format(prefix))
#
# If the application is already installed remove it, this will also take care
# of closing it.
#
if self.name in run("powershell Get-AppxPackage -Name {0}".format(self.name)):
run("powershell Remove-AppxPackage {0}".format(self.packageFullName))
#
# Remove any previous package we have extracted to ensure we use a
# fresh build
#
if os.path.exists(layout):
shutil.rmtree(layout)
os.makedirs(layout)
print("Unpacking package: {0} to {1}....".format(os.path.basename(package), layout))
run("MakeAppx.exe unpack /p \"{0}\" /d \"{1}\" /l".format(package, layout))
print("Registering application to run from layout...")
dependenciesDir = os.path.join(os.path.dirname(package), "Dependencies", arch)
for f in filter(lambda f: f.endswith(".appx"), os.listdir(dependenciesDir)):
run("powershell Add-AppxPackage -Path \"{0}\" -ForceApplicationShutdown".format(os.path.join(dependenciesDir, f)))
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
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),
getattr(DesiredCapabilities, capabilities))
elif current.config.browser != "Manual":
from selenium import webdriver
if not hasattr(webdriver, current.config.browser):
raise RuntimeError("unknown browser `{0}'".format(current.config.browser))
if current.config.browser == "Firefox":
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.
#
profile = webdriver.FirefoxProfile(os.path.join(toplevel, "scripts", "selenium", "firefox"))
self.driver = webdriver.Firefox(firefox_profile=profile)
else:
self.driver = getattr(webdriver, current.config.browser)()
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 not 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/start".format(self.host))
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 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):
parseOptions(self, options)
driver = self.drivers.get(self.driver)
if not driver:
raise RuntimeError("unknown driver `{0}'".format(self.driver))
return driver(options)
@classmethod
def 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):
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"})
self.filters = [re.compile(a) for a in self.filters]
self.rfilters = [re.compile(a) for a in self.rfilters]
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.communicator = None
self.interface = ""
self.processControllers = {}
def setConfigs(self, configs):
self.configs = configs
def useIceBinDist(self, mapping):
return self.useBinDist(mapping, "ICE_BIN_DIST")
def getIceDir(self, mapping, current):
return self.getInstallDir(mapping, current, "ICE_HOME", "ICE_BIN_DIST")
def useBinDist(self, mapping, envName):
env = os.environ.get(envName, "").split()
return 'all' in env or mapping.name in env
def getInstallDir(self, mapping, current, envHomeName, envBinDistName):
if self.useBinDist(mapping, envBinDistName):
if envHomeName == "ICE_HOME":
return platform.getIceInstallDir(mapping, current)
else:
return platform.getInstallDir(mapping, current, envHomeName)
elif mapping:
return mapping.getPath()
else:
return toplevel
def getSliceDir(self, mapping, current):
return platform.getSliceDir(self.getIceDir(mapping, current) if self.useIceBinDist(mapping) else toplevel)
def isWorkerThread(self):
return False
def getTestEndpoint(self, portnum, protocol="default"):
return "{0} -p {1}".format(protocol, self.getTestPort(portnum))
def getTestPort(self, portnum):
return 12010 + portnum
def getArgs(self, process, current):
### Return driver specific arguments
return []
def getProps(self, process, current):
props = {}
if isinstance(process, IceProcess):
if not self.host:
props["Ice.Default.Host"] = "0:0:0:0:0:0:0:1" if current.config.ipv6 else "127.0.0.1"
else:
props["Ice.Default.Host"] = self.host
return props
def getMappings(self):
### Return additional mappings to load required by the driver
return []
def 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
pythonMapping = Mapping.getByName("python")
if pythonMapping:
for p in pythonMapping.getPythonDirs(pythonMapping.getPath(), self.configs[pythonMapping]):
sys.path.append(p)
import Ice
Ice.loadSlice(os.path.join(toplevel, "scripts", "Controller.ice"))
initData = Ice.InitializationData()
initData.properties = Ice.createProperties()
# Load IceSSL, this is useful to talk with WSS for JavaScript
initData.properties.setProperty("Ice.Plugin.IceSSL", "IceSSL:createIceSSL")
initData.properties.setProperty("IceSSL.DefaultDir", os.path.join(toplevel, "certs"))
initData.properties.setProperty("IceSSL.CertFile", "server.p12")
initData.properties.setProperty("IceSSL.Password", "password")
initData.properties.setProperty("IceSSL.Keychain", "test.keychain")
initData.properties.setProperty("IceSSL.KeychainPassword", "password")
initData.properties.setProperty("IceSSL.VerifyPeer", "0");
initData.properties.setProperty("Ice.Plugin.IceDiscovery", "IceDiscovery:createIceDiscovery")
initData.properties.setProperty("IceDiscovery.DomainId", "TestController")
initData.properties.setProperty("IceDiscovery.Interface", self.interface)
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.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 isinstance(process.getMapping(current), JavaScriptMapping) and current.config.browser:
processController = BrowserProcessController
elif process and (isinstance(process.getMapping(current), AndroidMapping) or
isinstance(process.getMapping(current), AndroidCompatMapping)):
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=", "uwp", "openssl"])
@classmethod
def usage(self):
print("")
print("C++ Mapping options:")
print("--cpp-config= C++ build configuration for native executables (overrides --config).")
print("--cpp-platform= C++ build platform for native executables (overrides --platform).")
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" })
def canRun(self, current):
if not Mapping.Config.canRun(self, current):
return False
# No C++11 tests for IceStorm, IceGrid, etc
if self.cpp11:
testId = current.testcase.getTestSuite().getId()
parent = re.match(r'^([\w]*).*', testId).group(1)
if parent in ["IceStorm", "IceBridge"]:
return False
elif parent in ["IceGrid"] and testId not in ["IceGrid/simple"]:
return False
elif parent in ["Glacier2"] and testId not in ["Glacier2/application", "Glacier2/sessionHelper"]:
return False
return True
def getNugetPackage(self, compiler, version):
return "zeroc.ice.{0}.{1}".format(compiler, version)
def getDefaultExe(self, processType, config):
return platform.getDefaultExe(processType, config)
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):
keychainFile = "server.keychain" if server else "client.keychain"
props.update({
"IceSSL.KeychainPassword" : "password",
"IceSSL.Keychain": keychainFile
})
return props
def getPluginEntryPoint(self, plugin, process, current):
return {
"IceSSL" : "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.getLibDir(process, current))
#
# Add the test suite library directories to the platform library path environment variable.
#
if current.testcase:
for d in set([current.getBuildDir(d) for d in current.testcase.getTestSuite().getLibDirs()]):
libPaths.append(d)
env = {}
if len(libPaths) > 0:
env[platform.getLdPathEnvName()] = os.pathsep.join(libPaths)
return env
def getDefaultSource(self, processType):
return {
"client" : "Client.cpp",
"server" : "Server.cpp",
"serveramd" : "ServerAMD.cpp",
"collocated" : "Collocated.cpp",
}[processType]
class JavaMapping(Mapping):
def getCommandLine(self, current, process, exe, args):
javaHome = os.getenv("JAVA_HOME", "")
java = os.path.join(javaHome, "bin", "java") if javaHome else "java"
if process.isFromBinDir():
return "{0} {1} {2}".format(java, exe, args)
assert(current.testcase.getPath().startswith(self.getTestsPath()))
package = "test." + current.testcase.getPath()[len(self.getTestsPath()) + 1:].replace(os.sep, ".")
javaArgs = self.getJavaArgs(process, current)
if javaArgs:
return "{0} {1} {2}.{3} {4}".format(java, " ".join(javaArgs), package, exe, args)
else:
return "{0} {1}.{2} {3}".format(java, package, exe, args)
def getJavaArgs(self, process, current):
return []
def getSSLProps(self, process, current):
props = Mapping.getSSLProps(self, process, current)
props.update({
"IceSSL.Keystore": "server.jks" if isinstance(process, Server) else "client.jks",
})
return props
def getPluginEntryPoint(self, plugin, process, current):
return {
"IceSSL" : "com.zeroc.IceSSL.PluginFactory",
"IceBT" : "com.zeroc.IceBT.PluginFactory",
"IceDiscovery" : "com.zeroc.IceDiscovery.PluginFactory",
"IceLocatorDiscovery" : "com.zeroc.IceLocatorDiscovery.PluginFactory"
}[plugin]
def getEnv(self, process, current):
return { "CLASSPATH" : os.path.join(self.path, "lib", "test.jar") }
def getTestsPath(self):
return os.path.join(self.path, "test/src/main/java/test")
def getDefaultSource(self, processType):
return self.getDefaultExe(processType) + ".java"
def getDefaultExe(self, processType, config=None):
return {
"client" : "Client",
"server" : "Server",
"serveramd" : "AMDServer",
"collocated" : "Collocated",
"icebox": "com.zeroc.IceBox.Server",
"iceboxadmin" : "com.zeroc.IceBox.Admin",
}[processType]
class JavaCompatMapping(JavaMapping):
def getPluginEntryPoint(self, plugin, process, current):
return {
"IceSSL" : "IceSSL.PluginFactory",
"IceBT" : "IceBT.PluginFactory",
"IceDiscovery" : "IceDiscovery.PluginFactory",
"IceLocatorDiscovery" : "IceLocatorDiscovery.PluginFactory"
}[plugin]
def getDefaultExe(self, processType, config=None):
return {
"client" : "Client",
"server" : "Server",
"serveramd" : "AMDServer",
"collocated" : "Collocated",
"icebox": "IceBox.Server",
"iceboxadmin" : "IceBox.Admin",
}[processType]
class Android:
@classmethod
def getUnsuportedTests(self, protocol):
tests = [
"Ice/hash",
"Ice/faultTolerance",
"Ice/metrics",
"Ice/networkProxy",
"Ice/throughput",
"Ice/plugin",
"Ice/logger",
"Ice/properties"]
if protocol in ["ssl", "wss"]:
tests += [
"Ice/binding",
"Ice/timeout",
"Ice/udp"]
if protocol in ["bt", "bts"]:
tests += [
"Ice/info",
"Ice/udp"]
return tests
class AndroidMapping(JavaMapping):
class Config(Mapping.Config):
@classmethod
def getSupportedArgs(self):
return ("", ["device=", "avd=", "androidemulator"])
@classmethod
def usage(self):
print("")
print("Android Mapping options:")
print("--device= ID of the emulator or device used to run the tests.")
print("--androidemulator Run tests in emulator as opposed to a real device.")
print("--avd= Start specific Android Virtual Device")
def __init__(self, options=[]):
Mapping.Config.__init__(self, options)
parseOptions(self, options)
self.androidemulator = self.androidemulator or self.avd
self.apk = "controller/build/outputs/apk/debug/testController-debug.apk"
def getSSLProps(self, process, current):
props = JavaMapping.getSSLProps(self, process, current)
props.update({
"IceSSL.KeystoreType" : "BKS",
"IceSSL.TruststoreType" : "BKS",
"Ice.InitPlugins" : "0",
"IceSSL.Keystore": "server.bks" if isinstance(process, Server) else "client.bks"})
return props
def getTestsPath(self):
return os.path.join(self.path, "../test/src/main/java/test")
def getCommonTestsPath(self):
return os.path.join(self.path, "..", "..", "scripts", "tests")
def filterTestSuite(self, testId, config, filters=[], rfilters=[]):
if not testId.startswith("Ice/") or testId in Android.getUnsuportedTests(config.protocol):
return True
return JavaMapping.filterTestSuite(self, testId, config, filters, rfilters)
def getSDKPackage(self):
return "system-images;android-25;google_apis;x86_64"
class AndroidCompatMapping(JavaCompatMapping):
class Config(Mapping.Config):
@classmethod
def getSupportedArgs(self):
return ("", ["device=", "avd=", "androidemulator"])
@classmethod
def usage(self):
print("")
print("Android Mapping options:")
print("--device= Id of the emulator or device used to run the tests.")
print("--androidemulator Run tests in emulator as opposed to a real device.")
print("--avd Start emulator image")
def __init__(self, options=[]):
Mapping.Config.__init__(self, options)
parseOptions(self, options)
self.androidemulator = self.androidemulator or self.avd
self.apk = "test/controller/build/outputs/apk/debug/testController-debug.apk"
def getSSLProps(self, process, current):
props = JavaCompatMapping.getSSLProps(self, process, current)
props.update({
"IceSSL.KeystoreType" : "BKS",
"IceSSL.TruststoreType" : "BKS",
"Ice.InitPlugins" : "0",
"IceSSL.Keystore": "server.bks" if isinstance(process, Server) else "client.bks"})
return props
def getTestsPath(self):
return os.path.join(self.path, "../test/src/main/java/test")
def getCommonTestsPath(self):
return os.path.join(self.path, "..", "..", "scripts", "tests")
def filterTestSuite(self, testId, config, filters=[], rfilters=[]):
if not testId.startswith("Ice/") or testId in Android.getUnsuportedTests(config.protocol):
return True
return JavaCompatMapping.filterTestSuite(self, testId, config, filters, rfilters)
def getSDKPackage(self):
return "system-images;android-21;google_apis;x86_64"
class CSharpMapping(Mapping):
class Config(Mapping.Config):
@classmethod
def getSupportedArgs(self):
return ("", ["netframework="])
@classmethod
def usage(self):
print("")
print("--netframework Run C# tests using Ice netstandard2.0 libraries and tests")
print(" build with given .NET Framework [netcoreapp2.0|net4.6]")
def __init__(self, options=[]):
Mapping.Config.__init__(self, options)
parseOptions(self, options, { "netframework" : "netframework" })
#
# For non Windows platforms the default is netcoreapp2.0 for windows empty
# means to run test agains .NET Framework 4.5 Ice build
#
supportedframeworks = ["netcoreapp2.0"]
if isinstance(platform, Windows):
supportedframeworks += ["net461", "net462", "net47", "net471"]
if self.netframework:
if not self.netframework in supportedframeworks:
raise RuntimeError("Unssuported .NET Framework `{0}'".format(self.netframework))
else:
self.netframework = "" if isinstance(platform, Windows) else "netcoreapp2.0"
def canRun(self, current):
testId = current.testcase.getTestSuite().getId()
if not Mapping.Config.canRun(self, current):
return False
if self.netframework:
#
# The following tests require multicast, on Unix platforms it's currently only supported
# with IPv4 due to .NET Core bug https://github.com/dotnet/corefx/issues/25525
#
if not isinstance(platform, Windows) and self.ipv6 and testId in ["Ice/udp",
"IceDiscovery/simple",
"IceGrid/simple"]:
return False
if isinstance(platform, Darwin) and (self.protocol in ["ssl", "wss"] or
"IceSSL" in testId or
"IceDiscovery" in testId):
return False
return True
def getBuildDir(self, name, current):
if current.config.netframework:
return os.path.join("msbuild", name, "netstandard2.0", current.config.netframework)
else:
return os.path.join("msbuild", name, "net45")
def getSSLProps(self, process, current):
props = Mapping.getSSLProps(self, process, current)
props.update({
"IceSSL.Password": "password",
"IceSSL.DefaultDir": os.path.join(toplevel, "certs"),
"IceSSL.CAs": "cacert.pem",
"IceSSL.VerifyPeer": "0" if current.config.protocol == "wss" else "2",
"IceSSL.CertFile": "server.p12" if isinstance(process, Server) else "client.p12",
})
return props
def getPluginEntryPoint(self, plugin, process, current):
plugindir = "{0}/{1}".format(current.driver.getIceDir(self, current), "lib")
if current.config.netframework:
plugindir = os.path.join(plugindir, "netstandard2.0")
else:
plugindir = os.path.join(plugindir, "net45")
#
# If the plug-in assemblie 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 assemblie.
#
proccessType = current.testcase.getProcessType(process)
if proccessType:
testdir = os.path.join(current.testcase.getPath(), self.getBuildDir(proccessType, current))
if os.path.isfile(os.path.join(testdir, plugin + ".dll")):
plugindir = testdir
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 current.driver.useIceBinDist(self):
framework = "net45" if not current.config.netframework else current.config.netframework
env['PATH'] = os.path.join(platform.getIceInstallDir(self, current), "tools", framework)
if not current.config.netframework:
print("DEVPATH From: {}".format(platform.getIceInstallDir(self, current)))
env['DEVPATH'] = os.path.join(platform.getIceInstallDir(self, current), "lib", "net45")
else:
env['PATH'] = os.path.join(toplevel, "cpp", "msbuild", "packages",
"bzip2.{0}.1.0.6.9".format(platform.getPlatformToolset()),
"build", "native", "bin", "x64", "Release")
if not current.config.netframework:
env['DEVPATH'] = os.path.join(current.driver.getIceDir(self, current), "lib", "net45")
return env
def getDefaultSource(self, processType):
return {
"client" : "Client.cs",
"server" : "Server.cs",
"serveramd" : "ServerAMD.cs",
"collocated" : "Collocated.cs",
}[processType]
def getDefaultExe(self, processType, config):
return processType
def getNugetPackage(self, compiler, version):
return "zeroc.ice.net.{0}".format(version)
def getCommandLine(self, current, process, exe, args):
cmd = ""
if current.config.netframework:
cmd = "dotnet "
if exe == "icebox":
if current.driver.useIceBinDist(self):
cmd += os.path.join(platform.getIceInstallDir(self, current), "tools", "netcoreapp2.0", "iceboxnet.dll")
else:
cmd += os.path.join(self.path, "bin", "netcoreapp2.0", "iceboxnet.dll")
else:
if current.config.netframework == "netcoreapp2.0":
cmd += os.path.join(current.testcase.getPath(), self.getBuildDir(exe, current), "{0}.dll".format(exe))
else:
cmd += os.path.join(current.testcase.getPath(), self.getBuildDir(exe, current), "{0}".format(exe))
else:
if exe == "icebox":
if current.driver.useIceBinDist(self):
cmd = os.path.join(platform.getIceInstallDir(self, current), "tools", "net45", "iceboxnet.exe")
else:
cmd = os.path.join(self.path, "bin", "net45", "iceboxnet.exe")
else:
cmd = os.path.join(current.testcase.getPath(), self.getBuildDir(exe, current), exe)
return cmd + " " + args
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 current.driver.getIceDir(self, current) != platform.getIceInstallDir(self, current):
# If not installed in the default platform installation directory, add
# the Ice C++ library directory to the library path
env[platform.getLdPathEnvName()] = Mapping.getByName("cpp").getLibDir(process, current)
return env
def getNugetPackage(self, compiler, version):
return "zeroc.ice.{0}.{1}".format(compiler, version)
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]
class PythonMapping(CppBasedMapping):
class Config(CppBasedMapping.Config):
mappingName = "python"
mappingDesc = "Python"
def getCommandLine(self, current, process, exe, args):
return "\"{0}\" {1} {2}".format(sys.executable, exe, args)
def getEnv(self, process, current):
env = CppBasedMapping.getEnv(self, process, current)
if current.driver.getIceDir(self, current) != platform.getIceInstallDir(self, current):
# If not installed in the default platform installation directory, add
# the Ice python directory to PYTHONPATH
dirs = self.getPythonDirs(current.driver.getIceDir(self, current), current.config)
env["PYTHONPATH"] = os.pathsep.join(dirs)
return env
def getPythonDirs(self, iceDir, config):
dirs = []
if isinstance(platform, Windows):
dirs.append(os.path.join(iceDir, "python", config.buildPlatform, config.buildConfig))
dirs.append(os.path.join(iceDir, "python"))
return dirs
def getDefaultExe(self, processType, config):
return self.getDefaultSource(processType)
def getDefaultSource(self, processType):
return {
"client" : "Client.py",
"server" : "Server.py",
"serveramd" : "ServerAMD.py",
"collocated" : "Collocated.py",
}[processType]
class CppBasedClientMapping(CppBasedMapping):
def loadTestSuites(self, tests, config, filters, rfilters):
Mapping.loadTestSuites(self, tests, config, filters, rfilters)
self.getServerMapping().loadTestSuites(self.testsuites.keys(), config)
def getServerMapping(self, testId=None):
return Mapping.getByName("cpp") # By default, run clients against C++ mapping executables
def getDefaultExe(self, processType, config):
return self.getDefaultSource(processType)
class RubyMapping(CppBasedClientMapping):
class Config(CppBasedClientMapping.Config):
mappingName = "ruby"
mappingDesc = "Ruby"
def getCommandLine(self, current, process, exe, args):
return "ruby " + exe + " " + args
def getEnv(self, process, current):
env = CppBasedMapping.getEnv(self, process, current)
if current.driver.getIceDir(self, current) != platform.getIceInstallDir(self, current):
# If not installed in the default platform installation directory, add
# the Ice ruby directory to RUBYLIB
env["RUBYLIB"] = os.path.join(self.path, "ruby")
return env
def getDefaultSource(self, processType):
return { "client" : "Client.rb" }[processType]
class PhpMapping(CppBasedClientMapping):
class Config(CppBasedClientMapping.Config):
mappingName = "php"
mappingDesc = "PHP"
def getEnv(self, process, current):
env = CppBasedMapping.getEnv(self, process, current)
if (isinstance(platform, Windows) and
(current.driver.useIceBinDist(self) or "cpp" in os.environ.get("ICE_BIN_DIST", "").split())):
env[platform.getLdPathEnvName()] = self.getBinDir(process, current)
return env
def getCommandLine(self, current, process, exe, args):
phpArgs = []
#
# If Ice is not installed in the system directory, specify its location with PHP
# configuration arguments.
#
if current.driver.getIceDir(self, current) != platform.getIceInstallDir(self, current):
useBinDist = current.driver.useIceBinDist(self)
if isinstance(platform, Windows):
buildPlatform = current.driver.configs[self].buildPlatform
config = "Debug" if current.driver.configs[self].buildConfig.find("Debug") >= 0 else "Release"
extension = "php_ice_nts.dll" if "NTS" in run("php -v") else "php_ice.dll"
extensionDir = os.path.join(self.path, "lib", buildPlatform, config)
includePath = self.getLibDir(process, current)
else:
extension = "ice.so"
extensionDir = self.getLibDir(process, current)
includePath = "{0}/{1}".format(current.driver.getIceDir(self, current), "php" if useBinDist else "lib")
phpArgs += ["-n"] # Do not load any php.ini files
phpArgs += ["-d", "extension_dir='{0}'".format(extensionDir)]
phpArgs += ["-d", "extension='{0}'".format(extension)]
phpArgs += ["-d", "include_path='{0}'".format(includePath)]
if hasattr(process, "getPhpArgs"):
phpArgs += process.getPhpArgs(current)
return "php {0} -f {1} -- {2}".format(" ".join(phpArgs), 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 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 JavaScriptMapping(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)
self.es5 = False
self.browser = ""
self.worker = False
parseOptions(self, options)
if self.browser and self.protocol == "tcp":
self.protocol = "ws"
if self.browser in ["Edge", "Ie"]:
self.es5 = True
def loadTestSuites(self, tests, config, filters, rfilters):
Mapping.loadTestSuites(self, tests, config, filters, rfilters)
self.getServerMapping().loadTestSuites(list(self.testsuites.keys()) + ["Ice/echo"], config, filters, rfilters)
def getServerMapping(self, 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, testsuite):
if processType in ["server", "serveramd"]:
return [EchoServer(), Server()]
return Mapping.getDefaultProcesses(self, processType, testsuite)
def getCommandLine(self, current, process, exe, args):
if current.config.es5:
return "node {0}/test/Common/run.js --es5 {1} {2}".format(self.path, exe, args)
else:
return "node {0}/test/Common/run.js {1} {2}".format(self.path, exe, args)
def getDefaultSource(self, processType):
return { "client" : "Client.js", "serveramd" : "ServerAMD.js", "server" : "Server.js" }[processType]
def getDefaultExe(self, processType, config=None):
return self.getDefaultSource(processType).replace(".js", "")
def getEnv(self, process, current):
env = Mapping.getEnv(self, process, current)
env["NODE_PATH"] = self.getTestCwd(process, current)
return env
def getSSLProps(self, process, current):
return {}
def getTestCwd(self, process, current):
if current.config.es5:
# Change to the ES5 test directory if testing ES5
return os.path.join(self.path, "test", "es5", current.testcase.getTestSuite().getId())
else:
return os.path.join(self.path, "test", current.testcase.getTestSuite().getId())
def computeTestCases(self, testId, files):
if testId.find("es5") > -1:
return # Ignore es5 directories
return Mapping.computeTestCases(self, testId, files)
def getOptions(self, current):
options = {
"protocol" : ["ws", "wss"] if current.config.browser else ["tcp"],
"compress" : [False],
"ipv6" : [False],
"serialize" : [False],
"mx" : [False],
"es5" : [False, True],
"worker" : [False, True] if current.config.browser else [False],
}
# Edge and Ie only support ES5 for now
if current.config.browser in ["Edge", "Ie"]:
options["es5"] = [True]
return options
try:
from Glacier2Util import *
from IceBoxUtil import *
from IceBridgeUtil import *
from IcePatch2Util import *
from IceGridUtil import *
from IceStormUtil import *
except ImportError:
pass
from LocalDriver import *
#
# Supported mappings
#
for m in filter(lambda x: os.path.isdir(os.path.join(toplevel, x)), os.listdir(toplevel)):
if m == "cpp" or re.match("cpp-.*", m):
Mapping.add(m, CppMapping())
elif m == "java-compat" or re.match("java-compat-.*", m):
Mapping.add(m, JavaCompatMapping())
elif m == "java" or re.match("java-.*", m):
Mapping.add(m, JavaMapping())
elif m == "python" or re.match("python-.*", m):
Mapping.add(m, PythonMapping())
elif m == "ruby" or re.match("ruby-.*", m):
Mapping.add(m, RubyMapping())
elif m == "php" or re.match("php-.*", m):
Mapping.add(m, PhpMapping())
elif m == "js" or re.match("js-.*", m):
Mapping.add(m, JavaScriptMapping())
elif m == "objective-c" or re.match("objective-c-*", m):
Mapping.add(m, ObjCMapping())
try:
if not isinstance(platform, Windows):
#
# Check if the .NET Core SDK is installed
#
run("dotnet --version")
Mapping.add("csharp", CSharpMapping())
except:
pass
#
# Check if the Android SDK is installed and eventually add the Android mappings
#
try:
run("adb version")
Mapping.add(os.path.join("java-compat", "android"), AndroidCompatMapping())
Mapping.add(os.path.join("java", "android"), AndroidMapping())
except:
pass
#
# Check if Matlab is installed and eventually add the Matlab mapping
#
try:
run("where matlab" if isinstance(platform, Windows) else "which matlab")
Mapping.add("matlab", MatlabMapping())
except:
pass
def runTestsWithPath(path):
runTests([Mapping.getByPath(path)])
def runTests(mappings=None, drivers=None):
if not mappings:
mappings = Mapping.getAll()
if not drivers:
drivers = Driver.getAll()
def usage():
print("Usage: " + sys.argv[0] + " [options] [tests]")
print("")
print("Options:")
print("-h | --help Show this message")
Driver.commonUsage()
for driver in drivers:
driver.usage()
Mapping.Config.commonUsage()
for mapping in mappings:
mapping.Config.usage()
print("")
driver = None
try:
options = [Driver.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)
#
# 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():
mapping.loadTestSuites(args, configs[mapping], driver.filters, driver.rfilters)
#
# Finally, run the test suites with the driver.
#
try:
sys.exit(driver.run(mappings, args))
except KeyboardInterrupt:
pass
finally:
driver.destroy()
except Exception as e:
print(sys.argv[0] + ": unexpected exception raised:\n" + traceback.format_exc())
sys.exit(1)