Bug 753605 - Add emulator classes to build/mobile, r=jmaher, DONTBUILD because NPOTB

This commit is contained in:
Mihnea Balaur 2012-07-19 16:14:44 -07:00
Родитель c87728bbb9
Коммит d8d14d6204
3 изменённых файлов: 427 добавлений и 0 удалений

Просмотреть файл

@ -0,0 +1,80 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
import os
import platform
from emulator import Emulator
class B2GEmulator(Emulator):
def __init__(self, homedir=None, noWindow=False, logcat_dir=None, arch="x86",
emulatorBinary=None, res='480x800', userdata=None,
memory='512', partition_size='512'):
super(B2GEmulator, self).__init__(noWindow=noWindow, logcat_dir=logcat_dir,
arch=arch, emulatorBinary=emulatorBinary,
res=res, userdata=userdata,
memory=memory, partition_size=partition_size)
self.homedir = homedir
if self.homedir is not None:
self.homedir = os.path.expanduser(homedir)
def _check_file(self, filePath):
if not os.path.exists(filePath):
raise Exception(('File not found: %s; did you pass the B2G home '
'directory as the homedir parameter, or set '
'B2G_HOME correctly?') % filePath)
def _check_for_adb(self, host_dir):
self.adb = os.path.join(self.homedir, 'out', 'host', host_dir, 'bin', 'adb')
if not os.path.exists(self.adb):
self.adb = os.path.join(self.homedir, 'bin/adb')
super(B2GEmulator, self)._check_for_adb()
def _locate_files(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)
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")
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")
kernel = "prebuilts/qemu-kernel/arm/kernel-qemu-armv7"
sysdir = "out/target/product/generic"
self.tail_args = ["-cpu", "cortex-a8"]
self._check_for_adb(host_dir)
if not self.binary:
self.binary = os.path.join(self.homedir, binary)
self._check_file(self.binary)
self.kernelImg = os.path.join(self.homedir, kernel)
self._check_file(self.kernelImg)
self.sysDir = os.path.join(self.homedir, sysdir)
self._check_file(self.sysDir)
if not self.dataImg:
self.dataImg = os.path.join(self.sysDir, 'userdata.img')
self._check_file(self.dataImg)

295
build/mobile/emulator.py Normal file
Просмотреть файл

