michael@0: #!/usr/bin/env python michael@0: # michael@0: # This Source Code Form is subject to the terms of the Mozilla Public michael@0: # License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: # file, You can obtain one at http://mozilla.org/MPL/2.0/. michael@0: michael@0: import sys michael@0: import os michael@0: sys.path.insert(0, os.path.abspath(os.path.realpath(os.path.dirname(sys.argv[0])))) michael@0: michael@0: import traceback michael@0: from remotexpcshelltests import RemoteXPCShellTestThread, XPCShellRemote, RemoteXPCShellOptions michael@0: from mozdevice import devicemanagerADB, DMError michael@0: michael@0: DEVICE_TEST_ROOT = '/data/local/tests' michael@0: michael@0: michael@0: from marionette import Marionette michael@0: michael@0: class B2GXPCShellTestThread(RemoteXPCShellTestThread): michael@0: # Overridden michael@0: def launchProcess(self, cmd, stdout, stderr, env, cwd): michael@0: try: michael@0: # This returns 1 even when tests pass - hardcode returncode to 0 (bug 773703) michael@0: outputFile = RemoteXPCShellTestThread.launchProcess(self, cmd, stdout, stderr, env, cwd) michael@0: self.shellReturnCode = 0 michael@0: except DMError: michael@0: self.shellReturnCode = -1 michael@0: outputFile = "xpcshelloutput" michael@0: f = open(outputFile, "a") michael@0: f.write("\n%s" % traceback.format_exc()) michael@0: f.close() michael@0: return outputFile michael@0: michael@0: class B2GXPCShellRemote(XPCShellRemote): michael@0: # Overridden michael@0: def setLD_LIBRARY_PATH(self): michael@0: self.env['LD_LIBRARY_PATH'] = '/system/b2g' michael@0: if not self.options.use_device_libs: michael@0: # overwrite /system/b2g if necessary michael@0: XPCShellRemote.setLD_LIBRARY_PATH(self) michael@0: michael@0: # Overridden michael@0: def setupUtilities(self): michael@0: if self.options.clean: michael@0: # Ensure a fresh directory structure for our tests michael@0: self.clean() michael@0: self.device.mkDir(self.options.remoteTestRoot) michael@0: michael@0: XPCShellRemote.setupUtilities(self) michael@0: michael@0: def clean(self): michael@0: print >>sys.stderr, "\nCleaning files from previous run.." michael@0: self.device.removeDir(self.options.remoteTestRoot) michael@0: michael@0: # Overriden michael@0: def setupTestDir(self): michael@0: if self.device._useZip: michael@0: return XPCShellRemote.setupTestDir(self) michael@0: michael@0: for root, dirs, files in os.walk(self.xpcDir): michael@0: for filename in files: michael@0: rel_path = os.path.relpath(os.path.join(root, filename), self.xpcDir) michael@0: test_file = os.path.join(self.remoteScriptsDir, rel_path) michael@0: print 'pushing %s' % test_file michael@0: self.device.pushFile(os.path.join(root, filename), test_file, retryLimit=10) michael@0: michael@0: # Overridden michael@0: def pushLibs(self): michael@0: if not self.options.use_device_libs: michael@0: count = XPCShellRemote.pushLibs(self) michael@0: if not count: michael@0: # couldn't find any libs, fallback to device libs michael@0: self.env['LD_LIBRARY_PATH'] = '/system/b2g' michael@0: self.options.use_device_libs = True michael@0: michael@0: class B2GOptions(RemoteXPCShellOptions): michael@0: michael@0: def __init__(self): michael@0: RemoteXPCShellOptions.__init__(self) michael@0: defaults = {} michael@0: michael@0: self.add_option('--b2gpath', action='store', michael@0: type='string', dest='b2g_path', michael@0: help="Path to B2G repo or qemu dir") michael@0: defaults['b2g_path'] = None michael@0: michael@0: self.add_option('--emupath', action='store', michael@0: type='string', dest='emu_path', michael@0: help="Path to emulator folder (if different " michael@0: "from b2gpath") michael@0: michael@0: self.add_option('--no-clean', action='store_false', michael@0: dest='clean', michael@0: help="Do not clean TESTROOT. Saves [lots of] time") michael@0: defaults['clean'] = True michael@0: michael@0: defaults['emu_path'] = None michael@0: michael@0: self.add_option('--emulator', action='store', michael@0: type='string', dest='emulator', michael@0: help="Architecture of emulator to use: x86 or arm") michael@0: defaults['emulator'] = None michael@0: michael@0: self.add_option('--no-window', action='store_true', michael@0: dest='no_window', michael@0: help="Pass --no-window to the emulator") michael@0: defaults['no_window'] = False michael@0: michael@0: self.add_option('--adbpath', action='store', michael@0: type='string', dest='adb_path', michael@0: help="Path to adb") michael@0: defaults['adb_path'] = 'adb' michael@0: michael@0: self.add_option('--address', action='store', michael@0: type='string', dest='address', michael@0: help="host:port of running Gecko instance to connect to") michael@0: defaults['address'] = None michael@0: michael@0: self.add_option('--use-device-libs', action='store_true', michael@0: dest='use_device_libs', michael@0: help="Don't push .so's") michael@0: defaults['use_device_libs'] = False michael@0: self.add_option("--gecko-path", action="store", michael@0: type="string", dest="geckoPath", michael@0: help="the path to a gecko distribution that should " michael@0: "be installed on the emulator prior to test") michael@0: defaults["geckoPath"] = None michael@0: self.add_option("--logcat-dir", action="store", michael@0: type="string", dest="logcat_dir", michael@0: help="directory to store logcat dump files") michael@0: defaults["logcat_dir"] = None michael@0: self.add_option('--busybox', action='store', michael@0: type='string', dest='busybox', michael@0: help="Path to busybox binary to install on device") michael@0: defaults['busybox'] = None michael@0: michael@0: defaults["remoteTestRoot"] = DEVICE_TEST_ROOT michael@0: defaults['dm_trans'] = 'adb' michael@0: defaults['debugger'] = None michael@0: defaults['debuggerArgs'] = None michael@0: michael@0: self.set_defaults(**defaults) michael@0: michael@0: def verifyRemoteOptions(self, options): michael@0: if options.b2g_path is None: michael@0: self.error("Need to specify a --b2gpath") michael@0: michael@0: if options.geckoPath and not options.emulator: michael@0: self.error("You must specify --emulator if you specify --gecko-path") michael@0: michael@0: if options.logcat_dir and not options.emulator: michael@0: self.error("You must specify --emulator if you specify --logcat-dir") michael@0: return RemoteXPCShellOptions.verifyRemoteOptions(self, options) michael@0: michael@0: def run_remote_xpcshell(parser, options, args): michael@0: options = parser.verifyRemoteOptions(options) michael@0: michael@0: # Create the Marionette instance michael@0: kwargs = {} michael@0: if options.emulator: michael@0: kwargs['emulator'] = options.emulator michael@0: if options.no_window: michael@0: kwargs['noWindow'] = True michael@0: if options.geckoPath: michael@0: kwargs['gecko_path'] = options.geckoPath michael@0: if options.logcat_dir: michael@0: kwargs['logcat_dir'] = options.logcat_dir michael@0: if options.busybox: michael@0: kwargs['busybox'] = options.busybox michael@0: if options.symbolsPath: michael@0: kwargs['symbols_path'] = options.symbolsPath michael@0: if options.b2g_path: michael@0: kwargs['homedir'] = options.emu_path or options.b2g_path michael@0: if options.address: michael@0: host, port = options.address.split(':') michael@0: kwargs['host'] = host michael@0: kwargs['port'] = int(port) michael@0: kwargs['baseurl'] = 'http://%s:%d/' % (host, int(port)) michael@0: if options.emulator: michael@0: kwargs['connectToRunningEmulator'] = True michael@0: marionette = Marionette(**kwargs) michael@0: michael@0: if options.emulator: michael@0: dm = marionette.emulator.dm michael@0: else: michael@0: # Create the DeviceManager instance michael@0: kwargs = {'adbPath': options.adb_path} michael@0: if options.deviceIP: michael@0: kwargs['host'] = options.deviceIP michael@0: kwargs['port'] = options.devicePort michael@0: kwargs['deviceRoot'] = options.remoteTestRoot michael@0: dm = devicemanagerADB.DeviceManagerADB(**kwargs) michael@0: michael@0: if not options.remoteTestRoot: michael@0: options.remoteTestRoot = dm.getDeviceRoot() michael@0: xpcsh = B2GXPCShellRemote(dm, options, args) michael@0: michael@0: # we don't run concurrent tests on mobile michael@0: options.sequential = True michael@0: michael@0: try: michael@0: if not xpcsh.runTests(xpcshell='xpcshell', testdirs=args[0:], michael@0: testClass=B2GXPCShellTestThread, michael@0: mobileArgs=xpcsh.mobileArgs, michael@0: **options.__dict__): michael@0: sys.exit(1) michael@0: except: michael@0: print "Automation Error: Exception caught while running tests" michael@0: traceback.print_exc() michael@0: sys.exit(1) michael@0: michael@0: def main(): michael@0: parser = B2GOptions() michael@0: options, args = parser.parse_args() michael@0: michael@0: run_remote_xpcshell(parser, options, args) michael@0: michael@0: # You usually run this like : michael@0: # python runtestsb2g.py --emulator arm --b2gpath $B2GPATH --manifest $MANIFEST [--xre-path $MOZ_HOST_BIN michael@0: # --adbpath $ADB_PATH michael@0: # ...] michael@0: # michael@0: # For xUnit output you should also pass in --tests-root-dir ..objdir-gecko/_tests michael@0: # --xunit-file ..objdir_gecko/_tests/results.xml michael@0: # --xunit-suite-name xpcshell-tests michael@0: if __name__ == '__main__': michael@0: main()