Wed, 31 Dec 2014 06:55:50 +0100
Added tag UPSTREAM_283F7C6 for changeset ca08bd8f51b2
michael@0 | 1 | # This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 2 | # License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. |
michael@0 | 4 | |
michael@0 | 5 | import mozinfo |
michael@0 | 6 | import moznetwork |
michael@0 | 7 | import optparse |
michael@0 | 8 | import os |
michael@0 | 9 | import tempfile |
michael@0 | 10 | |
michael@0 | 11 | from automationutils import addCommonOptions, isURL |
michael@0 | 12 | from mozprofile import DEFAULT_PORTS |
michael@0 | 13 | |
michael@0 | 14 | here = os.path.abspath(os.path.dirname(__file__)) |
michael@0 | 15 | |
michael@0 | 16 | try: |
michael@0 | 17 | from mozbuild.base import MozbuildObject |
michael@0 | 18 | build_obj = MozbuildObject.from_environment(cwd=here) |
michael@0 | 19 | except ImportError: |
michael@0 | 20 | build_obj = None |
michael@0 | 21 | |
michael@0 | 22 | __all__ = ["MochitestOptions", "B2GOptions"] |
michael@0 | 23 | |
michael@0 | 24 | VMWARE_RECORDING_HELPER_BASENAME = "vmwarerecordinghelper" |
michael@0 | 25 | |
michael@0 | 26 | class MochitestOptions(optparse.OptionParser): |
michael@0 | 27 | """Usage instructions for runtests.py. |
michael@0 | 28 | All arguments are optional. |
michael@0 | 29 | If --chrome is specified, chrome tests will be run instead of web content tests. |
michael@0 | 30 | If --browser-chrome is specified, browser-chrome tests will be run instead of web content tests. |
michael@0 | 31 | See <http://mochikit.com/doc/html/MochiKit/Logging.html> for details on the logging levels. |
michael@0 | 32 | """ |
michael@0 | 33 | |
michael@0 | 34 | LOG_LEVELS = ("DEBUG", "INFO", "WARNING", "ERROR", "FATAL") |
michael@0 | 35 | LEVEL_STRING = ", ".join(LOG_LEVELS) |
michael@0 | 36 | mochitest_options = [ |
michael@0 | 37 | [["--close-when-done"], |
michael@0 | 38 | { "action": "store_true", |
michael@0 | 39 | "dest": "closeWhenDone", |
michael@0 | 40 | "default": False, |
michael@0 | 41 | "help": "close the application when tests are done running", |
michael@0 | 42 | }], |
michael@0 | 43 | [["--appname"], |
michael@0 | 44 | { "action": "store", |
michael@0 | 45 | "type": "string", |
michael@0 | 46 | "dest": "app", |
michael@0 | 47 | "default": None, |
michael@0 | 48 | "help": "absolute path to application, overriding default", |
michael@0 | 49 | }], |
michael@0 | 50 | [["--utility-path"], |
michael@0 | 51 | { "action": "store", |
michael@0 | 52 | "type": "string", |
michael@0 | 53 | "dest": "utilityPath", |
michael@0 | 54 | "default": build_obj.bindir if build_obj is not None else None, |
michael@0 | 55 | "help": "absolute path to directory containing utility programs (xpcshell, ssltunnel, certutil)", |
michael@0 | 56 | }], |
michael@0 | 57 | [["--certificate-path"], |
michael@0 | 58 | { "action": "store", |
michael@0 | 59 | "type": "string", |
michael@0 | 60 | "dest": "certPath", |
michael@0 | 61 | "help": "absolute path to directory containing certificate store to use testing profile", |
michael@0 | 62 | "default": os.path.join(build_obj.topsrcdir, 'build', 'pgo', 'certs') if build_obj is not None else None, |
michael@0 | 63 | }], |
michael@0 | 64 | [["--log-file"], |
michael@0 | 65 | { "action": "store", |
michael@0 | 66 | "type": "string", |
michael@0 | 67 | "dest": "logFile", |
michael@0 | 68 | "metavar": "FILE", |
michael@0 | 69 | "help": "file to which logging occurs", |
michael@0 | 70 | "default": "", |
michael@0 | 71 | }], |
michael@0 | 72 | [["--hide-subtests"], |
michael@0 | 73 | { "action": "store_true", |
michael@0 | 74 | "dest": "hide_subtests", |
michael@0 | 75 | "help": "only show subtest log output if there was a failure", |
michael@0 | 76 | "default": False, |
michael@0 | 77 | }], |
michael@0 | 78 | [["--autorun"], |
michael@0 | 79 | { "action": "store_true", |
michael@0 | 80 | "dest": "autorun", |
michael@0 | 81 | "help": "start running tests when the application starts", |
michael@0 | 82 | "default": False, |
michael@0 | 83 | }], |
michael@0 | 84 | [["--timeout"], |
michael@0 | 85 | { "type": "int", |
michael@0 | 86 | "dest": "timeout", |
michael@0 | 87 | "help": "per-test timeout in seconds", |
michael@0 | 88 | "default": None, |
michael@0 | 89 | }], |
michael@0 | 90 | [["--total-chunks"], |
michael@0 | 91 | { "type": "int", |
michael@0 | 92 | "dest": "totalChunks", |
michael@0 | 93 | "help": "how many chunks to split the tests up into", |
michael@0 | 94 | "default": None, |
michael@0 | 95 | }], |
michael@0 | 96 | [["--this-chunk"], |
michael@0 | 97 | { "type": "int", |
michael@0 | 98 | "dest": "thisChunk", |
michael@0 | 99 | "help": "which chunk to run", |
michael@0 | 100 | "default": None, |
michael@0 | 101 | }], |
michael@0 | 102 | [["--chunk-by-dir"], |
michael@0 | 103 | { "type": "int", |
michael@0 | 104 | "dest": "chunkByDir", |
michael@0 | 105 | "help": "group tests together in the same chunk that are in the same top chunkByDir directories", |
michael@0 | 106 | "default": 0, |
michael@0 | 107 | }], |
michael@0 | 108 | [["--shuffle"], |
michael@0 | 109 | { "dest": "shuffle", |
michael@0 | 110 | "action": "store_true", |
michael@0 | 111 | "help": "randomize test order", |
michael@0 | 112 | "default": False, |
michael@0 | 113 | }], |
michael@0 | 114 | [["--console-level"], |
michael@0 | 115 | { "action": "store", |
michael@0 | 116 | "type": "choice", |
michael@0 | 117 | "dest": "consoleLevel", |
michael@0 | 118 | "choices": LOG_LEVELS, |
michael@0 | 119 | "metavar": "LEVEL", |
michael@0 | 120 | "help": "one of %s to determine the level of console " |
michael@0 | 121 | "logging" % LEVEL_STRING, |
michael@0 | 122 | "default": None, |
michael@0 | 123 | }], |
michael@0 | 124 | [["--file-level"], |
michael@0 | 125 | { "action": "store", |
michael@0 | 126 | "type": "choice", |
michael@0 | 127 | "dest": "fileLevel", |
michael@0 | 128 | "choices": LOG_LEVELS, |
michael@0 | 129 | "metavar": "LEVEL", |
michael@0 | 130 | "help": "one of %s to determine the level of file " |
michael@0 | 131 | "logging if a file has been specified, defaulting " |
michael@0 | 132 | "to INFO" % LEVEL_STRING, |
michael@0 | 133 | "default": "INFO", |
michael@0 | 134 | }], |
michael@0 | 135 | [["--chrome"], |
michael@0 | 136 | { "action": "store_true", |
michael@0 | 137 | "dest": "chrome", |
michael@0 | 138 | "help": "run chrome Mochitests", |
michael@0 | 139 | "default": False, |
michael@0 | 140 | }], |
michael@0 | 141 | [["--ipcplugins"], |
michael@0 | 142 | { "action": "store_true", |
michael@0 | 143 | "dest": "ipcplugins", |
michael@0 | 144 | "help": "run ipcplugins Mochitests", |
michael@0 | 145 | "default": False, |
michael@0 | 146 | }], |
michael@0 | 147 | [["--test-path"], |
michael@0 | 148 | { "action": "store", |
michael@0 | 149 | "type": "string", |
michael@0 | 150 | "dest": "testPath", |
michael@0 | 151 | "help": "start in the given directory's tests", |
michael@0 | 152 | "default": "", |
michael@0 | 153 | }], |
michael@0 | 154 | [["--start-at"], |
michael@0 | 155 | { "action": "store", |
michael@0 | 156 | "type": "string", |
michael@0 | 157 | "dest": "startAt", |
michael@0 | 158 | "help": "skip over tests until reaching the given test", |
michael@0 | 159 | "default": "", |
michael@0 | 160 | }], |
michael@0 | 161 | [["--end-at"], |
michael@0 | 162 | { "action": "store", |
michael@0 | 163 | "type": "string", |
michael@0 | 164 | "dest": "endAt", |
michael@0 | 165 | "help": "don't run any tests after the given one", |
michael@0 | 166 | "default": "", |
michael@0 | 167 | }], |
michael@0 | 168 | [["--browser-chrome"], |
michael@0 | 169 | { "action": "store_true", |
michael@0 | 170 | "dest": "browserChrome", |
michael@0 | 171 | "help": "run browser chrome Mochitests", |
michael@0 | 172 | "default": False, |
michael@0 | 173 | }], |
michael@0 | 174 | [["--subsuite"], |
michael@0 | 175 | { "action": "store", |
michael@0 | 176 | "dest": "subsuite", |
michael@0 | 177 | "help": "subsuite of tests to run", |
michael@0 | 178 | "default": "", |
michael@0 | 179 | }], |
michael@0 | 180 | [["--webapprt-content"], |
michael@0 | 181 | { "action": "store_true", |
michael@0 | 182 | "dest": "webapprtContent", |
michael@0 | 183 | "help": "run WebappRT content tests", |
michael@0 | 184 | "default": False, |
michael@0 | 185 | }], |
michael@0 | 186 | [["--webapprt-chrome"], |
michael@0 | 187 | { "action": "store_true", |
michael@0 | 188 | "dest": "webapprtChrome", |
michael@0 | 189 | "help": "run WebappRT chrome tests", |
michael@0 | 190 | "default": False, |
michael@0 | 191 | }], |
michael@0 | 192 | [["--a11y"], |
michael@0 | 193 | { "action": "store_true", |
michael@0 | 194 | "dest": "a11y", |
michael@0 | 195 | "help": "run accessibility Mochitests", |
michael@0 | 196 | "default": False, |
michael@0 | 197 | }], |
michael@0 | 198 | [["--setenv"], |
michael@0 | 199 | { "action": "append", |
michael@0 | 200 | "type": "string", |
michael@0 | 201 | "dest": "environment", |
michael@0 | 202 | "metavar": "NAME=VALUE", |
michael@0 | 203 | "help": "sets the given variable in the application's " |
michael@0 | 204 | "environment", |
michael@0 | 205 | "default": [], |
michael@0 | 206 | }], |
michael@0 | 207 | [["--exclude-extension"], |
michael@0 | 208 | { "action": "append", |
michael@0 | 209 | "type": "string", |
michael@0 | 210 | "dest": "extensionsToExclude", |
michael@0 | 211 | "help": "excludes the given extension from being installed " |
michael@0 | 212 | "in the test profile", |
michael@0 | 213 | "default": [], |
michael@0 | 214 | }], |
michael@0 | 215 | [["--browser-arg"], |
michael@0 | 216 | { "action": "append", |
michael@0 | 217 | "type": "string", |
michael@0 | 218 | "dest": "browserArgs", |
michael@0 | 219 | "metavar": "ARG", |
michael@0 | 220 | "help": "provides an argument to the test application", |
michael@0 | 221 | "default": [], |
michael@0 | 222 | }], |
michael@0 | 223 | [["--leak-threshold"], |
michael@0 | 224 | { "action": "store", |
michael@0 | 225 | "type": "int", |
michael@0 | 226 | "dest": "leakThreshold", |
michael@0 | 227 | "metavar": "THRESHOLD", |
michael@0 | 228 | "help": "fail if the number of bytes leaked through " |
michael@0 | 229 | "refcounted objects (or bytes in classes with " |
michael@0 | 230 | "MOZ_COUNT_CTOR and MOZ_COUNT_DTOR) is greater " |
michael@0 | 231 | "than the given number", |
michael@0 | 232 | "default": 0, |
michael@0 | 233 | }], |
michael@0 | 234 | [["--fatal-assertions"], |
michael@0 | 235 | { "action": "store_true", |
michael@0 | 236 | "dest": "fatalAssertions", |
michael@0 | 237 | "help": "abort testing whenever an assertion is hit " |
michael@0 | 238 | "(requires a debug build to be effective)", |
michael@0 | 239 | "default": False, |
michael@0 | 240 | }], |
michael@0 | 241 | [["--extra-profile-file"], |
michael@0 | 242 | { "action": "append", |
michael@0 | 243 | "dest": "extraProfileFiles", |
michael@0 | 244 | "help": "copy specified files/dirs to testing profile", |
michael@0 | 245 | "default": [], |
michael@0 | 246 | }], |
michael@0 | 247 | [["--install-extension"], |
michael@0 | 248 | { "action": "append", |
michael@0 | 249 | "dest": "extensionsToInstall", |
michael@0 | 250 | "help": "install the specified extension in the testing profile." |
michael@0 | 251 | "The extension file's name should be <id>.xpi where <id> is" |
michael@0 | 252 | "the extension's id as indicated in its install.rdf." |
michael@0 | 253 | "An optional path can be specified too.", |
michael@0 | 254 | "default": [], |
michael@0 | 255 | }], |
michael@0 | 256 | [["--profile-path"], |
michael@0 | 257 | { "action": "store", |
michael@0 | 258 | "type": "string", |
michael@0 | 259 | "dest": "profilePath", |
michael@0 | 260 | "help": "Directory where the profile will be stored." |
michael@0 | 261 | "This directory will be deleted after the tests are finished", |
michael@0 | 262 | "default": tempfile.mkdtemp(), |
michael@0 | 263 | }], |
michael@0 | 264 | [["--testing-modules-dir"], |
michael@0 | 265 | { "action": "store", |
michael@0 | 266 | "type": "string", |
michael@0 | 267 | "dest": "testingModulesDir", |
michael@0 | 268 | "help": "Directory where testing-only JS modules are located.", |
michael@0 | 269 | "default": None, |
michael@0 | 270 | }], |
michael@0 | 271 | [["--use-vmware-recording"], |
michael@0 | 272 | { "action": "store_true", |
michael@0 | 273 | "dest": "vmwareRecording", |
michael@0 | 274 | "help": "enables recording while the application is running " |
michael@0 | 275 | "inside a VMware Workstation 7.0 or later VM", |
michael@0 | 276 | "default": False, |
michael@0 | 277 | }], |
michael@0 | 278 | [["--repeat"], |
michael@0 | 279 | { "action": "store", |
michael@0 | 280 | "type": "int", |
michael@0 | 281 | "dest": "repeat", |
michael@0 | 282 | "metavar": "REPEAT", |
michael@0 | 283 | "help": "repeats the test or set of tests the given number of times, ie: repeat: 1 will run the test twice.", |
michael@0 | 284 | "default": 0, |
michael@0 | 285 | }], |
michael@0 | 286 | [["--run-until-failure"], |
michael@0 | 287 | { "action": "store_true", |
michael@0 | 288 | "dest": "runUntilFailure", |
michael@0 | 289 | "help": "Run tests repeatedly and stops on the first time a test fails. " |
michael@0 | 290 | "Default cap is 30 runs, which can be overwritten with the --repeat parameter.", |
michael@0 | 291 | "default": False, |
michael@0 | 292 | }], |
michael@0 | 293 | [["--run-only-tests"], |
michael@0 | 294 | { "action": "store", |
michael@0 | 295 | "type": "string", |
michael@0 | 296 | "dest": "runOnlyTests", |
michael@0 | 297 | "help": "JSON list of tests that we only want to run. [DEPRECATED- please use --test-manifest]", |
michael@0 | 298 | "default": None, |
michael@0 | 299 | }], |
michael@0 | 300 | [["--test-manifest"], |
michael@0 | 301 | { "action": "store", |
michael@0 | 302 | "type": "string", |
michael@0 | 303 | "dest": "testManifest", |
michael@0 | 304 | "help": "JSON list of tests to specify 'runtests'. Old format for mobile specific tests", |
michael@0 | 305 | "default": None, |
michael@0 | 306 | }], |
michael@0 | 307 | [["--manifest"], |
michael@0 | 308 | { "action": "store", |
michael@0 | 309 | "type": "string", |
michael@0 | 310 | "dest": "manifestFile", |
michael@0 | 311 | "help": ".ini format of tests to run.", |
michael@0 | 312 | "default": None, |
michael@0 | 313 | }], |
michael@0 | 314 | [["--failure-file"], |
michael@0 | 315 | { "action": "store", |
michael@0 | 316 | "type": "string", |
michael@0 | 317 | "dest": "failureFile", |
michael@0 | 318 | "help": "Filename of the output file where we can store a .json list of failures to be run in the future with --run-only-tests.", |
michael@0 | 319 | "default": None, |
michael@0 | 320 | }], |
michael@0 | 321 | [["--run-slower"], |
michael@0 | 322 | { "action": "store_true", |
michael@0 | 323 | "dest": "runSlower", |
michael@0 | 324 | "help": "Delay execution between test files.", |
michael@0 | 325 | "default": False, |
michael@0 | 326 | }], |
michael@0 | 327 | [["--metro-immersive"], |
michael@0 | 328 | { "action": "store_true", |
michael@0 | 329 | "dest": "immersiveMode", |
michael@0 | 330 | "help": "launches tests in immersive browser", |
michael@0 | 331 | "default": False, |
michael@0 | 332 | }], |
michael@0 | 333 | [["--httpd-path"], |
michael@0 | 334 | { "action": "store", |
michael@0 | 335 | "type": "string", |
michael@0 | 336 | "dest": "httpdPath", |
michael@0 | 337 | "default": None, |
michael@0 | 338 | "help": "path to the httpd.js file", |
michael@0 | 339 | }], |
michael@0 | 340 | [["--setpref"], |
michael@0 | 341 | { "action": "append", |
michael@0 | 342 | "type": "string", |
michael@0 | 343 | "default": [], |
michael@0 | 344 | "dest": "extraPrefs", |
michael@0 | 345 | "metavar": "PREF=VALUE", |
michael@0 | 346 | "help": "defines an extra user preference", |
michael@0 | 347 | }], |
michael@0 | 348 | [["--jsdebugger"], |
michael@0 | 349 | { "action": "store_true", |
michael@0 | 350 | "default": False, |
michael@0 | 351 | "dest": "jsdebugger", |
michael@0 | 352 | "help": "open the browser debugger", |
michael@0 | 353 | }], |
michael@0 | 354 | [["--debug-on-failure"], |
michael@0 | 355 | { "action": "store_true", |
michael@0 | 356 | "default": False, |
michael@0 | 357 | "dest": "debugOnFailure", |
michael@0 | 358 | "help": "breaks execution and enters the JS debugger on a test failure. Should be used together with --jsdebugger." |
michael@0 | 359 | }], |
michael@0 | 360 | [["--e10s"], |
michael@0 | 361 | { "action": "store_true", |
michael@0 | 362 | "default": False, |
michael@0 | 363 | "dest": "e10s", |
michael@0 | 364 | "help": "Run tests with electrolysis preferences and test filtering enabled.", |
michael@0 | 365 | }], |
michael@0 | 366 | [["--dmd-path"], |
michael@0 | 367 | { "action": "store", |
michael@0 | 368 | "default": None, |
michael@0 | 369 | "dest": "dmdPath", |
michael@0 | 370 | "help": "Specifies the path to the directory containing the shared library for DMD.", |
michael@0 | 371 | }], |
michael@0 | 372 | [["--dump-output-directory"], |
michael@0 | 373 | { "action": "store", |
michael@0 | 374 | "default": None, |
michael@0 | 375 | "dest": "dumpOutputDirectory", |
michael@0 | 376 | "help": "Specifies the directory in which to place dumped memory reports.", |
michael@0 | 377 | }], |
michael@0 | 378 | [["--dump-about-memory-after-test"], |
michael@0 | 379 | { "action": "store_true", |
michael@0 | 380 | "default": False, |
michael@0 | 381 | "dest": "dumpAboutMemoryAfterTest", |
michael@0 | 382 | "help": "Produce an about:memory dump after each test in the directory specified " |
michael@0 | 383 | "by --dump-output-directory." |
michael@0 | 384 | }], |
michael@0 | 385 | [["--dump-dmd-after-test"], |
michael@0 | 386 | { "action": "store_true", |
michael@0 | 387 | "default": False, |
michael@0 | 388 | "dest": "dumpDMDAfterTest", |
michael@0 | 389 | "help": "Produce a DMD dump after each test in the directory specified " |
michael@0 | 390 | "by --dump-output-directory." |
michael@0 | 391 | }], |
michael@0 | 392 | [["--slowscript"], |
michael@0 | 393 | { "action": "store_true", |
michael@0 | 394 | "default": False, |
michael@0 | 395 | "dest": "slowscript", |
michael@0 | 396 | "help": "Do not set the JS_DISABLE_SLOW_SCRIPT_SIGNALS env variable; " |
michael@0 | 397 | "when not set, recoverable but misleading SIGSEGV instances " |
michael@0 | 398 | "may occur in Ion/Odin JIT code." |
michael@0 | 399 | }], |
michael@0 | 400 | [["--screenshot-on-fail"], |
michael@0 | 401 | { "action": "store_true", |
michael@0 | 402 | "default": False, |
michael@0 | 403 | "dest": "screenshotOnFail", |
michael@0 | 404 | "help": "Take screenshots on all test failures. Set $MOZ_UPLOAD_DIR to a directory for storing the screenshots." |
michael@0 | 405 | }], |
michael@0 | 406 | [["--quiet"], |
michael@0 | 407 | { "action": "store_true", |
michael@0 | 408 | "default": False, |
michael@0 | 409 | "dest": "quiet", |
michael@0 | 410 | "help": "Do not print test log lines unless a failure occurs." |
michael@0 | 411 | }], |
michael@0 | 412 | [["--pidfile"], |
michael@0 | 413 | { "action": "store", |
michael@0 | 414 | "type": "string", |
michael@0 | 415 | "dest": "pidFile", |
michael@0 | 416 | "help": "name of the pidfile to generate", |
michael@0 | 417 | "default": "", |
michael@0 | 418 | }], |
michael@0 | 419 | ] |
michael@0 | 420 | |
michael@0 | 421 | def __init__(self, **kwargs): |
michael@0 | 422 | |
michael@0 | 423 | optparse.OptionParser.__init__(self, **kwargs) |
michael@0 | 424 | for option, value in self.mochitest_options: |
michael@0 | 425 | self.add_option(*option, **value) |
michael@0 | 426 | addCommonOptions(self) |
michael@0 | 427 | self.set_usage(self.__doc__) |
michael@0 | 428 | |
michael@0 | 429 | def verifyOptions(self, options, mochitest): |
michael@0 | 430 | """ verify correct options and cleanup paths """ |
michael@0 | 431 | |
michael@0 | 432 | mozinfo.update({"e10s": options.e10s}) # for test manifest parsing. |
michael@0 | 433 | |
michael@0 | 434 | if options.app is None: |
michael@0 | 435 | if build_obj is not None: |
michael@0 | 436 | options.app = build_obj.get_binary_path() |
michael@0 | 437 | else: |
michael@0 | 438 | self.error("could not find the application path, --appname must be specified") |
michael@0 | 439 | |
michael@0 | 440 | if options.totalChunks is not None and options.thisChunk is None: |
michael@0 | 441 | self.error("thisChunk must be specified when totalChunks is specified") |
michael@0 | 442 | |
michael@0 | 443 | if options.totalChunks: |
michael@0 | 444 | if not 1 <= options.thisChunk <= options.totalChunks: |
michael@0 | 445 | self.error("thisChunk must be between 1 and totalChunks") |
michael@0 | 446 | |
michael@0 | 447 | if options.xrePath is None: |
michael@0 | 448 | # default xrePath to the app path if not provided |
michael@0 | 449 | # but only if an app path was explicitly provided |
michael@0 | 450 | if options.app != self.defaults['app']: |
michael@0 | 451 | options.xrePath = os.path.dirname(options.app) |
michael@0 | 452 | elif build_obj is not None: |
michael@0 | 453 | # otherwise default to dist/bin |
michael@0 | 454 | options.xrePath = build_obj.bindir |
michael@0 | 455 | else: |
michael@0 | 456 | self.error("could not find xre directory, --xre-path must be specified") |
michael@0 | 457 | |
michael@0 | 458 | # allow relative paths |
michael@0 | 459 | options.xrePath = mochitest.getFullPath(options.xrePath) |
michael@0 | 460 | options.profilePath = mochitest.getFullPath(options.profilePath) |
michael@0 | 461 | options.app = mochitest.getFullPath(options.app) |
michael@0 | 462 | if options.dmdPath is not None: |
michael@0 | 463 | options.dmdPath = mochitest.getFullPath(options.dmdPath) |
michael@0 | 464 | |
michael@0 | 465 | if not os.path.exists(options.app): |
michael@0 | 466 | msg = """\ |
michael@0 | 467 | Error: Path %(app)s doesn't exist. |
michael@0 | 468 | Are you executing $objdir/_tests/testing/mochitest/runtests.py?""" |
michael@0 | 469 | self.error(msg % {"app": options.app}) |
michael@0 | 470 | return None |
michael@0 | 471 | |
michael@0 | 472 | if options.utilityPath: |
michael@0 | 473 | options.utilityPath = mochitest.getFullPath(options.utilityPath) |
michael@0 | 474 | |
michael@0 | 475 | if options.certPath: |
michael@0 | 476 | options.certPath = mochitest.getFullPath(options.certPath) |
michael@0 | 477 | |
michael@0 | 478 | if options.symbolsPath and not isURL(options.symbolsPath): |
michael@0 | 479 | options.symbolsPath = mochitest.getFullPath(options.symbolsPath) |
michael@0 | 480 | |
michael@0 | 481 | # Set server information on the options object |
michael@0 | 482 | options.webServer = '127.0.0.1' |
michael@0 | 483 | options.httpPort = DEFAULT_PORTS['http'] |
michael@0 | 484 | options.sslPort = DEFAULT_PORTS['https'] |
michael@0 | 485 | # options.webSocketPort = DEFAULT_PORTS['ws'] |
michael@0 | 486 | options.webSocketPort = str(9988) # <- http://hg.mozilla.org/mozilla-central/file/b871dfb2186f/build/automation.py.in#l30 |
michael@0 | 487 | # The default websocket port is incorrect in mozprofile; it is |
michael@0 | 488 | # set to the SSL proxy setting. See: |
michael@0 | 489 | # see https://bugzilla.mozilla.org/show_bug.cgi?id=916517 |
michael@0 | 490 | |
michael@0 | 491 | if options.vmwareRecording: |
michael@0 | 492 | if not mozinfo.isWin: |
michael@0 | 493 | self.error("use-vmware-recording is only supported on Windows.") |
michael@0 | 494 | mochitest.vmwareHelperPath = os.path.join( |
michael@0 | 495 | options.utilityPath, VMWARE_RECORDING_HELPER_BASENAME + ".dll") |
michael@0 | 496 | if not os.path.exists(mochitest.vmwareHelperPath): |
michael@0 | 497 | self.error("%s not found, cannot automate VMware recording." % |
michael@0 | 498 | mochitest.vmwareHelperPath) |
michael@0 | 499 | |
michael@0 | 500 | if options.testManifest and options.runOnlyTests: |
michael@0 | 501 | self.error("Please use --test-manifest only and not --run-only-tests") |
michael@0 | 502 | |
michael@0 | 503 | if options.runOnlyTests: |
michael@0 | 504 | if not os.path.exists(os.path.abspath(os.path.join(here, options.runOnlyTests))): |
michael@0 | 505 | self.error("unable to find --run-only-tests file '%s'" % options.runOnlyTests) |
michael@0 | 506 | options.runOnly = True |
michael@0 | 507 | options.testManifest = options.runOnlyTests |
michael@0 | 508 | options.runOnlyTests = None |
michael@0 | 509 | |
michael@0 | 510 | if options.manifestFile and options.testManifest: |
michael@0 | 511 | self.error("Unable to support both --manifest and --test-manifest/--run-only-tests at the same time") |
michael@0 | 512 | |
michael@0 | 513 | if options.webapprtContent and options.webapprtChrome: |
michael@0 | 514 | self.error("Only one of --webapprt-content and --webapprt-chrome may be given.") |
michael@0 | 515 | |
michael@0 | 516 | if options.jsdebugger: |
michael@0 | 517 | options.extraPrefs += [ |
michael@0 | 518 | "devtools.debugger.remote-enabled=true", |
michael@0 | 519 | "devtools.debugger.chrome-enabled=true", |
michael@0 | 520 | "devtools.chrome.enabled=true", |
michael@0 | 521 | "devtools.debugger.prompt-connection=false" |
michael@0 | 522 | ] |
michael@0 | 523 | options.autorun = False |
michael@0 | 524 | |
michael@0 | 525 | if options.debugOnFailure and not options.jsdebugger: |
michael@0 | 526 | self.error("--debug-on-failure should be used together with --jsdebugger.") |
michael@0 | 527 | |
michael@0 | 528 | # Try to guess the testing modules directory. |
michael@0 | 529 | # This somewhat grotesque hack allows the buildbot machines to find the |
michael@0 | 530 | # modules directory without having to configure the buildbot hosts. This |
michael@0 | 531 | # code should never be executed in local runs because the build system |
michael@0 | 532 | # should always set the flag that populates this variable. If buildbot ever |
michael@0 | 533 | # passes this argument, this code can be deleted. |
michael@0 | 534 | if options.testingModulesDir is None: |
michael@0 | 535 | possible = os.path.join(here, os.path.pardir, 'modules') |
michael@0 | 536 | |
michael@0 | 537 | if os.path.isdir(possible): |
michael@0 | 538 | options.testingModulesDir = possible |
michael@0 | 539 | |
michael@0 | 540 | # Even if buildbot is updated, we still want this, as the path we pass in |
michael@0 | 541 | # to the app must be absolute and have proper slashes. |
michael@0 | 542 | if options.testingModulesDir is not None: |
michael@0 | 543 | options.testingModulesDir = os.path.normpath(options.testingModulesDir) |
michael@0 | 544 | |
michael@0 | 545 | if not os.path.isabs(options.testingModulesDir): |
michael@0 | 546 | options.testingModulesDir = os.path.abspath(options.testingModulesDir) |
michael@0 | 547 | |
michael@0 | 548 | if not os.path.isdir(options.testingModulesDir): |
michael@0 | 549 | self.error('--testing-modules-dir not a directory: %s' % |
michael@0 | 550 | options.testingModulesDir) |
michael@0 | 551 | |
michael@0 | 552 | options.testingModulesDir = options.testingModulesDir.replace('\\', '/') |
michael@0 | 553 | if options.testingModulesDir[-1] != '/': |
michael@0 | 554 | options.testingModulesDir += '/' |
michael@0 | 555 | |
michael@0 | 556 | if options.immersiveMode: |
michael@0 | 557 | if not mozinfo.isWin: |
michael@0 | 558 | self.error("immersive is only supported on Windows 8 and up.") |
michael@0 | 559 | mochitest.immersiveHelperPath = os.path.join( |
michael@0 | 560 | options.utilityPath, "metrotestharness.exe") |
michael@0 | 561 | if not os.path.exists(mochitest.immersiveHelperPath): |
michael@0 | 562 | self.error("%s not found, cannot launch immersive tests." % |
michael@0 | 563 | mochitest.immersiveHelperPath) |
michael@0 | 564 | |
michael@0 | 565 | if options.runUntilFailure: |
michael@0 | 566 | if not options.repeat: |
michael@0 | 567 | options.repeat = 29 |
michael@0 | 568 | |
michael@0 | 569 | if options.dumpOutputDirectory is None: |
michael@0 | 570 | options.dumpOutputDirectory = tempfile.gettempdir() |
michael@0 | 571 | |
michael@0 | 572 | if options.dumpAboutMemoryAfterTest or options.dumpDMDAfterTest: |
michael@0 | 573 | if not os.path.isdir(options.dumpOutputDirectory): |
michael@0 | 574 | self.error('--dump-output-directory not a directory: %s' % |
michael@0 | 575 | options.dumpOutputDirectory) |
michael@0 | 576 | |
michael@0 | 577 | return options |
michael@0 | 578 | |
michael@0 | 579 | |
michael@0 | 580 | class B2GOptions(MochitestOptions): |
michael@0 | 581 | b2g_options = [ |
michael@0 | 582 | [["--b2gpath"], |
michael@0 | 583 | { "action": "store", |
michael@0 | 584 | "type": "string", |
michael@0 | 585 | "dest": "b2gPath", |
michael@0 | 586 | "help": "path to B2G repo or qemu dir", |
michael@0 | 587 | "default": None, |
michael@0 | 588 | }], |
michael@0 | 589 | [["--desktop"], |
michael@0 | 590 | { "action": "store_true", |
michael@0 | 591 | "dest": "desktop", |
michael@0 | 592 | "help": "Run the tests on a B2G desktop build", |
michael@0 | 593 | "default": False, |
michael@0 | 594 | }], |
michael@0 | 595 | [["--marionette"], |
michael@0 | 596 | { "action": "store", |
michael@0 | 597 | "type": "string", |
michael@0 | 598 | "dest": "marionette", |
michael@0 | 599 | "help": "host:port to use when connecting to Marionette", |
michael@0 | 600 | "default": None, |
michael@0 | 601 | }], |
michael@0 | 602 | [["--emulator"], |
michael@0 | 603 | { "action": "store", |
michael@0 | 604 | "type": "string", |
michael@0 | 605 | "dest": "emulator", |
michael@0 | 606 | "help": "Architecture of emulator to use: x86 or arm", |
michael@0 | 607 | "default": None, |
michael@0 | 608 | }], |
michael@0 | 609 | [["--wifi"], |
michael@0 | 610 | { "action": "store", |
michael@0 | 611 | "type": "string", |
michael@0 | 612 | "dest": "wifi", |
michael@0 | 613 | "help": "Devine wifi configuration for on device mochitest", |
michael@0 | 614 | "default": False, |
michael@0 | 615 | }], |
michael@0 | 616 | [["--sdcard"], |
michael@0 | 617 | { "action": "store", |
michael@0 | 618 | "type": "string", |
michael@0 | 619 | "dest": "sdcard", |
michael@0 | 620 | "help": "Define size of sdcard: 1MB, 50MB...etc", |
michael@0 | 621 | "default": "10MB", |
michael@0 | 622 | }], |
michael@0 | 623 | [["--no-window"], |
michael@0 | 624 | { "action": "store_true", |
michael@0 | 625 | "dest": "noWindow", |
michael@0 | 626 | "help": "Pass --no-window to the emulator", |
michael@0 | 627 | "default": False, |
michael@0 | 628 | }], |
michael@0 | 629 | [["--adbpath"], |
michael@0 | 630 | { "action": "store", |
michael@0 | 631 | "type": "string", |
michael@0 | 632 | "dest": "adbPath", |
michael@0 | 633 | "help": "path to adb", |
michael@0 | 634 | "default": "adb", |
michael@0 | 635 | }], |
michael@0 | 636 | [["--deviceIP"], |
michael@0 | 637 | { "action": "store", |
michael@0 | 638 | "type": "string", |
michael@0 | 639 | "dest": "deviceIP", |
michael@0 | 640 | "help": "ip address of remote device to test", |
michael@0 | 641 | "default": None, |
michael@0 | 642 | }], |
michael@0 | 643 | [["--devicePort"], |
michael@0 | 644 | { "action": "store", |
michael@0 | 645 | "type": "string", |
michael@0 | 646 | "dest": "devicePort", |
michael@0 | 647 | "help": "port of remote device to test", |
michael@0 | 648 | "default": 20701, |
michael@0 | 649 | }], |
michael@0 | 650 | [["--remote-logfile"], |
michael@0 | 651 | { "action": "store", |
michael@0 | 652 | "type": "string", |
michael@0 | 653 | "dest": "remoteLogFile", |
michael@0 | 654 | "help": "Name of log file on the device relative to the device root. \ |
michael@0 | 655 | PLEASE ONLY USE A FILENAME.", |
michael@0 | 656 | "default" : None, |
michael@0 | 657 | }], |
michael@0 | 658 | [["--remote-webserver"], |
michael@0 | 659 | { "action": "store", |
michael@0 | 660 | "type": "string", |
michael@0 | 661 | "dest": "remoteWebServer", |
michael@0 | 662 | "help": "ip address where the remote web server is hosted at", |
michael@0 | 663 | "default": None, |
michael@0 | 664 | }], |
michael@0 | 665 | [["--http-port"], |
michael@0 | 666 | { "action": "store", |
michael@0 | 667 | "type": "string", |
michael@0 | 668 | "dest": "httpPort", |
michael@0 | 669 | "help": "ip address where the remote web server is hosted at", |
michael@0 | 670 | "default": None, |
michael@0 | 671 | }], |
michael@0 | 672 | [["--ssl-port"], |
michael@0 | 673 | { "action": "store", |
michael@0 | 674 | "type": "string", |
michael@0 | 675 | "dest": "sslPort", |
michael@0 | 676 | "help": "ip address where the remote web server is hosted at", |
michael@0 | 677 | "default": None, |
michael@0 | 678 | }], |
michael@0 | 679 | [["--gecko-path"], |
michael@0 | 680 | { "action": "store", |
michael@0 | 681 | "type": "string", |
michael@0 | 682 | "dest": "geckoPath", |
michael@0 | 683 | "help": "the path to a gecko distribution that should \ |
michael@0 | 684 | be installed on the emulator prior to test", |
michael@0 | 685 | "default": None, |
michael@0 | 686 | }], |
michael@0 | 687 | [["--profile"], |
michael@0 | 688 | { "action": "store", |
michael@0 | 689 | "type": "string", |
michael@0 | 690 | "dest": "profile", |
michael@0 | 691 | "help": "for desktop testing, the path to the \ |
michael@0 | 692 | gaia profile to use", |
michael@0 | 693 | "default": None, |
michael@0 | 694 | }], |
michael@0 | 695 | [["--logcat-dir"], |
michael@0 | 696 | { "action": "store", |
michael@0 | 697 | "type": "string", |
michael@0 | 698 | "dest": "logcat_dir", |
michael@0 | 699 | "help": "directory to store logcat dump files", |
michael@0 | 700 | "default": None, |
michael@0 | 701 | }], |
michael@0 | 702 | [['--busybox'], |
michael@0 | 703 | { "action": 'store', |
michael@0 | 704 | "type": 'string', |
michael@0 | 705 | "dest": 'busybox', |
michael@0 | 706 | "help": "Path to busybox binary to install on device", |
michael@0 | 707 | "default": None, |
michael@0 | 708 | }], |
michael@0 | 709 | [['--profile-data-dir'], |
michael@0 | 710 | { "action": 'store', |
michael@0 | 711 | "type": 'string', |
michael@0 | 712 | "dest": 'profile_data_dir', |
michael@0 | 713 | "help": "Path to a directory containing preference and other \ |
michael@0 | 714 | data to be installed into the profile", |
michael@0 | 715 | "default": os.path.join(here, 'profile_data'), |
michael@0 | 716 | }], |
michael@0 | 717 | ] |
michael@0 | 718 | |
michael@0 | 719 | def __init__(self): |
michael@0 | 720 | MochitestOptions.__init__(self) |
michael@0 | 721 | |
michael@0 | 722 | for option in self.b2g_options: |
michael@0 | 723 | self.add_option(*option[0], **option[1]) |
michael@0 | 724 | |
michael@0 | 725 | defaults = {} |
michael@0 | 726 | defaults["httpPort"] = DEFAULT_PORTS['http'] |
michael@0 | 727 | defaults["sslPort"] = DEFAULT_PORTS['https'] |
michael@0 | 728 | defaults["remoteTestRoot"] = "/data/local/tests" |
michael@0 | 729 | defaults["logFile"] = "mochitest.log" |
michael@0 | 730 | defaults["autorun"] = True |
michael@0 | 731 | defaults["closeWhenDone"] = True |
michael@0 | 732 | defaults["testPath"] = "" |
michael@0 | 733 | defaults["extensionsToExclude"] = ["specialpowers"] |
michael@0 | 734 | self.set_defaults(**defaults) |
michael@0 | 735 | |
michael@0 | 736 | def verifyRemoteOptions(self, options): |
michael@0 | 737 | if options.remoteWebServer == None: |
michael@0 | 738 | if os.name != "nt": |
michael@0 | 739 | options.remoteWebServer = moznetwork.get_ip() |
michael@0 | 740 | else: |
michael@0 | 741 | self.error("You must specify a --remote-webserver=<ip address>") |
michael@0 | 742 | options.webServer = options.remoteWebServer |
michael@0 | 743 | |
michael@0 | 744 | if options.geckoPath and not options.emulator: |
michael@0 | 745 | self.error("You must specify --emulator if you specify --gecko-path") |
michael@0 | 746 | |
michael@0 | 747 | if options.logcat_dir and not options.emulator: |
michael@0 | 748 | self.error("You must specify --emulator if you specify --logcat-dir") |
michael@0 | 749 | |
michael@0 | 750 | if not os.path.isdir(options.xrePath): |
michael@0 | 751 | self.error("--xre-path '%s' is not a directory" % options.xrePath) |
michael@0 | 752 | xpcshell = os.path.join(options.xrePath, 'xpcshell') |
michael@0 | 753 | if not os.access(xpcshell, os.F_OK): |
michael@0 | 754 | self.error('xpcshell not found at %s' % xpcshell) |
michael@0 | 755 | if self.elf_arm(xpcshell): |
michael@0 | 756 | self.error('--xre-path points to an ARM version of xpcshell; it ' |
michael@0 | 757 | 'should instead point to a version that can run on ' |
michael@0 | 758 | 'your desktop') |
michael@0 | 759 | |
michael@0 | 760 | if options.pidFile != "": |
michael@0 | 761 | f = open(options.pidFile, 'w') |
michael@0 | 762 | f.write("%s" % os.getpid()) |
michael@0 | 763 | f.close() |
michael@0 | 764 | |
michael@0 | 765 | return options |
michael@0 | 766 | |
michael@0 | 767 | def verifyOptions(self, options, mochitest): |
michael@0 | 768 | # since we are reusing verifyOptions, it will exit if App is not found |
michael@0 | 769 | temp = options.app |
michael@0 | 770 | options.app = __file__ |
michael@0 | 771 | tempPort = options.httpPort |
michael@0 | 772 | tempSSL = options.sslPort |
michael@0 | 773 | tempIP = options.webServer |
michael@0 | 774 | options = MochitestOptions.verifyOptions(self, options, mochitest) |
michael@0 | 775 | options.webServer = tempIP |
michael@0 | 776 | options.app = temp |
michael@0 | 777 | options.sslPort = tempSSL |
michael@0 | 778 | options.httpPort = tempPort |
michael@0 | 779 | |
michael@0 | 780 | return options |
michael@0 | 781 | |
michael@0 | 782 | def elf_arm(self, filename): |
michael@0 | 783 | data = open(filename, 'rb').read(20) |
michael@0 | 784 | return data[:4] == "\x7fELF" and ord(data[18]) == 40 # EM_ARM |