Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
michael@0 | 1 | #!/usr/bin/env python |
michael@0 | 2 | # |
michael@0 | 3 | # This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 5 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. |
michael@0 | 6 | |
michael@0 | 7 | import sys |
michael@0 | 8 | import os |
michael@0 | 9 | sys.path.insert(0, os.path.abspath(os.path.realpath(os.path.dirname(sys.argv[0])))) |
michael@0 | 10 | |
michael@0 | 11 | import traceback |
michael@0 | 12 | from remotexpcshelltests import RemoteXPCShellTestThread, XPCShellRemote, RemoteXPCShellOptions |
michael@0 | 13 | from mozdevice import devicemanagerADB, DMError |
michael@0 | 14 | |
michael@0 | 15 | DEVICE_TEST_ROOT = '/data/local/tests' |
michael@0 | 16 | |
michael@0 | 17 | |
michael@0 | 18 | from marionette import Marionette |
michael@0 | 19 | |
michael@0 | 20 | class B2GXPCShellTestThread(RemoteXPCShellTestThread): |
michael@0 | 21 | # Overridden |
michael@0 | 22 | def launchProcess(self, cmd, stdout, stderr, env, cwd): |
michael@0 | 23 | try: |
michael@0 | 24 | # This returns 1 even when tests pass - hardcode returncode to 0 (bug 773703) |
michael@0 | 25 | outputFile = RemoteXPCShellTestThread.launchProcess(self, cmd, stdout, stderr, env, cwd) |
michael@0 | 26 | self.shellReturnCode = 0 |
michael@0 | 27 | except DMError: |
michael@0 | 28 | self.shellReturnCode = -1 |
michael@0 | 29 | outputFile = "xpcshelloutput" |
michael@0 | 30 | f = open(outputFile, "a") |
michael@0 | 31 | f.write("\n%s" % traceback.format_exc()) |
michael@0 | 32 | f.close() |
michael@0 | 33 | return outputFile |
michael@0 | 34 | |
michael@0 | 35 | class B2GXPCShellRemote(XPCShellRemote): |
michael@0 | 36 | # Overridden |
michael@0 | 37 | def setLD_LIBRARY_PATH(self): |
michael@0 | 38 | self.env['LD_LIBRARY_PATH'] = '/system/b2g' |
michael@0 | 39 | if not self.options.use_device_libs: |
michael@0 | 40 | # overwrite /system/b2g if necessary |
michael@0 | 41 | XPCShellRemote.setLD_LIBRARY_PATH(self) |
michael@0 | 42 | |
michael@0 | 43 | # Overridden |
michael@0 | 44 | def setupUtilities(self): |
michael@0 | 45 | if self.options.clean: |
michael@0 | 46 | # Ensure a fresh directory structure for our tests |
michael@0 | 47 | self.clean() |
michael@0 | 48 | self.device.mkDir(self.options.remoteTestRoot) |
michael@0 | 49 | |
michael@0 | 50 | XPCShellRemote.setupUtilities(self) |
michael@0 | 51 | |
michael@0 | 52 | def clean(self): |
michael@0 | 53 | print >>sys.stderr, "\nCleaning files from previous run.." |
michael@0 | 54 | self.device.removeDir(self.options.remoteTestRoot) |
michael@0 | 55 | |
michael@0 | 56 | # Overriden |
michael@0 | 57 | def setupTestDir(self): |
michael@0 | 58 | if self.device._useZip: |
michael@0 | 59 | return XPCShellRemote.setupTestDir(self) |
michael@0 | 60 | |
michael@0 | 61 | for root, dirs, files in os.walk(self.xpcDir): |
michael@0 | 62 | for filename in files: |
michael@0 | 63 | rel_path = os.path.relpath(os.path.join(root, filename), self.xpcDir) |
michael@0 | 64 | test_file = os.path.join(self.remoteScriptsDir, rel_path) |
michael@0 | 65 | print 'pushing %s' % test_file |
michael@0 | 66 | self.device.pushFile(os.path.join(root, filename), test_file, retryLimit=10) |
michael@0 | 67 | |
michael@0 | 68 | # Overridden |
michael@0 | 69 | def pushLibs(self): |
michael@0 | 70 | if not self.options.use_device_libs: |
michael@0 | 71 | count = XPCShellRemote.pushLibs(self) |
michael@0 | 72 | if not count: |
michael@0 | 73 | # couldn't find any libs, fallback to device libs |
michael@0 | 74 | self.env['LD_LIBRARY_PATH'] = '/system/b2g' |
michael@0 | 75 | self.options.use_device_libs = True |
michael@0 | 76 | |
michael@0 | 77 | class B2GOptions(RemoteXPCShellOptions): |
michael@0 | 78 | |
michael@0 | 79 | def __init__(self): |
michael@0 | 80 | RemoteXPCShellOptions.__init__(self) |
michael@0 | 81 | defaults = {} |
michael@0 | 82 | |
michael@0 | 83 | self.add_option('--b2gpath', action='store', |
michael@0 | 84 | type='string', dest='b2g_path', |
michael@0 | 85 | help="Path to B2G repo or qemu dir") |
michael@0 | 86 | defaults['b2g_path'] = None |
michael@0 | 87 | |
michael@0 | 88 | self.add_option('--emupath', action='store', |
michael@0 | 89 | type='string', dest='emu_path', |
michael@0 | 90 | help="Path to emulator folder (if different " |
michael@0 | 91 | "from b2gpath") |
michael@0 | 92 | |
michael@0 | 93 | self.add_option('--no-clean', action='store_false', |
michael@0 | 94 | dest='clean', |
michael@0 | 95 | help="Do not clean TESTROOT. Saves [lots of] time") |
michael@0 | 96 | defaults['clean'] = True |
michael@0 | 97 | |
michael@0 | 98 | defaults['emu_path'] = None |
michael@0 | 99 | |
michael@0 | 100 | self.add_option('--emulator', action='store', |
michael@0 | 101 | type='string', dest='emulator', |
michael@0 | 102 | help="Architecture of emulator to use: x86 or arm") |
michael@0 | 103 | defaults['emulator'] = None |
michael@0 | 104 | |
michael@0 | 105 | self.add_option('--no-window', action='store_true', |
michael@0 | 106 | dest='no_window', |
michael@0 | 107 | help="Pass --no-window to the emulator") |
michael@0 | 108 | defaults['no_window'] = False |
michael@0 | 109 | |
michael@0 | 110 | self.add_option('--adbpath', action='store', |
michael@0 | 111 | type='string', dest='adb_path', |
michael@0 | 112 | help="Path to adb") |
michael@0 | 113 | defaults['adb_path'] = 'adb' |
michael@0 | 114 | |
michael@0 | 115 | self.add_option('--address', action='store', |
michael@0 | 116 | type='string', dest='address', |
michael@0 | 117 | help="host:port of running Gecko instance to connect to") |
michael@0 | 118 | defaults['address'] = None |
michael@0 | 119 | |
michael@0 | 120 | self.add_option('--use-device-libs', action='store_true', |
michael@0 | 121 | dest='use_device_libs', |
michael@0 | 122 | help="Don't push .so's") |
michael@0 | 123 | defaults['use_device_libs'] = False |
michael@0 | 124 | self.add_option("--gecko-path", action="store", |
michael@0 | 125 | type="string", dest="geckoPath", |
michael@0 | 126 | help="the path to a gecko distribution that should " |
michael@0 | 127 | "be installed on the emulator prior to test") |
michael@0 | 128 | defaults["geckoPath"] = None |
michael@0 | 129 | self.add_option("--logcat-dir", action="store", |
michael@0 | 130 | type="string", dest="logcat_dir", |
michael@0 | 131 | help="directory to store logcat dump files") |
michael@0 | 132 | defaults["logcat_dir"] = None |
michael@0 | 133 | self.add_option('--busybox', action='store', |
michael@0 | 134 | type='string', dest='busybox', |
michael@0 | 135 | help="Path to busybox binary to install on device") |
michael@0 | 136 | defaults['busybox'] = None |
michael@0 | 137 | |
michael@0 | 138 | defaults["remoteTestRoot"] = DEVICE_TEST_ROOT |
michael@0 | 139 | defaults['dm_trans'] = 'adb' |
michael@0 | 140 | defaults['debugger'] = None |
michael@0 | 141 | defaults['debuggerArgs'] = None |
michael@0 | 142 | |
michael@0 | 143 | self.set_defaults(**defaults) |
michael@0 | 144 | |
michael@0 | 145 | def verifyRemoteOptions(self, options): |
michael@0 | 146 | if options.b2g_path is None: |
michael@0 | 147 | self.error("Need to specify a --b2gpath") |
michael@0 | 148 | |
michael@0 | 149 | if options.geckoPath and not options.emulator: |
michael@0 | 150 | self.error("You must specify --emulator if you specify --gecko-path") |
michael@0 | 151 | |
michael@0 | 152 | if options.logcat_dir and not options.emulator: |
michael@0 | 153 | self.error("You must specify --emulator if you specify --logcat-dir") |
michael@0 | 154 | return RemoteXPCShellOptions.verifyRemoteOptions(self, options) |
michael@0 | 155 | |
michael@0 | 156 | def run_remote_xpcshell(parser, options, args): |
michael@0 | 157 | options = parser.verifyRemoteOptions(options) |
michael@0 | 158 | |
michael@0 | 159 | # Create the Marionette instance |
michael@0 | 160 | kwargs = {} |
michael@0 | 161 | if options.emulator: |
michael@0 | 162 | kwargs['emulator'] = options.emulator |
michael@0 | 163 | if options.no_window: |
michael@0 | 164 | kwargs['noWindow'] = True |
michael@0 | 165 | if options.geckoPath: |
michael@0 | 166 | kwargs['gecko_path'] = options.geckoPath |
michael@0 | 167 | if options.logcat_dir: |
michael@0 | 168 | kwargs['logcat_dir'] = options.logcat_dir |
michael@0 | 169 | if options.busybox: |
michael@0 | 170 | kwargs['busybox'] = options.busybox |
michael@0 | 171 | if options.symbolsPath: |
michael@0 | 172 | kwargs['symbols_path'] = options.symbolsPath |
michael@0 | 173 | if options.b2g_path: |
michael@0 | 174 | kwargs['homedir'] = options.emu_path or options.b2g_path |
michael@0 | 175 | if options.address: |
michael@0 | 176 | host, port = options.address.split(':') |
michael@0 | 177 | kwargs['host'] = host |
michael@0 | 178 | kwargs['port'] = int(port) |
michael@0 | 179 | kwargs['baseurl'] = 'http://%s:%d/' % (host, int(port)) |
michael@0 | 180 | if options.emulator: |
michael@0 | 181 | kwargs['connectToRunningEmulator'] = True |
michael@0 | 182 | marionette = Marionette(**kwargs) |
michael@0 | 183 | |
michael@0 | 184 | if options.emulator: |
michael@0 | 185 | dm = marionette.emulator.dm |
michael@0 | 186 | else: |
michael@0 | 187 | # Create the DeviceManager instance |
michael@0 | 188 | kwargs = {'adbPath': options.adb_path} |
michael@0 | 189 | if options.deviceIP: |
michael@0 | 190 | kwargs['host'] = options.deviceIP |
michael@0 | 191 | kwargs['port'] = options.devicePort |
michael@0 | 192 | kwargs['deviceRoot'] = options.remoteTestRoot |
michael@0 | 193 | dm = devicemanagerADB.DeviceManagerADB(**kwargs) |
michael@0 | 194 | |
michael@0 | 195 | if not options.remoteTestRoot: |
michael@0 | 196 | options.remoteTestRoot = dm.getDeviceRoot() |
michael@0 | 197 | xpcsh = B2GXPCShellRemote(dm, options, args) |
michael@0 | 198 | |
michael@0 | 199 | # we don't run concurrent tests on mobile |
michael@0 | 200 | options.sequential = True |
michael@0 | 201 | |
michael@0 | 202 | try: |
michael@0 | 203 | if not xpcsh.runTests(xpcshell='xpcshell', testdirs=args[0:], |
michael@0 | 204 | testClass=B2GXPCShellTestThread, |
michael@0 | 205 | mobileArgs=xpcsh.mobileArgs, |
michael@0 | 206 | **options.__dict__): |
michael@0 | 207 | sys.exit(1) |
michael@0 | 208 | except: |
michael@0 | 209 | print "Automation Error: Exception caught while running tests" |
michael@0 | 210 | traceback.print_exc() |
michael@0 | 211 | sys.exit(1) |
michael@0 | 212 | |
michael@0 | 213 | def main(): |
michael@0 | 214 | parser = B2GOptions() |
michael@0 | 215 | options, args = parser.parse_args() |
michael@0 | 216 | |
michael@0 | 217 | run_remote_xpcshell(parser, options, args) |
michael@0 | 218 | |
michael@0 | 219 | # You usually run this like : |
michael@0 | 220 | # python runtestsb2g.py --emulator arm --b2gpath $B2GPATH --manifest $MANIFEST [--xre-path $MOZ_HOST_BIN |
michael@0 | 221 | # --adbpath $ADB_PATH |
michael@0 | 222 | # ...] |
michael@0 | 223 | # |
michael@0 | 224 | # For xUnit output you should also pass in --tests-root-dir ..objdir-gecko/_tests |
michael@0 | 225 | # --xunit-file ..objdir_gecko/_tests/results.xml |
michael@0 | 226 | # --xunit-suite-name xpcshell-tests |
michael@0 | 227 | if __name__ == '__main__': |
michael@0 | 228 | main() |