Bug 789976 - Add --gecko-path argument to Marionette, r=ahal, DONTBUILD (NPOTB)
authorJonathan Griffin <jgriffin@mozilla.com>
Thu, 27 Sep 2012 13:47:17 -0700
changeset 108361 510300b96e626fd649d9311efcab359c037461ea
parent 108360 7b385ab021186f18d6cd6e54b8cbec463d193a7a
child 108445 2d96ee8d9dd44ac0e80d79b33828f05cef79cedb
push id82
push usershu@rfrn.org
push dateFri, 05 Oct 2012 13:20:22 +0000
reviewersahal, DONTBUILD
bugs789976
milestone18.0a1
Bug 789976 - Add --gecko-path argument to Marionette, r=ahal, DONTBUILD (NPOTB)
testing/marionette/client/marionette/emulator.py
testing/marionette/client/marionette/marionette.py
testing/marionette/client/marionette/marionette_test.py
testing/marionette/client/marionette/runtests.py
--- a/testing/marionette/client/marionette/emulator.py
+++ b/testing/marionette/client/marionette/emulator.py
@@ -31,18 +31,19 @@ class LogcatProc(ProcessHandlerMixin):
         f.write(line + "\n")
         f.flush()
 
 
 class Emulator(object):
 
     deviceRe = re.compile(r"^emulator-(\d+)(\s*)(.*)$")
 
-    def __init__(self, homedir=None, noWindow=False, logcat_dir=None, arch="x86",
-                  emulatorBinary=None, res='480x800', sdcard=None, userdata=None):
+    def __init__(self, homedir=None, noWindow=False, logcat_dir=None,
+                 arch="x86", emulatorBinary=None, res='480x800', sdcard=None,
+                 userdata=None, gecko_path=None):
         self.port = None
         self._emulator_launched = False
         self.proc = None
         self.marionette_port = None
         self.telnet = None
         self._tmp_sdcard = None
         self._tmp_userdata = None
         self._adb_started = False
@@ -54,37 +55,38 @@ class Emulator(object):
         self.battery = EmulatorBattery(self)
         self.homedir = homedir
         self.sdcard = sdcard
         self.noWindow = noWindow
         if self.homedir is not None:
             self.homedir = os.path.expanduser(homedir)
         self.dataImg = userdata
         self.copy_userdata = self.dataImg is None
+        self.gecko_path = gecko_path
 
     def _check_for_b2g(self):
         if self.homedir is None:
             self.homedir = os.getenv('B2G_HOME')
         if self.homedir is None:
             raise Exception('Must define B2G_HOME or pass the homedir parameter')
         self._check_file(self.homedir)
 
-        oldstyle_homedir = os.path.join(self.homedir, 'glue','gonk-ics')
+        oldstyle_homedir = os.path.join(self.homedir, 'glue', 'gonk-ics')
         if os.access(oldstyle_homedir, os.F_OK):
             self.homedir = oldstyle_homedir
 
         if self.arch not in ("x86", "arm"):
             raise Exception("Emulator architecture must be one of x86, arm, got: %s" %
                             self.arch)
 
         host_dir = "linux-x86"
         if platform.system() == "Darwin":
             host_dir = "darwin-x86"
 
-        host_bin_dir = os.path.join("out","host", host_dir, "bin")
+        host_bin_dir = os.path.join("out", "host", host_dir, "bin")
 
         if self.arch == "x86":
             binary = os.path.join(host_bin_dir, "emulator-x86")
             kernel = "prebuilts/qemu-kernel/x86/kernel-qemu"
             sysdir = "out/target/product/generic_x86"
             self.tail_args = []
         else:
             binary = os.path.join(host_bin_dir, "emulator")
@@ -120,20 +122,20 @@ class Emulator(object):
     def _check_file(self, filePath):
         if not os.access(filePath, os.F_OK):
             raise Exception(('File not found: %s; did you pass the B2G home '
                              'directory as the homedir parameter, or set '
                              'B2G_HOME correctly?') % filePath)
 
     @property
     def args(self):
