1# 2# Copyright (c) ZeroC, Inc. All rights reserved. 3# 4 5import os, sys, runpy, getopt, traceback, types, threading, time, datetime, re, itertools, random, subprocess, shutil 6import copy, inspect, xml.sax.saxutils 7 8isPython2 = sys.version_info[0] == 2 9if isPython2: 10 import Queue as queue 11 from StringIO import StringIO 12else: 13 import queue 14 from io import StringIO 15 16from collections import OrderedDict 17import Expect 18 19toplevel = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) 20 21def run(cmd, cwd=None, err=False, stdout=False, stdin=None, stdinRepeat=True): 22 p = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE, stdout=None if stdout else subprocess.PIPE, 23 stderr=subprocess.STDOUT, cwd=cwd) 24 try: 25 if stdin: 26 try: 27 while True: 28 p.stdin.write(stdin) 29 p.stdin.flush() 30 if not stdinRepeat: 31 break 32 time.sleep(1) 33 except: 34 pass 35 36 out = (p.stderr if stdout else p.stdout).read().decode('UTF-8').strip() 37 if(not err and p.wait() != 0) or (err and p.wait() == 0) : 38 raise RuntimeError(cmd + " failed:\n" + out) 39 finally: 40 # 41 # Without this we get warnings when running with python_d on Windows 42 # 43 # ResourceWarning: unclosed file <_io.TextIOWrapper name=3 encoding='cp1252'> 44 # 45 (p.stderr if stdout else p.stdout).close() 46 try: 47 p.stdin.close() 48 except Exception: 49 pass 50 return out 51 52def val(v, quoteValue=True): 53 if type(v) == bool: 54 return "1" if v else "0" 55 elif type(v) == str: 56 if not quoteValue or v.find(" ") < 0: 57 return v 58 v = v.replace("\\", "\\\\").replace("\"", "\\\"") 59 return "\"{0}\"".format(v) 60 else: 61 return str(v) 62 63illegalXMLChars = re.compile(u'[\x00-\x08\x0b\x0c\x0e-\x1F\uD800-\uDFFF\uFFFE\uFFFF]') 64 65def escapeXml(s, attribute=False): 66 # Remove backspace characters from the output (they aren't accepted by Jenkins XML parser) 67 if isPython2: 68 s = "".join(ch for ch in unicode(s.decode("utf-8")) if ch != u"\u0008").encode("utf-8") 69 else: 70 s = "".join(ch for ch in s if ch != u"\u0008") 71 s = illegalXMLChars.sub("?", s) # Strip invalid XML characters 72 return xml.sax.saxutils.quoteattr(s) if attribute else xml.sax.saxutils.escape(s) 73 74""" 75Component abstract class. The driver and mapping classes rely on the component 76class to provide component specific information. 77""" 78class Component(object): 79 80 def __init__(self): 81 self.nugetVersion = None 82 83 """ 84 Returns whether or not to use the binary distribution. 85 """ 86 def useBinDist(self, mapping, current): 87 return True 88 89 """ 90 Returns the component installation directory if using a binary distribution 91 or the mapping directory if using a source distribution. 92 """ 93 def getInstallDir(self, mapping, current): 94 raise Error("must be overriden") 95 96 def getSourceDir(self): 97 return toplevel 98 99 def getTestDir(self, mapping): 100 if isinstance(mapping, JavaMapping): 101 return os.path.join(mapping.getPath(), "test", "src", "main", "java", "test") 102 elif isinstance(mapping, TypeScriptMapping): 103 return os.path.join(mapping.getPath(), "test", "typescript") 104 return os.path.join(mapping.getPath(), "test") 105 106 def getScriptDir(self): 107 return os.path.join(self.getSourceDir(), "scripts", "tests") 108 109 def getPhpExtension(self, mapping, current): 110 raise RuntimeError("must be overriden if component provides php mapping") 111 112 def getNugetPackage(self, mapping): 113 return "zeroc.{0}.{1}".format(self.__class__.__name__.lower(), 114 "net" if isinstance(mapping, CSharpMapping) else platform.getPlatformToolset()) 115 116 def getNugetPackageVersion(self, mapping): 117 if not self.nugetVersion: 118 file = self.getNugetPackageVersionFile(mapping) 119 if file.endswith(".nuspec"): 120 expr = "<version>(.*)</version>" 121 elif file.endswith("packages.config"): 122 expr = "id=\"{0}\" version=\"(.*)\" target".format(self.getNugetPackage(mapping)) 123 if expr: 124 with open(file, "r") as config: 125 m = re.search(expr, config.read()) 126 if m: 127 self.nugetVersion = m.group(1) 128 if not self.nugetVersion: 129 raise RuntimeError("couldn't figure out the nuget version from `{0}'".format(file)) 130 return self.nugetVersion 131 132 def getNugetPackageVersionFile(self, mapping): 133 raise RuntimeError("must be overriden if component provides C++ or C# nuget packages") 134 135 def getFilters(self, mapping, config): 136 return ([], []) 137 138 def canRun(self, testId, mapping, current): 139 return True 140 141 def isMainThreadOnly(self, testId): 142 return True 143 144 def getDefaultProcesses(self, mapping, processType, testId): 145 return None 146 147 def getDefaultExe(self, mapping, processType): 148 return None 149 150 def getDefaultSource(self, mapping, processType): 151 return None 152 153 def getOptions(self, testcase, current): 154 return None 155 156 def getRunOrder(self): 157 return [] 158 159 def getEnv(self, process, current): 160 return {} 161 162 def getProps(self, process, current): 163 return {} 164 165 def isCross(self, testId): 166 return False 167 168 def getSliceDir(self, mapping, current): 169 installDir = self.getInstallDir(mapping, current) 170 if installDir.endswith(mapping.name): 171 installDir = installDir[0:len(installDir) - len(mapping.name) - 1] 172 if platform.getInstallDir() and installDir == platform.getInstallDir(): 173 return os.path.join(installDir, "share", "ice", "slice") 174 else: 175 return os.path.join(installDir, "slice") 176 177 def getBinDir(self, process, mapping, current): 178 return platform._getBinDir(self, process, mapping, current) 179 180 def getLibDir(self, process, mapping, current): 181 return platform._getLibDir(self, process, mapping, current) 182 183 def getPhpIncludePath(self, mapping, current): 184 return "{0}/{1}".format(self.getInstallDir(mapping, current), "php" if self.useBinDist(mapping, current) else "lib") 185 186 def _useBinDist(self, mapping, current, envName): 187 env = os.environ.get(envName, "").split() 188 return 'all' in env or mapping.name in env 189 190 def _getInstallDir(self, mapping, current, envHomeName): 191 if self.useBinDist(mapping, current): 192 # On Windows or for the C# mapping we first look for Nuget packages rather than the binary installation 193 if isinstance(platform, Windows) or isinstance(mapping, CSharpMapping): 194 packageDir = platform.getNugetPackageDir(self, mapping, current) 195 if envHomeName and not os.path.exists(packageDir): 196 home = os.environ.get(envHomeName, "") 197 if not home or not os.path.exists(home): 198 raise RuntimeError("Cannot detect a valid distribution in `" + envHomeName + "'") 199 return home 200 else: 201 return packageDir 202 else: 203 return os.environ.get(envHomeName, platform.getInstallDir()) 204 elif mapping: 205 return mapping.getPath() 206 else: 207 return self.getSourceDir() 208 209class Platform(object): 210 211 def __init__(self): 212 try: 213 run("dotnet --version") 214 self.nugetPackageCache = re.search("info : global-packages: (.*)", 215 run("dotnet nuget locals --list global-packages")).groups(1)[0] 216 except: 217 self.nugetPackageCache = None 218 219 def init(self, component): 220 self.parseBuildVariables(component, { 221 "supported-platforms" : ("supportedPlatforms", lambda s : s.split(" ")), 222 "supported-configs" : ("supportedConfigs", lambda s : s.split(" ")) 223 }) 224 225 def hasDotNet(self): 226 return self.nugetPackageCache != None 227 228 def parseBuildVariables(self, component, variables): 229 # Run make to get the values of the given variables 230 if os.path.exists(os.path.join(component.getSourceDir(), "Makefile")): # Top level makefile 231 cwd = component.getSourceDir() 232 elif Mapping.getByName("cpp"): 233 cwd = Mapping.getByName("cpp").getPath() 234 235 output = run('gmake print V="{0}"'.format(" ".join(variables.keys())), cwd=cwd) 236 for l in output.split("\n"): 237 match = re.match(r'^.*:.*: (.*) = (.*)', l) 238 if match and match.group(1): 239 if match.group(1) in variables: 240 (varname, valuefn) = variables[match.group(1).strip()] 241 value = match.group(2).strip() or "" 242 setattr(self, varname, valuefn(value) if valuefn else value) 243 244 def getDefaultBuildPlatform(self): 245 return self.supportedPlatforms[0] 246 247 def getDefaultBuildConfig(self): 248 return self.supportedConfigs[0] 249 250 def _getBinDir(self, component, process, mapping, current): 251 installDir = component.getInstallDir(mapping, current) 252 if isinstance(mapping, CSharpMapping): 253 if component.useBinDist(mapping, current): 254 return os.path.join(installDir, "tools", "netcoreapp2.1") 255 else: 256 return os.path.join(installDir, "bin", "netcoreapp2.1") 257 return os.path.join(installDir, "bin") 258 259 def _getLibDir(self, component, process, mapping, current): 260 installDir = component.getInstallDir(mapping, current) 261 if isinstance(mapping, CSharpMapping): 262 return os.path.join(installDir, "lib", "netstandard2.0") 263 return os.path.join(installDir, "lib") 264 265 def getBuildSubDir(self, mapping, name, current): 266 # Return the build sub-directory, to be overriden by specializations 267 buildPlatform = current.driver.configs[mapping].buildPlatform 268 buildConfig = current.driver.configs[mapping].buildConfig 269 return os.path.join("build", buildPlatform, buildConfig) 270 271 def getLdPathEnvName(self): 272 return "LD_LIBRARY_PATH" 273 274 def getInstallDir(self): 275 return "/usr" 276 277 def getNugetPackageDir(self, component, mapping, current): 278 if not self.nugetPackageCache: 279 return None 280 return os.path.join(self.nugetPackageCache, 281 component.getNugetPackage(mapping), 282 component.getNugetPackageVersion(mapping)) 283 284 def hasOpenSSL(self): 285 # This is used by the IceSSL test suite to figure out how to setup certificates 286 return False 287 288 def getDotNetExe(self): 289 return "dotnet" 290 291class Darwin(Platform): 292 293 def getDefaultBuildPlatform(self): 294 return "macosx" 295 296 def getLdPathEnvName(self): 297 return "DYLD_LIBRARY_PATH" 298 299 def getInstallDir(self): 300 return "/usr/local" 301 302class AIX(Platform): 303 304 def hasOpenSSL(self): 305 return True 306 307class FreeBSD(Platform): 308 309 def __init__(self): 310 self.nugetPackageCache = None 311 312 def hasOpenSSL(self): 313 return True 314 315 def getSliceDir(self, iceDir): 316 installDir = self.getInstallDir(mapping, current) 317 return os.path.join(installDir, "slice") 318 319 def getDefaultExe(self, name, config): 320 if name == "icebox": 321 if config.cpp11: 322 name += "++11" 323 return name 324 325 def canRun(self, mapping, current): 326 return Platform.canRun(self, mapping, current) 327 328class Linux(Platform): 329 330 def __init__(self): 331 Platform.__init__(self) 332 self.multiArch = {} 333 334 def init(self, component): 335 Platform.init(self, component) 336 self.parseBuildVariables(component, { 337 "linux_id" : ("linuxId", None), 338 "build-platform" : ("buildPlatform", None), 339 "foreign-platforms" : ("foreignPlatforms", lambda s : s.split(" ") if s else []), 340 }) 341 if self.linuxId in ["ubuntu", "debian"]: 342 for p in [self.buildPlatform] + self.foreignPlatforms: 343 self.multiArch[p] = run("dpkg-architecture -f -a{0} -qDEB_HOST_MULTIARCH 2> /dev/null".format(p)) 344 345 346 def hasOpenSSL(self): 347 return True 348 349 def _getBinDir(self, component, process, mapping, current): 350 installDir = component.getInstallDir(mapping, current) 351 if isinstance(mapping, CSharpMapping): 352 return Platform._getBinDir(self, component, process, mapping, current) 353 354 if self.linuxId in ["ubuntu", "debian"]: 355 binDir = os.path.join(installDir, "bin", self.multiArch[current.driver.configs[mapping].buildPlatform]) 356 if os.path.exists(binDir): 357 return binDir 358 return os.path.join(installDir, "bin") 359 360 def _getLibDir(self, component, process, mapping, current): 361 installDir = component.getInstallDir(mapping, current) 362 if isinstance(mapping, CSharpMapping): 363 return Platform._getLibDir(self, component, process, mapping, current) 364 365 buildPlatform = current.driver.configs[mapping].buildPlatform 366 367 # PHP module is always installed in the lib directory for the default build platform 368 if isinstance(mapping, PhpMapping) and buildPlatform == self.getDefaultBuildPlatform(): 369 return os.path.join(installDir, "lib") 370 371 if self.linuxId in ["centos", "rhel", "fedora"]: 372 return os.path.join(installDir, "lib64" if buildPlatform == "x64" else "lib") 373 elif self.linuxId in ["ubuntu", "debian"]: 374 return os.path.join(installDir, "lib", self.multiArch[buildPlatform]) 375 return os.path.join(installDir, "lib") 376 377 def getBuildSubDir(self, mapping, name, current): 378 buildPlatform = current.driver.configs[mapping].buildPlatform 379 buildConfig = current.driver.configs[mapping].buildConfig 380 if self.linuxId in ["ubuntu", "debian"]: 381 return os.path.join("build", self.multiArch[buildPlatform], buildConfig) 382 else: 383 return os.path.join("build", buildPlatform, buildConfig) 384 385 def getLinuxId(self): 386 return self.linuxId 387 388class Windows(Platform): 389 390 def __init__(self): 391 Platform.__init__(self) 392 self.compiler = None 393 394 def parseBuildVariables(self, component, variables): 395 pass # Nothing to do, we don't support the make build system on Windows 396 397 def getDefaultBuildPlatform(self): 398 return "x64" if "X64" in os.environ.get("PLATFORM", "") else "Win32" 399 400 def getDefaultBuildConfig(self): 401 return "Release" 402 403 def getCompiler(self): 404 if self.compiler != None: 405 return self.compiler 406 if os.environ.get("CPP_COMPILER", "") != "": 407 self.compiler = os.environ["CPP_COMPILER"] 408 else: 409 try: 410 out = run("cl") 411 if out.find("Version 16.") != -1: 412 self.compiler = "v100" 413 elif out.find("Version 17.") != -1: 414 self.compiler = "v110" 415 elif out.find("Version 18.") != -1: 416 self.compiler = "v120" 417 elif out.find("Version 19.00.") != -1: 418 self.compiler = "v140" 419 elif out.find("Version 19.") != -1: 420 self.compiler = "v141" 421 else: 422 raise RuntimeError("Unknown compiler version:\n{0}".format(out)) 423 except: 424 self.compiler = "" 425 return self.compiler 426 427 def getPlatformToolset(self): 428 return self.getCompiler().replace("VC", "v") 429 430 def _getBinDir(self, component, process, mapping, current): 431 installDir = component.getInstallDir(mapping, current) 432 platform = current.driver.configs[mapping].buildPlatform 433 config = "Debug" if current.driver.configs[mapping].buildConfig.find("Debug") >= 0 else "Release" 434 if component.useBinDist(mapping, current): 435 if installDir != self.getNugetPackageDir(component, mapping, current): 436 return os.path.join(installDir, "bin") 437 elif isinstance(process, SliceTranslator): 438 return os.path.join(installDir, "tools") 439 elif isinstance(mapping, CSharpMapping): 440 return os.path.join(installDir, "tools", mapping.getBinTargetFramework(current)) 441 elif process.isReleaseOnly(): 442 # Some services are only available in release mode in the Nuget package 443 return os.path.join(installDir, "build", "native", "bin", platform, "Release") 444 else: 445 return os.path.join(installDir, "build", "native", "bin", platform, config) 446 else: 447 if isinstance(mapping, CSharpMapping): 448 return os.path.join(installDir, "bin", mapping.getBinTargetFramework(current)) 449 elif isinstance(mapping, PhpMapping): 450 return os.path.join(self.getNugetPackageDir(component, mapping, current), 451 "build", "native", "bin", platform, config) 452 else: 453 return os.path.join(installDir, "bin", platform, config) 454 455 def _getLibDir(self, component, process, mapping, current): 456 installDir = component.getInstallDir(mapping, current) 457 if isinstance(mapping, CSharpMapping): 458 return os.path.join(installDir, "lib", mapping.getLibTargetFramework(current)) 459 else: 460 platform = current.driver.configs[mapping].buildPlatform 461 config = "Debug" if current.driver.configs[mapping].buildConfig.find("Debug") >= 0 else "Release" 462 if isinstance(mapping, PhpMapping): 463 return os.path.join(installDir, "lib", "php-{0}".format(current.config.phpVersion), platform, config) 464 elif component.useBinDist(mapping, current): 465 return os.path.join(installDir, "build", "native", "bin", platform, config) 466 else: 467 return os.path.join(installDir, "bin", platform, config) 468 469 def getBuildSubDir(self, mapping, name, current): 470 buildPlatform = current.driver.configs[mapping].buildPlatform 471 buildConfig = current.driver.configs[mapping].buildConfig 472 if os.path.exists(os.path.join(current.testcase.getPath(current), "msbuild", name)): 473 return os.path.join("msbuild", name, buildPlatform, buildConfig) 474 else: 475 return os.path.join("msbuild", buildPlatform, buildConfig) 476 477 def getLdPathEnvName(self): 478 return "PATH" 479 480 def getInstallDir(self): 481 return None # No default installation directory on Windows 482 483 def getNugetPackageDir(self, component, mapping, current): 484 if isinstance(mapping, CSharpMapping) and current.config.dotnetcore: 485 return Platform.getNugetPackageDir(self, component, mapping, current) 486 else: 487 package = "{0}.{1}".format(component.getNugetPackage(mapping), component.getNugetPackageVersion(mapping)) 488 # The package directory is either under the msbuild directory or in the mapping directory depending 489 # on where the solution is located. 490 if os.path.exists(os.path.join(mapping.path, "msbuild", "packages")): 491 return os.path.join(mapping.path, "msbuild", "packages", package) 492 else: 493 return os.path.join(mapping.path, "packages", package) 494 495 def getDotNetExe(self): 496 try: 497 return run("where dotnet").strip() 498 except: 499 return None 500 501def parseOptions(obj, options, mapped={}): 502 # Transform configuration options provided on the command line to 503 # object data members. The data members must be already set on the 504 # object and with the correct type. 505 if not hasattr(obj, "parsedOptions"): 506 obj.parsedOptions=[] 507 remaining = [] 508 for (o, a) in options: 509 if o.startswith("--"): o = o[2:] 510 if o.startswith("-"): o = o[1:] 511 if not a and o.startswith("no-"): 512 a = "false" 513 o = o[3:] 514 if o in mapped: 515 o = mapped[o] 516 517 if hasattr(obj, o): 518 if isinstance(getattr(obj, o), bool): 519 setattr(obj, o, True if not a else (a.lower() in ["yes", "true", "1"])) 520 elif isinstance(getattr(obj, o), list): 521 l = getattr(obj, o) 522 l.append(a) 523 else: 524 if not a and not isinstance(a, str): 525 a = "0" 526 setattr(obj, o, type(getattr(obj, o))(a)) 527 if not o in obj.parsedOptions: 528 obj.parsedOptions.append(o) 529 else: 530 remaining.append((o, a)) 531 options[:] = remaining 532 533""" 534Mapping abstract class. The mapping class provides mapping specific information. 535Multiple components can share the same mapping rules as long as the layout is 536similar. 537""" 538class Mapping(object): 539 540 mappings = OrderedDict() 541 542 class Config(object): 543 544 @classmethod 545 def getSupportedArgs(self): 546 return ("", ["config=", "platform=", "protocol=", "target=", "compress", "ipv6", "no-ipv6", "serialize", 547 "mx", "cprops=", "sprops="]) 548 549 @classmethod 550 def usage(self): 551 pass 552 553 @classmethod 554 def commonUsage(self): 555 print("") 556 print("Mapping options:") 557 print("--protocol=<prot> Run with the given protocol.") 558 print("--compress Run the tests with protocol compression.") 559 print("--ipv6 Use IPv6 addresses.") 560 print("--serialize Run with connection serialization.") 561 print("--mx Run with metrics enabled.") 562 print("--cprops=<properties> Specifies a list of additional client properties.") 563 print("--sprops=<properties> Specifies a list of additional server properties.") 564 print("--config=<config> Build configuration for native executables.") 565 print("--platform=<platform> Build platform for native executables.") 566 567 def __init__(self, options=[]): 568 # Build configuration 569 self.parsedOptions = [] 570 self.buildConfig = os.environ.get("CONFIGS", "").split(" ")[0] 571 if self.buildConfig: 572 self.parsedOptions.append("buildConfig") 573 else: 574 self.buildConfig = platform.getDefaultBuildConfig() 575 576 self.buildPlatform = os.environ.get("PLATFORMS", "").split(" ")[0] 577 if self.buildPlatform: 578 self.parsedOptions.append("buildPlatform") 579 else: 580 self.buildPlatform = platform.getDefaultBuildPlatform() 581 582 self.pathOverride = "" 583 self.protocol = "tcp" 584 self.compress = False 585 self.serialize = False 586 self.ipv6 = False 587 self.mx = False 588 self.cprops = [] 589 self.sprops = [] 590 591 # Options bellow are not parsed by the base class by still initialized here for convenience (this 592 # avoid having to check the configuration type) 593 self.uwp = False 594 self.openssl = False 595 self.browser = "" 596 self.es5 = False 597 self.worker = False 598 self.dotnetcore = False 599 self.framework = "" 600 self.android = False 601 self.xamarin = False 602 self.device = "" 603 self.avd = "" 604 self.phpVersion = "7.1" 605 606 parseOptions(self, options, { "config" : "buildConfig", "platform" : "buildPlatform" }) 607 608 def __str__(self): 609 s = [] 610 for o in self.parsedOptions: 611 v = getattr(self, o) 612 if v: s.append(o if type(v) == bool else str(v)) 613 return ",".join(s) 614 615 def getAll(self, current, testcase, rand=False): 616 617 # 618 # A generator to generate combinations of options (e.g.: tcp/compress/mx, ssl/ipv6/serialize, etc) 619 # 620 def gen(supportedOptions): 621 622 if not supportedOptions: 623 yield self 624 return 625 626 supportedOptions = supportedOptions.copy() 627 supportedOptions.update(testcase.getMapping().getOptions(current)) 628 supportedOptions.update(testcase.getTestSuite().getOptions(current)) 629 supportedOptions.update(testcase.getOptions(current)) 630 631 for o in self.parsedOptions: 632 # Remove options which were explicitly set 633 if o in supportedOptions: 634 del supportedOptions[o] 635 636 if len(supportedOptions) == 0: 637 yield self 638 return 639 640 # Find the option with the longest list of values 641 length = max([len(v) for v in supportedOptions.values()]) 642 643 # Replace the values with a cycle iterator on the values 644 for (k, v) in supportedOptions.items(): 645 supportedOptions[k] = itertools.cycle(random.sample(v, len(v)) if rand else v) 646 647 # Now, for the length of the longest array of values, we return 648 # an array with the supported option combinations 649 for i in range(0, length): 650 options = [] 651 for k, v in supportedOptions.items(): 652 v = next(v) 653 if v: 654 if type(v) == bool: 655 if v: 656 options.append(("--{0}".format(k), None)) 657 else: 658 options.append(("--{0}".format(k), v)) 659 660 # Add parsed options 661 for o in self.parsedOptions: 662 v = getattr(self, o) 663 if type(v) == bool: 664 if v: 665 options.append(("--{0}".format(o), None)) 666 elif type(v) == list: 667 options += [("--{0}".format(o), e) for e in v] 668 else: 669 options.append(("--{0}".format(o), v)) 670 671 yield self.__class__(options) 672 673 return [c for c in gen(current.driver.filterOptions(current.driver.getComponent().getOptions(testcase, current)))] 674 675 def canRun(self, testId, current): 676 if not current.driver.getComponent().canRun(testId, current.testcase.getMapping(), current): 677 return False 678 679 options = {} 680 options.update(current.testcase.getMapping().getOptions(current)) 681 options.update(current.testcase.getTestSuite().getOptions(current)) 682 options.update(current.testcase.getOptions(current)) 683 684 for (k, v) in options.items(): 685 if hasattr(self, k): 686 if not getattr(self, k) in v: 687 return False 688 elif hasattr(current.driver, k): 689 if not getattr(current.driver, k) in v: 690 return False 691 else: 692 return True 693 694 def cloneRunnable(self, current): 695 # 696 # Clone this configuration and make sure all the options are supported 697 # 698 options = {} 699 options.update(current.testcase.getMapping().getOptions(current)) 700 options.update(current.testcase.getTestSuite().getOptions(current)) 701 options.update(current.testcase.getOptions(current)) 702 clone = copy.copy(self) 703 for o in self.parsedOptions: 704 if o in options and getattr(self, o) not in options[o]: 705 setattr(clone, o, options[o][0] if len(options[o]) > 0 else None) 706 return clone 707 708 def cloneAndOverrideWith(self, current): 709 # 710 # Clone this configuration and override options with options from the given configuration 711 # (the parent configuraton). This is usefull when running cross-testing. For example, JS 712 # tests don't support all the options so we clone the C++ configuration and override the 713 # options that are set on the JS configuration. 714 # 715 clone = copy.copy(self) 716 for o in current.config.parsedOptions + ["protocol"]: 717 if o not in ["buildConfig", "buildPlatform"]: 718 setattr(clone, o, getattr(current.config, o)) 719 clone.parsedOptions = current.config.parsedOptions 720 return clone 721 722 def getArgs(self, process, current): 723 return [] 724 725 def getProps(self, process, current): 726 props = {} 727 if isinstance(process, IceProcess): 728 props["Ice.Warn.Connections"] = True 729 if self.protocol: 730 props["Ice.Default.Protocol"] = self.protocol 731 if self.compress: 732 props["Ice.Override.Compress"] = "1" 733 if self.serialize: 734 props["Ice.ThreadPool.Server.Serialize"] = "1" 735 props["Ice.IPv6"] = self.ipv6 736 if self.ipv6: 737 props["Ice.PreferIPv6Address"] = True 738 if self.mx: 739 props["Ice.Admin.Endpoints"] = "tcp -h \"::1\"" if self.ipv6 else "tcp -h 127.0.0.1" 740 props["Ice.Admin.InstanceName"] = "Server" if isinstance(process, Server) else "Client" 741 props["IceMX.Metrics.Debug.GroupBy"] ="id" 742 props["IceMX.Metrics.Parent.GroupBy"] = "parent" 743 props["IceMX.Metrics.All.GroupBy"] = "none" 744 745 # 746 # Speed up Windows testing. We override the connect timeout for some tests which are 747 # establishing connections to inactive ports. It takes around 1s for such connection 748 # establishment to fail on Windows. 749 # 750 # if isinstance(platform, Windows): 751 # if current.testsuite.getId().startswith("IceGrid") or \ 752 # current.testsuite.getId() in ["Ice/binding", 753 # "Ice/location", 754 # "Ice/background", 755 # "Ice/faultTolerance", 756 # "Ice/services", 757 # "IceDiscovery/simple"]: 758 # props["Ice.Override.ConnectTimeout"] = "400" 759 760 # Additional properties specified on the command line with --cprops or --sprops 761 additionalProps = [] 762 if self.cprops and isinstance(process, Client): 763 additionalProps = self.cprops 764 elif self.sprops and isinstance(process, Server): 765 additionalProps = self.sprops 766 for pps in additionalProps: 767 for p in pps.split(" "): 768 if p.find("=") > 0: 769 (k , v) = p.split("=") 770 props[k] = v 771 else: 772 props[p] = True 773 774 return props 775 776 @classmethod 777 def getByName(self, name): 778 if not name in self.mappings: 779 raise RuntimeError("unknown mapping `{0}'".format(name)) 780 return self.mappings.get(name) 781 782 @classmethod 783 def getByPath(self, path): 784 path = os.path.normpath(path) 785 for m in self.mappings.values(): 786 if path.startswith(os.path.normpath(m.getTestDir())): 787 return m 788 789 @classmethod 790 def getAllByPath(self, path): 791 path = os.path.abspath(path) 792 mappings = [] 793 for m in self.mappings.values(): 794 if path.startswith(m.getPath() + os.sep): 795 mappings.append(m) 796 return mappings 797 798 @classmethod 799 def add(self, name, mapping, component, path=None): 800 name = name.replace("\\", "/") 801 self.mappings[name] = mapping.init(name, component, path) 802 803 @classmethod 804 def remove(self, name): 805 del self.mappings[name] 806 807 @classmethod 808 def getAll(self, driver=None): 809 return [m for m in self.mappings.values() if not driver or driver.matchLanguage(str(m))] 810 811 def __init__(self, path=None): 812 self.name = None 813 self.path = os.path.abspath(path) if path else None 814 self.testsuites = {} 815 816 def init(self, name, component, path=None): 817 self.name = name 818 self.component = component 819 if not self.path: 820 self.path = os.path.normpath(os.path.join(self.component.getSourceDir(), path or name)) 821 return self 822 823 def __str__(self): 824 return self.name 825 826 def getTestDir(self): 827 return self.component.getTestDir(self) 828 829 def createConfig(self, options): 830 return self.Config(options) 831 832 def filterTestSuite(self, testId, config, filters=[], rfilters=[]): 833 if len(filters) > 0: 834 for f in filters: 835 if f.search(self.name + "/" + testId): 836 break 837 else: 838 return True 839 840 if len(rfilters) > 0: 841 for f in rfilters: 842 if f.search(self.name + "/" + testId): 843 return True 844 845 return False 846 847 def loadTestSuites(self, tests, config, filters=[], rfilters=[]): 848 global currentMapping 849 currentMapping = self 850 try: 851 origsyspath = sys.path 852 prefix = os.path.commonprefix([toplevel, self.component.getScriptDir()]) 853 moduleprefix = self.component.getScriptDir()[len(prefix) + 1:].replace(os.sep, ".") + "." 854 sys.path = [prefix] + sys.path 855 for test in tests or [""]: 856 testDir = self.component.getTestDir(self) 857 for root, dirs, files in os.walk(os.path.join(testDir, test.replace('/', os.sep))): 858 testId = root[len(testDir) + 1:] 859 if os.sep != "/": 860 testId = testId.replace(os.sep, "/") 861 862 if self.filterTestSuite(testId, config, filters, rfilters): 863 continue 864 865 # 866 # First check if there's a test.py file in the test directory, if there's one use it. 867 # 868 if "test.py" in files : 869 # 870 # WORKAROUND for Python issue 15230 (fixed in 3.2) where run_path doesn't work correctly. 871 # 872 #runpy.run_path(os.path.join(root, "test.py")) 873 origsyspath = sys.path 874 sys.path = [root] + sys.path 875 runpy.run_module("test", init_globals=globals(), run_name=root) 876 origsyspath = sys.path 877 continue 878 879 # 880 # If there's no test.py file in the test directory, we check if there's a common 881 # script for the test in scripts/tests. If there's one we use it. 882 # 883 if os.path.isfile(os.path.join(self.component.getScriptDir(), testId + ".py")): 884 runpy.run_module(moduleprefix + testId.replace("/", "."), init_globals=globals(), run_name=root) 885 continue 886 887 # 888 # Finally, we try to "discover/compute" the test by looking up for well-known 889 # files. 890 # 891 testcases = self.computeTestCases(testId, files) 892 if testcases: 893 TestSuite(root, testcases) 894 finally: 895 currentMapping = None 896 sys.path = origsyspath 897 898 def getTestSuites(self, ids=[]): 899 if not ids: 900 return self.testsuites.values() 901 return [self.testsuites[testSuiteId] for testSuiteId in ids if testSuiteId in self.testsuites] 902 903 def addTestSuite(self, testsuite): 904 assert len(testsuite.path) > len(self.component.getTestDir(self)) + 1 905 testSuiteId = testsuite.path[len(self.component.getTestDir(self)) + 1:].replace('\\', '/') 906 self.testsuites[testSuiteId] = testsuite 907 return testSuiteId 908 909 def findTestSuite(self, testsuite): 910 return self.testsuites.get(testsuite if isinstance(testsuite, str) else testsuite.id) 911 912 def computeTestCases(self, testId, files): 913 914 # Instantiate a new test suite if the directory contains well-known source files. 915 def checkFile(f, m): 916 try: 917 # If given mapping is same as local mapping, just check the files set, otherwise check 918 # with the mapping 919 return (self.getDefaultSource(f) in files) if m == self else m.hasSource(testId, f) 920 except KeyError: 921 # Expected if the mapping doesn't support the process type 922 return False 923 924 checkClient = lambda f: checkFile(f, self.getClientMapping(testId)) 925 checkServer = lambda f: checkFile(f, self.getServerMapping(testId)) 926 927 testcases = [] 928 if checkClient("client") and checkServer("server"): 929 testcases.append(ClientServerTestCase()) 930 if checkClient("client") and checkServer("serveramd") and self.getServerMapping(testId) == self: 931 testcases.append(ClientAMDServerTestCase()) 932 if checkClient("client") and checkServer("servertie") and self.getServerMapping(testId) == self: 933 testcases.append(ClientTieServerTestCase()) 934 if checkClient("client") and checkServer("serveramdtie") and self.getServerMapping(testId) == self: 935 testcases.append(ClientAMDTieServerTestCase()) 936 if checkClient("client") and len(testcases) == 0: 937 testcases.append(ClientTestCase()) 938 if checkClient("collocated"): 939 testcases.append(CollocatedTestCase()) 940 if len(testcases) > 0: 941 return testcases 942 943 def hasSource(self, testId, processType): 944 try: 945 return os.path.exists(os.path.join(self.component.getTestDir(self), testId, self.getDefaultSource(processType))) 946 except KeyError: 947 return False 948 949 def getPath(self): 950 return self.path 951 952 def getTestCwd(self, process, current): 953 return current.testcase.getPath(current) 954 955 def getDefaultSource(self, processType): 956 default = self.component.getDefaultSource(self, processType) 957 if default: 958 return default 959 return self._getDefaultSource(processType) 960 961 def getDefaultProcesses(self, processType, testsuite): 962 default = self.component.getDefaultProcesses(self, processType, testsuite.getId()) 963 if default: 964 return default 965 return self._getDefaultProcesses(processType) 966 967 def getDefaultExe(self, processType): 968 default = self.component.getDefaultExe(self, processType) 969 if default: 970 return default 971 return self._getDefaultExe(processType) 972 973 def _getDefaultSource(self, processType): 974 return processType 975 976 def _getDefaultProcesses(self, processType): 977 # 978 # If no server or client is explicitly set with a testcase, getDefaultProcess is called 979 # to figure out which process class to instantiate. 980 # 981 name, ext = os.path.splitext(self.getDefaultSource(processType)) 982 if name in globals(): 983 return [globals()[name]()] 984 return [Server()] if processType.startswith("server") else [Client()] if processType else [] 985 986 def _getDefaultExe(self, processType): 987 return os.path.splitext(self.getDefaultSource(processType))[0] 988 989 def getClientMapping(self, testId=None): 990 # The client mapping is always the same as this mapping. 991 return self 992 993 def getServerMapping(self, testId=None): 994 # Can be overridden for client-only mapping that relies on another mapping for servers 995 return self 996 997 def getBuildDir(self, name, current): 998 return platform.getBuildSubDir(self, name, current) 999 1000 def getCommandLine(self, current, process, exe, args): 1001 cmd = "" 1002 if process.isFromBinDir(): 1003 # If it's a process from the bin directory, the location is platform specific 1004 # so we check with the platform. 1005 cmd = os.path.join(self.component.getBinDir(process, self, current), exe) 1006 elif current.testcase: 1007 # If it's a process from a testcase, the binary is in the test build directory. 1008 cmd = os.path.join(current.testcase.getPath(current), current.getBuildDir(exe), exe) 1009 else: 1010 cmd = exe 1011 1012 if isinstance(platform, Windows) and not exe.endswith(".exe"): 1013 cmd += ".exe" 1014 1015 return cmd + " " + args if args else cmd 1016 1017 def getProps(self, process, current): 1018 props = {} 1019 if isinstance(process, IceProcess): 1020 if current.config.protocol in ["bt", "bts"]: 1021 props["Ice.Plugin.IceBT"] = self.getPluginEntryPoint("IceBT", process, current) 1022 if current.config.protocol in ["ssl", "wss", "bts", "iaps"]: 1023 props.update(self.getSSLProps(process, current)) 1024 return props 1025 1026 def getSSLProps(self, process, current): 1027 sslProps = { 1028 "Ice.Plugin.IceSSL" : self.getPluginEntryPoint("IceSSL", process, current), 1029 "IceSSL.Password": "password", 1030 "IceSSL.DefaultDir": "" if current.config.buildPlatform == "iphoneos" else os.path.join(self.component.getSourceDir(), "certs"), 1031 } 1032 1033 # 1034 # If the client doesn't support client certificates, set IceSSL.VerifyPeer to 0 1035 # 1036 if isinstance(process, Server): 1037 if isinstance(current.testsuite.getMapping(), JavaScriptMixin): 1038 sslProps["IceSSL.VerifyPeer"] = 0 1039 1040 return sslProps 1041 1042 def getArgs(self, process, current): 1043 return [] 1044 1045 def getEnv(self, process, current): 1046 return {} 1047 1048 def getOptions(self, current): 1049 return {} 1050 1051# 1052# A Runnable can be used as a "client" for in test cases, it provides 1053# implements run, setup and teardown methods. 1054# 1055class Runnable(object): 1056 1057 def __init__(self, desc=None): 1058 self.desc = desc 1059 1060 def setup(self, current): 1061 ### Only called when ran from testcase 1062 pass 1063 1064 def teardown(self, current, success): 1065 ### Only called when ran from testcase 1066 pass 1067 1068 def run(self, current): 1069 pass 1070 1071# 1072# A Process describes how to run an executable process. 1073# 1074class Process(Runnable): 1075 1076 processType = None 1077 1078 def __init__(self, exe=None, outfilters=None, quiet=False, args=None, props=None, envs=None, desc=None, 1079 mapping=None, preexec_fn=None, traceProps=None): 1080 Runnable.__init__(self, desc) 1081 self.exe = exe 1082 self.outfilters = outfilters or [] 1083 self.quiet = quiet 1084 self.args = args or [] 1085 self.props = props or {} 1086 self.traceProps = traceProps or {} 1087 self.envs = envs or {} 1088 self.mapping = mapping 1089 self.preexec_fn = preexec_fn 1090 1091 def __str__(self): 1092 if not self.exe: 1093 return str(self.__class__) 1094 return self.exe + (" ({0})".format(self.desc) if self.desc else "") 1095 1096 def getOutput(self, current, encoding="utf-8"): 1097 assert(self in current.processes) 1098 1099 def d(s): 1100 return s if isPython2 else s.decode(encoding) if isinstance(s, bytes) else s 1101 1102 output = d(current.processes[self].getOutput()) 1103 try: 1104 # Apply outfilters to the output 1105 if len(self.outfilters) > 0: 1106 lines = output.split('\n') 1107 newLines = [] 1108 previous = "" 1109 for line in [line + '\n' for line in lines]: 1110 for f in self.outfilters: 1111 if isinstance(f, types.LambdaType) or isinstance(f, types.FunctionType): 1112 line = f(line) 1113 elif f.search(line): 1114 break 1115 else: 1116 if line.endswith('\n'): 1117 if previous: 1118 newLines.append(previous + line) 1119 previous = "" 1120 else: 1121 newLines.append(line) 1122 else: 1123 previous += line 1124 output = "".join(newLines) 1125 output = output.strip() 1126 return output + '\n' if output else "" 1127 except Exception as ex: 1128 print("unexpected exception while filtering process output:\n" + str(ex)) 1129 raise 1130 1131 def run(self, current, args=[], props={}, exitstatus=0, timeout=None): 1132 class WatchDog: 1133 1134 def __init__(self, timeout): 1135 self.lastProgressTime = time.time() 1136 self.lock = threading.Lock() 1137 1138 def reset(self): 1139 with self.lock: self.lastProgressTime = time.time() 1140 1141 def timedOut(self, timeout): 1142 with self.lock: 1143 return (time.time() - self.lastProgressTime) >= timeout 1144 1145 watchDog = WatchDog(timeout) 1146 self.start(current, args, props, watchDog=watchDog) 1147 process = current.processes[self] 1148 1149 if timeout is None: 1150 # If it's not a local process use a large timeout as the watch dog might not 1151 # get invoked (TODO: improve remote processes to use the watch dog) 1152 timeout = 60 if isinstance(process, Expect.Expect) else 480 1153 1154 if not self.quiet and not current.driver.isWorkerThread(): 1155 # Print out the process output to stdout if we're running the client form the main thread. 1156 process.trace(self.outfilters) 1157 try: 1158 while True: 1159 try: 1160 process.waitSuccess(exitstatus=exitstatus, timeout=30) 1161 break 1162 except KeyboardInterrupt: 1163 current.driver.setInterrupt(True) 1164 raise 1165 except Expect.TIMEOUT: 1166 if watchDog and watchDog.timedOut(timeout): 1167 print("process {0} is hanging - {1}".format(process, time.strftime("%x %X"))) 1168 if current.driver.isInterrupted(): 1169 self.stop(current, False, exitstatus) 1170 raise 1171 finally: 1172 self.stop(current, True, exitstatus) 1173 1174 def getEffectiveArgs(self, current, args): 1175 allArgs = [] 1176 allArgs += current.driver.getArgs(self, current) 1177 allArgs += current.config.getArgs(self, current) 1178 allArgs += self.getMapping(current).getArgs(self, current) 1179 allArgs += current.testcase.getArgs(self, current) 1180 allArgs += self.getArgs(current) 1181 allArgs += self.args(self, current) if callable(self.args) else self.args 1182 allArgs += args 1183 allArgs = [a.encode("utf-8") if type(a) == "unicode" else str(a) for a in allArgs] 1184 return allArgs 1185 1186 def getEffectiveProps(self, current, props): 1187 allProps = {} 1188 allProps.update(current.driver.getProps(self, current)) 1189 allProps.update(current.driver.getComponent().getProps(self, current)) 1190 allProps.update(current.config.getProps(self, current)) 1191 allProps.update(self.getMapping(current).getProps(self, current)) 1192 allProps.update(current.testcase.getProps(self, current)) 1193 allProps.update(self.getProps(current)) 1194 allProps.update(self.props(self, current) if callable(self.props) else self.props) 1195 allProps.update(props) 1196 return allProps 1197 1198 def getEffectiveEnv(self, current): 1199 1200 def merge(envs, newEnvs): 1201 if platform.getLdPathEnvName() in newEnvs and platform.getLdPathEnvName() in envs: 1202 newEnvs[platform.getLdPathEnvName()] += os.pathsep + envs[platform.getLdPathEnvName()] 1203 envs.update(newEnvs) 1204 1205 allEnvs = {} 1206 merge(allEnvs, current.driver.getComponent().getEnv(self, current)) 1207 merge(allEnvs, self.getMapping(current).getEnv(self, current)) 1208 merge(allEnvs, current.testcase.getEnv(self, current)) 1209 merge(allEnvs, self.getEnv(current)) 1210 merge(allEnvs, self.envs(self, current) if callable(self.envs) else self.envs) 1211 return allEnvs 1212 1213 def getEffectiveTraceProps(self, current): 1214 traceProps = {} 1215 traceProps.update(current.testcase.getTraceProps(self, current)) 1216 traceProps.update(self.traceProps(self, current) if callable(self.traceProps) else self.traceProps) 1217 return traceProps 1218 1219 def start(self, current, args=[], props={}, watchDog=None): 1220 allArgs = self.getEffectiveArgs(current, args) 1221 allProps = self.getEffectiveProps(current, props) 1222 allEnvs = self.getEffectiveEnv(current) 1223 1224 processController = current.driver.getProcessController(current, self) 1225 current.processes[self] = processController.start(self, current, allArgs, allProps, allEnvs, watchDog) 1226 try: 1227 self.waitForStart(current) 1228 except: 1229 self.stop(current) 1230 raise 1231 1232 def waitForStart(self, current): 1233 # To be overridden in specialization to wait for a token indicating the process readiness. 1234 pass 1235 1236 def stop(self, current, waitSuccess=False, exitstatus=0): 1237 if self in current.processes: 1238 process = current.processes[self] 1239 try: 1240 # Wait for the process to exit successfully by itself. 1241 if not process.isTerminated() and waitSuccess: 1242 while True: 1243 try: 1244 process.waitSuccess(exitstatus=exitstatus, timeout=30) 1245 break 1246 except KeyboardInterrupt: 1247 current.driver.setInterrupt(True) 1248 raise 1249 except Expect.TIMEOUT: 1250 print("process {0} is hanging on shutdown - {1}".format(process, time.strftime("%x %X"))) 1251 if current.driver.isInterrupted(): 1252 raise 1253 finally: 1254 if not process.isTerminated(): 1255 process.terminate() 1256 if not self.quiet: # Write the output to the test case (but not on stdout) 1257 current.write(self.getOutput(current), stdout=False) 1258 1259 def teardown(self, current, success): 1260 if self in current.processes: 1261 current.processes[self].teardown(current, success) 1262 1263 def expect(self, current, pattern, timeout=60): 1264 assert(self in current.processes and isinstance(current.processes[self], Expect.Expect)) 1265 return current.processes[self].expect(pattern, timeout) 1266 1267 def expectall(self, current, pattern, timeout=60): 1268 assert(self in current.processes and isinstance(current.processes[self], Expect.Expect)) 1269 return current.processes[self].expectall(pattern, timeout) 1270 1271 def sendline(self, current, data): 1272 assert(self in current.processes and isinstance(current.processes[self], Expect.Expect)) 1273 return current.processes[self].sendline(data) 1274 1275 def getMatch(self, current): 1276 assert(self in current.processes and isinstance(current.processes[self], Expect.Expect)) 1277 return current.processes[self].match 1278 1279 def isStarted(self, current): 1280 return self in current.processes and not current.processes[self].isTerminated() 1281 1282 def isFromBinDir(self): 1283 return False 1284 1285 def isReleaseOnly(self): 1286 return False 1287 1288 def getArgs(self, current): 1289 return [] 1290 1291 def getProps(self, current): 1292 return {} 1293 1294 def getEnv(self, current): 1295 return {} 1296 1297 def getMapping(self, current): 1298 return self.mapping or current.testcase.getMapping() 1299 1300 def getExe(self, current): 1301 processType = self.processType or current.testcase.getProcessType(self) 1302 return self.exe or self.getMapping(current).getDefaultExe(processType) 1303 1304 def getCommandLine(self, current, args=""): 1305 return self.getMapping(current).getCommandLine(current, self, self.getExe(current), args).strip() 1306 1307# 1308# A simple client (used to run Slice/IceUtil clients for example) 1309# 1310class SimpleClient(Process): 1311 pass 1312 1313# 1314# An IceProcess specialization class. This is used by drivers to figure out if 1315# the process accepts Ice configuration properties. 1316# 1317class IceProcess(Process): 1318 pass 1319 1320# 1321# An Ice server process. It's possible to configure when the server is considered 1322# ready by setting readyCount or ready. The start method will only return once 1323# the server is considered "ready". It can also be configure to wait (the default) 1324# or not wait for shutdown when the stop method is invoked. 1325# 1326class Server(IceProcess): 1327 1328 def __init__(self, exe=None, waitForShutdown=True, readyCount=1, ready=None, startTimeout=300, *args, **kargs): 1329 IceProcess.__init__(self, exe, *args, **kargs) 1330 self.waitForShutdown = waitForShutdown 1331 self.readyCount = readyCount 1332 self.ready = ready 1333 self.startTimeout = startTimeout 1334 1335 def getProps(self, current): 1336 props = IceProcess.getProps(self, current) 1337 props.update({ 1338 "Ice.ThreadPool.Server.Size": 1, 1339 "Ice.ThreadPool.Server.SizeMax": 3, 1340 "Ice.ThreadPool.Server.SizeWarn": 0, 1341 }) 1342 props.update(current.driver.getProcessProps(current, self.ready, self.readyCount + (1 if current.config.mx else 0))) 1343 return props 1344 1345 def waitForStart(self, current): 1346 # Wait for the process to be ready 1347 current.processes[self].waitReady(self.ready, self.readyCount + (1 if current.config.mx else 0), self.startTimeout) 1348 1349 # Filter out remaining ready messages 1350 self.outfilters.append(re.compile("[^\n]+ ready")) 1351 1352 # If we are not asked to be quiet and running from the main thread, print the server output 1353 if not self.quiet and not current.driver.isWorkerThread(): 1354 current.processes[self].trace(self.outfilters) 1355 1356 def stop(self, current, waitSuccess=False, exitstatus=0): 1357 IceProcess.stop(self, current, waitSuccess and self.waitForShutdown, exitstatus) 1358 1359# 1360# An Ice client process. 1361# 1362class Client(IceProcess): 1363 pass 1364 1365# 1366# Executables for processes inheriting this marker class are looked up in the 1367# Ice distribution bin directory. 1368# 1369class ProcessFromBinDir: 1370 1371 def isFromBinDir(self): 1372 return True 1373 1374# 1375# Executables for processes inheriting this marker class are only provided 1376# as a Release executble on Windows 1377# 1378class ProcessIsReleaseOnly: 1379 1380 def isReleaseOnly(self): 1381 return True 1382 1383 1384class SliceTranslator(ProcessFromBinDir, ProcessIsReleaseOnly, SimpleClient): 1385 1386 def __init__(self, translator): 1387 SimpleClient.__init__(self, exe=translator, quiet=True, mapping=Mapping.getByName("cpp")) 1388 1389 def getCommandLine(self, current, args=""): 1390 # 1391 # Look for slice2py installed by Pip if not found in the bin directory 1392 # 1393 if self.exe == "slice2py": 1394 translator = self.getMapping(current).getCommandLine(current, self, self.getExe(current), "") 1395 if os.path.exists(translator): 1396 return translator + " " + args if args else translator 1397 elif isinstance(platform, Windows): 1398 return os.path.join(os.path.dirname(sys.executable), "Scripts", "slice2py.exe") + " " + args if args else translator 1399 elif os.path.exists("/usr/local/bin/slice2py"): 1400 return "/usr/local/bin/slice2py" + " " + args if args else translator 1401 else: 1402 import slice2py 1403 return sys.executable + " " + os.path.normpath( 1404 os.path.join(slice2py.__file__, "..", "..", "..", "..", "bin", "slice2py")) + " " + args if args else translator 1405 else: 1406 return Process.getCommandLine(self, current, args) 1407 1408class ServerAMD(Server): 1409 pass 1410 1411class Collocated(Client): 1412 pass 1413 1414class EchoServer(Server): 1415 1416 def __init__(self): 1417 Server.__init__(self, mapping=Mapping.getByName("cpp"), quiet=True, waitForShutdown=False) 1418 1419 def getProps(self, current): 1420 props = Server.getProps(self, current) 1421 props["Ice.MessageSizeMax"] = 8192 # Don't limit the amount of data to transmit between client/server 1422 return props 1423 1424 def getCommandLine(self, current, args=""): 1425 current.push(self.mapping.findTestSuite("Ice/echo").findTestCase("server")) 1426 try: 1427 return Server.getCommandLine(self, current, args) 1428 finally: 1429 current.pop() 1430 1431# 1432# A test case is composed of servers and clients. When run, all servers are started 1433# sequentially. When the servers are ready, the clients are also ran sequentially. 1434# Once all the clients are terminated, the servers are stopped (which waits for the 1435# successful completion of the server). 1436# 1437# A TestCase is also a "Runnable", like the Process class. In other words, it can be 1438# used a client to allow nested test cases. 1439# 1440class TestCase(Runnable): 1441 1442 def __init__(self, name, client=None, clients=None, server=None, servers=None, args=None, props=None, envs=None, 1443 options=None, desc=None, traceProps=None): 1444 Runnable.__init__(self, desc) 1445 1446 self.name = name 1447 self.parent = None 1448 self.mapping = None 1449 self.testsuite = None 1450 self.options = options or {} 1451 self.args = args or [] 1452 self.props = props or {} 1453 self.traceProps = traceProps or {} 1454 self.envs = envs or {} 1455 1456 # 1457 # Setup client list, "client" can be a string in which case it's assumed to 1458 # to the client executable name. 1459 # 1460 self.clients = clients 1461 if client: 1462 client = Client(exe=client) if isinstance(client, str) else client 1463 self.clients = [client] if not self.clients else self.clients + [client] 1464 1465 # 1466 # Setup server list, "server" can be a string in which case it's assumed to 1467 # to the server executable name. 1468 # 1469 self.servers = servers 1470 if server: 1471 server = Server(exe=server) if isinstance(server, str) else server 1472 self.servers = [server] if not self.servers else self.servers + [server] 1473 1474 def __str__(self): 1475 return self.name 1476 1477 def init(self, mapping, testsuite): 1478 # init is called when the testcase is added to the given testsuite 1479 self.mapping = mapping 1480 self.testsuite = testsuite 1481 1482 # 1483 # If no clients are explicitly specified, we instantiate one if getClientType() 1484 # returns the type of client to instantiate (client, collocated, etc) 1485 # 1486 testId = self.testsuite.getId() 1487 if not self.clients: 1488 if self.getClientType(): 1489 self.clients = self.mapping.getClientMapping(testId).getDefaultProcesses(self.getClientType(), testsuite) 1490 else: 1491 self.clients = [] 1492 1493 # 1494 # If no servers are explicitly specified, we instantiate one if getServerType() 1495 # returns the type of server to instantiate (server, serveramd, etc) 1496 # 1497 if not self.servers: 1498 if self.getServerType(): 1499 self.servers = self.mapping.getServerMapping(testId).getDefaultProcesses(self.getServerType(), testsuite) 1500 else: 1501 self.servers = [] 1502 1503 def getOptions(self, current): 1504 return self.options(current) if callable(self.options) else self.options 1505 1506 def canRun(self, current): 1507 # Can be overriden 1508 return True 1509 1510 def setupServerSide(self, current): 1511 # Can be overridden to perform setup activities before the server side is started 1512 pass 1513 1514 def teardownServerSide(self, current, success): 1515 # Can be overridden to perform terddown after the server side is stopped 1516 pass 1517 1518 def setupClientSide(self, current): 1519 # Can be overridden to perform setup activities before the client side is started 1520 pass 1521 1522 def teardownClientSide(self, current, success): 1523 # Can be overridden to perform terddown after the client side is stopped 1524 pass 1525 1526 def startServerSide(self, current): 1527 for server in self.servers: 1528 self._startServer(current, server) 1529 1530 def stopServerSide(self, current, success): 1531 for server in reversed(self.servers): 1532 self._stopServer(current, server, success) 1533 1534 def runClientSide(self, current): 1535 for client in self.clients: 1536 self._runClient(current, client) 1537 1538 def getTestSuite(self): 1539 return self.testsuite 1540 1541 def getParent(self): 1542 return self.parent 1543 1544 def getName(self): 1545 return self.name 1546 1547 def getPath(self, current): 1548 path = self.testsuite.getPath() 1549 if current.config.pathOverride: 1550 return path.replace(toplevel, current.config.pathOverride) 1551 else: 1552 return path 1553 1554 def getMapping(self): 1555 return self.mapping 1556 1557 def getArgs(self, process, current): 1558 return self.args 1559 1560 def getProps(self, process, current): 1561 return self.props 1562 1563 def getTraceProps(self, process, current): 1564 return self.traceProps 1565 1566 def getEnv(self, process, current): 1567 return self.envs 1568 1569 def getProcessType(self, process): 1570 if process in self.clients: 1571 return self.getClientType() 1572 elif process in self.servers: 1573 return self.getServerType() 1574 elif isinstance(process, Server): 1575 return self.getServerType() 1576 else: 1577 return self.getClientType() 1578 1579 def getClientType(self): 1580 # Overridden by test case specialization to specify the type of client to instantiate 1581 # if no client is explicitly provided 1582 return None 1583 1584 def getServerType(self): 1585 # Overridden by test case specialization to specify the type of client to instantiate 1586 # if no server is explicitly provided 1587 return None 1588 1589 def getServerTestCase(self, cross=None): 1590 testsuite = (cross or self.mapping).getServerMapping(self.testsuite.getId()).findTestSuite(self.testsuite) 1591 return testsuite.findTestCase(self) if testsuite else None 1592 1593 def getClientTestCase(self): 1594 testsuite = self.mapping.getClientMapping(self.testsuite.getId()).findTestSuite(self.testsuite) 1595 return testsuite.findTestCase(self) if testsuite else None 1596 1597 def _startServerSide(self, current): 1598 # Set the host to use for the server side 1599 current.push(self) 1600 current.host = current.driver.getProcessController(current).getHost(current) 1601 self.setupServerSide(current) 1602 try: 1603 self.startServerSide(current) 1604 return current.host 1605 except: 1606 self._stopServerSide(current, False) 1607 raise 1608 finally: 1609 current.pop() 1610 1611 def _stopServerSide(self, current, success): 1612 current.push(self) 1613 try: 1614 self.stopServerSide(current, success) 1615 finally: 1616 for server in reversed(self.servers): 1617 if server.isStarted(current): 1618 self._stopServer(current, server, False) 1619 self.teardownServerSide(current, success) 1620 current.pop() 1621 1622 def _startServer(self, current, server): 1623 if server.desc: 1624 current.write("starting {0}... ".format(server.desc)) 1625 server.setup(current) 1626 server.start(current) 1627 if server.desc: 1628 current.writeln("ok") 1629 1630 def _stopServer(self, current, server, success): 1631 try: 1632 server.stop(current, success) 1633 except: 1634 success = False 1635 raise 1636 finally: 1637 server.teardown(current, success) 1638 1639 def _runClientSide(self, current, host=None): 1640 current.push(self, host) 1641 self.setupClientSide(current) 1642 success = False 1643 try: 1644 self.runClientSide(current) 1645 success = True 1646 finally: 1647 self.teardownClientSide(current, success) 1648 current.pop() 1649 1650 def _runClient(self, current, client): 1651 success = False 1652 if client.desc: 1653 current.writeln("running {0}...".format(client.desc)) 1654 client.setup(current) 1655 try: 1656 client.run(current) 1657 success = True 1658 finally: 1659 client.teardown(current, success) 1660 1661 def run(self, current): 1662 try: 1663 current.push(self) 1664 if not self.parent: 1665 current.result.started(current) 1666 self.setup(current) 1667 self.runWithDriver(current) 1668 self.teardown(current, True) 1669 if not self.parent: 1670 current.result.succeeded(current) 1671 except Exception as ex: 1672 self.teardown(current, False) 1673 if not self.parent: 1674 current.result.failed(current, traceback.format_exc() if current.driver.debug else str(ex)) 1675 raise 1676 finally: 1677 current.pop() 1678 1679class ClientTestCase(TestCase): 1680 1681 def __init__(self, name="client", *args, **kargs): 1682 TestCase.__init__(self, name, *args, **kargs) 1683 1684 def runWithDriver(self, current): 1685 current.driver.runTestCase(current) 1686 1687 def getClientType(self): 1688 return "client" 1689 1690class ClientServerTestCase(ClientTestCase): 1691 1692 def __init__(self, name="client/server", *args, **kargs): 1693 TestCase.__init__(self, name, *args, **kargs) 1694 1695 def runWithDriver(self, current): 1696 current.driver.runClientServerTestCase(current) 1697 1698 def getServerType(self): 1699 return "server" 1700 1701class CollocatedTestCase(ClientTestCase): 1702 1703 def __init__(self, name="collocated", *args, **kargs): 1704 TestCase.__init__(self, name, *args, **kargs) 1705 1706 def getClientType(self): 1707 return "collocated" 1708 1709class ClientAMDServerTestCase(ClientServerTestCase): 1710 1711 def __init__(self, name="client/amd server", *args, **kargs): 1712 ClientServerTestCase.__init__(self, name, *args, **kargs) 1713 1714 def getServerType(self): 1715 return "serveramd" 1716 1717class ClientTieServerTestCase(ClientServerTestCase): 1718 1719 def __init__(self, name="client/tie server", *args, **kargs): 1720 ClientServerTestCase.__init__(self, name, *args, **kargs) 1721 1722 def getServerType(self): 1723 return "servertie" 1724 1725class ClientAMDTieServerTestCase(ClientServerTestCase): 1726 1727 def __init__(self, name="client/amd tie server", *args, **kargs): 1728 ClientServerTestCase.__init__(self, name, *args, **kargs) 1729 1730 def getServerType(self): 1731 return "serveramdtie" 1732 1733class Result: 1734 1735 getKey = lambda self, current: (current.testcase, current.config) if isinstance(current, Driver.Current) else current 1736 getDesc = lambda self, current: current.desc if isinstance(current, Driver.Current) else "" 1737 1738 def __init__(self, testsuite, writeToStdout): 1739 self.testsuite = testsuite 1740 self._failed = {} 1741 self._skipped = {} 1742 self._stdout = StringIO() 1743 self._writeToStdout = writeToStdout 1744 self._testcases = {} 1745 self._duration = 0 1746 self._testCaseDuration = 0; 1747 1748 def start(self): 1749 self._duration = time.time() 1750 1751 def finished(self): 1752 self._duration = time.time() - self._duration 1753 1754 def started(self, current): 1755 self._testCaseDuration = time.time(); 1756 self._start = self._stdout.tell() 1757 1758 def failed(self, current, exception): 1759 print(exception) 1760 key = self.getKey(current) 1761 self._testCaseDuration = time.time() - self._testCaseDuration; 1762 self.writeln("\ntest in {0} failed:\n{1}".format(self.testsuite, exception)) 1763 self._testcases[key] = (self._start, self._stdout.tell(), self._testCaseDuration, self.getDesc(current)) 1764 self._failed[key] = exception 1765 1766 # If ADDRINUSE, dump the current processes 1767 output = self.getOutput(key) 1768 for s in ["EADDRINUSE", "Address already in use"]: 1769 if output.find(s) >= 0: 1770 if isinstance(platform, Windows): 1771 self.writeln(run("netstat -on")) 1772 self.writeln(run("powershell.exe \"Get-Process | Select id,name,path\"")) 1773 else: 1774 self.writeln(run("lsof -n -P -i; ps ax")) 1775 1776 def succeeded(self, current): 1777 key = self.getKey(current) 1778 self._testCaseDuration = time.time() - self._testCaseDuration; 1779 self._testcases[key] = (self._start, self._stdout.tell(), self._testCaseDuration, self.getDesc(current)) 1780 1781 def skipped(self, current, reason): 1782 self.writeln("skipped, " + reason) 1783 self._skipped[self.getKey(current)] = reason 1784 1785 def isSuccess(self): 1786 return len(self._failed) == 0 1787 1788 def getFailed(self): 1789 return self._failed 1790 1791 def getDuration(self): 1792 return self._duration 1793 1794 def getOutput(self, key=None): 1795 if key: 1796 if key in self._testcases: 1797 (start, end, duration, desc) = self._testcases[key] 1798 self._stdout.seek(start) 1799 try: 1800 return self._stdout.read(end - start) 1801 finally: 1802 self._stdout.seek(0, os.SEEK_END) 1803 1804 return self._stdout.getvalue() 1805 1806 def write(self, msg, stdout=True): 1807 if self._writeToStdout and stdout: 1808 try: 1809 sys.stdout.write(msg) 1810 except UnicodeEncodeError: 1811 # 1812 # The console doesn't support the encoding of the message, we convert the message 1813 # to an UTF-8 byte sequence and print out the byte sequence. We replace all the 1814 # double backslash from the byte sequence string representation to single back 1815 # slash. 1816 # 1817 sys.stdout.write(str(msg.encode("utf-8")).replace("\\\\", "\\")) 1818 sys.stdout.flush() 1819 self._stdout.write(msg) 1820 1821 def writeln(self, msg, stdout=True): 1822 if self._writeToStdout and stdout: 1823 try: 1824 print(msg) 1825 except UnicodeEncodeError: 1826 # 1827 # The console doesn't support the encoding of the message, we convert the message 1828 # to an UTF-8 byte sequence and print out the byte sequence. We replace all the 1829 # double backslash from the byte sequence string representation to single back 1830 # slash. 1831 # 1832 print(str(msg.encode("utf-8")).replace("\\\\", "\\")) 1833 self._stdout.write(msg) 1834 self._stdout.write("\n") 1835 1836 def writeAsXml(self, out, hostname=""): 1837 out.write(' <testsuite tests="{0}" failures="{1}" skipped="{2}" time="{3:.9f}" name="{5}/{4}">\n' 1838 .format(len(self._testcases) - 2, 1839 len(self._failed), 1840 0, 1841 self._duration, 1842 self.testsuite, 1843 self.testsuite.getMapping())) 1844 1845 for (k, v) in self._testcases.items(): 1846 if isinstance(k, str): 1847 # Don't keep track of setup/teardown steps 1848 continue 1849 1850 # Don't write skipped tests, this doesn't really provide useful information and clutters 1851 # the output. 1852 if k in self._skipped: 1853 continue 1854 1855 (tc, cf) = k 1856 (s, e, d, c) = v 1857 if c: 1858 name = "{0} [{1}]".format(tc, c) 1859 else: 1860 name = str(tc) 1861 if hostname: 1862 name += " on " + hostname 1863 out.write(' <testcase name="{0}" time="{1:.9f}" classname="{2}.{3}">\n' 1864 .format(name, 1865 d, 1866 self.testsuite.getMapping(), 1867 self.testsuite.getId().replace("/", "."))) 1868 if k in self._failed: 1869 last = self._failed[k].strip().split('\n') 1870 if len(last) > 0: 1871 last = last[len(last) - 1] 1872 if hostname: 1873 last = "Failed on {0}\n{1}".format(hostname, last) 1874 out.write(' <failure message={1}>{0}</failure>\n'.format(escapeXml(self._failed[k]), 1875 escapeXml(last, True))) 1876 # elif k in self._skipped: 1877 # out.write(' <skipped message="{0}"/>\n'.format(escapeXml(self._skipped[k], True))) 1878 out.write(' <system-out>\n') 1879 if hostname: 1880 out.write('Running on {0}\n'.format(hostname)) 1881 out.write(escapeXml(self.getOutput(k))) 1882 out.write(' </system-out>\n') 1883 out.write(' </testcase>\n') 1884 1885 out.write( '</testsuite>\n') 1886 1887class TestSuite(object): 1888 1889 def __init__(self, path, testcases=None, options=None, libDirs=None, runOnMainThread=False, chdir=False, 1890 multihost=True, mapping=None): 1891 global currentMapping 1892 self.path = os.path.dirname(path) if os.path.basename(path) == "test.py" else path 1893 self.mapping = currentMapping or Mapping.getByPath(self.path) 1894 self.id = self.mapping.addTestSuite(self) 1895 self.options = options or {} 1896 self.libDirs = libDirs or [] 1897 self.runOnMainThread = runOnMainThread 1898 self.chdir = chdir 1899 self.multihost = multihost 1900 if self.chdir: 1901 # Only tests running on main thread can change the current working directory 1902 self.runOnMainThread = True 1903 1904 if testcases is None: 1905 files = [f for f in os.listdir(self.path) if os.path.isfile(os.path.join(self.path, f))] 1906 testcases = self.mapping.computeTestCases(self.id, files) 1907 self.testcases = OrderedDict() 1908 for testcase in testcases if testcases else []: 1909 testcase.init(self.mapping, self) 1910 if testcase.name in self.testcases: 1911 raise RuntimeError("duplicate testcase {0} in testsuite {1}".format(testcase, self)) 1912 self.testcases[testcase.name] = testcase 1913 1914 def __str__(self): 1915 return self.id 1916 1917 def getId(self): 1918 return self.id 1919 1920 def getOptions(self, current): 1921 return self.options(current) if callable(self.options) else self.options 1922 1923 def getPath(self): 1924 return self.path 1925 1926 def getMapping(self): 1927 return self.mapping 1928 1929 def getLibDirs(self): 1930 return self.libDirs 1931 1932 def isMainThreadOnly(self, driver): 1933 if self.runOnMainThread or driver.getComponent().isMainThreadOnly(self.id): 1934 return True 1935 for m in [CppMapping, JavaMapping, CSharpMapping, PythonMapping, PhpMapping, RubyMapping, JavaScriptMixin]: 1936 if isinstance(self.mapping, m): 1937 config = driver.configs[self.mapping] 1938 if "iphone" in config.buildPlatform or config.uwp or config.browser or config.android: 1939 return True # Not supported yet for tests that require a remote process controller 1940 return False 1941 else: 1942 return True 1943 1944 def addTestCase(self, testcase): 1945 if testcase.name in self.testcases: 1946 raise RuntimeError("duplicate testcase {0} in testsuite {1}".format(testcase, self)) 1947 testcase.init(self.mapping, self) 1948 self.testcases[testcase.name] = testcase 1949 1950 def findTestCase(self, testcase): 1951 return self.testcases.get(testcase if isinstance(testcase, str) else testcase.name) 1952 1953 def getTestCases(self): 1954 return self.testcases.values() 1955 1956 def setup(self, current): 1957 pass 1958 1959 def run(self, current): 1960 try: 1961 current.result.start() 1962 cwd=None 1963 if self.chdir: 1964 cwd = os.getcwd() 1965 os.chdir(self.path) 1966 current.driver.runTestSuite(current) 1967 finally: 1968 if cwd: os.chdir(cwd) 1969 current.result.finished() 1970 1971 def teardown(self, current, success): 1972 pass 1973 1974 def isMultiHost(self): 1975 return self.multihost 1976 1977class ProcessController: 1978 1979 def __init__(self, current): 1980 pass 1981 1982 def start(self, process, current, args, props, envs, watchDog): 1983 raise NotImplemented() 1984 1985 def destroy(self, driver): 1986 pass 1987 1988class LocalProcessController(ProcessController): 1989 1990 class LocalProcess(Expect.Expect): 1991 1992 def __init__(self, traceFile, *args, **kargs): 1993 Expect.Expect.__init__(self, *args, **kargs) 1994 self.traceFile = traceFile 1995 1996 def waitReady(self, ready, readyCount, startTimeout): 1997 if ready: 1998 self.expect("%s ready\n" % ready, timeout = startTimeout) 1999 else: 2000 while readyCount > 0: 2001 self.expect("[^\n]+ ready\n", timeout = startTimeout) 2002 readyCount -= 1 2003 2004 def isTerminated(self): 2005 return self.p is None 2006 2007 def teardown(self, current, success): 2008 if self.traceFile: 2009 if success or current.driver.isInterrupted(): 2010 os.remove(self.traceFile) 2011 else: 2012 current.writeln("saved {0}".format(self.traceFile)) 2013 2014 def getHost(self, current): 2015 return current.driver.getHost(current.config.protocol, current.config.ipv6) 2016 2017 def start(self, process, current, args, props, envs, watchDog): 2018 2019 # 2020 # Props and arguments can use the format parameters set below in the kargs 2021 # dictionary. It's time to convert them to their values. 2022 # 2023 kargs = { 2024 "process": process, 2025 "testcase": current.testcase, 2026 "testdir": current.testsuite.getPath(), 2027 "builddir": current.getBuildDir(process.getExe(current)), 2028 } 2029 2030 traceFile = "" 2031 if not isinstance(process.getMapping(current), JavaScriptMixin): 2032 traceProps = process.getEffectiveTraceProps(current) 2033 if traceProps: 2034 if "Ice.ProgramName" in props: 2035 programName = props["Ice.ProgramName"] 2036 else: 2037 programName = process.exe or current.testcase.getProcessType(process) 2038 traceFile = os.path.join(current.testsuite.getPath(), 2039 "{0}-{1}.log".format(programName, time.strftime("%m%d%y-%H%M"))) 2040 if isinstance(process.getMapping(current), ObjCMapping): 2041 traceProps["Ice.StdErr"] = traceFile 2042 else: 2043 traceProps["Ice.LogFile"] = traceFile 2044 props.update(traceProps) 2045 2046 args = ["--{0}={1}".format(k, val(v)) for k,v in props.items()] + [val(a) for a in args] 2047 for k, v in envs.items(): 2048 envs[k] = val(v, quoteValue=False) 2049 2050 cmd = "" 2051 if current.driver.valgrind: 2052 cmd += "valgrind -q --child-silent-after-fork=yes --leak-check=full --suppressions=\"{0}\" ".format( 2053 os.path.join(current.driver.getComponent().getSourceDir(), "config", "valgrind.sup")) 2054 exe = process.getCommandLine(current, " ".join(args)) 2055 cmd += exe.format(**kargs) 2056 2057 if current.driver.debug: 2058 if len(envs) > 0: 2059 current.writeln("({0} env={1})".format(cmd, envs)) 2060 else: 2061 current.writeln("({0})".format(cmd)) 2062 2063 env = os.environ.copy() 2064 env.update(envs) 2065 mapping = process.getMapping(current) 2066 cwd = mapping.getTestCwd(process, current) 2067 process = LocalProcessController.LocalProcess(command=cmd, 2068 startReader=False, 2069 env=env, 2070 cwd=cwd, 2071 desc=process.desc or exe, 2072 preexec_fn=process.preexec_fn, 2073 mapping=str(mapping), 2074 traceFile=traceFile) 2075 process.startReader(watchDog) 2076 return process 2077 2078class RemoteProcessController(ProcessController): 2079 2080 class RemoteProcess: 2081 def __init__(self, exe, proxy): 2082 self.exe = exe 2083 self.proxy = proxy 2084 self.terminated = False 2085 self.stdout = False 2086 2087 def __str__(self): 2088 return "{0} proxy={1}".format(self.exe, self.proxy) 2089 2090 def waitReady(self, ready, readyCount, startTimeout): 2091 self.proxy.waitReady(startTimeout) 2092 2093 def waitSuccess(self, exitstatus=0, timeout=60): 2094 import Ice 2095 try: 2096 result = self.proxy.waitSuccess(timeout) 2097 except Ice.UserException: 2098 raise Expect.TIMEOUT("waitSuccess timeout") 2099 except Ice.LocalException: 2100 raise 2101 if exitstatus != result: 2102 raise RuntimeError("unexpected exit status: expected: %d, got %d\n" % (exitstatus, result)) 2103 2104 def getOutput(self): 2105 return self.output 2106 2107 def trace(self, outfilters): 2108 self.stdout = True 2109 2110 def isTerminated(self): 2111 return self.terminated 2112 2113 def terminate(self): 2114 self.output = self.proxy.terminate().strip() 2115 self.terminated = True 2116 if self.stdout and self.output: 2117 print(self.output) 2118 2119 def teardown(self, current, success): 2120 pass 2121 2122 def __init__(self, current, endpoints="tcp"): 2123 self.processControllerProxies = {} 2124 self.controllerApps = [] 2125 self.driver = current.driver 2126 self.cond = threading.Condition() 2127 2128 comm = current.driver.getCommunicator() 2129 import Test 2130 2131 class ProcessControllerRegistryI(Test.Common.ProcessControllerRegistry): 2132 2133 def __init__(self, remoteProcessController): 2134 self.remoteProcessController = remoteProcessController 2135 2136 def setProcessController(self, proxy, current): 2137 import Test 2138 proxy = Test.Common.ProcessControllerPrx.uncheckedCast(current.con.createProxy(proxy.ice_getIdentity())) 2139 self.remoteProcessController.setProcessController(proxy) 2140 2141 import Ice 2142 comm.getProperties().setProperty("Adapter.AdapterId", Ice.generateUUID()) 2143 self.adapter = comm.createObjectAdapterWithEndpoints("Adapter", endpoints) 2144 self.adapter.add(ProcessControllerRegistryI(self), comm.stringToIdentity("Util/ProcessControllerRegistry")) 2145 self.adapter.activate() 2146 2147 def __str__(self): 2148 return "remote controller" 2149 2150 def getHost(self, current): 2151 return self.getController(current).getHost(current.config.protocol, current.config.ipv6) 2152 2153 def getController(self, current): 2154 ident = self.getControllerIdentity(current) 2155 if type(ident) == str: 2156 ident = current.driver.getCommunicator().stringToIdentity(ident) 2157 2158 import Ice 2159 import Test 2160 2161 proxy = None 2162 with self.cond: 2163 if ident in self.processControllerProxies: 2164 proxy = self.processControllerProxies[ident] 2165 if proxy: 2166 try: 2167 proxy.ice_ping() 2168 return proxy 2169 except Ice.NoEndpointException: 2170 self.clearProcessController(proxy) 2171 2172 comm = current.driver.getCommunicator() 2173 2174 if current.driver.controllerApp: 2175 if ident in self.controllerApps: 2176 self.restartControllerApp(current, ident) # Controller must have crashed, restart it 2177 else: 2178 self.controllerApps.append(ident) 2179 self.startControllerApp(current, ident) 2180 2181 # Use well-known proxy and IceDiscovery to discover the process controller object from the app. 2182 proxy = Test.Common.ProcessControllerPrx.uncheckedCast(comm.stringToProxy(comm.identityToString(ident))) 2183 try: 2184 proxy.ice_ping() 2185 with self.cond: 2186 self.processControllerProxies[ident] = proxy 2187 return self.processControllerProxies[ident] 2188 except Exception: 2189 pass 2190 2191 # Wait 30 seconds for a process controller to be registered with the ProcessControllerRegistry 2192 with self.cond: 2193 if not ident in self.processControllerProxies: 2194 self.cond.wait(30) 2195 if ident in self.processControllerProxies: 2196 return self.processControllerProxies[ident] 2197 2198 raise RuntimeError("couldn't reach the remote controller `{0}'".format(ident)) 2199 2200 def setProcessController(self, proxy): 2201 with self.cond: 2202 self.processControllerProxies[proxy.ice_getIdentity()] = proxy 2203 conn = proxy.ice_getConnection() 2204 if(hasattr(conn, "setCloseCallback")): 2205 proxy.ice_getConnection().setCloseCallback(lambda conn : self.clearProcessController(proxy, conn)) 2206 else: 2207 import Ice 2208 class CallbackI(Ice.ConnectionCallback): 2209 def __init__(self, registry): 2210 self.registry = registry 2211 2212 def heartbeath(self, conn): 2213 pass 2214 2215 def closed(self, conn): 2216 self.registry.clearProcessController(proxy, conn) 2217 2218 proxy.ice_getConnection().setCallback(CallbackI(self)) 2219 2220 self.cond.notifyAll() 2221 2222 def clearProcessController(self, proxy, conn=None): 2223 with self.cond: 2224 if proxy.ice_getIdentity() in self.processControllerProxies: 2225 if not conn: 2226 conn = proxy.ice_getCachedConnection() 2227 if conn == self.processControllerProxies[proxy.ice_getIdentity()].ice_getCachedConnection(): 2228 del self.processControllerProxies[proxy.ice_getIdentity()] 2229 2230 def startControllerApp(self, current, ident): 2231 pass 2232 2233 def restartControllerApp(self, current, ident): 2234 self.stopControllerApp(ident) 2235 self.startControllerApp(current, ident) 2236 2237 def stopControllerApp(self, ident): 2238 pass 2239 2240 def start(self, process, current, args, props, envs, watchDog): 2241 # Get the process controller 2242 processController = self.getController(current) 2243 2244 # TODO: support envs? 2245 2246 exe = process.getExe(current) 2247 args = ["--{0}={1}".format(k, val(v, quoteValue=False)) for k,v in props.items()] + [val(a) for a in args] 2248 if current.driver.debug: 2249 current.writeln("(executing `{0}/{1}' on `{2}' args = {3})".format(current.testsuite, exe, self, args)) 2250 prx = processController.start(str(current.testsuite), exe, args) 2251 2252 # Create bi-dir proxy in case we're talking to a bi-bir process controller. 2253 if self.adapter: 2254 prx = processController.ice_getConnection().createProxy(prx.ice_getIdentity()) 2255 import Test 2256 return RemoteProcessController.RemoteProcess(exe, Test.Common.ProcessPrx.uncheckedCast(prx)) 2257 2258 def destroy(self, driver): 2259 if driver.controllerApp: 2260 for ident in self.controllerApps: 2261 self.stopControllerApp(ident) 2262 self.controllerApps = [] 2263 if self.adapter: 2264 self.adapter.destroy() 2265 2266class AndroidProcessController(RemoteProcessController): 2267 2268 def __init__(self, current): 2269 endpoint = None 2270 if current.config.xamarin or current.config.device: 2271 endpoint = "tcp -h 0.0.0.0 -p 15001" 2272 elif current.config.avd or not current.config.device: 2273 endpoint = "tcp -h 127.0.0.1 -p 15001" 2274 RemoteProcessController.__init__(self, current, endpoint) 2275 self.device = current.config.device 2276 self.avd = current.config.avd 2277 self.emulator = None # Keep a reference to the android emulator process 2278 2279 def __str__(self): 2280 return "Android" 2281 2282 def getControllerIdentity(self, current): 2283 if isinstance(current.testcase.getMapping(), CSharpMapping): 2284 return "AndroidXamarin/ProcessController" 2285 elif isinstance(current.testcase.getMapping(), JavaCompatMapping): 2286 return "AndroidCompat/ProcessController" 2287 else: 2288 return "Android/ProcessController" 2289 2290 def adb(self): 2291 return "adb -d" if self.device == "usb" else "adb" 2292 2293 def startEmulator(self, avd): 2294 # 2295 # First check if the AVD image is available 2296 # 2297 print("starting the emulator... ") 2298 out = run("emulator -list-avds") 2299 if avd not in out: 2300 raise RuntimeError("couldn't find AVD `{}'".format(avd)) 2301 2302 # 2303 # Find and unused port to run android emulator, between 5554 and 5584 2304 # 2305 port = -1 2306 out = run("adb devices -l") 2307 for p in range(5554, 5586, 2): 2308 if not "emulator-{}".format(p) in out: 2309 port = p 2310 2311 if port == -1: 2312 raise RuntimeError("cannot find free port in range 5554-5584, to run android emulator") 2313 2314 self.device = "emulator-{}".format(port) 2315 cmd = "emulator -avd {0} -port {1} -noaudio -partition-size 768 -no-snapshot".format(avd, port) 2316 self.emulator = subprocess.Popen(cmd, shell=True) 2317 2318 if self.emulator.poll(): 2319 raise RuntimeError("failed to start the Android emulator `{}' on port {}".format(avd, port)) 2320 2321 self.avd = avd 2322 2323 # 2324 # Wait for the device to be ready 2325 # 2326 t = time.time() 2327 while True: 2328 try: 2329 lines = run("{} shell getprop sys.boot_completed".format(self.adb())) 2330 if len(lines) > 0 and lines[0].strip() == "1": 2331 break 2332 except RuntimeError: 2333 pass # expected if device is offline 2334 # 2335 # If the emulator doesn't complete boot in 300 seconds give up 2336 # 2337 if (time.time() - t) > 300: 2338 raise RuntimeError("couldn't start the Android emulator `{}'".format(avd)) 2339 time.sleep(2) 2340 2341 def startControllerApp(self, current, ident): 2342 2343 # Stop previous controller app before starting new one 2344 for ident in self.controllerApps: 2345 self.stopControllerApp(ident) 2346 2347 if current.config.avd: 2348 self.startEmulator(current.config.avd) 2349 elif not current.config.device: 2350 # Create Android Virtual Device 2351 sdk = current.testcase.getMapping().getSDKPackage() 2352 print("creating virtual device ({0})... ".format(sdk)) 2353 try: 2354 run("avdmanager delete avd -n IceTests") # Delete the created device 2355 except: 2356 pass 2357 # The SDK is downloaded by test VMs instead of here. 2358 #run("sdkmanager \"{0}\"".format(sdk), stdout=True, stdin="yes", stdinRepeat=True) # yes to accept licenses 2359 run("avdmanager create avd -k \"{0}\" -d \"Nexus 6\" -n IceTests".format(sdk)) 2360 self.startEmulator("IceTests") 2361 elif current.config.device != "usb": 2362 run("adb connect {}".format(current.config.device)) 2363 2364 run("{} install -t -r {}".format(self.adb(), current.testcase.getMapping().getApk(current))) 2365 run("{} shell am start -n \"{}\" -a android.intent.action.MAIN -c android.intent.category.LAUNCHER".format( 2366 self.adb(), current.testcase.getMapping().getActivityName())) 2367 2368 def stopControllerApp(self, ident): 2369 try: 2370 run("{} shell pm uninstall com.zeroc.testcontroller".format(self.adb())) 2371 except: 2372 pass 2373 2374 if self.avd: 2375 try: 2376 run("{} emu kill".format(self.adb())) 2377 except: 2378 pass 2379 2380 if self.avd == "IceTests": 2381 try: 2382 run("avdmanager delete avd -n IceTests") # Delete the created device 2383 except: 2384 pass 2385 2386 # 2387 # Wait for the emulator to shutdown 2388 # 2389 if self.emulator: 2390 sys.stdout.write("Waiting for the emulator to shutdown..") 2391 sys.stdout.flush() 2392 while True: 2393 if self.emulator.poll() != None: 2394 print(" ok") 2395 break 2396 sys.stdout.write(".") 2397 sys.stdout.flush() 2398 time.sleep(0.5) 2399 2400 try: 2401 run("adb kill-server") 2402 except: 2403 pass 2404 2405class iOSSimulatorProcessController(RemoteProcessController): 2406 2407 device = "iOSSimulatorProcessController" 2408 deviceID = "com.apple.CoreSimulator.SimDeviceType.iPhone-X" 2409 2410 def __init__(self, current): 2411 RemoteProcessController.__init__(self, current, "tcp -h 0.0.0.0 -p 15001" if current.config.xamarin else None) 2412 self.simulatorID = None 2413 self.runtimeID = None 2414 # Pick the last iOS simulator runtime ID in the list of iOS simulators (assumed to be the latest). 2415 try: 2416 for r in run("xcrun simctl list runtimes").split('\n'): 2417 m = re.search("iOS .* \\(.*\\) - (.*)", r) 2418 if m: 2419 self.runtimeID = m.group(1) 2420 except: 2421 pass 2422 if not self.runtimeID: 2423 self.runtimeID = "com.apple.CoreSimulator.SimRuntime.iOS-12-0" # Default value 2424 2425 def __str__(self): 2426 return "iOS Simulator" 2427 2428 def getControllerIdentity(self, current): 2429 return current.testcase.getMapping().getIOSControllerIdentity(current) 2430 2431 def startControllerApp(self, current, ident): 2432 mapping = current.testcase.getMapping() 2433 appFullPath = mapping.getIOSAppFullPath(current) 2434 2435 sys.stdout.write("launching simulator... ") 2436 sys.stdout.flush() 2437 try: 2438 run("xcrun simctl boot \"{0}\"".format(self.device)) 2439 except Exception as ex: 2440 if str(ex).find("Booted") >= 0: 2441 pass 2442 elif str(ex).find("Invalid device") >= 0: 2443 # Create the simulator device if it doesn't exist 2444 self.simulatorID = run("xcrun simctl create \"{0}\" {1} {2}".format(self.device, self.deviceID, self.runtimeID)) 2445 run("xcrun simctl boot \"{0}\"".format(self.device)) 2446 else: 2447 raise 2448 print("ok") 2449 2450 sys.stdout.write("launching {0}... ".format(os.path.basename(appFullPath))) 2451 sys.stdout.flush() 2452 2453 if not os.path.exists(appFullPath): 2454 raise RuntimeError("couldn't find iOS simulator controller application, did you build it?") 2455 run("xcrun simctl install \"{0}\" \"{1}\"".format(self.device, appFullPath)) 2456 run("xcrun simctl launch \"{0}\" {1}".format(self.device, ident.name)) 2457 print("ok") 2458 2459 def restartControllerApp(self, current, ident): 2460 try: 2461 run("xcrun simctl terminate \"{0}\" {1}".format(self.device, ident.name)) 2462 except: 2463 pass 2464 run("xcrun simctl launch \"{0}\" {1}".format(self.device, ident.name)) 2465 2466 def stopControllerApp(self, ident): 2467 try: 2468 run("xcrun simctl uninstall \"{0}\" {1}".format(self.device, ident.name)) 2469 except: 2470 pass 2471 2472 def destroy(self, driver): 2473 RemoteProcessController.destroy(self, driver) 2474 2475 sys.stdout.write("shutting down simulator... ") 2476 sys.stdout.flush() 2477 try: 2478 run("xcrun simctl shutdown \"{0}\"".format(self.simulatorID)) 2479 except: 2480 pass 2481 print("ok") 2482 2483 if self.simulatorID: 2484 sys.stdout.write("destroying simulator... ") 2485 sys.stdout.flush() 2486 try: 2487 run("xcrun simctl delete \"{0}\"".format(self.simulatorID)) 2488 except: 2489 pass 2490 print("ok") 2491 2492class iOSDeviceProcessController(RemoteProcessController): 2493 2494 appPath = "cpp/test/ios/controller/build" 2495 2496 def __init__(self, current): 2497 RemoteProcessController.__init__(self, current, "tcp -h 0.0.0.0 -p 15001" if current.config.xamarin else None) 2498 2499 def __str__(self): 2500 return "iOS Device" 2501 2502 def getControllerIdentity(self, current): 2503 return current.testcase.getMapping().getIOSControllerIdentity(current) 2504 2505 def startControllerApp(self, current, ident): 2506 # TODO: use ios-deploy to deploy and run the application on an attached device? 2507 pass 2508 2509 def stopControllerApp(self, ident): 2510 pass 2511 2512class UWPProcessController(RemoteProcessController): 2513 2514 def __init__(self, current): 2515 RemoteProcessController.__init__(self, current, "tcp -h 127.0.0.1 -p 15001") 2516 self.name = current.testcase.getMapping().getUWPPackageName() 2517 self.appUserModelId = current.testcase.getMapping().getUWPUserModelId() 2518 2519 def __str__(self): 2520 return "UWP" 2521 2522 def getControllerIdentity(self, current): 2523 if isinstance(current.testcase.getMapping(), CSharpMapping): 2524 return "UWPXamarin/ProcessController" 2525 else: 2526 return "UWP/ProcessController" 2527 2528 def startControllerApp(self, current, ident): 2529 platform = current.config.buildPlatform 2530 config = current.config.buildConfig 2531 arch = "X86" if platform == "Win32" else "X64" 2532 2533 self.packageFullName = current.testcase.getMapping().getUWPPackageFullName(platform) 2534 packageFullPath = current.testcase.getMapping().getUWPPackageFullPath(platform, config) 2535 2536 # 2537 # If the application is already installed remove it, this will also take care 2538 # of closing it. 2539 # 2540 if self.name in run("powershell Get-AppxPackage -Name {0}".format(self.name)): 2541 run("powershell Remove-AppxPackage {0}".format(self.packageFullName)) 2542 2543 # 2544 # Remove any previous package we have extracted to ensure we use a 2545 # fresh build 2546 # 2547 layout = os.path.join(current.testcase.getMapping().getPath(), "AppX") 2548 if os.path.exists(layout): 2549 shutil.rmtree(layout) 2550 os.makedirs(layout) 2551 2552 print("Unpacking package: {0} to {1}....".format(os.path.basename(packageFullPath), layout)) 2553 run("MakeAppx.exe unpack /p \"{0}\" /d \"{1}\" /l".format(packageFullPath, layout)) 2554 2555 print("Registering application to run from layout...") 2556 2557 for root, dirs, files in os.walk(os.path.join(os.path.dirname(packageFullPath), "Dependencies", arch)): 2558 for f in files: 2559 self.installPackage(os.path.join(root, f), arch) 2560 2561 run("powershell Add-AppxPackage -Register \"{0}/AppxManifest.xml\" -ForceApplicationShutdown".format(layout)) 2562 run("CheckNetIsolation LoopbackExempt -a -n={0}".format(self.appUserModelId)) 2563 2564 # 2565 # microsoft.windows.softwarelogo.appxlauncher.exe returns the PID as return code 2566 # and 0 on case of failures. We pass err=True to run to handle this. 2567 # 2568 print("starting UWP controller app...") 2569 run('"{0}" {1}!App'.format( 2570 "C:/Program Files (x86)/Windows Kits/10/App Certification Kit/microsoft.windows.softwarelogo.appxlauncher.exe", 2571 self.appUserModelId), err=True) 2572 2573 def stopControllerApp(self, ident): 2574 try: 2575 run("powershell Remove-AppxPackage {0}".format(self.packageFullName)) 2576 run("CheckNetIsolation LoopbackExempt -c -n={0}".format(self.appUserModelId)) 2577 except: 2578 pass 2579 2580 def getPackageVersion(self, package): 2581 import zipfile 2582 import xml.etree.ElementTree as ElementTree 2583 with zipfile.ZipFile(package) as zipfile: 2584 with zipfile.open('AppxManifest.xml') as file: 2585 xml = ElementTree.fromstring(file.read()) 2586 identity = xml.find("{http://schemas.microsoft.com/appx/manifest/foundation/windows10}Identity") 2587 return tuple(map(int, identity.attrib['Version'].split("."))) 2588 2589 def installPackage(self, package, arch): 2590 packages = { 2591 "Microsoft.VCLibs.x64.14.00.appx" : "Microsoft.VCLibs.140.00", 2592 "Microsoft.VCLibs.x86.14.00.appx" : "Microsoft.VCLibs.140.00", 2593 "Microsoft.VCLibs.x64.Debug.14.00.appx" : "Microsoft.VCLibs.140.00.Debug", 2594 "Microsoft.VCLibs.x86.Debug.14.00.appx" : "Microsoft.VCLibs.140.00.Debug", 2595 "Microsoft.NET.CoreRuntime.2.1.appx" : "Microsoft.NET.CoreRuntime.2.1" 2596 } 2597 packageName = packages[os.path.basename(package)] 2598 output = run("powershell Get-AppxPackage -Name {0}".format(packageName)) 2599 m = re.search("Architecture.*: {0}\\r\\nResourceId.*:.*\\r\\nVersion.*: (.*)\\r\\n".format(arch), output) 2600 installedVersion = tuple(map(int, m.group(1).split("."))) if m else (0, 0, 0, 0) 2601 version = self.getPackageVersion(package) 2602 if installedVersion <= version: 2603 run("powershell Add-AppxPackage -Path \"{0}\" -ForceApplicationShutdown".format(package)) 2604 2605class BrowserProcessController(RemoteProcessController): 2606 2607 def __init__(self, current): 2608 self.host = current.driver.host or "127.0.0.1" 2609 RemoteProcessController.__init__(self, current, "ws -h {0} -p 15002:wss -h {0} -p 15003".format(self.host)) 2610 self.httpServer = None 2611 self.url = None 2612 self.driver = None 2613 try: 2614 cmd = "node -e \"require('./bin/HttpServer')()\""; 2615 cwd = current.testcase.getMapping().getPath() 2616 self.httpServer = Expect.Expect(cmd, cwd=cwd) 2617 self.httpServer.expect("listening on ports") 2618 2619 if current.config.browser.startswith("Remote:"): 2620 from selenium import webdriver 2621 from selenium.webdriver.common.desired_capabilities import DesiredCapabilities 2622 (driver, capabilities, port) = current.config.browser.split(":") 2623 self.driver = webdriver.Remote("http://localhost:{0}".format(port), 2624 desired_capabilities=getattr(DesiredCapabilities, capabilities), 2625 keep_alive=True) 2626 elif current.config.browser != "Manual": 2627 from selenium import webdriver 2628 if current.config.browser.find(":") > 0: 2629 (driver, port) = current.config.browser.split(":") 2630 else: 2631 (driver, port) = (current.config.browser, 0) 2632 2633 if not hasattr(webdriver, driver): 2634 raise RuntimeError("unknown browser `{0}'".format(driver)) 2635 2636 if driver == "Firefox": 2637 if isinstance(platform, Linux) and os.environ.get("DISPLAY", "") != ":1" and os.environ.get("USER", "") == "ubuntu": 2638 current.writeln("error: DISPLAY is unset, setting it to :1") 2639 os.environ["DISPLAY"] = ":1" 2640 2641 # 2642 # We need to specify a profile for Firefox. This profile only provides the cert8.db which 2643 # contains our Test CA cert. It should be possible to avoid this by setting the webdriver 2644 # acceptInsecureCerts capability but it's only supported by latest Firefox releases. 2645 # 2646 profilepath = os.path.join(current.driver.getComponent().getSourceDir(), "scripts", "selenium", "firefox") 2647 profile = webdriver.FirefoxProfile(profilepath) 2648 self.driver = webdriver.Firefox(firefox_profile=profile) 2649 elif driver == "Ie": 2650 # Make sure we start with a clean cache 2651 capabilities = webdriver.DesiredCapabilities.INTERNETEXPLORER.copy() 2652 capabilities["ie.ensureCleanSession"] = True 2653 self.driver = webdriver.Ie(capabilities=capabilities) 2654 elif driver == "Safari" and port > 0: 2655 self.driver = webdriver.Safari(port=int(port), reuse_service=True) 2656 else: 2657 self.driver = getattr(webdriver, driver)() 2658 except: 2659 self.destroy(current.driver) 2660 raise 2661 2662 def __str__(self): 2663 return str(self.driver) if self.driver else "Manual" 2664 2665 def getControllerIdentity(self, current): 2666 # 2667 # Load the controller page each time we're asked for the controller and if we're running 2668 # another testcase, the controller page will connect to the process controller registry 2669 # to register itself with this script. 2670 # 2671 testsuite = ("es5/" if current.config.es5 else "") + str(current.testsuite) 2672 if current.config.protocol == "wss": 2673 protocol = "https" 2674 port = "9090" 2675 cport = "15003" 2676 else: 2677 protocol = "http" 2678 port = "8080" 2679 cport = "15002" 2680 url = "{0}://{5}:{1}/test/{2}/controller.html?port={3}&worker={4}".format(protocol, 2681 port, 2682 testsuite, 2683 cport, 2684 current.config.worker, 2685 self.host) 2686 if url != self.url: 2687 self.url = url 2688 if self.driver: 2689 self.driver.get(url) 2690 else: 2691 # If no process controller is registered, we request the user to load the controller 2692 # page in the browser. Once loaded, the controller will register and we'll redirect to 2693 # the correct testsuite page. 2694 ident = current.driver.getCommunicator().stringToIdentity("Browser/ProcessController") 2695 prx = None 2696 with self.cond: 2697 while True: 2698 if ident in self.processControllerProxies: 2699 prx = self.processControllerProxies[ident] 2700 break 2701 print("Please load http://{0}:8080/{1}".format(self.host, 2702 "es5/start" if current.config.es5 else "start")) 2703 self.cond.wait(5) 2704 2705 try: 2706 import Test 2707 Test.Common.BrowserProcessControllerPrx.uncheckedCast(prx).redirect(url) 2708 except: 2709 pass 2710 finally: 2711 self.clearProcessController(prx, prx.ice_getCachedConnection()) 2712 2713 return "Browser/ProcessController" 2714 2715 def getController(self, current): 2716 try: 2717 return RemoteProcessController.getController(self, current) 2718 except RuntimeError as ex: 2719 if self.driver: 2720 # Print out the client & server console element values 2721 for element in ["clientConsole", "serverConsole"]: 2722 try: 2723 console = self.driver.find_element_by_id(element).get_attribute('value') 2724 if len(console) > 0: 2725 print("controller {0} value:\n{1}".format(element, console)) 2726 except Exception as exc: 2727 print("couldn't get controller {0} value:\n{1}".format(element, exc)) 2728 pass 2729 # Print out the browser log 2730 try: 2731 print("browser log:\n{0}".format(self.driver.get_log("browser"))) 2732 except: 2733 pass # Not all browsers support retrieving the browser console log 2734 raise ex 2735 2736 def destroy(self, driver): 2737 if self.httpServer: 2738 self.httpServer.terminate() 2739 self.httpServer = None 2740 2741 try: 2742 self.driver.quit() 2743 except: 2744 pass 2745 2746class Driver: 2747 2748 class Current: 2749 2750 def __init__(self, driver, testsuite, result): 2751 self.driver = driver 2752 self.testsuite = testsuite 2753 self.config = driver.configs[testsuite.getMapping()] 2754 self.desc = "" 2755 self.result = result 2756 self.host = None 2757 self.testcase = None 2758 self.testcases = [] 2759 self.processes = {} 2760 self.dirs = [] 2761 self.files = [] 2762 2763 def getTestEndpoint(self, *args, **kargs): 2764 return self.driver.getTestEndpoint(*args, **kargs) 2765 2766 def getBuildDir(self, name): 2767 return self.testcase.getMapping().getBuildDir(name, self) 2768 2769 def getPluginEntryPoint(self, plugin, process): 2770 return self.testcase.getMapping().getPluginEntryPoint(plugin, process, self) 2771 2772 def write(self, *args, **kargs): 2773 self.result.write(*args, **kargs) 2774 2775 def writeln(self, *args, **kargs): 2776 self.result.writeln(*args, **kargs) 2777 2778 def push(self, testcase, host=None): 2779 if not testcase.mapping: 2780 assert(not testcase.parent and not testcase.testsuite) 2781 testcase.mapping = self.testcase.getMapping() 2782 testcase.testsuite = self.testcase.getTestSuite() 2783 testcase.parent = self.testcase 2784 self.testcases.append((self.testcase, self.config, self.host)) 2785 self.testcase = testcase 2786 self.config = self.driver.configs[self.testcase.getMapping()].cloneAndOverrideWith(self) 2787 self.host = host 2788 2789 def pop(self): 2790 assert(self.testcase) 2791 testcase = self.testcase 2792 (self.testcase, self.config, self.host) = self.testcases.pop() 2793 if testcase.parent and self.testcase != testcase: 2794 testcase.mapping = None 2795 testcase.testsuite = None 2796 testcase.parent = None 2797 2798 def createFile(self, path, lines, encoding=None): 2799 path = os.path.join(self.testsuite.getPath(), path.decode("utf-8") if isPython2 else path) 2800 with open(path, "w", encoding=encoding) if not isPython2 and encoding else open(path, "w") as file: 2801 for l in lines: 2802 file.write("%s\n" % l) 2803 self.files.append(path) 2804 2805 def mkdirs(self, dirs): 2806 for d in dirs if isinstance(dirs, list) else [dirs]: 2807 d = os.path.join(self.testsuite.getPath(), d) 2808 self.dirs.append(d) 2809 if not os.path.exists(d): 2810 os.makedirs(d) 2811 2812 def destroy(self): 2813 for d in self.dirs: 2814 if os.path.exists(d): shutil.rmtree(d) 2815 for f in self.files: 2816 if os.path.exists(f): os.unlink(f) 2817 2818 drivers = {} 2819 driver = "local" 2820 2821 @classmethod 2822 def add(self, name, driver, default=False): 2823 if default: 2824 Driver.driver = name 2825 self.driver = name 2826 self.drivers[name] = driver 2827 2828 @classmethod 2829 def getAll(self): 2830 return list(self.drivers.values()) 2831 2832 @classmethod 2833 def create(self, options, component): 2834 parseOptions(self, options) 2835 driver = self.drivers.get(self.driver) 2836 if not driver: 2837 raise RuntimeError("unknown driver `{0}'".format(self.driver)) 2838 return driver(options, component) 2839 2840 @classmethod 2841 def getSupportedArgs(self): 2842 return ("dlrR", ["debug", "driver=", "filter=", "rfilter=", "host=", "host-ipv6=", "host-bt=", "interface=", 2843 "controller-app", "valgrind", "languages=", "rlanguages="]) 2844 2845 @classmethod 2846 def usage(self): 2847 pass 2848 2849 @classmethod 2850 def commonUsage(self): 2851 print("") 2852 print("Driver options:") 2853 print("-d | --debug Verbose information.") 2854 print("--driver=<driver> Use the given driver (local, client, server or remote).") 2855 print("--filter=<regex> Run all the tests that match the given regex.") 2856 print("--rfilter=<regex> Run all the tests that do not match the given regex.") 2857 print("--languages=l1,l2,... List of comma-separated language mappings to test.") 2858 print("--rlanguages=l1,l2,.. List of comma-separated language mappings to not test.") 2859 print("--host=<addr> The IPv4 address to use for Ice.Default.Host.") 2860 print("--host-ipv6=<addr> The IPv6 address to use for Ice.Default.Host.") 2861 print("--host-bt=<addr> The Bluetooth address to use for Ice.Default.Host.") 2862 print("--interface=<IP> The multicast interface to use to discover controllers.") 2863 print("--controller-app Start the process controller application.") 2864 print("--valgrind Start executables with valgrind.") 2865 2866 def __init__(self, options, component): 2867 self.component = component 2868 self.debug = False 2869 self.filters = [] 2870 self.rfilters = [] 2871 self.host = "" 2872 self.hostIPv6 = "" 2873 self.hostBT = "" 2874 self.controllerApp = False 2875 self.valgrind = False 2876 self.languages = ",".join(os.environ.get("LANGUAGES", "").split(" ")) 2877 self.languages = [self.languages] if self.languages else [] 2878 self.rlanguages = [] 2879 self.failures = [] 2880 2881 parseOptions(self, options, { "d": "debug", 2882 "r" : "filters", 2883 "R" : "rfilters", 2884 "filter" : "filters", 2885 "rfilter" : "rfilters", 2886 "host-ipv6" : "hostIPv6", 2887 "host-bt" : "hostBT", 2888 "controller-app" : "controllerApp"}) 2889 2890 if self.languages: 2891 self.languages = [i for sublist in [l.split(",") for l in self.languages] for i in sublist] 2892 if self.rlanguages: 2893 self.rlanguages = [i for sublist in [l.split(",") for l in self.rlanguages] for i in sublist] 2894 2895 (self.filters, self.rfilters) = ([re.compile(a) for a in self.filters], [re.compile(a) for a in self.rfilters]) 2896 2897 self.communicator = None 2898 self.interface = "" 2899 self.processControllers = {} 2900 2901 def setConfigs(self, configs): 2902 self.configs = configs 2903 2904 def getFilters(self, mapping, config): 2905 # Return the driver and component filters 2906 (filters, rfilters) = self.component.getFilters(mapping, config) 2907 (filters, rfilters) = ([re.compile(a) for a in filters], [re.compile(a) for a in rfilters]) 2908 return (self.filters + filters, self.rfilters + rfilters) 2909 2910 def getHost(self, protocol, ipv6): 2911 if protocol == "bt": 2912 if not self.hostBT: 2913 raise RuntimeError("no Bluetooth address set with --host-bt") 2914 return self.hostBT 2915 elif ipv6: 2916 return self.hostIPv6 or "::1" 2917 else: 2918 return self.host or "127.0.0.1" 2919 2920 def getComponent(self): 2921 return self.component 2922 2923 def isWorkerThread(self): 2924 return False 2925 2926 def getTestEndpoint(self, portnum, protocol="default"): 2927 return "{0} -p {1}".format(protocol, self.getTestPort(portnum)) 2928 2929 def getTestPort(self, portnum): 2930 return 12010 + portnum 2931 2932 def getArgs(self, process, current): 2933 ### Return driver specific arguments 2934 return [] 2935 2936 def getProps(self, process, current): 2937 props = {} 2938 if isinstance(process, IceProcess): 2939 if not self.host: 2940 props["Ice.Default.Host"] = "0:0:0:0:0:0:0:1" if current.config.ipv6 else "127.0.0.1" 2941 else: 2942 props["Ice.Default.Host"] = self.host 2943 return props 2944 2945 def getMappings(self): 2946 ### Return additional mappings to load required by the driver 2947 return [] 2948 2949 def matchLanguage(self, language): 2950 if self.languages and language not in self.languages: 2951 return False 2952 if self.rlanguages and language in self.rlanguages: 2953 return False 2954 return True 2955 2956 def getCommunicator(self): 2957 self.initCommunicator() 2958 return self.communicator 2959 2960 def initCommunicator(self): 2961 if self.communicator: 2962 return 2963 2964 try: 2965 import Ice 2966 except ImportError: 2967 # Try to add the local Python build to the sys.path 2968 try: 2969 pythonMapping = Mapping.getByName("python") 2970 if pythonMapping: 2971 for p in pythonMapping.getPythonDirs(pythonMapping.getPath(), self.configs[pythonMapping]): 2972 sys.path.append(p) 2973 except RuntimeError: 2974 print("couldn't find IcePy, running these tests require it to be installed or built") 2975 2976 import Ice 2977 Ice.loadSlice(os.path.join(self.component.getSourceDir(), "scripts", "Controller.ice")) 2978 2979 initData = Ice.InitializationData() 2980 initData.properties = Ice.createProperties() 2981 2982 # Load IceSSL, this is useful to talk with WSS for JavaScript 2983 initData.properties.setProperty("Ice.Plugin.IceSSL", "IceSSL:createIceSSL") 2984 initData.properties.setProperty("IceSSL.DefaultDir", os.path.join(self.component.getSourceDir(), "certs")) 2985 initData.properties.setProperty("IceSSL.CertFile", "server.p12") 2986 initData.properties.setProperty("IceSSL.Password", "password") 2987 initData.properties.setProperty("IceSSL.Keychain", "test.keychain") 2988 initData.properties.setProperty("IceSSL.KeychainPassword", "password") 2989 initData.properties.setProperty("IceSSL.VerifyPeer", "0"); 2990 2991 initData.properties.setProperty("Ice.Plugin.IceDiscovery", "IceDiscovery:createIceDiscovery") 2992 initData.properties.setProperty("IceDiscovery.DomainId", "TestController") 2993 initData.properties.setProperty("IceDiscovery.Interface", self.interface) 2994 initData.properties.setProperty("Ice.Default.Host", self.interface) 2995 initData.properties.setProperty("Ice.ThreadPool.Server.Size", "10") 2996 # initData.properties.setProperty("Ice.Trace.Protocol", "1") 2997 # initData.properties.setProperty("Ice.Trace.Network", "3") 2998 # initData.properties.setProperty("Ice.StdErr", "allTests.log") 2999 initData.properties.setProperty("Ice.Override.Timeout", "10000") 3000 initData.properties.setProperty("Ice.Override.ConnectTimeout", "1000") 3001 self.communicator = Ice.initialize(initData) 3002 3003 def getProcessController(self, current, process=None): 3004 processController = None 3005 if current.config.buildPlatform == "iphonesimulator": 3006 processController = iOSSimulatorProcessController 3007 elif current.config.buildPlatform == "iphoneos": 3008 processController = iOSDeviceProcessController 3009 elif current.config.uwp: 3010 # No SSL server-side support in UWP. 3011 if current.config.protocol in ["ssl", "wss"] and not isinstance(process, Client): 3012 processController = LocalProcessController 3013 else: 3014 processController = UWPProcessController 3015 elif process and current.config.browser and isinstance(process.getMapping(current), JavaScriptMixin): 3016 processController = BrowserProcessController 3017 elif process and current.config.android: 3018 processController = AndroidProcessController 3019 else: 3020 processController = LocalProcessController 3021 3022 if processController in self.processControllers: 3023 return self.processControllers[processController] 3024 3025 # Instantiate the controller 3026 self.processControllers[processController] = processController(current) 3027 return self.processControllers[processController] 3028 3029 def getProcessProps(self, current, ready, readyCount): 3030 props = {} 3031 if ready or readyCount > 0: 3032 if current.config.buildPlatform not in ["iphonesimulator", "iphoneos"]: 3033 props["Ice.PrintAdapterReady"] = 1 3034 return props 3035 3036 def destroy(self): 3037 for controller in self.processControllers.values(): 3038 controller.destroy(self) 3039 3040 if self.communicator: 3041 self.communicator.destroy() 3042 3043class CppMapping(Mapping): 3044 3045 class Config(Mapping.Config): 3046 3047 @classmethod 3048 def getSupportedArgs(self): 3049 return ("", ["cpp-config=", "cpp-platform=", "cpp-path=", "uwp", "openssl"]) 3050 3051 @classmethod 3052 def usage(self): 3053 print("") 3054 print("C++ Mapping options:") 3055 print("--cpp-path=<path> Path of alternate source tree for the C++ mapping.") 3056 print("--cpp-config=<config> C++ build configuration for native executables (overrides --config).") 3057 print("--cpp-platform=<platform> C++ build platform for native executables (overrides --platform).") 3058 print("--uwp Run UWP (Universal Windows Platform).") 3059 print("--openssl Run SSL tests with OpenSSL instead of the default platform SSL engine.") 3060 3061 def __init__(self, options=[]): 3062 Mapping.Config.__init__(self, options) 3063 3064 # Derive from the build config the cpp11 option. This is used by canRun to allow filtering 3065 # tests on the cpp11 value in the testcase options specification 3066 self.cpp11 = self.buildConfig.lower().find("cpp11") >= 0 3067 3068 parseOptions(self, options, { "cpp-config" : "buildConfig", 3069 "cpp-platform" : "buildPlatform", 3070 "cpp-path" : "pathOverride" }) 3071 3072 if self.pathOverride: 3073 self.pathOverride = os.path.abspath(self.pathOverride) 3074 3075 def getOptions(self, current): 3076 return { "compress" : [False] } if current.config.uwp else {} 3077 3078 def getProps(self, process, current): 3079 props = Mapping.getProps(self, process, current) 3080 if isinstance(process, IceProcess): 3081 props["Ice.NullHandleAbort"] = True 3082 props["Ice.PrintStackTraces"] = "1" 3083 return props 3084 3085 def getSSLProps(self, process, current): 3086 props = Mapping.getSSLProps(self, process, current) 3087 server = isinstance(process, Server) 3088 uwp = current.config.uwp 3089 3090 props.update({ 3091 "IceSSL.CAs": "cacert.pem", 3092 "IceSSL.CertFile": "server.p12" if server else "ms-appx:///client.p12" if uwp else "client.p12" 3093 }) 3094 if isinstance(platform, Darwin): 3095 props.update({ 3096 "IceSSL.KeychainPassword" : "password", 3097 "IceSSL.Keychain": "server.keychain" if server else "client.keychain" 3098 }) 3099 return props 3100 3101 def getPluginEntryPoint(self, plugin, process, current): 3102 return { 3103 "IceSSL" : "IceSSLOpenSSL:createIceSSLOpenSSL" if current.config.openssl else "IceSSL:createIceSSL", 3104 "IceBT" : "IceBT:createIceBT", 3105 "IceDiscovery" : "IceDiscovery:createIceDiscovery", 3106 "IceLocatorDiscovery" : "IceLocatorDiscovery:createIceLocatorDiscovery" 3107 }[plugin] 3108 3109 def getEnv(self, process, current): 3110 # 3111 # On Windows, add the testcommon directories to the PATH 3112 # 3113 libPaths = [] 3114 if isinstance(platform, Windows): 3115 testcommon = os.path.join(self.path, "test", "Common") 3116 if os.path.exists(testcommon): 3117 libPaths.append(os.path.join(testcommon, self.getBuildDir("testcommon", current))) 3118 3119 # 3120 # On most platforms, we also need to add the library directory to the library path environment variable. 3121 # 3122 if not isinstance(platform, Darwin): 3123 libPaths.append(self.component.getLibDir(process, self, current)) 3124 3125 # 3126 # Add the test suite library directories to the platform library path environment variable. 3127 # 3128 if current.testcase: 3129 for d in set([current.getBuildDir(d) for d in current.testcase.getTestSuite().getLibDirs()]): 3130 libPaths.append(d) 3131 3132 env = {} 3133 if len(libPaths) > 0: 3134 env[platform.getLdPathEnvName()] = os.pathsep.join(libPaths) 3135 return env 3136 3137 def _getDefaultSource(self, processType): 3138 return { 3139 "client" : "Client.cpp", 3140 "server" : "Server.cpp", 3141 "serveramd" : "ServerAMD.cpp", 3142 "collocated" : "Collocated.cpp", 3143 "subscriber" : "Subscriber.cpp", 3144 "publisher" : "Publisher.cpp", 3145 }[processType] 3146 3147 def _getDefaultExe(self, processType): 3148 return Mapping._getDefaultExe(self, processType).lower() 3149 3150 def getUWPPackageName(self): 3151 return "ice-uwp-controller.cpp" 3152 3153 def getUWPUserModelId(self): 3154 return "ice-uwp-controller.cpp_3qjctahehqazm" 3155 3156 def getUWPPackageFullName(self, platform): 3157 return "{0}_1.0.0.0_{1}__3qjctahehqazm".format(self.getUWPPackageName(), 3158 "x86" if platform == "Win32" else platform) 3159 3160 def getUWPPackageFullPath(self, platform, config): 3161 prefix = "controller_1.0.0.0_{0}{1}".format(platform, "_{0}".format(config) if config == "Debug" else "") 3162 return os.path.join(self.component.getSourceDir(), "cpp", "msbuild", "AppPackages", "controller", 3163 "{0}_Test".format(prefix), "{0}.appx".format(prefix)) 3164 3165 def getIOSControllerIdentity(self, current): 3166 category = "iPhoneSimulator" if current.config.buildPlatform == "iphonesimulator" else "iPhoneOS" 3167 mapping = "Cpp11" if current.config.cpp11 else "Cpp98" 3168 return "{0}/com.zeroc.{1}-Test-Controller".format(category, mapping) 3169 3170 def getIOSAppFullPath(self, current): 3171 appName = "C++11 Test Controller.app" if current.config.cpp11 else "C++98 Test Controller.app" 3172 path = os.path.join(self.component.getTestDir(self), "ios", "controller") 3173 path = os.path.join(path, "build-{0}-{1}".format(current.config.buildPlatform, current.config.buildConfig)) 3174 build = "Debug" if os.path.exists(os.path.join(path, "Debug-{0}".format(current.config.buildPlatform))) else "Release" 3175 return os.path.join(path, "{0}-{1}".format(build, current.config.buildPlatform), appName) 3176 3177class JavaMapping(Mapping): 3178 3179 class Config(Mapping.Config): 3180 3181 @classmethod 3182 def getSupportedArgs(self): 3183 return ("", ["device=", "avd=", "android"]) 3184 3185 @classmethod 3186 def usage(self): 3187 print("") 3188 print("Java Mapping options:") 3189 print("--android Run the Android tests.") 3190 print("--device=<device-id> ID of the Android emulator or device used to run the tests.") 3191 print("--avd=<name> Start specific Android Virtual Device.") 3192 3193 def getCommandLine(self, current, process, exe, args): 3194 javaHome = os.getenv("JAVA_HOME", "") 3195 java = os.path.join(javaHome, "bin", "java") if javaHome else "java" 3196 javaArgs = self.getJavaArgs(process, current) 3197 if process.isFromBinDir(): 3198 if javaArgs: 3199 return "{0} -ea {1} {2} {3}".format(java, " ".join(javaArgs), exe, args) 3200 else: 3201 return "{0} -ea {1} {2}".format(java, exe, args) 3202 3203 testdir = self.component.getTestDir(self) 3204 assert(current.testcase.getPath(current).startswith(testdir)) 3205 package = "test." + current.testcase.getPath(current)[len(testdir) + 1:].replace(os.sep, ".") 3206 javaArgs = self.getJavaArgs(process, current) 3207 if javaArgs: 3208 return "{0} -ea {1} -Dtest.class={2}.{3} test.TestDriver {4}".format(java, " ".join(javaArgs), package, exe, args) 3209 else: 3210 return "{0} -ea -Dtest.class={1}.{2} test.TestDriver {3}".format(java, package, exe, args) 3211 3212 def getJavaArgs(self, process, current): 3213 # TODO: WORKAROUND for https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=911925 3214 if isinstance(platform, Linux) and platform.getLinuxId() in ["debian", "ubuntu"]: 3215 return ["-Djdk.net.URLClassPath.disableClassPathURLCheck=true"] 3216 return [] 3217 3218 def getSSLProps(self, process, current): 3219 props = Mapping.getSSLProps(self, process, current) 3220 if current.config.android: 3221 props.update({ 3222 "IceSSL.KeystoreType" : "BKS", 3223 "IceSSL.TruststoreType" : "BKS", 3224 "Ice.InitPlugins" : "0", 3225 "IceSSL.Keystore": "server.bks" if isinstance(process, Server) else "client.bks" 3226 }) 3227 else: 3228 props.update({ 3229 "IceSSL.Keystore": "server.jks" if isinstance(process, Server) else "client.jks", 3230 }) 3231 return props 3232 3233 def getPluginEntryPoint(self, plugin, process, current): 3234 return { 3235 "IceSSL" : "com.zeroc.IceSSL.PluginFactory", 3236 "IceBT" : "com.zeroc.IceBT.PluginFactory", 3237 "IceDiscovery" : "com.zeroc.IceDiscovery.PluginFactory", 3238 "IceLocatorDiscovery" : "com.zeroc.IceLocatorDiscovery.PluginFactory" 3239 }[plugin] 3240 3241 def getEnv(self, process, current): 3242 return { "CLASSPATH" : os.path.join(self.path, "lib", "test.jar") } 3243 3244 def _getDefaultSource(self, processType): 3245 return { 3246 "client" : "Client.java", 3247 "server" : "Server.java", 3248 "serveramd" : "AMDServer.java", 3249 "servertie" : "TieServer.java", 3250 "serveramdtie" : "AMDTieServer.java", 3251 "collocated" : "Collocated.java", 3252 }[processType] 3253 3254 def getSDKPackage(self): 3255 return "system-images;android-27;google_apis;x86" 3256 3257 def getApk(self, current): 3258 return os.path.join(self.getPath(), "test", "android", "controller", "build", "outputs", "apk", "debug", 3259 "controller-debug.apk") 3260 3261 def getActivityName(self): 3262 return "com.zeroc.testcontroller/.ControllerActivity" 3263 3264class JavaCompatMapping(JavaMapping): 3265 3266 def getPluginEntryPoint(self, plugin, process, current): 3267 return { 3268 "IceSSL" : "IceSSL.PluginFactory", 3269 "IceBT" : "IceBT.PluginFactory", 3270 "IceDiscovery" : "IceDiscovery.PluginFactory", 3271 "IceLocatorDiscovery" : "IceLocatorDiscovery.PluginFactory" 3272 }[plugin] 3273 3274 def getEnv(self, process, current): 3275 classPath = [os.path.join(self.path, "lib", "test.jar")] 3276 if os.path.exists(os.path.join(self.path, "lib", "IceTestLambda.jar")): 3277 classPath += [os.path.join(self.path, "lib", "IceTestLambda.jar")] 3278 return { "CLASSPATH" : os.pathsep.join(classPath) } 3279 3280 def getSDKPackage(self): 3281 return "system-images;android-21;google_apis;x86_64" 3282 3283class CSharpMapping(Mapping): 3284 3285 class Config(Mapping.Config): 3286 3287 @classmethod 3288 def getSupportedArgs(self): 3289 return ("", ["dotnetcore", "framework="]) 3290 3291 @classmethod 3292 def usage(self): 3293 print("") 3294 print("--dotnetcore Run C# tests using .NET Core") 3295 print("--framework=<TargetFramework> Choose the framework used to run .NET tests") 3296 3297 def __init__(self, options=[]): 3298 Mapping.Config.__init__(self, options) 3299 3300 if not self.dotnetcore and not isinstance(platform, Windows): 3301 self.dotnetcore = True 3302 3303 if self.dotnetcore: 3304 self.libTargetFramework = "netstandard2.0" 3305 self.binTargetFramework = "netcoreapp2.1" if self.framework == "" else self.framework 3306 self.testTargetFramework = "netcoreapp2.1" if self.framework == "" else self.framework 3307 else: 3308 self.libTargetFramework = "net45" if self.framework == "" else "netstandard2.0" 3309 self.binTargetFramework = "net45" if self.framework == "" else self.framework 3310 self.testTargetFramework = "net45" if self.framework == "" else self.framework 3311 3312 # Set Xamarin flag if UWP/iOS or Android testing flag is also specified 3313 if self.uwp or self.android or "iphone" in self.buildPlatform: 3314 self.xamarin = True 3315 3316 def getBinTargetFramework(self, current): 3317 return current.config.binTargetFramework 3318 3319 def getLibTargetFramework(self, current): 3320 return current.config.libTargetFramework 3321 3322 def getTargetFramework(self, current): 3323 return current.config.testTargetFramework 3324 3325 def getBuildDir(self, name, current): 3326 if current.config.dotnetcore or current.config.framework != "": 3327 return os.path.join("msbuild", name, "netstandard2.0", self.getTargetFramework(current)) 3328 else: 3329 return os.path.join("msbuild", name, self.getTargetFramework(current)) 3330 3331 def getProps(self, process, current): 3332 props = Mapping.getProps(self, process, current) 3333 if current.config.xamarin: 3334 # 3335 # With SSL we need to delay the creation of the admin adapter until the plug-in has 3336 # been initialized. 3337 # 3338 if current.config.protocol in ["ssl", "wss"] and current.config.mx: 3339 props["Ice.Admin.DelayCreation"] = "1" 3340 return props 3341 3342 def getOptions(self, current): 3343 if current.config.xamarin and current.config.uwp: 3344 # 3345 # Do not run MX tests with SSL it cause problems with Xamarin UWP implementation 3346 # 3347 return {"mx" : ["False"]} if current.config.protocol in ["ssl", "wss"] else {} 3348 else: 3349 return {} 3350 3351 def getSSLProps(self, process, current): 3352 props = Mapping.getSSLProps(self, process, current) 3353 props.update({ 3354 "IceSSL.Password": "password", 3355 "IceSSL.DefaultDir": os.path.join(self.component.getSourceDir(), "certs"), 3356 "IceSSL.CAs": "cacert.pem", 3357 "IceSSL.VerifyPeer": "0" if current.config.protocol == "wss" else "2", 3358 "IceSSL.CertFile": "server.p12" if isinstance(process, Server) else "client.p12", 3359 }) 3360 if current.config.xamarin: 3361 props["Ice.InitPlugins"] = 0 3362 props["IceSSL.CAs"] = "cacert.der"; 3363 return props 3364 3365 def getPluginEntryPoint(self, plugin, process, current): 3366 if current.config.xamarin: 3367 plugindir = "" 3368 else: 3369 plugindir = self.component.getLibDir(process, self, current) 3370 3371 # 3372 # If the plug-in assembly exists in the test directory, this is a good indication that the 3373 # test include a reference to the plug-in, in this case we must use the test dir as the 3374 # plug-in base directory to avoid loading two instances of the same assembly. 3375 # 3376 proccessType = current.testcase.getProcessType(process) 3377 if proccessType: 3378 testdir = os.path.join(current.testcase.getPath(current), self.getBuildDir(proccessType, current)) 3379 if os.path.isfile(os.path.join(testdir, plugin + ".dll")): 3380 plugindir = testdir 3381 plugindir += os.sep 3382 3383 return { 3384 "IceSSL" : plugindir + "IceSSL.dll:IceSSL.PluginFactory", 3385 "IceDiscovery" : plugindir + "IceDiscovery.dll:IceDiscovery.PluginFactory", 3386 "IceLocatorDiscovery" : plugindir + "IceLocatorDiscovery.dll:IceLocatorDiscovery.PluginFactory" 3387 }[plugin] 3388 3389 def getEnv(self, process, current): 3390 env = {} 3391 if isinstance(platform, Windows): 3392 if self.component.useBinDist(self, current): 3393 env['PATH'] = self.component.getBinDir(process, self, current) 3394 else: 3395 env['PATH'] = os.path.join(self.component.getSourceDir(), "cpp", "msbuild", "packages", 3396 "bzip2.{0}.1.0.6.10".format(platform.getPlatformToolset()), 3397 "build", "native", "bin", "x64", "Release") 3398 if not current.config.dotnetcore: 3399 env['DEVPATH'] = self.component.getLibDir(process, self, current) 3400 return env 3401 3402 def _getDefaultSource(self, processType): 3403 return { 3404 "client" : "Client.cs", 3405 "server" : "Server.cs", 3406 "serveramd" : "ServerAMD.cs", 3407 "servertie" : "ServerTie.cs", 3408 "serveramdtie" : "ServerAMDTie.cs", 3409 "collocated" : "Collocated.cs", 3410 }[processType] 3411 3412 def _getDefaultExe(self, processType): 3413 return Mapping._getDefaultExe(self, processType).lower() 3414 3415 def getCommandLine(self, current, process, exe, args): 3416 if process.isFromBinDir(): 3417 path = self.component.getBinDir(process, self, current) 3418 else: 3419 path = os.path.join(current.testcase.getPath(current), current.getBuildDir(exe)) 3420 3421 if current.config.dotnetcore: 3422 return "dotnet " + os.path.join(path, exe) + ".dll " + args 3423 else: 3424 return os.path.join(path, exe) + ".exe " + args 3425 3426 def getSDKPackage(self): 3427 return "system-images;android-27;google_apis;x86" 3428 3429 def getApk(self, current): 3430 return os.path.join(self.getPath(), "test", "xamarin", "controller.Android", "bin", current.config.buildConfig, 3431 "com.zeroc.testcontroller-Signed.apk") 3432 3433 def getActivityName(self): 3434 return "com.zeroc.testcontroller/controller.MainActivity" 3435 3436 def getUWPPackageName(self): 3437 return "ice-uwp-controller.xamarin" 3438 3439 def getUWPUserModelId(self): 3440 return "ice-uwp-controller.xamarin_3qjctahehqazm" 3441 3442 def getUWPPackageFullName(self, platform): 3443 return "{0}_1.0.0.0_{1}__3qjctahehqazm".format(self.getUWPPackageName(), platform) 3444 3445 def getUWPPackageFullPath(self, platform, config): 3446 prefix = "controller.UWP_1.0.0.0_{0}{1}".format(platform, "_{0}".format(config) if config == "Debug" else "") 3447 return os.path.join(self.getPath(), "test", "xamarin", "controller.UWP", "AppPackages", 3448 "{0}_Test".format(prefix), "{0}.appx".format(prefix)) 3449 3450 def getIOSControllerIdentity(self, current): 3451 if current.config.buildPlatform == "iphonesimulator": 3452 return "iPhoneSimulator/com.zeroc.Xamarin-Test-Controller" 3453 else: 3454 return "iPhoneOS/com.zeroc.Xamarin-Test-Controller" 3455 3456 def getIOSAppFullPath(self, current): 3457 return os.path.join(self.getPath(), "test", "xamarin", "controller.iOS", "bin", "iPhoneSimulator", 3458 current.config.buildConfig, "controller.iOS.app") 3459 3460class CppBasedMapping(Mapping): 3461 3462 class Config(Mapping.Config): 3463 3464 @classmethod 3465 def getSupportedArgs(self): 3466 return ("", [self.mappingName + "-config=", self.mappingName + "-platform=", "openssl"]) 3467 3468 @classmethod 3469 def usage(self): 3470 print("") 3471 print(self.mappingDesc + " mapping options:") 3472 print("--{0}-config=<config> {1} build configuration for native executables (overrides --config)." 3473 .format(self.mappingName, self.mappingDesc)) 3474 print("--{0}-platform=<platform> {1} build platform for native executables (overrides --platform)." 3475 .format(self.mappingName, self.mappingDesc)) 3476 print("--openssl Run SSL tests with OpenSSL instead of the default platform SSL engine.") 3477 3478 def __init__(self, options=[]): 3479 Mapping.Config.__init__(self, options) 3480 parseOptions(self, options, 3481 { self.mappingName + "-config" : "buildConfig", 3482 self.mappingName + "-platform" : "buildPlatform" }) 3483 3484 def getSSLProps(self, process, current): 3485 return Mapping.getByName("cpp").getSSLProps(process, current) 3486 3487 def getPluginEntryPoint(self, plugin, process, current): 3488 return Mapping.getByName("cpp").getPluginEntryPoint(plugin, process, current) 3489 3490 def getEnv(self, process, current): 3491 env = Mapping.getEnv(self, process, current) 3492 if self.component.getInstallDir(self, current) != platform.getInstallDir(): 3493 # If not installed in the default platform installation directory, add 3494 # the C++ library directory to the library path 3495 env[platform.getLdPathEnvName()] = self.component.getLibDir(process, Mapping.getByName("cpp"), current) 3496 return env 3497 3498class ObjCMapping(CppBasedMapping): 3499 3500 def getTestSuites(self, ids=[]): 3501 return Mapping.getTestSuites(self, ids) if isinstance(platform, Darwin) else [] 3502 3503 def findTestSuite(self, testsuite): 3504 return Mapping.findTestSuite(self, testsuite) if isinstance(platform, Darwin) else None 3505 3506 class Config(CppBasedMapping.Config): 3507 mappingName = "objc" 3508 mappingDesc = "Objective-C" 3509 3510 def __init__(self, options=[]): 3511 Mapping.Config.__init__(self, options) 3512 self.arc = self.buildConfig.lower().find("arc") >= 0 3513 3514 def _getDefaultSource(self, processType): 3515 return { 3516 "client" : "Client.m", 3517 "server" : "Server.m", 3518 "collocated" : "Collocated.m", 3519 }[processType] 3520 3521 def _getDefaultExe(self, processType): 3522 return Mapping._getDefaultExe(self, processType).lower() 3523 3524 def getIOSControllerIdentity(self, current): 3525 category = "iPhoneSimulator" if current.config.buildPlatform == "iphonesimulator" else "iPhoneOS" 3526 mapping = "ObjC-ARC" if current.config.arc else "ObjC" 3527 return "{0}/com.zeroc.{1}-Test-Controller".format(category, mapping) 3528 3529 def getIOSAppFullPath(self, current): 3530 appName = "Objective-C ARC Test Controller.app" if current.config.arc else "Objective-C Test Controller.app" 3531 path = os.path.join(self.component.getTestDir(self), "ios", "controller") 3532 path = os.path.join(path, "build-{0}-{1}".format(current.config.buildPlatform, current.config.buildConfig)) 3533 build = "Debug" if os.path.exists(os.path.join(path, "Debug-{0}".format(current.config.buildPlatform))) else "Release" 3534 return os.path.join(path, "{0}-{1}".format(build, current.config.buildPlatform), appName) 3535 3536class PythonMapping(CppBasedMapping): 3537 3538 class Config(CppBasedMapping.Config): 3539 mappingName = "python" 3540 mappingDesc = "Python" 3541 3542 def getCommandLine(self, current, process, exe, args): 3543 return "\"{0}\" {1} {2} {3}".format(sys.executable, 3544 os.path.join(self.path, "test", "TestHelper.py"), 3545 exe, 3546 args) 3547 3548 def getEnv(self, process, current): 3549 env = CppBasedMapping.getEnv(self, process, current) 3550 dirs = [] 3551 if self.component.getInstallDir(self, current) != platform.getInstallDir(): 3552 # If not installed in the default platform installation directory, add 3553 # the Ice python directory to PYTHONPATH 3554 dirs += self.getPythonDirs(self.component.getInstallDir(self, current), current.config) 3555 dirs += [current.testcase.getPath(current)] 3556 env["PYTHONPATH"] = os.pathsep.join(dirs) 3557 return env 3558 3559 def getPythonDirs(self, iceDir, config): 3560 dirs = [] 3561 if isinstance(platform, Windows): 3562 dirs.append(os.path.join(iceDir, "python", config.buildPlatform, config.buildConfig)) 3563 dirs.append(os.path.join(iceDir, "python")) 3564 return dirs 3565 3566 def _getDefaultSource(self, processType): 3567 return { 3568 "client" : "Client.py", 3569 "server" : "Server.py", 3570 "serveramd" : "ServerAMD.py", 3571 "collocated" : "Collocated.py", 3572 }[processType] 3573 3574class CppBasedClientMapping(CppBasedMapping): 3575 3576 def loadTestSuites(self, tests, config, filters, rfilters): 3577 Mapping.loadTestSuites(self, tests, config, filters, rfilters) 3578 self.getServerMapping().loadTestSuites(self.testsuites.keys(), config) 3579 3580 def getServerMapping(self, testId=None): 3581 return Mapping.getByName("cpp") # By default, run clients against C++ mapping executables 3582 3583class RubyMapping(CppBasedClientMapping): 3584 3585 class Config(CppBasedClientMapping.Config): 3586 mappingName = "ruby" 3587 mappingDesc = "Ruby" 3588 3589 def getCommandLine(self, current, process, exe, args): 3590 return "ruby {0} {1} {2}".format(os.path.join(self.path, "test", "TestHelper.rb"), exe, args) 3591 3592 def getEnv(self, process, current): 3593 env = CppBasedMapping.getEnv(self, process, current) 3594 dirs = [] 3595 if self.component.getInstallDir(self, current) != platform.getInstallDir(): 3596 # If not installed in the default platform installation directory, add 3597 # the Ice ruby directory to RUBYLIB 3598 dirs += [os.path.join(self.path, "ruby")] 3599 dirs += [current.testcase.getPath(current)] 3600 env["RUBYLIB"] = os.pathsep.join(dirs) 3601 return env 3602 3603 def _getDefaultSource(self, processType): 3604 return { "client" : "Client.rb" }[processType] 3605 3606class PhpMapping(CppBasedClientMapping): 3607 3608 class Config(CppBasedClientMapping.Config): 3609 mappingName = "php" 3610 mappingDesc = "PHP" 3611 3612 @classmethod 3613 def getSupportedArgs(self): 3614 return ("", ["php-version="]) 3615 3616 @classmethod 3617 def usage(self): 3618 print("") 3619 print("PHP Mapping options:") 3620 print("--php-version=[7.1|7.2|7.3] PHP Version used for Windows builds") 3621 3622 3623 def __init__(self, options=[]): 3624 CppBasedClientMapping.Config.__init__(self, options) 3625 parseOptions(self, options, { "php-version" : "phpVersion" }) 3626 3627 def getCommandLine(self, current, process, exe, args): 3628 phpArgs = [] 3629 php = "php" 3630 3631 # 3632 # On Windows, when using a source distribution use the php executable from 3633 # the Nuget PHP dependency. 3634 # 3635 if isinstance(platform, Windows) and not self.component.useBinDist(self, current): 3636 nugetVersions = { 3637 "7.1": "7.1.17", 3638 "7.2": "7.2.8", 3639 "7.3": "7.3.0" 3640 } 3641 nugetVersion = nugetVersions[current.config.phpVersion] 3642 threadSafe = current.driver.configs[self].buildConfig in ["Debug", "Release"] 3643 buildPlatform = current.driver.configs[self].buildPlatform 3644 buildConfig = "Debug" if current.driver.configs[self].buildConfig.find("Debug") >= 0 else "Release" 3645 packageName = "php-{0}-{1}.{2}".format(current.config.phpVersion, "ts" if threadSafe else "nts", nugetVersion) 3646 php = os.path.join(self.path, "msbuild", "packages", packageName, "build", "native", "bin", 3647 buildPlatform, buildConfig, "php.exe") 3648 3649 # 3650 # If Ice is not installed in the system directory, specify its location with PHP 3651 # configuration arguments. 3652 # 3653 if isinstance(platform, Windows) and not self.component.useBinDist(self, current) or \ 3654 platform.getInstallDir() and self.component.getInstallDir(self, current) != platform.getInstallDir(): 3655 phpArgs += ["-n"] # Do not load any php.ini files 3656 phpArgs += ["-d", "extension_dir='{0}'".format(self.component.getLibDir(process, self, current))] 3657 phpArgs += ["-d", "extension='{0}'".format(self.component.getPhpExtension(self, current))] 3658 phpArgs += ["-d", "include_path='{0}'".format(self.component.getPhpIncludePath(self, current))] 3659 3660 if hasattr(process, "getPhpArgs"): 3661 phpArgs += process.getPhpArgs(current) 3662 3663 return "{0} {1} -f {2} -- {3} {4}".format(php, 3664 " ".join(phpArgs), 3665 os.path.join(self.path, "test", "TestHelper.php"), 3666 exe, 3667 args) 3668 3669 def _getDefaultSource(self, processType): 3670 return { "client" : "Client.php" }[processType] 3671 3672class MatlabMapping(CppBasedClientMapping): 3673 3674 class Config(CppBasedClientMapping.Config): 3675 mappingName = "matlab" 3676 mappingDesc = "MATLAB" 3677 3678 def getCommandLine(self, current, process, exe, args): 3679 return "matlab -nodesktop -nosplash -wait -log -minimize -r \"cd '{0}', runTest {1} {2} {3}\"".format( 3680 os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "matlab", "test", "lib")), 3681 self.getTestCwd(process, current), 3682 os.path.join(current.config.buildPlatform, current.config.buildConfig), 3683 args) 3684 3685 def getServerMapping(self, testId=None): 3686 return Mapping.getByName("python") # Run clients against Python mapping servers 3687 3688 def _getDefaultSource(self, processType): 3689 return { "client" : "client.m" }[processType] 3690 3691 def getOptions(self, current): 3692 # 3693 # Metrics tests configuration not supported with MATLAB they use the Admin adapter. 3694 # 3695 options = CppBasedClientMapping.getOptions(self, current) 3696 options["mx"] = [ False ] 3697 return options 3698 3699 3700class JavaScriptMixin(): 3701 3702 def loadTestSuites(self, tests, config, filters, rfilters): 3703 Mapping.loadTestSuites(self, tests, config, filters, rfilters) 3704 self.getServerMapping().loadTestSuites(list(self.testsuites.keys()) + ["Ice/echo"], config) 3705 3706 def getServerMapping(self, testId=None): 3707 if testId and self.hasSource(testId, "server"): 3708 return self 3709 else: 3710 return Mapping.getByName("cpp") # Run clients against C++ mapping servers if no JS server provided 3711 3712 def _getDefaultProcesses(self, processType): 3713 if processType.startswith("server"): 3714 return [EchoServer(), Server()] 3715 return Mapping._getDefaultProcesses(self, processType) 3716 3717 def getCommonDir(self, current): 3718 return os.path.join(self.getPath(), "test", "Common") 3719 3720 def getCommandLine(self, current, process, exe, args): 3721 return "node {0}/run.js {1} {2}".format(self.getCommonDir(current), exe, args) 3722 3723 def getEnv(self, process, current): 3724 env = Mapping.getEnv(self, process, current) 3725 env["NODE_PATH"] = os.pathsep.join([self.getCommonDir(current), self.getTestCwd(process, current)]) 3726 return env 3727 3728 def getSSLProps(self, process, current): 3729 return {} 3730 3731 def getOptions(self, current): 3732 options = { 3733 "protocol" : ["ws", "wss"] if current.config.browser else ["tcp"], 3734 "compress" : [False], 3735 "ipv6" : [False], 3736 "serialize" : [False], 3737 "mx" : [False], 3738 } 3739 return options 3740 3741 3742class JavaScriptMapping(JavaScriptMixin,Mapping): 3743 3744 class Config(Mapping.Config): 3745 3746 @classmethod 3747 def getSupportedArgs(self): 3748 return ("", ["es5", "browser=", "worker"]) 3749 3750 @classmethod 3751 def usage(self): 3752 print("") 3753 print("JavaScript mapping options:") 3754 print("--es5 Use JavaScript ES5 (Babel compiled code).") 3755 print("--browser=<name> Run with the given browser.") 3756 print("--worker Run with Web workers enabled.") 3757 3758 def __init__(self, options=[]): 3759 Mapping.Config.__init__(self, options) 3760 3761 if self.browser and self.protocol == "tcp": 3762 self.protocol = "ws" 3763 3764 # Ie only support ES5 for now 3765 if self.browser in ["Ie"]: 3766 self.es5 = True 3767 3768 def getCommonDir(self, current): 3769 if current.config.es5: 3770 return os.path.join(self.getPath(), "test", "es5", "Common") 3771 else: 3772 return os.path.join(self.getPath(), "test", "Common") 3773 3774 def _getDefaultSource(self, processType): 3775 return { "client" : "Client.js", "serveramd" : "ServerAMD.js", "server" : "Server.js" }[processType] 3776 3777 def getTestCwd(self, process, current): 3778 if current.config.es5: 3779 # Change to the ES5 test directory if testing ES5 3780 return os.path.join(self.path, "test", "es5", current.testcase.getTestSuite().getId()) 3781 else: 3782 return os.path.join(self.path, "test", current.testcase.getTestSuite().getId()) 3783 3784 def getOptions(self, current): 3785 options = JavaScriptMixin.getOptions(self, current) 3786 options.update({ 3787 "es5" : [True] if current.config.es5 else [False, True], 3788 "worker" : [False, True] if current.config.browser and current.config.browser != "Ie" else [False], 3789 }) 3790 return options 3791 3792class TypeScriptMapping(JavaScriptMixin,Mapping): 3793 3794 class Config(Mapping.Config): 3795 3796 def canRun(self, testId, current): 3797 return Mapping.Config.canRun(self, testId, current) and self.browser != "Ie" # IE doesn't support ES6 3798 3799 def _getDefaultSource(self, processType): 3800 return { "client" : "Client.ts", "serveramd" : "ServerAMD.ts", "server" : "Server.ts" }[processType] 3801 3802# 3803# Instantiate platform global variable 3804# 3805platform = None 3806if sys.platform == "darwin": 3807 platform = Darwin() 3808elif sys.platform.startswith("aix"): 3809 platform = AIX() 3810elif sys.platform.startswith("freebsd") or sys.platform.startswith("dragonfly"): 3811 platform = FreeBSD() 3812elif sys.platform.startswith("linux") or sys.platform.startswith("gnukfreebsd"): 3813 platform = Linux() 3814elif sys.platform == "win32" or sys.platform[:6] == "cygwin": 3815 platform = Windows() 3816if not platform: 3817 print("can't run on unknown platform `{0}'".format(sys.platform)) 3818 sys.exit(1) 3819 3820# 3821# Import component classes and instantiate the default component 3822# 3823from Component import * 3824 3825# 3826# Initialize the platform with component 3827# 3828platform.init(component) 3829 3830# 3831# Import local driver 3832# 3833from LocalDriver import * 3834 3835def runTestsWithPath(path): 3836 mappings = Mapping.getAllByPath(path) 3837 if not mappings: 3838 print("couldn't find mapping for `{0}' (is this mapping supported on this platform?)".format(path)) 3839 sys.exit(0) 3840 runTests(mappings) 3841 3842def runTests(mappings=None, drivers=None): 3843 if not mappings: 3844 mappings = Mapping.getAll() 3845 if not drivers: 3846 drivers = Driver.getAll() 3847 3848 def usage(): 3849 print("Usage: " + sys.argv[0] + " [options] [tests]") 3850 print("") 3851 print("Options:") 3852 print("-h | --help Show this message") 3853 3854 Driver.commonUsage() 3855 for driver in drivers: 3856 driver.usage() 3857 3858 Mapping.Config.commonUsage() 3859 for mapping in mappings: 3860 mapping.Config.usage() 3861 3862 print("") 3863 3864 driver = None 3865 try: 3866 options = [Driver.getSupportedArgs(), Mapping.Config.getSupportedArgs()] 3867 options += [driver.getSupportedArgs() for driver in drivers] 3868 options += [mapping.Config.getSupportedArgs() for mapping in Mapping.getAll()] 3869 shortOptions = "h" 3870 longOptions = ["help"] 3871 for so, lo in options: 3872 shortOptions += so 3873 longOptions += lo 3874 opts, args = getopt.gnu_getopt(sys.argv[1:], shortOptions, longOptions) 3875 3876 for (o, a) in opts: 3877 if o in ["-h", "--help"]: 3878 usage() 3879 sys.exit(0) 3880 3881 # 3882 # Create the driver 3883 # 3884 driver = Driver.create(opts, component) 3885 3886 # 3887 # Create the configurations for each mapping. 3888 # 3889 configs = {} 3890 for mapping in Mapping.getAll(): 3891 if mapping not in configs: 3892 configs[mapping] = mapping.createConfig(opts[:]) 3893 3894 # 3895 # If the user specified --languages/rlanguages, only run matching mappings. 3896 # 3897 mappings = [m for m in mappings if driver.matchLanguage(str(m))] 3898 3899 # 3900 # Provide the configurations to the driver and load the test suites for each mapping. 3901 # 3902 driver.setConfigs(configs) 3903 for mapping in mappings + driver.getMappings(): 3904 (filters, rfilters) = driver.getFilters(mapping, configs[mapping]) 3905 mapping.loadTestSuites(args, configs[mapping], filters, rfilters) 3906 3907 # 3908 # Finally, run the test suites with the driver. 3909 # 3910 try: 3911 sys.exit(driver.run(mappings, args)) 3912 except KeyboardInterrupt: 3913 pass 3914 finally: 3915 driver.destroy() 3916 3917 except Exception as e: 3918 print(sys.argv[0] + ": unexpected exception raised:\n" + traceback.format_exc()) 3919 sys.exit(1) 3920