зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1274408: Remove emulator support from Marionette Harness code r=maja_zf
The emulator code in Marionette harness makes it very hard to be able to support other platforms. Since we no longer support B2G we should remove it. MozReview-Commit-ID: 21HYtUdtfHy --HG-- extra : rebase_source : 98b027494dd98b924a5a9f64c073c2f9197f1ad2
This commit is contained in:
Родитель
dd43f4c2a6
Коммит
17e6a49b39
|
@ -1,442 +0,0 @@
|
|||
# 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 cStringIO import StringIO
|
||||
import imp
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import time
|
||||
import types
|
||||
import weakref
|
||||
|
||||
from marionette_driver.marionette import Marionette
|
||||
from marionette_driver import transport
|
||||
|
||||
from .marionette_test import MarionetteTestCase
|
||||
|
||||
from b2ginstance import B2GInstance
|
||||
from .runtests import MarionetteTestRunner, cli
|
||||
|
||||
|
||||
class B2GUpdateMarionetteClient(transport.TcpTransport):
|
||||
RETRY_TIMEOUT = 5
|
||||
CONNECT_TIMEOUT = 30
|
||||
SEND_TIMEOUT = 60 * 5
|
||||
MAX_RETRIES = 24
|
||||
|
||||
def __init__(self, addr, port, runner):
|
||||
super(B2GUpdateMarionetteClient, self).__init__(addr, port, self.CONNECT_TIMEOUT)
|
||||
self.runner = runner
|
||||
|
||||
def connect(self):
|
||||
""" When using ADB port forwarding, the ADB server will actually accept
|
||||
connections even though there isn't a listening socket open on the
|
||||
device. Here we add a retry loop since we have to restart the debug
|
||||
server / b2g process for update tests
|
||||
"""
|
||||
for i in range(self.MAX_RETRIES):
|
||||
try:
|
||||
transport.TcpTransport.connect(self)
|
||||
break
|
||||
except:
|
||||
if i == self.MAX_RETRIES - 1:
|
||||
raise
|
||||
|
||||
time.sleep(self.RETRY_TIMEOUT)
|
||||
self.runner.port_forward()
|
||||
|
||||
# Upon success, reset the socket timeout to something more reasonable
|
||||
self.sock.settimeout(self.SEND_TIMEOUT)
|
||||
|
||||
|
||||
class B2GUpdateTestRunner(MarionetteTestRunner):
|
||||
match_re = re.compile(r'update_(smoke)?test_(.*)\.py$')
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
MarionetteTestRunner.__init__(self, **kwargs)
|
||||
if self.emulator or self.bin:
|
||||
raise Exception('Update tests do not support emulator or custom binaries')
|
||||
|
||||
if not self.address:
|
||||
raise Exception('Update tests must be run with '
|
||||
'--address=localhost:<port>')
|
||||
|
||||
self.host, port = self.address.split(':')
|
||||
self.port = int(port)
|
||||
self.test_handlers.append(self)
|
||||
|
||||
self.b2g = B2GInstance(homedir=kwargs.get('homedir'))
|
||||
self.update_tools = self.b2g.import_update_tools()
|
||||
self.adb = self.update_tools.AdbTool(path=self.b2g.adb_path,
|
||||
device=self.device_serial)
|
||||
|
||||
def match(self, filename):
|
||||
return self.match_re.match(filename) is not None
|
||||
|
||||
def add_tests_to_suite(self, mod_name, filepath, suite, testloader,
|
||||
marionette, testvars):
|
||||
""" Here the runner itself is a handler so we can forward along the
|
||||
instance to test cases.
|
||||
"""
|
||||
test_mod = imp.load_source(mod_name, filepath)
|
||||
|
||||
def add_test(testcase, testname, **kwargs):
|
||||
suite.addTest(testcase(weakref.ref(marionette),
|
||||
methodName=testname,
|
||||
filepath=filepath,
|
||||
testvars=testvars,
|
||||
**kwargs))
|
||||
|
||||
# The TestCase classes are apparently being loaded multiple times, so
|
||||
# using "isinstance" doesn't actually work. This function just compares
|
||||
# type names as a close enough analog.
|
||||
def has_super(cls, super_names):
|
||||
if not isinstance(super_names, (tuple, list)):
|
||||
super_names = (super_names)
|
||||
|
||||
base = cls
|
||||
while base:
|
||||
if base.__name__ in super_names:
|
||||
return True
|
||||
base = base.__base__
|
||||
return False
|
||||
|
||||
for name in dir(test_mod):
|
||||
testcase = getattr(test_mod, name)
|
||||
if not isinstance(testcase, (type, types.ClassType)):
|
||||
continue
|
||||
|
||||
# Support both B2GUpdateTestCase and MarionetteTestCase
|
||||
if has_super(testcase, 'B2GUpdateTestCase'):
|
||||
for testname in testloader.getTestCaseNames(testcase):
|
||||
add_test(testcase, testname, runner=self)
|
||||
elif has_super(testcase, ('MarionetteTestCase', 'TestCase')):
|
||||
for testname in testloader.getTestCaseNames(testcase):
|
||||
add_test(testcase, testname)
|
||||
|
||||
def start_marionette(self):
|
||||
MarionetteTestRunner.start_marionette(self)
|
||||
self.marionette.client = B2GUpdateMarionetteClient(self.host,
|
||||
self.port,
|
||||
self)
|
||||
|
||||
def reset(self, b2g_pid):
|
||||
if self.marionette.instance:
|
||||
self.marionette.instance.close()
|
||||
self.marionette.instance = None
|
||||
del self.marionette
|
||||
|
||||
self.start_marionette()
|
||||
self.b2g_pid = b2g_pid
|
||||
return self.marionette
|
||||
|
||||
def find_b2g_pid(self):
|
||||
pids = self.adb.get_pids('b2g')
|
||||
if len(pids) == 0:
|
||||
return None
|
||||
return pids[0]
|
||||
|
||||
def port_forward(self):
|
||||
try:
|
||||
self.adb.run('forward', 'tcp:%d' % self.port, 'tcp:2828')
|
||||
except:
|
||||
# This command causes non-0 return codes even though it succeeds
|
||||
pass
|
||||
|
||||
OTA, FOTA = "OTA", "FOTA"
|
||||
class B2GUpdateTestCase(MarionetteTestCase):
|
||||
""" A high level unit test for an OTA or FOTA update. This test case class
|
||||
has structural support for automatically waiting on an update to apply,
|
||||
and provides additional javascript support for separating a test into
|
||||
'pre-update' and 'post-update' lifecycles.
|
||||
|
||||
See test examples in toolkit/mozapps/update/test/marionette/update_test_*.py
|
||||
"""
|
||||
|
||||
MAX_OTA_WAIT = 60 * 2 # 2 minutes
|
||||
MAX_FOTA_WAIT = 60 * 10 # 10 minutes
|
||||
|
||||
def __init__(self, marionette_weakref, **kwargs):
|
||||
if 'runner' in kwargs:
|
||||
self.runner = kwargs['runner']
|
||||
del kwargs['runner']
|
||||
|
||||
update_test_js = os.path.join(os.path.dirname(__file__), 'atoms',
|
||||
'b2g_update_test.js')
|
||||
with open(update_test_js, 'r') as f:
|
||||
self.update_test_js = f.read()
|
||||
|
||||
self.b2g_pid = self.runner.find_b2g_pid()
|
||||
if not self.b2g_pid:
|
||||
raise Exception('B2G PID could not be found for update test')
|
||||
MarionetteTestCase.__init__(self, marionette_weakref, **kwargs)
|
||||
|
||||
self.testvars = self.testvars or {}
|
||||
self.status_newline = True
|
||||
self.loglines = []
|
||||
|
||||
def print_status(self, status, message=None):
|
||||
if self.status_newline:
|
||||
print ''
|
||||
self.status_newline = False
|
||||
|
||||
status_msg = 'UPDATE-TEST-' + status
|
||||
if message:
|
||||
status_msg += ': ' + message
|
||||
print status_msg
|
||||
|
||||
def setUp(self):
|
||||
MarionetteTestCase.setUp(self)
|
||||
self.marionette.set_context(Marionette.CONTEXT_CHROME)
|
||||
|
||||
def tearDown(self):
|
||||
# This completey overrides MarionetteTestCase.tearDown so we can control
|
||||
# logs being appended between various marionette runs
|
||||
self.marionette.set_context(Marionette.CONTEXT_CONTENT)
|
||||
self.marionette.execute_script("log('TEST-END: %s:%s')" %
|
||||
(self.filepath.replace('\\', '\\\\'), self.methodName))
|
||||
self.marionette.test_name = None
|
||||
|
||||
self.duration = time.time() - self.start_time
|
||||
if self.marionette.session is not None:
|
||||
self.loglines.extend(self.marionette.get_logs())
|
||||
self.marionette.delete_session()
|
||||
self.marionette = None
|
||||
|
||||
def reset(self, b2g_pid):
|
||||
self.print_status('RESET-MARIONETTE')
|
||||
self._marionette_weakref = weakref.ref(self.runner.reset(b2g_pid))
|
||||
self.marionette = self._marionette_weakref()
|
||||
if self.marionette.session is None:
|
||||
self.marionette.start_session()
|
||||
self.marionette.set_context(Marionette.CONTEXT_CHROME)
|
||||
|
||||
def execute_update_test(self, path, apply=None):
|
||||
self.execute_update_js(path, stage='pre-update',
|
||||
will_restart=(apply is not None))
|
||||
if not apply:
|
||||
return
|
||||
|
||||
if self.marionette.session is not None:
|
||||
self.loglines.extend(self.marionette.get_logs())
|
||||
|
||||
# This function will probably force-kill b2g, so we can't capture logs
|
||||
self.execute_update_js(path, stage='apply-update')
|
||||
self.wait_for_update(apply)
|
||||
|
||||
self.execute_update_js(path, stage='post-update')
|
||||
|
||||
def execute_update_js(self, path, stage=None, will_restart=True):
|
||||
data = self.update_test_js[:]
|
||||
with open(path, "r") as f:
|
||||
data += f.read()
|
||||
|
||||
status = 'EXEC'
|
||||
if stage:
|
||||
status += '-' + stage.upper()
|
||||
data += '\nrunUpdateTest("%s");' % stage
|
||||
|
||||
self.print_status(status, os.path.basename(path))
|
||||
|
||||
results = self.marionette.execute_async_script(data,
|
||||
script_args=[self.testvars])
|
||||
self.handle_results(path, stage, results)
|
||||
|
||||
def handle_results(self, path, stage, results):
|
||||
passed = results['passed']
|
||||
failed = results['failed']
|
||||
|
||||
fails = StringIO()
|
||||
stage_msg = ' %s' % stage if stage else ''
|
||||
fails.write('%d%s tests failed:\n' % (failed, stage_msg))
|
||||
|
||||
for failure in results['failures']:
|
||||
diag = failure.get('diag')
|
||||
diag_msg = "" if not diag else "| %s " % diag
|
||||
name = failure.get('name') or 'got false, expected true'
|
||||
fails.write('TEST-UNEXPECTED-FAIL | %s %s| %s\n' %
|
||||
(os.path.basename(path), diag_msg, name))
|
||||
self.assertEqual(0, failed, fails.getvalue())
|
||||
self.assertTrue(passed + failed > 0, 'no tests run')
|
||||
|
||||
def stage_update(self, **kwargs):
|
||||
mar = kwargs.get('complete_mar') or kwargs.get('partial_mar')
|
||||
short_mar = os.path.relpath(mar, os.path.dirname(os.path.dirname(mar)))
|
||||
self.print_status('STAGE', short_mar)
|
||||
|
||||
prefs = kwargs.pop('prefs', {})
|
||||
update_xml = kwargs.get('update_xml')
|
||||
if not update_xml:
|
||||
xml_kwargs = kwargs.copy()
|
||||
if 'update_dir' in xml_kwargs:
|
||||
del xml_kwargs['update_dir']
|
||||
if 'only_override' in xml_kwargs:
|
||||
del xml_kwargs['only_override']
|
||||
builder = self.runner.update_tools.UpdateXmlBuilder(**xml_kwargs)
|
||||
update_xml = builder.build_xml()
|
||||
|
||||
test_kwargs = {
|
||||
'adb_path': self.runner.adb.tool,
|
||||
'update_xml': update_xml,
|
||||
'only_override': kwargs.get('only_override', False),
|
||||
}
|
||||
for key in ('complete_mar', 'partial_mar', 'url_template', 'update_dir'):
|
||||
test_kwargs[key] = kwargs.get(key)
|
||||
|
||||
test_update = self.runner.update_tools.TestUpdate(**test_kwargs)
|
||||
test_update.test_update(write_url_pref=False, restart=False)
|
||||
if 'prefs' not in self.testvars:
|
||||
self.testvars['prefs'] = {}
|
||||
|
||||
self.testvars['prefs']['app.update.url.override'] = test_update.update_url
|
||||
self.testvars['prefs'].update(prefs)
|
||||
|
||||
def wait_for_update(self, type):
|
||||
if type == OTA:
|
||||
self.wait_for_ota_restart()
|
||||
elif type == FOTA:
|
||||
self.wait_for_fota_reboot()
|
||||
|
||||
def wait_for_new_b2g_pid(self, max_wait):
|
||||
for i in range(max_wait):
|
||||
b2g_pid = self.runner.find_b2g_pid()
|
||||
if b2g_pid and b2g_pid != self.b2g_pid:
|
||||
return b2g_pid
|
||||
time.sleep(1)
|
||||
|
||||
return None
|
||||
|
||||
def wait_for_ota_restart(self):
|
||||
self.print_status('WAIT-FOR-OTA-RESTART')
|
||||
|
||||
new_b2g_pid = self.wait_for_new_b2g_pid(self.MAX_OTA_WAIT)
|
||||
if not new_b2g_pid:
|
||||
self.fail('Timed out waiting for B2G process during OTA update')
|
||||
return
|
||||
|
||||
self.reset(new_b2g_pid)
|
||||
|
||||
def wait_for_fota_reboot(self):
|
||||
self.print_status('WAIT-FOR-FOTA-REBOOT')
|
||||
|
||||
# First wait for the device to go offline
|
||||
for i in range(self.MAX_FOTA_WAIT):
|
||||
online = self.runner.adb.get_online_devices()
|
||||
if self.runner.device not in online:
|
||||
break
|
||||
time.sleep(1)
|
||||
|
||||
if i == self.MAX_FOTA_WAIT - 1:
|
||||
self.fail('Timed out waiting for device to go offline during FOTA update')
|
||||
|
||||
# Now wait for the device to come back online
|
||||
for j in range(i, self.MAX_FOTA_WAIT):
|
||||
online = self.runner.adb.get_online_devices()
|
||||
if self.runner.device in online:
|
||||
break
|
||||
time.sleep(1)
|
||||
|
||||
if j == self.MAX_FOTA_WAIT - 1:
|
||||
self.fail('Timed out waiting for device to come back online during FOTA update')
|
||||
|
||||
# Finally wait for the B2G process
|
||||
for k in range(j, self.MAX_FOTA_WAIT):
|
||||
b2g_pid = self.runner.find_b2g_pid()
|
||||
if b2g_pid:
|
||||
self.reset(b2g_pid)
|
||||
return
|
||||
time.sleep(1)
|
||||
|
||||
self.fail('Timed out waiting for B2G process to start during FOTA update')
|
||||
|
||||
def flash(self, flash_script):
|
||||
flash_build = os.path.basename(os.path.dirname(flash_script))
|
||||
self.print_status('FLASH-BUILD', flash_build)
|
||||
|
||||
subprocess.check_call([flash_script, self.runner.device])
|
||||
self.runner.adb.run('wait-for-device')
|
||||
self.runner.port_forward()
|
||||
|
||||
self.b2g_pid = None
|
||||
b2g_pid = self.wait_for_new_b2g_pid(self.MAX_OTA_WAIT)
|
||||
if not b2g_pid:
|
||||
self.fail('Timed out waiting for B2G process to start after a flash')
|
||||
|
||||
self.reset(b2g_pid)
|
||||
|
||||
class B2GUpdateSmokeTestCase(B2GUpdateTestCase):
|
||||
""" An even higher-level update test that is meant to be run with builds
|
||||
and updates generated in an automated build environment.
|
||||
|
||||
Update smoke tests have two main differences from a plain update test:
|
||||
- They are meant to be passed various MARs and system images through the
|
||||
--testvars Marionette argument.
|
||||
- Before each test, the device can be flashed with a specified set of images
|
||||
by the test case.
|
||||
|
||||
See smoketest examples in
|
||||
toolkit/mozapps/update/test/marionette/update_smoketest_*.py
|
||||
"""
|
||||
|
||||
"The path of the Javascript file that has assertions to run"
|
||||
JS_PATH = None
|
||||
|
||||
""" Which build to flash and vars to stage before the test is run.
|
||||
Possible values: 'start', 'finish', or None
|
||||
"""
|
||||
START_WITH_BUILD = 'start'
|
||||
|
||||
"A map of prefs to set while staging, before the test is run"
|
||||
STAGE_PREFS = None
|
||||
|
||||
"Marionette script timeout"
|
||||
TIMEOUT = 2 * 60 * 1000
|
||||
|
||||
"""What kind of update to apply after the 'pre-update' tests have
|
||||
finished. Possible values: OTA, FOTA, None. None means "Don't apply"
|
||||
"""
|
||||
APPLY = OTA
|
||||
|
||||
def setUp(self):
|
||||
if self.START_WITH_BUILD:
|
||||
build = self.testvars[self.START_WITH_BUILD]
|
||||
self.flash(build['flash_script'])
|
||||
|
||||
B2GUpdateTestCase.setUp(self)
|
||||
|
||||
def stage_update(self, build=None, mar=None, **kwargs):
|
||||
if build and not mar:
|
||||
raise Exception('mar required with build')
|
||||
if mar and not build:
|
||||
raise Exception('build required with mar')
|
||||
|
||||
if not build:
|
||||
B2GUpdateTestCase.stage_update(self, **kwargs)
|
||||
return
|
||||
|
||||
build_vars = self.testvars[build]
|
||||
mar_key = mar + '_mar'
|
||||
mar_path = build_vars[mar_key]
|
||||
|
||||
stage_kwargs = {
|
||||
'build_id': build_vars['app_build_id'],
|
||||
'app_version': build_vars['app_version'],
|
||||
'platform_version': build_vars['platform_milestone'],
|
||||
mar_key: mar_path
|
||||
}
|
||||
stage_kwargs.update(kwargs)
|
||||
|
||||
if self.STAGE_PREFS:
|
||||
if 'prefs' not in stage_kwargs:
|
||||
stage_kwargs['prefs'] = self.STAGE_PREFS
|
||||
|
||||
B2GUpdateTestCase.stage_update(self, **stage_kwargs)
|
||||
|
||||
def execute_smoketest(self):
|
||||
self.marionette.set_script_timeout(self.TIMEOUT)
|
||||
self.execute_update_test(self.JS_PATH, apply=self.APPLY)
|
||||
|
||||
if __name__ == '__main__':
|
||||
cli(B2GUpdateTestRunner)
|
|
@ -428,25 +428,6 @@ class CommonTestCase(unittest.TestCase):
|
|||
# consistency.
|
||||
return self.test_name
|
||||
|
||||
def set_up_test_page(self, emulator, url="test.html", permissions=None):
|
||||
emulator.set_context("content")
|
||||
url = emulator.absolute_url(url)
|
||||
emulator.navigate(url)
|
||||
|
||||
if not permissions:
|
||||
return
|
||||
|
||||
emulator.set_context("chrome")
|
||||
emulator.execute_script("""
|
||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
let [url, permissions] = arguments;
|
||||
let uri = Services.io.newURI(url, null, null);
|
||||
permissions.forEach(function (perm) {
|
||||
Services.perms.add(uri, "sms", Components.interfaces.nsIPermissionManager.ALLOW_ACTION);
|
||||
});
|
||||
""", [url, permissions])
|
||||
emulator.set_context("content")
|
||||
|
||||
def setUp(self):
|
||||
# Convert the marionette weakref to an object, just for the
|
||||
# duration of the test; this is deleted in tearDown() to prevent
|
||||
|
@ -463,13 +444,6 @@ permissions.forEach(function (perm) {
|
|||
else:
|
||||
self.marionette.timeouts(self.marionette.TIMEOUT_PAGE, 30000)
|
||||
|
||||
if hasattr(self, 'test_container') and self.test_container:
|
||||
self.switch_into_test_container()
|
||||
elif hasattr(self, 'test_container') and self.test_container is False:
|
||||
if self.marionette.session_capabilities.has_key('b2g') \
|
||||
and self.marionette.session_capabilities['b2g'] == True:
|
||||
self.close_test_container()
|
||||
|
||||
def tearDown(self):
|
||||
pass
|
||||
|
||||
|
@ -496,58 +470,6 @@ permissions.forEach(function (perm) {
|
|||
pass
|
||||
self.marionette = None
|
||||
|
||||
def switch_into_test_container(self):
|
||||
self.marionette.set_context(self.marionette.CONTEXT_CONTENT)
|
||||
|
||||
frame = Wait(self.marionette, timeout=10, interval=0.2).until(element_present(
|
||||
'css selector',
|
||||
'iframe[src*="app://test-container.gaiamobile.org/index.html"]'
|
||||
))
|
||||
|
||||
self.marionette.switch_to_frame(frame)
|
||||
|
||||
def close_test_container(self):
|
||||
self.marionette.set_context(self.marionette.CONTEXT_CONTENT)
|
||||
|
||||
result = self.marionette.execute_async_script("""
|
||||
if((navigator.mozSettings == undefined) || (navigator.mozSettings == null) || (navigator.mozApps == undefined) || (navigator.mozApps == null)) {
|
||||
marionetteScriptFinished(false);
|
||||
return;
|
||||
}
|
||||
let setReq = navigator.mozSettings.createLock().set({'lockscreen.enabled': false});
|
||||
setReq.onsuccess = function() {
|
||||
let appsReq = navigator.mozApps.mgmt.getAll();
|
||||
appsReq.onsuccess = function() {
|
||||
let apps = appsReq.result;
|
||||
for (let i = 0; i < apps.length; i++) {
|
||||
let app = apps[i];
|
||||
if (app.manifest.name === 'Test Container') {
|
||||
window.wrappedJSObject.Service.request('AppWindowManager:kill', app.origin).then(function() {
|
||||
marionetteScriptFinished(true);
|
||||
}).catch(function() {
|
||||
marionetteScriptFinished(false);
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
marionetteScriptFinished(false);
|
||||
}
|
||||
appsReq.onerror = function() {
|
||||
marionetteScriptFinished(false);
|
||||
}
|
||||
}
|
||||
setReq.onerror = function() {
|
||||
marionetteScriptFinished(false);
|
||||
}""", script_timeout=60000)
|
||||
|
||||
if not result:
|
||||
raise Exception('Failed to close Test Container app')
|
||||
|
||||
Wait(self.marionette, timeout=10, interval=0.2).until(element_not_present(
|
||||
'css selector',
|
||||
'iframe[src*="app://test-container.gaiamobile.org/index.html"]'
|
||||
))
|
||||
|
||||
def setup_SpecialPowers_observer(self):
|
||||
self.marionette.set_context("chrome")
|
||||
self.marionette.execute_script("""
|
||||
|
@ -693,7 +615,6 @@ class MarionetteTestCase(CommonTestCase):
|
|||
filepath='', **kwargs):
|
||||
self._marionette_weakref = marionette_weakref
|
||||
self.marionette = None
|
||||
self.extra_emulator_index = -1
|
||||
self.methodName = methodName
|
||||
self.filepath = filepath
|
||||
self.testvars = kwargs.pop('testvars', None)
|
||||
|
@ -753,21 +674,6 @@ class MarionetteTestCase(CommonTestCase):
|
|||
|
||||
CommonTestCase.tearDown(self)
|
||||
|
||||
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,
|
||||
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
|
||||
|
||||
def wait_for_condition(self, method, timeout=30):
|
||||
timeout = float(timeout) + time.time()
|
||||
while time.time() < timeout:
|
||||
|
|
|
@ -68,13 +68,12 @@ class MarionetteTestResult(StructuredTestResult, TestResultCollection):
|
|||
self.testsRun = 0
|
||||
self.result_modifiers = [] # used by mixins to modify the result
|
||||
pid = kwargs.pop('b2g_pid')
|
||||
logcat_stdout = kwargs.pop('logcat_stdout')
|
||||
if pid:
|
||||
if B2GTestResultMixin not in self.__class__.__bases__:
|
||||
bases = [b for b in self.__class__.__bases__]
|
||||
bases.append(B2GTestResultMixin)
|
||||
self.__class__.__bases__ = tuple(bases)
|
||||
B2GTestResultMixin.__init__(self, b2g_pid=pid, logcat_stdout=logcat_stdout)
|
||||
B2GTestResultMixin.__init__(self, b2g_pid=pid)
|
||||
StructuredTestResult.__init__(self, *args, **kwargs)
|
||||
|
||||
@property
|
||||
|
@ -230,7 +229,6 @@ class MarionetteTextTestRunner(StructuredTestRunner):
|
|||
self.capabilities = kwargs.pop('capabilities')
|
||||
self.pre_run_functions = []
|
||||
self.b2g_pid = None
|
||||
self.logcat_stdout = kwargs.pop('logcat_stdout')
|
||||
|
||||
if self.capabilities["device"] != "desktop" and self.capabilities["browserName"] == "B2G":
|
||||
def b2g_pre_run():
|
||||
|
@ -249,7 +247,6 @@ class MarionetteTextTestRunner(StructuredTestRunner):
|
|||
marionette=self.marionette,
|
||||
b2g_pid=self.b2g_pid,
|
||||
logger=self.logger,
|
||||
logcat_stdout=self.logcat_stdout,
|
||||
result_callbacks=self.result_callbacks)
|
||||
|
||||
def run(self, test):
|
||||
|
@ -283,44 +280,11 @@ class BaseMarionetteArguments(ArgumentParser):
|
|||
action='count',
|
||||
help='Increase verbosity to include debug messages with -v, '
|
||||
'and trace messages with -vv.')
|
||||
self.add_argument('--emulator',
|
||||
choices=['x86', 'arm'],
|
||||
help='if no --address is given, then the harness will launch a B2G emulator on which to run '
|
||||
'emulator tests. if --address is given, then the harness assumes you are running an '
|
||||
'emulator already, and will run the emulator tests using that emulator. you need to '
|
||||
'specify which architecture to emulate for both cases')
|
||||
self.add_argument('--emulator-binary',
|
||||
help='launch a specific emulator binary rather than launching from the B2G built emulator')
|
||||
self.add_argument('--emulator-img',
|
||||
help='use a specific image file instead of a fresh one')
|
||||
self.add_argument('--emulator-res',
|
||||
help='set a custom resolution for the emulator'
|
||||
'Example: "480x800"')
|
||||
self.add_argument('--sdcard',
|
||||
help='size of sdcard to create for the emulator')
|
||||
self.add_argument('--no-window',
|
||||
action='store_true',
|
||||
default=False,
|
||||
help='when Marionette launches an emulator, start it with the -no-window argument')
|
||||
self.add_argument('--logcat-dir',
|
||||
dest='logdir',
|
||||
help='directory to store logcat dump files',
|
||||
type=dir_path)
|
||||
self.add_argument('--logcat-stdout',
|
||||
action='store_true',
|
||||
default=False,
|
||||
help='dump adb logcat to stdout')
|
||||
self.add_argument('--address',
|
||||
help='host:port of running Gecko instance to connect to')
|
||||
self.add_argument('--device',
|
||||
dest='device_serial',
|
||||
help='serial ID of a device to use for adb / fastboot')
|
||||
self.add_argument('--adb-host',
|
||||
help='host to use for adb connection')
|
||||
self.add_argument('--adb-port',
|
||||
help='port to use for adb connection')
|
||||
self.add_argument('--homedir',
|
||||
help='home directory of emulator files')
|
||||
self.add_argument('--app',
|
||||
help='application to use')
|
||||
self.add_argument('--app-arg',
|
||||
|
@ -474,26 +438,10 @@ class BaseMarionetteArguments(ArgumentParser):
|
|||
print '{0} does not exist'.format(path)
|
||||
sys.exit(1)
|
||||
|
||||
if not args.emulator and not args.address and not args.binary:
|
||||
print 'must specify --binary, --emulator or --address'
|
||||
if not args.address and not args.binary:
|
||||
print 'must specify --binary, or --address'
|
||||
sys.exit(1)
|
||||
|
||||
if args.emulator and args.binary:
|
||||
print 'can\'t specify both --emulator and --binary'
|
||||
sys.exit(1)
|
||||
|
||||
# check for valid resolution string, strip whitespaces
|
||||
try:
|
||||
if args.emulator_res:
|
||||
dims = args.emulator_res.split('x')
|
||||
assert len(dims) == 2
|
||||
width = str(int(dims[0]))
|
||||
height = str(int(dims[1]))
|
||||
args.emulator_res = 'x'.join([width, height])
|
||||
except:
|
||||
raise ValueError('Invalid emulator resolution format. '
|
||||
'Should be like "480x800".')
|
||||
|
||||
if args.total_chunks is not None and args.this_chunk is None:
|
||||
self.error('You must specify which chunk to run.')
|
||||
|
||||
|
@ -530,40 +478,31 @@ class BaseMarionetteTestRunner(object):
|
|||
textrunnerclass = MarionetteTextTestRunner
|
||||
driverclass = Marionette
|
||||
|
||||
def __init__(self, address=None, emulator=None, emulator_binary=None,
|
||||
emulator_img=None, emulator_res='480x800', homedir=None,
|
||||
def __init__(self, address=None,
|
||||
app=None, app_args=None, binary=None, profile=None,
|
||||
logger=None, no_window=False, logdir=None, logcat_stdout=False,
|
||||
logger=None, logdir=None,
|
||||
repeat=0, testvars=None, tree=None,
|
||||
device_serial=None, symbols_path=None, timeout=None,
|
||||
symbols_path=None, timeout=None,
|
||||
shuffle=False, shuffle_seed=random.randint(0, sys.maxint),
|
||||
sdcard=None, this_chunk=1, total_chunks=1, sources=None,
|
||||
server_root=None, gecko_log=None, result_callbacks=None,
|
||||
adb_host=None, adb_port=None, prefs=None, test_tags=None,
|
||||
prefs=None, test_tags=None,
|
||||
socket_timeout=BaseMarionetteArguments.socket_timeout_default,
|
||||
startup_timeout=None, addons=None, workspace=None,
|
||||
verbose=0, e10s=True, **kwargs):
|
||||
self.address = address
|
||||
self.emulator = emulator
|
||||
self.emulator_binary = emulator_binary
|
||||
self.emulator_img = emulator_img
|
||||
self.emulator_res = emulator_res
|
||||
self.homedir = homedir
|
||||
self.app = app
|
||||
self.app_args = app_args or []
|
||||
self.bin = binary
|
||||
self.profile = profile
|
||||
self.addons = addons
|
||||
self.logger = logger
|
||||
self.no_window = no_window
|
||||
self.httpd = None
|
||||
self.marionette = None
|
||||
self.logdir = logdir
|
||||
self.logcat_stdout = logcat_stdout
|
||||
self.repeat = repeat
|
||||
self.test_kwargs = kwargs
|
||||
self.tree = tree
|
||||
self.device_serial = device_serial
|
||||
self.symbols_path = symbols_path
|
||||
self.timeout = timeout
|
||||
self.socket_timeout = socket_timeout
|
||||
|
@ -582,13 +521,11 @@ class BaseMarionetteTestRunner(object):
|
|||
self.manifest_skipped_tests = []
|
||||
self.tests = []
|
||||
self.result_callbacks = result_callbacks or []
|
||||
self._adb_host = adb_host
|
||||
self._adb_port = adb_port
|
||||
self.prefs = prefs or {}
|
||||
self.test_tags = test_tags
|
||||
self.startup_timeout = startup_timeout
|
||||
self.workspace = workspace
|
||||
# If no workspace is set, default location for logcat and gecko.log is .
|
||||
# If no workspace is set, default location for gecko.log is .
|
||||
# and default location for profile is TMP
|
||||
self.workspace_path = workspace or os.getcwd()
|
||||
self.verbose = verbose
|
||||
|
@ -623,9 +560,6 @@ class BaseMarionetteTestRunner(object):
|
|||
self.logger.info('Using workspace for temporary data: '
|
||||
'"{}"'.format(self.workspace_path))
|
||||
|
||||
if self.emulator and not self.logdir:
|
||||
self.logdir = os.path.join(self.workspace_path or '', 'logcat')
|
||||
|
||||
if not gecko_log:
|
||||
self.gecko_log = os.path.join(self.workspace_path or '', 'gecko.log')
|
||||
else:
|
||||
|
@ -747,12 +681,8 @@ class BaseMarionetteTestRunner(object):
|
|||
os.mkdir(self.logdir)
|
||||
|
||||
kwargs = {
|
||||
'device_serial': self.device_serial,
|
||||
'symbols_path': self.symbols_path,
|
||||
'timeout': self.timeout,
|
||||
'socket_timeout': self.socket_timeout,
|
||||
'adb_host': self._adb_host,
|
||||
'adb_port': self._adb_port,
|
||||
'prefs': self.prefs,
|
||||
'startup_timeout': self.startup_timeout,
|
||||
'verbose': self.verbose,
|
||||
|
@ -769,20 +699,12 @@ class BaseMarionetteTestRunner(object):
|
|||
'gecko_log': self.gecko_log,
|
||||
})
|
||||
|
||||
if self.emulator:
|
||||
kwargs.update({
|
||||
'homedir': self.homedir,
|
||||
'logdir': self.logdir,
|
||||
})
|
||||
|
||||
if self.address:
|
||||
host, port = self.address.split(':')
|
||||
kwargs.update({
|
||||
'host': host,
|
||||
'port': int(port),
|
||||
})
|
||||
if self.emulator:
|
||||
kwargs['connectToRunningEmulator'] = True
|
||||
|
||||
if not self.bin:
|
||||
try:
|
||||
|
@ -792,15 +714,6 @@ class BaseMarionetteTestRunner(object):
|
|||
connection.close()
|
||||
except Exception, e:
|
||||
raise Exception("Connection attempt to %s:%s failed with error: %s" %(host,port,e))
|
||||
elif self.emulator:
|
||||
kwargs.update({
|
||||
'emulator': self.emulator,
|
||||
'emulator_binary': self.emulator_binary,
|
||||
'emulator_img': self.emulator_img,
|
||||
'emulator_res': self.emulator_res,
|
||||
'no_window': self.no_window,
|
||||
'sdcard': self.sdcard,
|
||||
})
|
||||
if self.workspace:
|
||||
kwargs['workspace'] = self.workspace_path
|
||||
return kwargs
|
||||
|
@ -917,10 +830,7 @@ setReq.onerror = function() {
|
|||
self.logger.info("running with e10s: {}".format(self.e10s))
|
||||
version_info = mozversion.get_version(binary=self.bin,
|
||||
sources=self.sources,
|
||||
dm_type=os.environ.get('DM_TRANS', 'adb'),
|
||||
device_serial=self.device_serial,
|
||||
adb_host=self.marionette.adb_host,
|
||||
adb_port=self.marionette.adb_port)
|
||||
dm_type=os.environ.get('DM_TRANS', 'adb') )
|
||||
|
||||
self.logger.suite_start(self.tests,
|
||||
version_info=version_info,
|
||||
|
@ -1091,7 +1001,6 @@ setReq.onerror = function() {
|
|||
runner = self.textrunnerclass(logger=self.logger,
|
||||
marionette=self.marionette,
|
||||
capabilities=self.capabilities,
|
||||
logcat_stdout=self.logcat_stdout,
|
||||
result_callbacks=self.result_callbacks)
|
||||
|
||||
if test_container:
|
||||
|
|
|
@ -67,8 +67,6 @@ def mach_parsed_kwargs(logger):
|
|||
call to mach marionette-test
|
||||
"""
|
||||
return {
|
||||
'adb_host': None,
|
||||
'adb_port': None,
|
||||
'addon': None,
|
||||
'address': None,
|
||||
'app': None,
|
||||
|
@ -76,10 +74,6 @@ def mach_parsed_kwargs(logger):
|
|||
'binary': u'/path/to/firefox',
|
||||
'device_serial': None,
|
||||
'e10s': False,
|
||||
'emulator': None,
|
||||
'emulator_binary': None,
|
||||
'emulator_img': None,
|
||||
'emulator_res': None,
|
||||
'gecko_log': None,
|
||||
'homedir': None,
|
||||
'jsdebugger': False,
|
||||
|
@ -96,7 +90,6 @@ def mach_parsed_kwargs(logger):
|
|||
'log_tbpl_level': None,
|
||||
'log_unittest': None,
|
||||
'log_xunit': None,
|
||||
'logcat_stdout': False,
|
||||
'logdir': None,
|
||||
'logger_name': 'Marionette-based Tests',
|
||||
'no_window': False,
|
||||
|
@ -250,7 +243,7 @@ def test_crash_is_recorded_as_error(empty_marionette_test,
|
|||
# collect results from the empty test
|
||||
result = MarionetteTestResult(
|
||||
marionette=empty_marionette_test._marionette_weakref(),
|
||||
b2g_pid=0, logcat_stdout=False, logger=logger, verbosity=None,
|
||||
b2g_pid=0, logger=logger, verbosity=None,
|
||||
stream=None, descriptions=None,
|
||||
)
|
||||
result.startTest(empty_marionette_test)
|
||||
|
|
|
@ -1,152 +0,0 @@
|
|||
# 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 unittest import skip
|
||||
|
||||
from marionette.marionette_test import MarionetteTestCase, skip_if_desktop, skip_unless_protocol
|
||||
from marionette_driver.errors import MarionetteException, JavascriptException
|
||||
|
||||
|
||||
class TestEmulatorContent(MarionetteTestCase):
|
||||
@skip_if_desktop
|
||||
def test_emulator_cmd(self):
|
||||
self.marionette.set_script_timeout(10000)
|
||||
expected = ["<build>", "OK"]
|
||||
res = self.marionette.execute_async_script(
|
||||
"runEmulatorCmd('avd name', marionetteScriptFinished)");
|
||||
self.assertEqual(res, expected)
|
||||
|
||||
@skip_if_desktop
|
||||
def test_emulator_shell(self):
|
||||
self.marionette.set_script_timeout(10000)
|
||||
expected = ["Hello World!"]
|
||||
res = self.marionette.execute_async_script(
|
||||
"runEmulatorShell(['echo', 'Hello World!'], marionetteScriptFinished)")
|
||||
self.assertEqual(res, expected)
|
||||
|
||||
@skip_if_desktop
|
||||
def test_emulator_order(self):
|
||||
self.marionette.set_script_timeout(10000)
|
||||
with self.assertRaises(MarionetteException):
|
||||
self.marionette.execute_async_script("""
|
||||
runEmulatorCmd("gsm status", function(res) {});
|
||||
marionetteScriptFinished(true);
|
||||
""")
|
||||
|
||||
|
||||
class TestEmulatorChrome(TestEmulatorContent):
|
||||
def setUp(self):
|
||||
super(TestEmulatorChrome, self).setUp()
|
||||
self.marionette.set_context("chrome")
|
||||
|
||||
|
||||
class TestEmulatorScreen(MarionetteTestCase):
|
||||
@skip_if_desktop
|
||||
def test_emulator_orientation(self):
|
||||
self.screen = self.marionette.emulator.screen
|
||||
self.screen.initialize()
|
||||
|
||||
self.assertEqual(self.screen.orientation, self.screen.SO_PORTRAIT_PRIMARY,
|
||||
'Orientation has been correctly initialized.')
|
||||
|
||||
self.screen.orientation = self.screen.SO_PORTRAIT_SECONDARY
|
||||
self.assertEqual(self.screen.orientation, self.screen.SO_PORTRAIT_SECONDARY,
|
||||
'Orientation has been set to portrait-secondary')
|
||||
|
||||
self.screen.orientation = self.screen.SO_LANDSCAPE_PRIMARY
|
||||
self.assertEqual(self.screen.orientation, self.screen.SO_LANDSCAPE_PRIMARY,
|
||||
'Orientation has been set to landscape-primary')
|
||||
|
||||
self.screen.orientation = self.screen.SO_LANDSCAPE_SECONDARY
|
||||
self.assertEqual(self.screen.orientation, self.screen.SO_LANDSCAPE_SECONDARY,
|
||||
'Orientation has been set to landscape-secondary')
|
||||
|
||||
self.screen.orientation = self.screen.SO_PORTRAIT_PRIMARY
|
||||
self.assertEqual(self.screen.orientation, self.screen.SO_PORTRAIT_PRIMARY,
|
||||
'Orientation has been set to portrait-primary')
|
||||
|
||||
|
||||
class TestEmulatorCallbacks(MarionetteTestCase):
|
||||
def setUp(self):
|
||||
MarionetteTestCase.setUp(self)
|
||||
self.original_emulator_cmd = self.marionette._emulator_cmd
|
||||
self.original_emulator_shell = self.marionette._emulator_shell
|
||||
self.marionette._emulator_cmd = self.mock_emulator_cmd
|
||||
self.marionette._emulator_shell = self.mock_emulator_shell
|
||||
|
||||
def tearDown(self):
|
||||
self.marionette._emulator_cmd = self.original_emulator_cmd
|
||||
self.marionette._emulator_shell = self.original_emulator_shell
|
||||
|
||||
def mock_emulator_cmd(self, *args):
|
||||
return self.marionette._send_emulator_result("cmd response")
|
||||
|
||||
def mock_emulator_shell(self, *args):
|
||||
return self.marionette._send_emulator_result("shell response")
|
||||
|
||||
def _execute_emulator(self, action, args):
|
||||
script = "%s(%s, function(res) { marionetteScriptFinished(res); })" % (action, args)
|
||||
return self.marionette.execute_async_script(script)
|
||||
|
||||
def emulator_cmd(self, cmd):
|
||||
return self._execute_emulator("runEmulatorCmd", escape(cmd))
|
||||
|
||||
def emulator_shell(self, *args):
|
||||
js_args = ", ".join(map(escape, args))
|
||||
js_args = "[%s]" % js_args
|
||||
return self._execute_emulator("runEmulatorShell", js_args)
|
||||
|
||||
def test_emulator_cmd_content(self):
|
||||
with self.marionette.using_context("content"):
|
||||
res = self.emulator_cmd("yo")
|
||||
self.assertEqual("cmd response", res)
|
||||
|
||||
def test_emulator_shell_content(self):
|
||||
with self.marionette.using_context("content"):
|
||||
res = self.emulator_shell("first", "second")
|
||||
self.assertEqual("shell response", res)
|
||||
|
||||
@skip_unless_protocol(lambda level: level >= 3)
|
||||
def test_emulator_result_error_content(self):
|
||||
with self.marionette.using_context("content"):
|
||||
with self.assertRaisesRegexp(JavascriptException, "TypeError"):
|
||||
self.marionette.execute_async_script("runEmulatorCmd()")
|
||||
|
||||
def test_emulator_cmd_chrome(self):
|
||||
with self.marionette.using_context("chrome"):
|
||||
res = self.emulator_cmd("yo")
|
||||
self.assertEqual("cmd response", res)
|
||||
|
||||
def test_emulator_shell_chrome(self):
|
||||
with self.marionette.using_context("chrome"):
|
||||
res = self.emulator_shell("first", "second")
|
||||
self.assertEqual("shell response", res)
|
||||
|
||||
@skip_unless_protocol(lambda level: level >= 3)
|
||||
def test_emulator_result_error_chrome(self):
|
||||
with self.marionette.using_context("chrome"):
|
||||
with self.assertRaisesRegexp(JavascriptException, "TypeError"):
|
||||
self.marionette.execute_async_script("runEmulatorCmd()")
|
||||
|
||||
def test_multiple_callbacks(self):
|
||||
res = self.marionette.execute_async_script("""
|
||||
runEmulatorCmd("what");
|
||||
runEmulatorCmd("ho");
|
||||
marionetteScriptFinished("Frobisher");
|
||||
""")
|
||||
self.assertEqual("Frobisher", res)
|
||||
|
||||
# This should work, but requires work on emulator callbacks:
|
||||
"""
|
||||
def test_multiple_nested_callbacks(self):
|
||||
res = self.marionette.execute_async_script('''
|
||||
runEmulatorCmd("what", function(res) {
|
||||
runEmulatorCmd("ho", marionetteScriptFinished);
|
||||
});''')
|
||||
self.assertEqual("cmd response", res)
|
||||
"""
|
||||
|
||||
|
||||
def escape(word):
|
||||
return "'%s'" % word
|
|
@ -90,9 +90,3 @@ class TestGetActiveFrameOOP(MarionetteTestCase):
|
|||
self.marionette.switch_to_frame()
|
||||
self.marionette.switch_to_frame(active_frame2)
|
||||
self.assertTrue("test_oop_2.html" in self.marionette.execute_script("return document.wrappedJSObject.location.href"))
|
||||
|
||||
# NOTE: For some reason the emulator, the contents of the OOP iframes are not
|
||||
# actually rendered, even though the page_source is correct. When this test runs
|
||||
# on a b2g device, the contents do appear
|
||||
# print self.marionette.get_url()
|
||||
# print self.marionette.page_source
|
||||
|
|
|
@ -139,18 +139,6 @@ class TestProto2Command(MessageTestCase):
|
|||
self.assertEqual("name", cmd.name)
|
||||
self.assertEqual("params", cmd.params)
|
||||
|
||||
def test_from_data_emulator_cmd(self):
|
||||
data = {"emulator_cmd": "emulator_cmd"}
|
||||
cmd = Proto2Command.from_data(data)
|
||||
self.assertEqual("runEmulatorCmd", cmd.name)
|
||||
self.assertEqual(data, cmd.params)
|
||||
|
||||
def test_from_data_emulator_shell(self):
|
||||
data = {"emulator_shell": "emulator_shell"}
|
||||
cmd = Proto2Command.from_data(data)
|
||||
self.assertEqual("runEmulatorShell", cmd.name)
|
||||
self.assertEqual(data, cmd.params)
|
||||
|
||||
def test_from_data_unknown(self):
|
||||
with self.assertRaises(ValueError):
|
||||
cmd = Proto2Command.from_data({})
|
||||
|
|
|
@ -32,7 +32,6 @@ skip-if = true # "Bug 896046"
|
|||
[test_typing.py]
|
||||
|
||||
[test_log.py]
|
||||
[test_emulator.py]
|
||||
|
||||
[test_about_pages.py]
|
||||
skip-if = buildapp == 'b2g'
|
||||
|
|
Загрузка…
Ссылка в новой задаче