-        qemuArgs =  [ self.binary,
-                      '-kernel', self.kernelImg,
-                      '-sysdir', self.sysDir,
-                      '-data', self.dataImg ]
+        qemuArgs = [self.binary,
+                    '-kernel', self.kernelImg,
+                    '-sysdir', self.sysDir,
+                    '-data', self.dataImg]
         if self._tmp_sdcard:
             qemuArgs.extend(['-sdcard', self._tmp_sdcard])
         if self.noWindow:
             qemuArgs.append('-no-window')
         qemuArgs.extend(['-memory', '512',
                          '-partition-size', '512',
                          '-verbose',
                          '-skin', self.res,
@@ -142,68 +144,72 @@ class Emulator(object):
         return qemuArgs
 
     @property
     def is_running(self):
         if self._emulator_launched:
             return self.proc is not None and self.proc.poll() is None
         else:
             return self.port is not None
- 
+
     def create_sdcard(self, sdcard):
-         self._tmp_sdcard = tempfile.mktemp(prefix='sdcard')
-         sdargs = [self.mksdcard, "-l" , "mySdCard", sdcard, self._tmp_sdcard]
-         sd = subprocess.Popen(sdargs, stdout= subprocess.PIPE, stderr=subprocess.STDOUT)
-         retcode = sd.wait()
-         if retcode: 
-             raise Exception('unable to create sdcard : exit code %d: %s' 
-                              % (retcode, adb.stdout.read()))
-         return None
+        self._tmp_sdcard = tempfile.mktemp(prefix='sdcard')
+        sdargs = [self.mksdcard, "-l", "mySdCard", sdcard, self._tmp_sdcard]
+        sd = subprocess.Popen(sdargs, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+        retcode = sd.wait()
+        if retcode:
+            raise Exception('unable to create sdcard : exit code %d: %s'
+                            % (retcode, sd.stdout.read()))
+        return None
 
     def _check_for_adb(self):
         host_dir = "linux-x86"
         if platform.system() == "Darwin":
             host_dir = "darwin-x86"
         adb = subprocess.Popen(['which', 'adb'],
                                 stdout=subprocess.PIPE,
                                 stderr=subprocess.STDOUT)
         if adb.wait() == 0:
-            self.adb = adb.stdout.read().strip() # remove trailing newline
+            self.adb = adb.stdout.read().strip()  # remove trailing newline
             return
-        adb_paths = [os.path.join(self.homedir,'glue','gonk','out','host',
-                      host_dir ,'bin','adb'),os.path.join(self.homedir, 'out',
-                      'host', host_dir,'bin', 'adb'),os.path.join(self.homedir,
-                      'bin','adb')]
+        adb_paths = [os.path.join(self.homedir, 'glue', 'gonk', 'out', 'host',
+                                  host_dir, 'bin', 'adb'),
+                     os.path.join(self.homedir, 'out', 'host', host_dir,
+                                  'bin', 'adb'),
+                     os.path.join(self.homedir, 'bin', 'adb')]
         for option in adb_paths:
             if os.path.exists(option):
                 self.adb = option
                 return
         raise Exception('adb not found!')
 
     def _run_adb(self, args):
         args.insert(0, self.adb)
+        if self.port:
+            args.insert(1, '-s')
+            args.insert(2, 'emulator-%d' % self.port)
         adb = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
         retcode = adb.wait()
         if retcode:
-            raise Exception('adb terminated with exit code %d: %s' 
+            raise Exception('adb terminated with exit code %d: %s'
                             % (retcode, adb.stdout.read()))
         return adb.stdout.read()
 
     def _get_telnet_response(self, command=None):
         output = []
         assert(self.telnet)
         if command is not None:
             self.telnet.write('%s\n' % command)
         while True:
             line = self.telnet.read_until('\n')
             output.append(line.rstrip())
             if line.startswith('OK'):
                 return output
             elif line.startswith('KO:'):
-                raise Exception ('bad telnet response: %s' % line)
+                raise Exception('bad telnet response: %s' % line)
 
     def _run_telnet(self, command):
         if not self.telnet:
             self.telnet = Telnet('localhost', self.port)
             self._get_telnet_response()
         return self._get_telnet_response(command)
 
     def close(self):
@@ -214,17 +220,17 @@ class Emulator(object):
             self._run_adb(['kill-server'])
             self._adb_started = False
         if self.proc:
             retcode = self.proc.poll()
             self.proc = None
             if self._tmp_userdata:
                 os.remove(self._tmp_userdata)
                 self._tmp_userdata = None
-            if self._tmp_sdcard: 
+            if self._tmp_sdcard:
                 os.remove(self._tmp_sdcard)
                 self._tmp_sdcard = None
             return retcode
         if self.logcat_proc:
             self.logcat_proc.kill()
         return 0
 
     def _get_adb_devices(self):
@@ -264,16 +270,18 @@ class Emulator(object):
         now = datetime.datetime.now()
         while online == set([]):
             time.sleep(1)
             if datetime.datetime.now() - now > datetime.timedelta(seconds=60):
                 raise Exception('timed out waiting for emulator to be available')
             online, offline = self._get_adb_devices()
         self.port = int(list(online)[0])
 
+        self.install_gecko()
+
     def start(self):
         self._check_for_b2g()
         self.start_adb()
 
         qemu_args = self.args[:]
         if self.copy_userdata:
             # Make a copy of the userdata.img for this instance of the emulator
             # to use.
@@ -296,26 +304,42 @@ class Emulator(object):
             online, offline = self._get_adb_devices()
         self.port = int(list(online - original_online)[0])
         self._emulator_launched = True
 
         if self.logcat_dir:
             self.save_logcat()
 
         # setup DNS fix for networking
-        self._run_adb(['-s', 'emulator-%d' % self.port,
-                       'shell', 'setprop', 'net.dns1', '10.0.2.3'])
+        self._run_adb(['shell', 'setprop', 'net.dns1', '10.0.2.3'])
+
+        self.install_gecko()
 
     def _save_logcat_proc(self, filename, cmd):
         self.logcat_proc = LogcatProc(filename, cmd)
         self.logcat_proc.run()
         self.logcat_proc.processOutput()
         self.logcat_proc.waitForFinish()
         self.logcat_proc = None
 
+    def install_gecko(self):
+        """
+        Install gecko into the emulator using adb push.  Restart b2g after the
+        installation.
+        """
+        if not self.gecko_path:
+            return
+        # need to remount so we can write to /system/b2g
+        self._run_adb(['remount'])
+        self._run_adb(['shell', 'stop', 'b2g'])
+        print 'installing gecko binaries'
+        self._run_adb(['push', self.gecko_path, '/system/b2g'])
+        print 'restarting B2G'
+        self._run_adb(['shell', 'start', 'b2g'])
+
     def rotate_log(self, srclog, index=1):
         """ Rotate a logfile, by recursively rotating logs further in the sequence,
             deleting the last file if necessary.
         """
         destlog = os.path.join(self.logcat_dir, 'emulator-%d.%d.log' % (self.port, index))
         if os.access(destlog, os.F_OK):
             if index == 3:
                 os.remove(destlog)
@@ -344,18 +368,17 @@ class Emulator(object):
         """
 
         import socket
         s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
         s.bind(("",0))
         local_port = s.getsockname()[1]
         s.close()
 
-        output = self._run_adb(['-s', 'emulator-%d' % self.port, 
-                                'forward',
+        output = self._run_adb(['forward',
                                 'tcp:%d' % local_port,
                                 'tcp:%d' % remote_port])
 
         self.marionette_port = local_port
 
         return local_port
 
     def wait_for_port(self, timeout=300):
@@ -369,9 +392,8 @@ class Emulator(object):
                 sock.close()
                 if '"from"' in data:
                     return True
             except:
                 import traceback
                 print traceback.format_exc()
             time.sleep(1)
         return False
-
--- a/testing/marionette/client/marionette/marionette.py
+++ b/testing/marionette/client/marionette/marionette.py
@@ -80,53 +80,58 @@ class HTMLElement(object):
 
 
 class Marionette(object):
 
     CONTEXT_CHROME = 'chrome'
     CONTEXT_CONTENT = 'content'
 
     def __init__(self, host='localhost', port=2828, bin=None, profile=None,
-                 emulator=None, sdcard= None, emulatorBinary=None, emulatorImg=None,
-                 emulator_res='480x800', connectToRunningEmulator=False,
-                 homedir=None, baseurl=None, noWindow=False, logcat_dir=None):
+                 emulator=None, sdcard=None, emulatorBinary=None,
+                 emulatorImg=None, emulator_res='480x800', gecko_path=None,
+                 connectToRunningEmulator=False, homedir=None, baseurl=None,
+                 noWindow=False, logcat_dir=None):
         self.host = host
         self.port = self.local_port = port
         self.bin = bin
         self.instance = None
         self.profile = profile
         self.session = None
         self.window = None
         self.emulator = None
         self.extra_emulators = []
         self.homedir = homedir
         self.baseurl = baseurl
         self.noWindow = noWindow
         self.logcat_dir = logcat_dir
+        self.gecko_path = gecko_path
 
         if bin:
             self.instance = GeckoInstance(host=self.host, port=self.port,
                                           bin=self.bin, profile=self.profile)
             self.instance.start()
             assert(self.instance.wait_for_port())
         if emulator:
             self.emulator = Emulator(homedir=homedir,
                                      noWindow=self.noWindow,
                                      logcat_dir=self.logcat_dir,
                                      arch=emulator,
                                      sdcard=sdcard,
                                      emulatorBinary=emulatorBinary,
                                      userdata=emulatorImg,
-                                     res=emulator_res)
+                                     res=emulator_res,
+                                     gecko_path=self.gecko_path)
             self.emulator.start()
             self.port = self.emulator.setup_port_forwarding(self.port)
             assert(self.emulator.wait_for_port())
 
         if connectToRunningEmulator:
-            self.emulator = Emulator(homedir=homedir, logcat_dir=self.logcat_dir)
+            self.emulator = Emulator(homedir=homedir,
+                                     logcat_dir=self.logcat_dir,
+                                     gecko_path=self.gecko_path)
             self.emulator.connect()
             self.port = self.emulator.setup_port_forwarding(self.port)
             assert(self.emulator.wait_for_port())
 
         self.client = MarionetteClient(self.host, self.port)
 
     def __del__(self):
         if self.emulator:
@@ -257,17 +262,17 @@ class Marionette(object):
     def set_search_timeout(self, timeout):
         response = self._send_message('setSearchTimeout', 'ok', value=timeout)
         return response
 
     @property
     def current_window_handle(self):
         self.window = self._send_message('getWindow', 'value')
         return self.window
-    
+
     @property
     def title(self):
         response = self._send_message('getTitle', 'value') 
         return response
 
     @property
     def window_handles(self):
         response = self._send_message('getWindows', 'value')
--- a/testing/marionette/client/marionette/marionette_test.py
+++ b/testing/marionette/client/marionette/marionette_test.py
@@ -124,17 +124,18 @@ class MarionetteTestCase(CommonTestCase)
 
     def get_new_emulator(self):
         self.extra_emulator_index += 1
         if len(self.marionette.extra_emulators) == self.extra_emulator_index:
             qemu  = Marionette(emulator=self.marionette.emulator.arch,
                                emulatorBinary=self.marionette.emulator.binary,
                                homedir=self.marionette.homedir,
                                baseurl=self.marionette.baseurl,
-                               noWindow=self.marionette.noWindow)
+                               noWindow=self.marionette.noWindow,
+                               gecko_path=self.marionette.gecko_path)
             qemu.start_session()
             self.marionette.extra_emulators.append(qemu)
         else:
             qemu = self.marionette.extra_emulators[self.extra_emulator_index]
         return qemu
 
 
 class MarionetteJSTestCase(CommonTestCase):
--- a/testing/marionette/client/marionette/runtests.py
+++ b/testing/marionette/client/marionette/runtests.py
@@ -166,17 +166,18 @@ class MarionetteTextTestRunner(unittest.
 
 class MarionetteTestRunner(object):
 
     def __init__(self, address=None, emulator=None, emulatorBinary=None,
                  emulatorImg=None, emulator_res='480x800', homedir=None,
                  bin=None, profile=None, autolog=False, revision=None,
                  es_server=None, rest_server=None, logger=None,
                  testgroup="marionette", noWindow=False, logcat_dir=None,
-                 xml_output=None, repeat=0, perf=False, perfserv=None):
+                 xml_output=None, repeat=0, perf=False, perfserv=None,
+                 gecko_path=None):
         self.address = address
         self.emulator = emulator
         self.emulatorBinary = emulatorBinary
         self.emulatorImg = emulatorImg
         self.emulator_res = emulator_res
         self.homedir = homedir
         self.bin = bin
         self.profile = profile
@@ -191,16 +192,17 @@ class MarionetteTestRunner(object):
         self.baseurl = None
         self.marionette = None
         self.logcat_dir = logcat_dir
         self.perfrequest = None
         self.xml_output = xml_output
         self.repeat = repeat
         self.perf = perf
         self.perfserv = perfserv
+        self.gecko_path = gecko_path
 
         # set up test handlers
         self.test_handlers = []
         self.register_handlers()
 
         self.reset_test_stats()
 
         if self.logger is None:
@@ -248,30 +250,32 @@ class MarionetteTestRunner(object):
                                          baseurl=self.baseurl)
         elif self.address:
             host, port = self.address.split(':')
             if self.emulator:
                 self.marionette = Marionette(host=host, port=int(port),
                                              connectToRunningEmulator=True,
                                              homedir=self.homedir,
                                              baseurl=self.baseurl,
-                                             logcat_dir=self.logcat_dir)
+                                             logcat_dir=self.logcat_dir,
+                                             gecko_path=self.gecko_path)
             else:
                 self.marionette = Marionette(host=host,
                                              port=int(port),
                                              baseurl=self.baseurl)
         elif self.emulator:
             self.marionette = Marionette(emulator=self.emulator,
                                          emulatorBinary=self.emulatorBinary,
                                          emulatorImg=self.emulatorImg,
                                          emulator_res=self.emulator_res,
                                          homedir=self.homedir,
                                          baseurl=self.baseurl,
                                          noWindow=self.noWindow,
-                                         logcat_dir=self.logcat_dir)
+                                         logcat_dir=self.logcat_dir,
+                                         gecko_path=self.gecko_path)
         else:
             raise Exception("must specify binary, address or emulator")
 
     def post_to_autolog(self, elapsedtime):
         self.logger.info('posting results to autolog')
 
         logfile = None
         if self.emulator:
@@ -599,17 +603,21 @@ def parse_options():
     parser.add_option('--perf-server', dest='perfserv', action='store',
                       default=None,
                       help='dataserver for perf data submission. Entering this value '
                       'will overwrite the perfserv value in any passed .ini files.')
     parser.add_option('--repeat', dest='repeat', action='store', type=int,
                       default=0, help='number of times to repeat the test(s).')
     parser.add_option('-x', '--xml-output', action='store', dest='xml_output',
                       help='XML output.')
- 
+    parser.add_option('--gecko-path', dest='gecko_path', action='store',
+                      default=None,
+                      help='path to B2G gecko binaries that should be '
+                      'installed on the device or emulator')
+
     options, tests = parser.parse_args()
 
     if not tests:
         parser.print_usage()
         parser.exit()
 
     if not options.emulator and not options.address and not options.bin:
         parser.print_usage()
@@ -648,17 +656,18 @@ def startTestRunner(runner_class, option
                           profile=options.profile,
                           noWindow=options.noWindow,
                           revision=options.revision,
                           testgroup=options.testgroup,
                           autolog=options.autolog,
                           xml_output=options.xml_output,
                           repeat=options.repeat,
                           perf=options.perf,
-                          perfserv=options.perfserv)
+                          perfserv=options.perfserv,
+                          gecko_path=options.gecko_path)
     runner.run_tests(tests, testtype=options.type)
     return runner
 
 def cli(runner_class=MarionetteTestRunner):
     options, tests = parse_options()
     runner = startTestRunner(runner_class, options, tests)
     if runner.failed > 0:
         sys.exit(10)