@ -0,0 +1,295 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
from abc import abstractmethod
import datetime
from mozprocess import ProcessHandlerMixin
import multiprocessing
import os
import re
import shutil
import socket
import subprocess
from telnetlib import Telnet
import tempfile
import time
from emulator_battery import EmulatorBattery
class LogcatProc(ProcessHandlerMixin):
"""Process handler for logcat which saves all output to a logfile.
"""
def __init__(self, logfile, cmd, **kwargs):
self.logfile = logfile
kwargs.setdefault('processOutputLine', []).append(self.log_output)
ProcessHandlerMixin.__init__(self, cmd, **kwargs)
def log_output(self, line):
f = open(self.logfile, 'a')
f.write(line + "\n")
f.flush()
class Emulator(object):
deviceRe = re.compile(r"^emulator-(\d+)(\s*)(.*)$")
def __init__(self, noWindow=False, logcat_dir=None, arch="x86",
emulatorBinary=None, res='480x800', userdata=None,
memory='512', partition_size='512'):
self.port = None
self._emulator_launched = False
self.proc = None
self.local_port = None
self.telnet = None
self._tmp_userdata = None
self._adb_started = False
self.logcat_dir = logcat_dir
self.logcat_proc = None
self.arch = arch
self.binary = emulatorBinary
self.memory = str(memory)
self.partition_size = str(partition_size)
self.res = res
self.battery = EmulatorBattery(self)
self.noWindow = noWindow
self.dataImg = userdata
self.copy_userdata = self.dataImg is None
def __del__(self):
if self.telnet:
self.telnet.write('exit\n')
self.telnet.read_all()
@property
def args(self):
qemuArgs = [self.binary,
'-kernel', self.kernelImg,
'-sysdir', self.sysDir,
'-data', self.dataImg]
if self.noWindow:
qemuArgs.append('-no-window')
qemuArgs.extend(['-memory', self.memory,
'-partition-size', self.partition_size,
'-verbose',
'-skin', self.res,
'-gpu', 'on',
'-qemu'] + self.tail_args)
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 _check_for_adb(self):
if not os.path.exists(self.adb):
adb = subprocess.Popen(['which', 'adb'],
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT)
retcode = adb.wait()
if retcode:
raise Exception('adb not found!')
out = adb.stdout.read().strip()
if len(out) and out.find('/') > -1:
self.adb = out
def _run_adb(self, args):
args.insert(0, self.adb)
adb = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
retcode = adb.wait()
if retcode:
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)
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):
if self.is_running and self._emulator_launched:
self.proc.terminate()
self.proc.wait()
if self._adb_started:
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
return retcode
if self.logcat_proc:
self.logcat_proc.kill()
return 0
def _get_adb_devices(self):
offline = set()
online = set()
output = self._run_adb(['devices'])
for line in output.split('\n'):
m = self.deviceRe.match(line)
if m:
if m.group(3) == 'offline':
offline.add(m.group(1))
else:
online.add(m.group(1))
return (online, offline)
def restart(self):
if not self._emulator_launched:
return
self.close()
self.start()
def start_adb(self):
result = self._run_adb(['start-server'])
# We keep track of whether we've started adb or not, so we know
# if we need to kill it.
if 'daemon started successfully' in result:
self._adb_started = True
else:
self._adb_started = False
def connect(self):
self._check_for_adb()
self.start_adb()
online, offline = self._get_adb_devices()
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])
@abstractmethod
def _locate_files(self):
pass
def start(self):
self._locate_files()
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.
self._tmp_userdata = tempfile.mktemp(prefix='emulator')
shutil.copyfile(self.dataImg, self._tmp_userdata)
qemu_args[qemu_args.index('-data') + 1] = self._tmp_userdata
original_online, original_offline = self._get_adb_devices()
self.proc = subprocess.Popen(qemu_args,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
online, offline = self._get_adb_devices()
now = datetime.datetime.now()
while online - original_online == set([]):
time.sleep(1)
if datetime.datetime.now() - now > datetime.timedelta(seconds=60):
raise Exception('timed out waiting for emulator to start')
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'])
def _save_logcat_proc(self, filename, cmd):
self.logcat_proc = LogcatProc(filename, cmd)
self.logcat_proc.run()
self.logcat_proc.waitForFinish()
self.logcat_proc = None
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.path.exists(destlog):
if index == 3:
os.remove(destlog)
else:
self.rotate_log(destlog, index + 1)
shutil.move(srclog, destlog)
def save_logcat(self):
""" Save the output of logcat to a file.
"""
filename = os.path.join(self.logcat_dir, "emulator-%d.log" % self.port)
if os.path.exists(filename):
self.rotate_log(filename)
cmd = [self.adb, '-s', 'emulator-%d' % self.port, 'logcat']
# We do this in a separate process because we call mozprocess's
# waitForFinish method to process logcat's output, and this method
# blocks.
proc = multiprocessing.Process(target=self._save_logcat_proc, args=(filename, cmd))
proc.daemon = True
proc.start()
def setup_port_forwarding(self, remote_port):
""" Set up TCP port forwarding to the specified port on the device,
using any availble local port, and return the local port.
"""
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',
'tcp:%d' % local_port,
'tcp:%d' % remote_port])
self.local_port = local_port
return local_port
def wait_for_port(self, timeout=300):
assert(self.local_port)
starttime = datetime.datetime.now()
while datetime.datetime.now() - starttime < datetime.timedelta(seconds=timeout):
try:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(('localhost', self.local_port))
data = sock.recv(16)
sock.close()
if '"from"' in data:
return True
except:
import traceback
print traceback.format_exc()
time.sleep(1)
return False

Просмотреть файл

@ -0,0 +1,52 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
class EmulatorBattery(object):
def __init__(self, emulator):
self.emulator = emulator
def get_state(self):
status = {}
state = {}
response = self.emulator._run_telnet('power display')
for line in response:
if ':' in line:
field, value = line.split(':')
value = value.strip()
if value == 'true':
value = True
elif value == 'false':
value = False
elif field == 'capacity':
value = float(value)
status[field] = value
state['level'] = status.get('capacity', 0.0) / 100
if status.get('AC') == 'online':
state['charging'] = True
else:
state['charging'] = False
return state
def get_charging(self):
return self.get_state()['charging']
def get_level(self):
return self.get_state()['level']
def set_level(self, level):
self.emulator._run_telnet('power capacity %d' % (level * 100))
def set_charging(self, charging):
if charging:
cmd = 'power ac on'
else:
cmd = 'power ac off'
self.emulator._run_telnet(cmd)
charging = property(get_charging, set_charging)
level = property(get_level, set_level)