testing/mochitest/runtestsb2g.py

Wed, 31 Dec 2014 06:55:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:55:50 +0100
changeset 2
7e26c7da4463
permissions
-rw-r--r--

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 file,
michael@0 3 # You can obtain one at http://mozilla.org/MPL/2.0/.
michael@0 4
michael@0 5 import json
michael@0 6 import os
michael@0 7 import posixpath
michael@0 8 import shutil
michael@0 9 import sys
michael@0 10 import tempfile
michael@0 11 import threading
michael@0 12 import time
michael@0 13 import traceback
michael@0 14
michael@0 15 here = os.path.abspath(os.path.dirname(__file__))
michael@0 16 sys.path.insert(0, here)
michael@0 17
michael@0 18 from runtests import Mochitest
michael@0 19 from runtests import MochitestUtilsMixin
michael@0 20 from runtests import MochitestOptions
michael@0 21 from runtests import MochitestServer
michael@0 22 from mochitest_options import B2GOptions, MochitestOptions
michael@0 23
michael@0 24 from marionette import Marionette
michael@0 25
michael@0 26 from mozdevice import DeviceManagerADB
michael@0 27 from mozprofile import Profile, Preferences
michael@0 28 from mozrunner import B2GRunner
michael@0 29 import mozlog
michael@0 30 import mozinfo
michael@0 31 import moznetwork
michael@0 32
michael@0 33 log = mozlog.getLogger('Mochitest')
michael@0 34
michael@0 35 class B2GMochitest(MochitestUtilsMixin):
michael@0 36 def __init__(self, marionette,
michael@0 37 out_of_process=True,
michael@0 38 profile_data_dir=None,
michael@0 39 locations=os.path.join(here, 'server-locations.txt')):
michael@0 40 super(B2GMochitest, self).__init__()
michael@0 41 self.marionette = marionette
michael@0 42 self.out_of_process = out_of_process
michael@0 43 self.locations_file = locations
michael@0 44 self.preferences = []
michael@0 45 self.webapps = None
michael@0 46 self.test_script = os.path.join(here, 'b2g_start_script.js')
michael@0 47 self.test_script_args = [self.out_of_process]
michael@0 48 self.product = 'b2g'
michael@0 49
michael@0 50 if profile_data_dir:
michael@0 51 self.preferences = [os.path.join(profile_data_dir, f)
michael@0 52 for f in os.listdir(profile_data_dir) if f.startswith('pref')]
michael@0 53 self.webapps = [os.path.join(profile_data_dir, f)
michael@0 54 for f in os.listdir(profile_data_dir) if f.startswith('webapp')]
michael@0 55
michael@0 56 # mozinfo is populated by the parent class
michael@0 57 if mozinfo.info['debug']:
michael@0 58 self.SERVER_STARTUP_TIMEOUT = 180
michael@0 59 else:
michael@0 60 self.SERVER_STARTUP_TIMEOUT = 90
michael@0 61
michael@0 62 def setup_common_options(self, options):
michael@0 63 test_url = self.buildTestPath(options)
michael@0 64 if len(self.urlOpts) > 0:
michael@0 65 test_url += "?" + "&".join(self.urlOpts)
michael@0 66 self.test_script_args.append(test_url)
michael@0 67
michael@0 68 def buildTestPath(self, options):
michael@0 69 # Skip over the manifest building that happens on desktop.
michael@0 70 return self.buildTestURL(options)
michael@0 71
michael@0 72 def build_profile(self, options):
michael@0 73 # preferences
michael@0 74 prefs = {}
michael@0 75 for path in self.preferences:
michael@0 76 prefs.update(Preferences.read_prefs(path))
michael@0 77
michael@0 78 for v in options.extraPrefs:
michael@0 79 thispref = v.split("=", 1)
michael@0 80 if len(thispref) < 2:
michael@0 81 print "Error: syntax error in --setpref=" + v
michael@0 82 sys.exit(1)
michael@0 83 prefs[thispref[0]] = thispref[1]
michael@0 84
michael@0 85 # interpolate the preferences
michael@0 86 interpolation = { "server": "%s:%s" % (options.webServer, options.httpPort),
michael@0 87 "OOP": "true" if self.out_of_process else "false" }
michael@0 88 prefs = json.loads(json.dumps(prefs) % interpolation)
michael@0 89 for pref in prefs:
michael@0 90 prefs[pref] = Preferences.cast(prefs[pref])
michael@0 91
michael@0 92 kwargs = {
michael@0 93 'addons': self.getExtensionsToInstall(options),
michael@0 94 'apps': self.webapps,
michael@0 95 'locations': self.locations_file,
michael@0 96 'preferences': prefs,
michael@0 97 'proxy': {"remote": options.webServer}
michael@0 98 }
michael@0 99
michael@0 100 if options.profile:
michael@0 101 self.profile = Profile.clone(options.profile, **kwargs)
michael@0 102 else:
michael@0 103 self.profile = Profile(**kwargs)
michael@0 104
michael@0 105 options.profilePath = self.profile.profile
michael@0 106 # TODO bug 839108 - mozprofile should probably handle this
michael@0 107 manifest = self.addChromeToProfile(options)
michael@0 108 self.copyExtraFilesToProfile(options)
michael@0 109 return manifest
michael@0 110
michael@0 111 def run_tests(self, options):
michael@0 112 """ Prepare, configure, run tests and cleanup """
michael@0 113
michael@0 114 self.leak_report_file = os.path.join(options.profilePath, "runtests_leaks.log")
michael@0 115 manifest = self.build_profile(options)
michael@0 116
michael@0 117 self.startServers(options, None)
michael@0 118 self.buildURLOptions(options, {'MOZ_HIDE_RESULTS_TABLE': '1'})
michael@0 119 self.test_script_args.append(not options.emulator)
michael@0 120 self.test_script_args.append(options.wifi)
michael@0 121
michael@0 122 if options.debugger or not options.autorun:
michael@0 123 timeout = None
michael@0 124 else:
michael@0 125 if not options.timeout:
michael@0 126 if mozinfo.info['debug']:
michael@0 127 options.timeout = 420
michael@0 128 else:
michael@0 129 options.timeout = 300
michael@0 130 timeout = options.timeout + 30.0
michael@0 131
michael@0 132 log.info("runtestsb2g.py | Running tests: start.")
michael@0 133 status = 0
michael@0 134 try:
michael@0 135 runner_args = { 'profile': self.profile,
michael@0 136 'devicemanager': self._dm,
michael@0 137 'marionette': self.marionette,
michael@0 138 'remote_test_root': self.remote_test_root,
michael@0 139 'symbols_path': options.symbolsPath,
michael@0 140 'test_script': self.test_script,
michael@0 141 'test_script_args': self.test_script_args }
michael@0 142 self.runner = B2GRunner(**runner_args)
michael@0 143 self.runner.start(outputTimeout=timeout)
michael@0 144 status = self.runner.wait()
michael@0 145 if status is None:
michael@0 146 # the runner has timed out
michael@0 147 status = 124
michael@0 148 except KeyboardInterrupt:
michael@0 149 log.info("runtests.py | Received keyboard interrupt.\n");
michael@0 150 status = -1
michael@0 151 except:
michael@0 152 traceback.print_exc()
michael@0 153 log.error("Automation Error: Received unexpected exception while running application\n")
michael@0 154 self.runner.check_for_crashes()
michael@0 155 status = 1
michael@0 156
michael@0 157 self.stopServers()
michael@0 158
michael@0 159 log.info("runtestsb2g.py | Running tests: end.")
michael@0 160
michael@0 161 if manifest is not None:
michael@0 162 self.cleanup(manifest, options)
michael@0 163 return status
michael@0 164
michael@0 165
michael@0 166 class B2GDeviceMochitest(B2GMochitest):
michael@0 167
michael@0 168 _dm = None
michael@0 169
michael@0 170 def __init__(self, marionette, devicemanager, profile_data_dir,
michael@0 171 local_binary_dir, remote_test_root=None, remote_log_file=None):
michael@0 172 B2GMochitest.__init__(self, marionette, out_of_process=True, profile_data_dir=profile_data_dir)
michael@0 173 self._dm = devicemanager
michael@0 174 self.remote_test_root = remote_test_root or self._dm.getDeviceRoot()
michael@0 175 self.remote_profile = posixpath.join(self.remote_test_root, 'profile')
michael@0 176 self.remote_log = remote_log_file or posixpath.join(self.remote_test_root, 'log', 'mochitest.log')
michael@0 177 self.local_log = None
michael@0 178 self.local_binary_dir = local_binary_dir
michael@0 179
michael@0 180 if not self._dm.dirExists(posixpath.dirname(self.remote_log)):
michael@0 181 self._dm.mkDirs(self.remote_log)
michael@0 182
michael@0 183 def cleanup(self, manifest, options):
michael@0 184 if self.local_log:
michael@0 185 self._dm.getFile(self.remote_log, self.local_log)
michael@0 186 self._dm.removeFile(self.remote_log)
michael@0 187
michael@0 188 if options.pidFile != "":
michael@0 189 try:
michael@0 190 os.remove(options.pidFile)
michael@0 191 os.remove(options.pidFile + ".xpcshell.pid")
michael@0 192 except:
michael@0 193 print "Warning: cleaning up pidfile '%s' was unsuccessful from the test harness" % options.pidFile
michael@0 194
michael@0 195 # stop and clean up the runner
michael@0 196 if getattr(self, 'runner', False):
michael@0 197 self.runner.cleanup()
michael@0 198 self.runner = None
michael@0 199
michael@0 200 def startServers(self, options, debuggerInfo):
michael@0 201 """ Create the servers on the host and start them up """
michael@0 202 savedXre = options.xrePath
michael@0 203 savedUtility = options.utilityPath
michael@0 204 savedProfie = options.profilePath
michael@0 205 options.xrePath = self.local_binary_dir
michael@0 206 options.utilityPath = self.local_binary_dir
michael@0 207 options.profilePath = tempfile.mkdtemp()
michael@0 208
michael@0 209 MochitestUtilsMixin.startServers(self, options, debuggerInfo)
michael@0 210
michael@0 211 options.xrePath = savedXre
michael@0 212 options.utilityPath = savedUtility
michael@0 213 options.profilePath = savedProfie
michael@0 214
michael@0 215 def buildURLOptions(self, options, env):
michael@0 216 self.local_log = options.logFile
michael@0 217 options.logFile = self.remote_log
michael@0 218 options.profilePath = self.profile.profile
michael@0 219 retVal = super(B2GDeviceMochitest, self).buildURLOptions(options, env)
michael@0 220
michael@0 221 self.setup_common_options(options)
michael@0 222
michael@0 223 options.profilePath = self.remote_profile
michael@0 224 options.logFile = self.local_log
michael@0 225 return retVal
michael@0 226
michael@0 227
michael@0 228 class B2GDesktopMochitest(B2GMochitest, Mochitest):
michael@0 229
michael@0 230 def __init__(self, marionette, profile_data_dir):
michael@0 231 B2GMochitest.__init__(self, marionette, out_of_process=False, profile_data_dir=profile_data_dir)
michael@0 232 Mochitest.__init__(self)
michael@0 233 self.certdbNew = True
michael@0 234
michael@0 235 def runMarionetteScript(self, marionette, test_script, test_script_args):
michael@0 236 assert(marionette.wait_for_port())
michael@0 237 marionette.start_session()
michael@0 238 marionette.set_context(marionette.CONTEXT_CHROME)
michael@0 239
michael@0 240 if os.path.isfile(test_script):
michael@0 241 f = open(test_script, 'r')
michael@0 242 test_script = f.read()
michael@0 243 f.close()
michael@0 244 self.marionette.execute_script(test_script,
michael@0 245 script_args=test_script_args)
michael@0 246
michael@0 247 def startTests(self):
michael@0 248 # This is run in a separate thread because otherwise, the app's
michael@0 249 # stdout buffer gets filled (which gets drained only after this
michael@0 250 # function returns, by waitForFinish), which causes the app to hang.
michael@0 251 thread = threading.Thread(target=self.runMarionetteScript,
michael@0 252 args=(self.marionette,
michael@0 253 self.test_script,
michael@0 254 self.test_script_args))
michael@0 255 thread.start()
michael@0 256
michael@0 257 def buildURLOptions(self, options, env):
michael@0 258 retVal = super(B2GDesktopMochitest, self).buildURLOptions(options, env)
michael@0 259
michael@0 260 self.setup_common_options(options)
michael@0 261
michael@0 262 # Copy the extensions to the B2G bundles dir.
michael@0 263 extensionDir = os.path.join(options.profilePath, 'extensions', 'staged')
michael@0 264 bundlesDir = os.path.join(os.path.dirname(options.app),
michael@0 265 'distribution', 'bundles')
michael@0 266
michael@0 267 for filename in os.listdir(extensionDir):
michael@0 268 shutil.rmtree(os.path.join(bundlesDir, filename), True)
michael@0 269 shutil.copytree(os.path.join(extensionDir, filename),
michael@0 270 os.path.join(bundlesDir, filename))
michael@0 271
michael@0 272 return retVal
michael@0 273
michael@0 274 def buildProfile(self, options):
michael@0 275 return self.build_profile(options)
michael@0 276
michael@0 277
michael@0 278 def run_remote_mochitests(parser, options):
michael@0 279 # create our Marionette instance
michael@0 280 kwargs = {}
michael@0 281 if options.emulator:
michael@0 282 kwargs['emulator'] = options.emulator
michael@0 283 if options.noWindow:
michael@0 284 kwargs['noWindow'] = True
michael@0 285 if options.geckoPath:
michael@0 286 kwargs['gecko_path'] = options.geckoPath
michael@0 287 if options.logcat_dir:
michael@0 288 kwargs['logcat_dir'] = options.logcat_dir
michael@0 289 if options.busybox:
michael@0 290 kwargs['busybox'] = options.busybox
michael@0 291 if options.symbolsPath:
michael@0 292 kwargs['symbols_path'] = options.symbolsPath
michael@0 293 # needless to say sdcard is only valid if using an emulator
michael@0 294 if options.sdcard:
michael@0 295 kwargs['sdcard'] = options.sdcard
michael@0 296 if options.b2gPath:
michael@0 297 kwargs['homedir'] = options.b2gPath
michael@0 298 if options.marionette:
michael@0 299 host, port = options.marionette.split(':')
michael@0 300 kwargs['host'] = host
michael@0 301 kwargs['port'] = int(port)
michael@0 302
michael@0 303 marionette = Marionette.getMarionetteOrExit(**kwargs)
michael@0 304
michael@0 305 if options.emulator:
michael@0 306 dm = marionette.emulator.dm
michael@0 307 else:
michael@0 308 # create the DeviceManager
michael@0 309 kwargs = {'adbPath': options.adbPath,
michael@0 310 'deviceRoot': options.remoteTestRoot}
michael@0 311 if options.deviceIP:
michael@0 312 kwargs.update({'host': options.deviceIP,
michael@0 313 'port': options.devicePort})
michael@0 314 dm = DeviceManagerADB(**kwargs)
michael@0 315
michael@0 316 options = parser.verifyRemoteOptions(options)
michael@0 317 if (options == None):
michael@0 318 print "ERROR: Invalid options specified, use --help for a list of valid options"
michael@0 319 sys.exit(1)
michael@0 320
michael@0 321 mochitest = B2GDeviceMochitest(marionette, dm, options.profile_data_dir, options.xrePath,
michael@0 322 remote_test_root=options.remoteTestRoot,
michael@0 323 remote_log_file=options.remoteLogFile)
michael@0 324
michael@0 325 options = parser.verifyOptions(options, mochitest)
michael@0 326 if (options == None):
michael@0 327 sys.exit(1)
michael@0 328
michael@0 329 retVal = 1
michael@0 330 try:
michael@0 331 mochitest.cleanup(None, options)
michael@0 332 retVal = mochitest.run_tests(options)
michael@0 333 except:
michael@0 334 print "Automation Error: Exception caught while running tests"
michael@0 335 traceback.print_exc()
michael@0 336 mochitest.stopServers()
michael@0 337 try:
michael@0 338 mochitest.cleanup(None, options)
michael@0 339 except:
michael@0 340 pass
michael@0 341 retVal = 1
michael@0 342
michael@0 343 sys.exit(retVal)
michael@0 344
michael@0 345 def run_desktop_mochitests(parser, options):
michael@0 346 # create our Marionette instance
michael@0 347 kwargs = {}
michael@0 348 if options.marionette:
michael@0 349 host, port = options.marionette.split(':')
michael@0 350 kwargs['host'] = host
michael@0 351 kwargs['port'] = int(port)
michael@0 352 marionette = Marionette.getMarionetteOrExit(**kwargs)
michael@0 353 mochitest = B2GDesktopMochitest(marionette, options.profile_data_dir)
michael@0 354
michael@0 355 # add a -bin suffix if b2g-bin exists, but just b2g was specified
michael@0 356 if options.app[-4:] != '-bin':
michael@0 357 if os.path.isfile("%s-bin" % options.app):
michael@0 358 options.app = "%s-bin" % options.app
michael@0 359
michael@0 360 options = MochitestOptions.verifyOptions(parser, options, mochitest)
michael@0 361 if options == None:
michael@0 362 sys.exit(1)
michael@0 363
michael@0 364 if options.desktop and not options.profile:
michael@0 365 raise Exception("must specify --profile when specifying --desktop")
michael@0 366
michael@0 367 options.browserArgs += ['-marionette']
michael@0 368
michael@0 369 sys.exit(mochitest.runTests(options, onLaunch=mochitest.startTests))
michael@0 370
michael@0 371 def main():
michael@0 372 parser = B2GOptions()
michael@0 373 options, args = parser.parse_args()
michael@0 374
michael@0 375 if options.desktop:
michael@0 376 run_desktop_mochitests(parser, options)
michael@0 377 else:
michael@0 378 run_remote_mochitests(parser, options)
michael@0 379
michael@0 380 if __name__ == "__main__":
michael@0 381 main()

mercurial