Merge autoland to central, a=merge

MozReview-Commit-ID: FTkalcNxO2k
This commit is contained in:
Wes Kocher 2016-12-28 16:00:35 -08:00
Родитель 7058ebe59d 642d96f221
Коммит 8294014f54
36 изменённых файлов: 654 добавлений и 372 удалений

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

@ -3,3 +3,4 @@
[browser_EventEmitter.js] [browser_EventEmitter.js]
[browser_Storage.js] [browser_Storage.js]
[browser_Heartbeat.js] [browser_Heartbeat.js]
skip-if = true # bug 1325409

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

@ -140,19 +140,36 @@ normalize_path = normalize_path()
@imports(_from='which', _import='which') @imports(_from='which', _import='which')
@imports(_from='which', _import='WhichError') @imports(_from='which', _import='WhichError')
@imports('itertools') @imports('itertools')
@imports('sys')
@imports(_from='os', _import='pathsep') @imports(_from='os', _import='pathsep')
@imports(_from='os', _import='environ')
def find_program(file, paths=None): def find_program(file, paths=None):
# The following snippet comes from `which` itself, with a slight
# modification to use lowercase extensions, because it's confusing rustup
# (on top of making results not really appealing to the eye).
# Windows has the concept of a list of extensions (PATHEXT env var).
if sys.platform.startswith("win"):
exts = [e.lower()
for e in environ.get("PATHEXT", "").split(pathsep)]
# If '.exe' is not in exts then obviously this is Win9x and
# or a bogus PATHEXT, then use a reasonable default.
if '.exe' not in exts:
exts = ['.com', '.exe', '.bat']
else:
exts = None
try: try:
if is_absolute_or_relative(file): if is_absolute_or_relative(file):
return normalize_path(which(os.path.basename(file), return normalize_path(which(os.path.basename(file),
[os.path.dirname(file)])) [os.path.dirname(file)], exts=exts))
if paths: if paths:
if not isinstance(paths, (list, tuple)): if not isinstance(paths, (list, tuple)):
die("Paths provided to find_program must be a list of strings, " die("Paths provided to find_program must be a list of strings, "
"not %r", paths) "not %r", paths)
paths = list(itertools.chain( paths = list(itertools.chain(
*(p.split(pathsep) for p in paths if p))) *(p.split(pathsep) for p in paths if p)))
return normalize_path(which(file, path=paths)) return normalize_path(which(file, path=paths, exts=exts))
except WhichError: except WhichError:
return None return None

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

@ -188,7 +188,7 @@
</div> </div>
</div> </div>
<div id="propertyContainer" class="theme-separator" tabindex="0"> <div id="propertyContainer" class="theme-separator" tabindex="0" dir="ltr">
</div> </div>
<div id="computedview-no-results" hidden="" data-localization="content=inspector.noProperties"></div> <div id="computedview-no-results" hidden="" data-localization="content=inspector.noProperties"></div>

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

@ -490,7 +490,8 @@ ServoStyleSet::NoteStyleSheetsChanged()
void void
ServoStyleSet::AssertTreeIsClean() ServoStyleSet::AssertTreeIsClean()
{ {
if (Element* root = mPresContext->Document()->GetRootElement()) { DocumentStyleRootIterator iter(mPresContext->Document());
while (Element* root = iter.GetNextStyleRoot()) {
Servo_AssertTreeIsClean(root); Servo_AssertTreeIsClean(root);
} }
} }

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

@ -369,7 +369,17 @@ class RefTest(object):
# Enable leaks detection to its own log file. # Enable leaks detection to its own log file.
self.leakLogFile = os.path.join(profileDir, "runreftest_leaks.log") self.leakLogFile = os.path.join(profileDir, "runreftest_leaks.log")
browserEnv["XPCOM_MEM_BLOAT_LOG"] = self.leakLogFile
# Leak checking was broken in reftest unnoticed for a length of time. During
# this time, a leak slipped into the crashtest suite. The leak checking was
# fixed by bug 1325148, but it couldn't land until the regression in crashtest
# was also fixed or backed out. Rather than waiting and risking new regressions,
# temporarily disable leak checking in crashtest. Fix is tracked by bug 1325215.
if options.suite == 'crashtest' and mozinfo.info['os'] == 'linux':
self.log.warning('WARNING | leakcheck disabled due to bug 1325215')
else:
browserEnv["XPCOM_MEM_BLOAT_LOG"] = self.leakLogFile
return browserEnv return browserEnv
def killNamedOrphans(self, pname): def killNamedOrphans(self, pname):

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

@ -146,11 +146,13 @@ class MachCommands(MachCommandBase):
try: try:
for future in as_completed(futures): for future in as_completed(futures):
output, ret = future.result() output, ret, test_path = future.result()
for line in output: for line in output:
self.log(logging.INFO, 'python-test', {'line': line.rstrip()}, '{line}') self.log(logging.INFO, 'python-test', {'line': line.rstrip()}, '{line}')
if ret and not return_code:
self.log(logging.ERROR, 'python-test', {'test_path': test_path, 'ret': ret}, 'Setting retcode to {ret} from {test_path}')
return_code = return_code or ret return_code = return_code or ret
except KeyboardInterrupt: except KeyboardInterrupt:
# Hack to force stop currently running threads. # Hack to force stop currently running threads.
@ -159,6 +161,7 @@ class MachCommands(MachCommandBase):
thread._threads_queues.clear() thread._threads_queues.clear()
raise raise
self.log(logging.INFO, 'python-test', {'return_code': return_code}, 'Return code from mach python-test: {return_code}')
return return_code return return_code
def _run_python_test(self, test_path): def _run_python_test(self, test_path):
@ -204,4 +207,4 @@ class MachCommands(MachCommandBase):
else: else:
_log('Test passed: {}'.format(test_path)) _log('Test passed: {}'.format(test_path))
return output, return_code return output, return_code, test_path

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

@ -537,6 +537,16 @@ class BaseBootstrapper(object):
cargo_bin = os.path.join(cargo_home, 'bin') cargo_bin = os.path.join(cargo_home, 'bin')
return cargo_home, cargo_bin return cargo_home, cargo_bin
def win_to_msys_path(self, path):
'''Convert a windows-style path to msys style.'''
drive, path = os.path.splitdrive(path)
path = '/'.join(path.split('\\'))
if drive:
if path[0] == '/':
path = path[1:]
path = '/%s/%s' % (drive[:-1], path)
return path
def print_rust_path_advice(self, template, cargo_home, cargo_bin): def print_rust_path_advice(self, template, cargo_home, cargo_bin):
# Suggest ~/.cargo/env if it exists. # Suggest ~/.cargo/env if it exists.
if os.path.exists(os.path.join(cargo_home, 'env')): if os.path.exists(os.path.join(cargo_home, 'env')):
@ -546,7 +556,7 @@ class BaseBootstrapper(object):
# so fall back to a manual PATH update. Bootstrap # so fall back to a manual PATH update. Bootstrap
# only runs under msys, so a unix-style shell command # only runs under msys, so a unix-style shell command
# is appropriate there. # is appropriate there.
cmd = 'export PATH=%s:$PATH' % cargo_bin cmd = 'export PATH=%s:$PATH' % self.win_to_msys_path(cargo_bin)
print(template % { print(template % {
'cargo_bin': cargo_bin, 'cargo_bin': cargo_bin,
'cmd': cmd, 'cmd': cmd,

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

@ -175,7 +175,7 @@ class ConfigureTestSandbox(ConfigureSandbox):
path_out.value = fake_short_path(path_in) path_out.value = fake_short_path(path_in)
return length return length
def which(self, command, path=None): def which(self, command, path=None, exts=None):
for parent in (path or self._search_path): for parent in (path or self._search_path):
c = mozpath.abspath(mozpath.join(parent, command)) c = mozpath.abspath(mozpath.join(parent, command))
for candidate in (c, ensure_exe_extension(c)): for candidate in (c, ensure_exe_extension(c)):

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

@ -475,6 +475,8 @@ def build_macosx_engine_payload(config, task, task_def):
@payload_builder('buildbot-bridge') @payload_builder('buildbot-bridge')
def build_buildbot_bridge_payload(config, task, task_def): def build_buildbot_bridge_payload(config, task, task_def):
del task['extra']['treeherder']
del task['extra']['treeherderEnv']
worker = task['worker'] worker = task['worker']
task_def['payload'] = { task_def['payload'] = {
'buildername': worker['buildername'], 'buildername': worker['buildername'],

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

@ -29,7 +29,7 @@ class TestDVCertificate(PuppeteerMixin, MarionetteTestCase):
with self.marionette.using_context('content'): with self.marionette.using_context('content'):
self.marionette.navigate(self.url) self.marionette.navigate(self.url)
self.assertEqual(self.locationbar.identity_box.get_attribute('className'), self.assertEqual(self.locationbar.identity_box.get_property('className'),
'verifiedDomain') 'verifiedDomain')
# Open the identity popup # Open the identity popup
@ -41,7 +41,7 @@ class TestDVCertificate(PuppeteerMixin, MarionetteTestCase):
cert = self.browser.tabbar.selected_tab.certificate cert = self.browser.tabbar.selected_tab.certificate
# The shown host equals to the certificate # The shown host equals to the certificate
self.assertEqual(self.identity_popup.view.main.host.get_attribute('textContent'), self.assertEqual(self.identity_popup.view.main.host.get_property('textContent'),
cert['commonName']) cert['commonName'])
# Only the secure label is visible in the main view # Only the secure label is visible in the main view
@ -52,7 +52,9 @@ class TestDVCertificate(PuppeteerMixin, MarionetteTestCase):
self.assertEqual(insecure_label.value_of_css_property('display'), 'none') self.assertEqual(insecure_label.value_of_css_property('display'), 'none')
self.identity_popup.view.main.expander.click() self.identity_popup.view.main.expander.click()
Wait(self.marionette).until(lambda _: self.identity_popup.view.security.selected) Wait(self.marionette).until(
lambda _: self.identity_popup.view.security.selected,
message='Security view of identity popup has not been selected.')
# Only the secure label is visible in the security view # Only the secure label is visible in the security view
secure_label = self.identity_popup.view.security.secure_connection_label secure_label = self.identity_popup.view.security.secure_connection_label
@ -62,7 +64,7 @@ class TestDVCertificate(PuppeteerMixin, MarionetteTestCase):
self.assertEqual(insecure_label.value_of_css_property('display'), 'none') self.assertEqual(insecure_label.value_of_css_property('display'), 'none')
verifier_label = self.browser.localize_property('identity.identified.verifier') verifier_label = self.browser.localize_property('identity.identified.verifier')
self.assertEqual(self.identity_popup.view.security.verifier.get_attribute('textContent'), self.assertEqual(self.identity_popup.view.security.verifier.get_property('textContent'),
verifier_label.replace("%S", cert['issuerOrganization'])) verifier_label.replace("%S", cert['issuerOrganization']))
def opener(mn): def opener(mn):
@ -73,11 +75,11 @@ class TestDVCertificate(PuppeteerMixin, MarionetteTestCase):
self.assertEqual(deck.selected_panel, deck.security) self.assertEqual(deck.selected_panel, deck.security)
self.assertEqual(deck.security.domain.get_attribute('value'), self.assertEqual(deck.security.domain.get_property('value'),
cert['commonName']) cert['commonName'])
self.assertEqual(deck.security.owner.get_attribute('value'), self.assertEqual(deck.security.owner.get_property('value'),
page_info_window.localize_property('securityNoOwner')) page_info_window.localize_property('securityNoOwner'))
self.assertEqual(deck.security.verifier.get_attribute('value'), self.assertEqual(deck.security.verifier.get_property('value'),
cert['issuerOrganization']) cert['issuerOrganization'])

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

@ -30,7 +30,7 @@ class TestEVCertificate(PuppeteerMixin, MarionetteTestCase):
self.marionette.navigate(self.url) self.marionette.navigate(self.url)
# Check the identity box # Check the identity box
self.assertEqual(self.locationbar.identity_box.get_attribute('className'), self.assertEqual(self.locationbar.identity_box.get_property('className'),
'verifiedIdentity') 'verifiedIdentity')
# Get the information from the certificate # Get the information from the certificate
@ -38,9 +38,9 @@ class TestEVCertificate(PuppeteerMixin, MarionetteTestCase):
address = self.puppeteer.security.get_address_from_certificate(cert) address = self.puppeteer.security.get_address_from_certificate(cert)
# Check the identity popup label displays # Check the identity popup label displays
self.assertEqual(self.locationbar.identity_organization_label.get_attribute('value'), self.assertEqual(self.locationbar.identity_organization_label.get_property('value'),
cert['organization']) cert['organization'])
self.assertEqual(self.locationbar.identity_country_label.get_attribute('value'), self.assertEqual(self.locationbar.identity_country_label.get_property('value'),
'(' + address['country'] + ')') '(' + address['country'] + ')')
# Open the identity popup # Open the identity popup
@ -50,7 +50,7 @@ class TestEVCertificate(PuppeteerMixin, MarionetteTestCase):
self.assertEqual(self.identity_popup.element.get_attribute('connection'), 'secure-ev') self.assertEqual(self.identity_popup.element.get_attribute('connection'), 'secure-ev')
# For EV certificates no hostname but the organization name is shown # For EV certificates no hostname but the organization name is shown
self.assertEqual(self.identity_popup.view.main.host.get_attribute('textContent'), self.assertEqual(self.identity_popup.view.main.host.get_property('textContent'),
cert['organization']) cert['organization'])
# Only the secure label is visible in the main view # Only the secure label is visible in the main view
@ -73,8 +73,7 @@ class TestEVCertificate(PuppeteerMixin, MarionetteTestCase):
self.assertEqual(insecure_label.value_of_css_property('display'), 'none') self.assertEqual(insecure_label.value_of_css_property('display'), 'none')
# Check the organization name # Check the organization name
self.assertEqual(security_view.owner.get_attribute('textContent'), self.assertEqual(security_view.owner.get_property('textContent'), cert['organization'])
cert['organization'])
# Check the owner location string # Check the owner location string
# More information: # More information:
@ -82,14 +81,12 @@ class TestEVCertificate(PuppeteerMixin, MarionetteTestCase):
location = self.browser.localize_property('identity.identified.state_and_country') location = self.browser.localize_property('identity.identified.state_and_country')
location = location.replace('%S', address['state'], 1).replace('%S', address['country']) location = location.replace('%S', address['state'], 1).replace('%S', address['country'])
location = address['city'] + '\n' + location location = address['city'] + '\n' + location
self.assertEqual(security_view.owner_location.get_attribute('textContent'), self.assertEqual(security_view.owner_location.get_property('textContent'), location)
location)
# Check the verifier # Check the verifier
l10n_verifier = self.browser.localize_property('identity.identified.verifier') l10n_verifier = self.browser.localize_property('identity.identified.verifier')
l10n_verifier = l10n_verifier.replace('%S', cert['issuerOrganization']) l10n_verifier = l10n_verifier.replace('%S', cert['issuerOrganization'])
self.assertEqual(security_view.verifier.get_attribute('textContent'), self.assertEqual(security_view.verifier.get_property('textContent'), l10n_verifier)
l10n_verifier)
# Open the Page Info window by clicking the More Information button # Open the Page Info window by clicking the More Information button
page_info = self.browser.open_page_info_window( page_info = self.browser.open_page_info_window(
@ -101,14 +98,14 @@ class TestEVCertificate(PuppeteerMixin, MarionetteTestCase):
# Verify the domain listed on the security panel # Verify the domain listed on the security panel
self.assertIn(cert['commonName'], self.assertIn(cert['commonName'],
page_info.deck.security.domain.get_attribute('value')) page_info.deck.security.domain.get_property('value'))
# Verify the owner listed on the security panel # Verify the owner listed on the security panel
self.assertEqual(page_info.deck.security.owner.get_attribute('value'), self.assertEqual(page_info.deck.security.owner.get_property('value'),
cert['organization']) cert['organization'])
# Verify the verifier listed on the security panel # Verify the verifier listed on the security panel
self.assertEqual(page_info.deck.security.verifier.get_attribute('value'), self.assertEqual(page_info.deck.security.verifier.get_property('value'),
cert['issuerOrganization']) cert['issuerOrganization'])
finally: finally:
page_info.close() page_info.close()

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

@ -25,7 +25,7 @@ class TestMixedContentPage(PuppeteerMixin, MarionetteTestCase):
with self.marionette.using_context('content'): with self.marionette.using_context('content'):
self.marionette.navigate(self.url) self.marionette.navigate(self.url)
self.assertEqual(self.locationbar.identity_box.get_attribute('className'), self.assertEqual(self.locationbar.identity_box.get_property('className'),
'unknownIdentity mixedDisplayContent') 'unknownIdentity mixedDisplayContent')
# Open the identity popup # Open the identity popup

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

@ -46,7 +46,7 @@ class TestMixedScriptContentBlocking(PuppeteerMixin, MarionetteTestCase):
# First call to Wait() needs a longer timeout due to the reload of the web page. # First call to Wait() needs a longer timeout due to the reload of the web page.
Wait(self.marionette, timeout=self.marionette.timeout.page_load).until( Wait(self.marionette, timeout=self.marionette.timeout.page_load).until(
lambda _: self.locationbar.identity_box.get_attribute('className') == identity, lambda _: self.locationbar.identity_box.get_property('className') == identity,
message='Expected identity "{}" not found'.format(identity) message='Expected identity "{}" not found'.format(identity)
) )

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

@ -39,7 +39,7 @@ class TestNoCertificate(PuppeteerMixin, MarionetteTestCase):
self.assertFalse(favicon_hidden, 'The identity icon is visible') self.assertFalse(favicon_hidden, 'The identity icon is visible')
# Check that the identity box organization label is blank # Check that the identity box organization label is blank
self.assertEqual(self.locationbar.identity_organization_label.get_attribute('value'), '', self.assertEqual(self.locationbar.identity_organization_label.get_property('value'), '',
'The organization has no label') 'The organization has no label')
# Open the identity popup # Open the identity popup
@ -70,12 +70,12 @@ class TestNoCertificate(PuppeteerMixin, MarionetteTestCase):
# Check the domain listed on the security panel contains the url's host name # Check the domain listed on the security panel contains the url's host name
self.assertIn(urlparse(self.url).hostname, self.assertIn(urlparse(self.url).hostname,
page_info.deck.security.domain.get_attribute('value')) page_info.deck.security.domain.get_property('value'))
# Check the owner label equals localized 'securityNoOwner' # Check the owner label equals localized 'securityNoOwner'
self.assertEqual(page_info.deck.security.owner.get_attribute('value'), self.assertEqual(page_info.deck.security.owner.get_property('value'),
page_info.localize_property('securityNoOwner')) page_info.localize_property('securityNoOwner'))
# Check the verifier label equals localized 'notset' # Check the verifier label equals localized 'notset'
self.assertEqual(page_info.deck.security.verifier.get_attribute('value'), self.assertEqual(page_info.deck.security.verifier.get_property('value'),
page_info.localize_property('notset')) page_info.localize_property('notset'))

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

@ -50,7 +50,7 @@ class TestSecurityNotification(PuppeteerMixin, MarionetteTestCase):
self.marionette.navigate(self.urls[1]) self.marionette.navigate(self.urls[1])
Wait(self.marionette).until(lambda _: ( Wait(self.marionette).until(lambda _: (
self.identity_box.get_attribute('className') == 'verifiedIdentity') self.identity_box.get_property('className') == 'verifiedIdentity')
) )
def test_insecure_website(self): def test_insecure_website(self):
@ -58,5 +58,5 @@ class TestSecurityNotification(PuppeteerMixin, MarionetteTestCase):
self.marionette.navigate(self.urls[2]) self.marionette.navigate(self.urls[2])
Wait(self.marionette).until(lambda _: ( Wait(self.marionette).until(lambda _: (
self.identity_box.get_attribute('className') == 'unknownIdentity') self.identity_box.get_property('className') == 'unknownIdentity')
) )

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

@ -76,7 +76,7 @@ class TestSSLStatusAfterRestart(PuppeteerMixin, MarionetteTestCase):
self.locationbar.open_identity_popup() self.locationbar.open_identity_popup()
# Check the type shown on the idenity popup doorhanger # Check the type shown on the identity popup doorhanger
self.assertEqual(self.identity_popup.element.get_attribute('connection'), self.assertEqual(self.identity_popup.element.get_attribute('connection'),
cert_type) cert_type)
@ -84,7 +84,7 @@ class TestSSLStatusAfterRestart(PuppeteerMixin, MarionetteTestCase):
Wait(self.marionette).until(lambda _: self.identity_popup.view.security.selected) Wait(self.marionette).until(lambda _: self.identity_popup.view.security.selected)
# Check the identity label # Check the identity label
self.assertEqual(self.locationbar.identity_organization_label.get_attribute('value'), self.assertEqual(self.locationbar.identity_organization_label.get_property('value'),
identity) identity)
# Get the information from the certificate # Get the information from the certificate
@ -101,10 +101,10 @@ class TestSSLStatusAfterRestart(PuppeteerMixin, MarionetteTestCase):
# If this is a wildcard cert, check only the domain # If this is a wildcard cert, check only the domain
if cert['commonName'].startswith('*'): if cert['commonName'].startswith('*'):
self.assertIn(self.puppeteer.security.get_domain_from_common_name(cert['commonName']), self.assertIn(self.puppeteer.security.get_domain_from_common_name(cert['commonName']),
page_info.deck.security.domain.get_attribute('value'), page_info.deck.security.domain.get_property('value'),
'Expected domain found in certificate for ' + url) 'Expected domain found in certificate for ' + url)
else: else:
self.assertEqual(page_info.deck.security.domain.get_attribute('value'), self.assertEqual(page_info.deck.security.domain.get_property('value'),
cert['commonName'], cert['commonName'],
'Domain value matches certificate common name.') 'Domain value matches certificate common name.')
@ -114,11 +114,11 @@ class TestSSLStatusAfterRestart(PuppeteerMixin, MarionetteTestCase):
else: else:
owner = page_info.localize_property('securityNoOwner') owner = page_info.localize_property('securityNoOwner')
self.assertEqual(page_info.deck.security.owner.get_attribute('value'), owner, self.assertEqual(page_info.deck.security.owner.get_property('value'), owner,
'Expected owner label found for ' + url) 'Expected owner label found for ' + url)
# Verify the verifier listed on the security panel # Verify the verifier listed on the security panel
self.assertEqual(page_info.deck.security.verifier.get_attribute('value'), self.assertEqual(page_info.deck.security.verifier.get_property('value'),
cert['issuerOrganization'], cert['issuerOrganization'],
'Verifier matches issuer of certificate for ' + url) 'Verifier matches issuer of certificate for ' + url)
page_info.close() page_info.close()

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

@ -27,31 +27,31 @@ class TestAboutWindow(PuppeteerMixin, MarionetteTestCase):
"""Test correct retrieval of elements.""" """Test correct retrieval of elements."""
self.assertNotEqual(self.about_window.dtds, []) self.assertNotEqual(self.about_window.dtds, [])
self.assertEqual(self.deck.element.get_attribute('localName'), 'deck') self.assertEqual(self.deck.element.get_property('localName'), 'deck')
# apply panel # apply panel
panel = self.deck.apply panel = self.deck.apply
self.assertEqual(panel.element.get_attribute('localName'), 'hbox') self.assertEqual(panel.element.get_property('localName'), 'hbox')
self.assertEqual(panel.button.get_attribute('localName'), 'button') self.assertEqual(panel.button.get_property('localName'), 'button')
# check_for_updates panel # check_for_updates panel
panel = self.deck.check_for_updates panel = self.deck.check_for_updates
self.assertEqual(panel.element.get_attribute('localName'), 'hbox') self.assertEqual(panel.element.get_property('localName'), 'hbox')
self.assertEqual(panel.button.get_attribute('localName'), 'button') self.assertEqual(panel.button.get_property('localName'), 'button')
# checking_for_updates panel # checking_for_updates panel
self.assertEqual(self.deck.checking_for_updates.element.get_attribute('localName'), 'hbox') self.assertEqual(self.deck.checking_for_updates.element.get_property('localName'), 'hbox')
# download_and_install panel # download_and_install panel
panel = self.deck.download_and_install panel = self.deck.download_and_install
self.assertEqual(panel.element.get_attribute('localName'), 'hbox') self.assertEqual(panel.element.get_property('localName'), 'hbox')
self.assertEqual(panel.button.get_attribute('localName'), 'button') self.assertEqual(panel.button.get_property('localName'), 'button')
# download_failed panel # download_failed panel
self.assertEqual(self.deck.download_failed.element.get_attribute('localName'), 'hbox') self.assertEqual(self.deck.download_failed.element.get_property('localName'), 'hbox')
# downloading panel # downloading panel
self.assertEqual(self.deck.downloading.element.get_attribute('localName'), 'hbox') self.assertEqual(self.deck.downloading.element.get_property('localName'), 'hbox')
def test_open_window(self): def test_open_window(self):
"""Test various opening strategies.""" """Test various opening strategies."""

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

@ -21,32 +21,32 @@ class TestPageInfoWindow(PuppeteerMixin, MarionetteTestCase):
self.assertNotEqual(page_info.dtds, []) self.assertNotEqual(page_info.dtds, [])
self.assertNotEqual(page_info.properties, []) self.assertNotEqual(page_info.properties, [])
self.assertEqual(page_info.deck.element.get_attribute('localName'), 'deck') self.assertEqual(page_info.deck.element.get_property('localName'), 'deck')
# feed panel # feed panel
self.assertEqual(page_info.deck.feed.element.get_attribute('localName'), 'vbox') self.assertEqual(page_info.deck.feed.element.get_property('localName'), 'vbox')
# general panel # general panel
self.assertEqual(page_info.deck.general.element.get_attribute('localName'), 'vbox') self.assertEqual(page_info.deck.general.element.get_property('localName'), 'vbox')
# media panel # media panel
self.assertEqual(page_info.deck.media.element.get_attribute('localName'), 'vbox') self.assertEqual(page_info.deck.media.element.get_property('localName'), 'vbox')
# permissions panel # permissions panel
self.assertEqual(page_info.deck.permissions.element.get_attribute('localName'), 'vbox') self.assertEqual(page_info.deck.permissions.element.get_property('localName'), 'vbox')
# security panel # security panel
panel = page_info.deck.select(page_info.deck.security) panel = page_info.deck.select(page_info.deck.security)
self.assertEqual(panel.element.get_attribute('localName'), 'vbox') self.assertEqual(panel.element.get_property('localName'), 'vbox')
self.assertEqual(panel.domain.get_attribute('localName'), 'textbox') self.assertEqual(panel.domain.get_property('localName'), 'textbox')
self.assertEqual(panel.owner.get_attribute('localName'), 'textbox') self.assertEqual(panel.owner.get_property('localName'), 'textbox')
self.assertEqual(panel.verifier.get_attribute('localName'), 'textbox') self.assertEqual(panel.verifier.get_property('localName'), 'textbox')
self.assertEqual(panel.view_certificate.get_attribute('localName'), 'button') self.assertEqual(panel.view_certificate.get_property('localName'), 'button')
self.assertEqual(panel.view_cookies.get_attribute('localName'), 'button') self.assertEqual(panel.view_cookies.get_property('localName'), 'button')
self.assertEqual(panel.view_passwords.get_attribute('localName'), 'button') self.assertEqual(panel.view_passwords.get_property('localName'), 'button')
def test_select(self): def test_select(self):
"""Test properties and methods for switching between panels.""" """Test properties and methods for switching between panels."""

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

@ -23,8 +23,8 @@ class TestTabBar(PuppeteerMixin, MarionetteTestCase):
self.assertEqual(len(tabbar.tabs), 1) self.assertEqual(len(tabbar.tabs), 1)
self.assertEqual(tabbar.tabs[0].handle, self.marionette.current_window_handle) self.assertEqual(tabbar.tabs[0].handle, self.marionette.current_window_handle)
self.assertEqual(tabbar.newtab_button.get_attribute('localName'), 'toolbarbutton') self.assertEqual(tabbar.newtab_button.get_property('localName'), 'toolbarbutton')
self.assertEqual(tabbar.toolbar.get_attribute('localName'), 'tabs') self.assertEqual(tabbar.toolbar.get_property('localName'), 'tabs')
def test_open_close(self): def test_open_close(self):
tabbar = self.browser.tabbar tabbar = self.browser.tabbar
@ -127,8 +127,8 @@ class TestTab(PuppeteerMixin, MarionetteTestCase):
self.assertEqual(tab.window, self.browser) self.assertEqual(tab.window, self.browser)
self.assertEqual(tab.tab_element.get_attribute('localName'), 'tab') self.assertEqual(tab.tab_element.get_property('localName'), 'tab')
self.assertEqual(tab.close_button.get_attribute('localName'), 'toolbarbutton') self.assertEqual(tab.close_button.get_property('localName'), 'toolbarbutton')
def test_certificate(self): def test_certificate(self):
url = self.marionette.absolute_url('layout/mozilla.html') url = self.marionette.absolute_url('layout/mozilla.html')

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

@ -26,11 +26,11 @@ class TestNavBar(PuppeteerMixin, MarionetteTestCase):
""") """)
def test_elements(self): def test_elements(self):
self.assertEqual(self.navbar.back_button.get_attribute('localName'), 'toolbarbutton') self.assertEqual(self.navbar.back_button.get_property('localName'), 'toolbarbutton')
self.assertEqual(self.navbar.forward_button.get_attribute('localName'), 'toolbarbutton') self.assertEqual(self.navbar.forward_button.get_property('localName'), 'toolbarbutton')
self.assertEqual(self.navbar.home_button.get_attribute('localName'), 'toolbarbutton') self.assertEqual(self.navbar.home_button.get_property('localName'), 'toolbarbutton')
self.assertEqual(self.navbar.menu_button.get_attribute('localName'), 'toolbarbutton') self.assertEqual(self.navbar.menu_button.get_property('localName'), 'toolbarbutton')
self.assertEqual(self.navbar.toolbar.get_attribute('localName'), 'toolbar') self.assertEqual(self.navbar.toolbar.get_property('localName'), 'toolbar')
def test_buttons(self): def test_buttons(self):
self.marionette.set_context('content') self.marionette.set_context('content')
@ -85,24 +85,24 @@ class TestLocationBar(PuppeteerMixin, MarionetteTestCase):
self.locationbar = self.browser.navbar.locationbar self.locationbar = self.browser.navbar.locationbar
def test_elements(self): def test_elements(self):
self.assertEqual(self.locationbar.urlbar.get_attribute('localName'), 'textbox') self.assertEqual(self.locationbar.urlbar.get_property('localName'), 'textbox')
self.assertIn('urlbar-input', self.locationbar.urlbar_input.get_attribute('className')) self.assertIn('urlbar-input', self.locationbar.urlbar_input.get_property('className'))
self.assertEqual(self.locationbar.connection_icon.get_attribute('localName'), 'image') self.assertEqual(self.locationbar.connection_icon.get_property('localName'), 'image')
self.assertEqual(self.locationbar.identity_box.get_attribute('localName'), 'box') self.assertEqual(self.locationbar.identity_box.get_property('localName'), 'box')
self.assertEqual(self.locationbar.identity_country_label.get_attribute('localName'), self.assertEqual(self.locationbar.identity_country_label.get_property('localName'),
'label') 'label')
self.assertEqual(self.locationbar.identity_organization_label.get_attribute('localName'), self.assertEqual(self.locationbar.identity_organization_label.get_property('localName'),
'label') 'label')
self.assertEqual(self.locationbar.identity_icon.get_attribute('localName'), 'image') self.assertEqual(self.locationbar.identity_icon.get_property('localName'), 'image')
self.assertEqual(self.locationbar.history_drop_marker.get_attribute('localName'), self.assertEqual(self.locationbar.history_drop_marker.get_property('localName'),
'dropmarker') 'dropmarker')
self.assertEqual(self.locationbar.reload_button.get_attribute('localName'), self.assertEqual(self.locationbar.reload_button.get_property('localName'),
'toolbarbutton') 'toolbarbutton')
self.assertEqual(self.locationbar.stop_button.get_attribute('localName'), self.assertEqual(self.locationbar.stop_button.get_property('localName'),
'toolbarbutton') 'toolbarbutton')
self.assertEqual(self.locationbar.contextmenu.get_attribute('localName'), 'menupopup') self.assertEqual(self.locationbar.contextmenu.get_property('localName'), 'menupopup')
self.assertEqual(self.locationbar.get_contextmenu_entry('paste').get_attribute('cmd'), self.assertEqual(self.locationbar.get_contextmenu_entry('paste').get_attribute('cmd'),
'cmd_paste') 'cmd_paste')
@ -162,7 +162,7 @@ class TestAutoCompleteResults(PuppeteerMixin, MarionetteTestCase):
visible_result_count = len(self.autocomplete_results.visible_results) visible_result_count = len(self.autocomplete_results.visible_results)
self.assertTrue(visible_result_count > 0) self.assertTrue(visible_result_count > 0)
self.assertEqual(visible_result_count, self.assertEqual(visible_result_count,
int(results.get_attribute('itemCount'))) int(results.get_property('itemCount')))
def test_close(self): def test_close(self):
self.browser.navbar.locationbar.urlbar.send_keys('a') self.browser.navbar.locationbar.urlbar.send_keys('a')
@ -226,39 +226,39 @@ class TestIdentityPopup(PuppeteerMixin, MarionetteTestCase):
# Test main view elements # Test main view elements
main = self.identity_popup.view.main main = self.identity_popup.view.main
self.assertEqual(main.element.get_attribute('localName'), 'panelview') self.assertEqual(main.element.get_property('localName'), 'panelview')
self.assertEqual(main.expander.get_attribute('localName'), 'button') self.assertEqual(main.expander.get_property('localName'), 'button')
self.assertEqual(main.host.get_attribute('localName'), 'label') self.assertEqual(main.host.get_property('localName'), 'label')
self.assertEqual(main.insecure_connection_label.get_attribute('localName'), self.assertEqual(main.insecure_connection_label.get_property('localName'),
'description') 'description')
self.assertEqual(main.internal_connection_label.get_attribute('localName'), self.assertEqual(main.internal_connection_label.get_property('localName'),
'description') 'description')
self.assertEqual(main.secure_connection_label.get_attribute('localName'), self.assertEqual(main.secure_connection_label.get_property('localName'),
'description') 'description')
self.assertEqual(main.permissions.get_attribute('localName'), 'vbox') self.assertEqual(main.permissions.get_property('localName'), 'vbox')
# Test security view elements # Test security view elements
security = self.identity_popup.view.security security = self.identity_popup.view.security
self.assertEqual(security.element.get_attribute('localName'), 'panelview') self.assertEqual(security.element.get_property('localName'), 'panelview')
self.assertEqual(security.host.get_attribute('localName'), 'label') self.assertEqual(security.host.get_property('localName'), 'label')
self.assertEqual(security.insecure_connection_label.get_attribute('localName'), self.assertEqual(security.insecure_connection_label.get_property('localName'),
'description') 'description')
self.assertEqual(security.secure_connection_label.get_attribute('localName'), self.assertEqual(security.secure_connection_label.get_property('localName'),
'description') 'description')
self.assertEqual(security.owner.get_attribute('localName'), 'description') self.assertEqual(security.owner.get_property('localName'), 'description')
self.assertEqual(security.owner_location.get_attribute('localName'), 'description') self.assertEqual(security.owner_location.get_property('localName'), 'description')
self.assertEqual(security.verifier.get_attribute('localName'), 'description') self.assertEqual(security.verifier.get_property('localName'), 'description')
self.assertEqual(security.disable_mixed_content_blocking_button.get_attribute('localName'), self.assertEqual(security.disable_mixed_content_blocking_button.get_property('localName'),
'button') 'button')
self.assertEqual(security.enable_mixed_content_blocking_button.get_attribute('localName'), self.assertEqual(security.enable_mixed_content_blocking_button.get_property('localName'),
'button') 'button')
self.assertEqual(security.more_info_button.get_attribute('localName'), 'button') self.assertEqual(security.more_info_button.get_property('localName'), 'button')
def test_open_close(self): def test_open_close(self):
with self.marionette.using_context('content'): with self.marionette.using_context('content'):

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

@ -36,13 +36,13 @@ class TestUpdateWizard(PuppeteerMixin, MarionetteTestCase):
def test_elements(self): def test_elements(self):
"""Test correct retrieval of elements.""" """Test correct retrieval of elements."""
self.assertEqual(self.wizard.element.get_attribute('localName'), 'wizard') self.assertEqual(self.wizard.element.get_property('localName'), 'wizard')
buttons = ('cancel_button', 'extra1_button', 'extra2_button', buttons = ('cancel_button', 'extra1_button', 'extra2_button',
'finish_button', 'next_button', 'previous_button', 'finish_button', 'next_button', 'previous_button',
) )
for button in buttons: for button in buttons:
self.assertEqual(getattr(self.wizard, button).get_attribute('localName'), self.assertEqual(getattr(self.wizard, button).get_property('localName'),
'button') 'button')
panels = ('checking', 'downloading', 'dummy', 'error_patching', 'error', panels = ('checking', 'downloading', 'dummy', 'error_patching', 'error',
@ -50,13 +50,13 @@ class TestUpdateWizard(PuppeteerMixin, MarionetteTestCase):
'manual_update', 'no_updates_found', 'updates_found_basic', 'manual_update', 'no_updates_found', 'updates_found_basic',
) )
for panel in panels: for panel in panels:
self.assertEqual(getattr(self.wizard, panel).element.get_attribute('localName'), self.assertEqual(getattr(self.wizard, panel).element.get_property('localName'),
'wizardpage') 'wizardpage')
# elements of the checking panel # elements of the checking panel
self.assertEqual(self.wizard.checking.progress.get_attribute('localName'), self.assertEqual(self.wizard.checking.progress.get_property('localName'),
'progressmeter') 'progressmeter')
# elements of the downloading panel # elements of the downloading panel
self.assertEqual(self.wizard.downloading.progress.get_attribute('localName'), self.assertEqual(self.wizard.downloading.progress.get_property('localName'),
'progressmeter') 'progressmeter')

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

@ -30,12 +30,11 @@ this.capture = {};
* The canvas element where the element has been painted on. * The canvas element where the element has been painted on.
*/ */
capture.element = function (node, highlights=[]) { capture.element = function (node, highlights=[]) {
let doc = node.ownerDocument; let win = node.ownerDocument.defaultView;
let win = doc.defaultView;
let rect = node.getBoundingClientRect(); let rect = node.getBoundingClientRect();
return capture.canvas( return capture.canvas(
doc, win,
rect.left, rect.left,
rect.top, rect.top,
rect.width, rect.width,
@ -44,12 +43,12 @@ capture.element = function (node, highlights=[]) {
}; };
/** /**
* Take a screenshot of the document's viewport, taking into account * Take a screenshot of the window's viewport by taking into account
* the current window's offset. * the current offsets.
* *
* @param {Document} document * @param {DOMWindow} win
* The DOM document providing the document element to capture, * The DOM window providing the document element to capture,
* and a window for determining the offset of the viewport. * and the offsets for the viewport.
* @param {Array.<Node>=} highlights * @param {Array.<Node>=} highlights
* Optional array of nodes, around which a border will be marked to * Optional array of nodes, around which a border will be marked to
* highlight them in the screenshot. * highlight them in the screenshot.
@ -57,25 +56,24 @@ capture.element = function (node, highlights=[]) {
* @return {HTMLCanvasElement} * @return {HTMLCanvasElement}
* The canvas element where the viewport has been painted on. * The canvas element where the viewport has been painted on.
*/ */
capture.viewport = function (document, highlights=[]) { capture.viewport = function (win, highlights=[]) {
let win = document.defaultView; let rootNode = win.document.documentElement;
let docEl = document.documentElement;
return capture.canvas( return capture.canvas(
document, win,
win.pageXOffset, win.pageXOffset,
win.pageYOffset, win.pageYOffset,
docEl.clientWidth, rootNode.clientWidth,
docEl.clientHeight, rootNode.clientHeight,
highlights); highlights);
}; };
/** /**
* Low-level interface to draw a rectangle off the framebuffer. * Low-level interface to draw a rectangle off the framebuffer.
* *
* @param {Document} document * @param {DOMWindow} win
* A DOM document providing the window used to the framebuffer, * The DOM window used for the framebuffer, and providing the interfaces
* and interfaces for creating an HTMLCanvasElement. * for creating an HTMLCanvasElement.
* @param {number} left * @param {number} left
* The left, X axis offset of the rectangle. * The left, X axis offset of the rectangle.
* @param {number} top * @param {number} top
@ -92,15 +90,23 @@ capture.viewport = function (document, highlights=[]) {
* The canvas on which the selection from the window's framebuffer * The canvas on which the selection from the window's framebuffer
* has been painted on. * has been painted on.
*/ */
capture.canvas = function (document, left, top, width, height, highlights=[]) { capture.canvas = function (win, left, top, width, height, highlights=[]) {
let win = document.defaultView; let scale = win.devicePixelRatio;
let canvas = document.createElementNS(XHTML_NS, "canvas"); let canvas = win.document.createElementNS(XHTML_NS, "canvas");
canvas.width = width; canvas.width = width * scale;
canvas.height = height; canvas.height = height * scale;
let ctx = canvas.getContext(CONTEXT_2D); let ctx = canvas.getContext(CONTEXT_2D);
ctx.drawWindow(win, left, top, width, height, BG_COLOUR); let flags = ctx.DRAWWINDOW_DRAW_CARET;
// Disabled in bug 1243415 for webplatform-test failures due to out of view elements.
// Needs https://github.com/w3c/web-platform-tests/issues/4383 fixed.
// ctx.DRAWWINDOW_DRAW_VIEW;
// Bug 1009762 - Crash in [@ mozilla::gl::ReadPixelsIntoDataSurface]
// ctx.DRAWWINDOW_USE_WIDGET_LAYERS;
ctx.scale(scale, scale);
ctx.drawWindow(win, left, top, width, height, BG_COLOUR, flags);
ctx = capture.highlight_(ctx, highlights, top, left); ctx = capture.highlight_(ctx, highlights, top, left);
return canvas; return canvas;

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

@ -1,19 +1,31 @@
<?xml version="1.0"?> <?xml version="1.0"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public <!-- 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 - 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/. --> - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<!DOCTYPE dialog [ <?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
]>
<dialog id="dialogTest"
buttons="accept, cancel"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<vbox id="things"> <dialog id="testDialog"
<checkbox id="testBox" label="box" /> xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
title="Test Dialog"
buttons="accept,cancel">
<vbox flex="1" style="min-width: 300px; min-height: 500px;">
<label>Settings</label>
<separator class="thin"/>
<richlistbox id="test-list" flex="1">
<richlistitem id="item-choose" orient="horizontal" selected="true">
<label id="choose-label" value="First Entry" flex="1"/>
<button id="choose-button" oncommand="" label="Choose..."/>
</richlistitem>
</richlistbox>
<separator class="thin"/>
<checkbox id="check-box" label="Test Mode 2" />
<hbox align="center">
<label id="text-box-label" control="text-box">Name:</label>
<textbox id="text-box" flex="1" />
</hbox>
</vbox> </vbox>
</dialog> </dialog>

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

@ -69,8 +69,14 @@ class HTMLElement(object):
"""Returns the requested property, or None if the property is """Returns the requested property, or None if the property is
not set. not set.
""" """
body = {"id": self.id, "name": name} try:
return self.marionette._send_message("getElementProperty", body, key="value") body = {"id": self.id, "name": name}
return self.marionette._send_message("getElementProperty", body, key="value")
except errors.UnknownCommandException:
# Keep backward compatibility for code which uses get_attribute() to
# also retrieve element properties.
# Remove when Firefox 55 is stable.
return self.get_attribute(name)
def click(self): def click(self):
self.marionette._send_message("clickElement", {"id": self.id}) self.marionette._send_message("clickElement", {"id": self.id})

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

@ -22,6 +22,7 @@ Cu.import("chrome://marionette/content/addon.js");
Cu.import("chrome://marionette/content/assert.js"); Cu.import("chrome://marionette/content/assert.js");
Cu.import("chrome://marionette/content/atom.js"); Cu.import("chrome://marionette/content/atom.js");
Cu.import("chrome://marionette/content/browser.js"); Cu.import("chrome://marionette/content/browser.js");
Cu.import("chrome://marionette/content/capture.js");
Cu.import("chrome://marionette/content/cert.js"); Cu.import("chrome://marionette/content/cert.js");
Cu.import("chrome://marionette/content/element.js"); Cu.import("chrome://marionette/content/element.js");
Cu.import("chrome://marionette/content/error.js"); Cu.import("chrome://marionette/content/error.js");
@ -1842,7 +1843,8 @@ GeckoDriver.prototype.getElementAttribute = function*(cmd, resp) {
case Context.CHROME: case Context.CHROME:
let win = this.getCurrentWindow(); let win = this.getCurrentWindow();
let el = this.curBrowser.seenEls.get(id, {frame: win}); let el = this.curBrowser.seenEls.get(id, {frame: win});
resp.body.value = atom.getElementAttribute(el, name, this.getCurrentWindow());
resp.body.value = el.getAttribute(name);
break; break;
case Context.CONTENT: case Context.CONTENT:
@ -2393,6 +2395,9 @@ GeckoDriver.prototype.clearImportedScripts = function*(cmd, resp) {
* Reference to a web element. * Reference to a web element.
* @param {string} highlights * @param {string} highlights
* List of web elements to highlight. * List of web elements to highlight.
* @param {boolean} full
* True to take a screenshot of the entire document element. Is not
* considered if {@code id} is not defined. Defaults to true.
* @param {boolean} hash * @param {boolean} hash
* True if the user requests a hash of the image data. * True if the user requests a hash of the image data.
* *
@ -2407,44 +2412,41 @@ GeckoDriver.prototype.takeScreenshot = function (cmd, resp) {
switch (this.context) { switch (this.context) {
case Context.CHROME: case Context.CHROME:
let win = this.getCurrentWindow(); let canvas;
let canvas = win.document.createElementNS("http://www.w3.org/1999/xhtml", "canvas"); let highlightEls = [];
let doc;
if (this.appName == "B2G") {
doc = win.document.body;
} else {
doc = win.document.documentElement;
}
let docRect = doc.getBoundingClientRect();
let width = docRect.width;
let height = docRect.height;
// Convert width and height from CSS pixels (potentially fractional) let container = {frame: this.getCurrentWindow().document.defaultView};
// to device pixels (integer).
let scale = win.devicePixelRatio;
canvas.setAttribute("width", Math.round(width * scale));
canvas.setAttribute("height", Math.round(height * scale));
let context = canvas.getContext("2d"); if (!container.frame) {
let flags; throw new NoSuchWindowError('Unable to locate window');
if (this.appName == "B2G") { }
flags =
context.DRAWWINDOW_DRAW_CARET | for (let h of highlights) {
context.DRAWWINDOW_DRAW_VIEW | let el = this.curBrowser.seenEls.get(h, container);
context.DRAWWINDOW_USE_WIDGET_LAYERS; highlightEls.push(el);
} else { }
// Bug 1075168: CanvasRenderingContext2D image is distorted
// when using certain flags in chrome context. // viewport
flags = if (!id && !full) {
context.DRAWWINDOW_DRAW_VIEW | canvas = capture.viewport(container.frame, highlightEls);
context.DRAWWINDOW_USE_WIDGET_LAYERS;
// element or full document element
} else {
let node;
if (id) {
node = this.curBrowser.seenEls.get(id, container);
} else {
node = container.frame.document.documentElement;
}
canvas = capture.element(node, highlightEls);
}
if (hash) {
return capture.toHash(canvas);
} else {
return capture.toBase64(canvas);
} }
context.scale(scale, scale);
context.drawWindow(win, 0, 0, width, height, "rgb(255,255,255)", flags);
let dataUrl = canvas.toDataURL("image/png", "");
let data = dataUrl.substring(dataUrl.indexOf(",") + 1);
resp.body.value = data;
break;
case Context.CONTENT: case Context.CONTENT:
if (hash) { if (hash) {

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

@ -51,7 +51,7 @@ class TestMouseAction(MarionetteTestCase):
def context_menu_state(): def context_menu_state():
with self.marionette.using_context("chrome"): with self.marionette.using_context("chrome"):
cm_el = self.marionette.find_element(By.ID, "contentAreaContextMenu") cm_el = self.marionette.find_element(By.ID, "contentAreaContextMenu")
return cm_el.get_attribute("state") return cm_el.get_property("state")
self.assertEqual("closed", context_menu_state()) self.assertEqual("closed", context_menu_state())
self.action.context_click(click_el).perform() self.action.context_click(click_el).perform()
@ -88,20 +88,20 @@ class TestMouseAction(MarionetteTestCase):
with self.marionette.using_context("chrome"): with self.marionette.using_context("chrome"):
urlbar = self.marionette.find_element(By.ID, "urlbar") urlbar = self.marionette.find_element(By.ID, "urlbar")
self.assertEqual("", urlbar.get_attribute("value")) self.assertEqual("", urlbar.get_property("value"))
urlbar.send_keys(test_word) urlbar.send_keys(test_word)
self.assertEqual(urlbar.get_attribute("value"), test_word) self.assertEqual(urlbar.get_property("value"), test_word)
(self.action.double_click(urlbar).perform() (self.action.double_click(urlbar).perform()
.key_down(self.mod_key) .key_down(self.mod_key)
.key_down("x").perform()) .key_down("x").perform())
self.assertEqual(urlbar.get_attribute("value"), "") self.assertEqual(urlbar.get_property("value"), "")
def test_chrome_context_click_action(self): def test_chrome_context_click_action(self):
self.marionette.set_context("chrome") self.marionette.set_context("chrome")
def context_menu_state(): def context_menu_state():
cm_el = self.marionette.find_element(By.ID, "tabContextMenu") cm_el = self.marionette.find_element(By.ID, "tabContextMenu")
return cm_el.get_attribute("state") return cm_el.get_property("state")
currtab = self.marionette.execute_script("return gBrowser.selectedTab") currtab = self.marionette.execute_script("return gBrowser.selectedTab")
self.assertEqual("closed", context_menu_state()) self.assertEqual("closed", context_menu_state())

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

@ -10,54 +10,104 @@ import urllib
from unittest import skip from unittest import skip
from marionette_driver.by import By from marionette_driver import By
from marionette_driver.errors import JavascriptException, NoSuchWindowException
from marionette_harness import MarionetteTestCase, WindowManagerMixin from marionette_harness import MarionetteTestCase, skip_if_mobile, WindowManagerMixin
def inline(doc, mime="text/html;charset=utf-8"): def inline(doc, mime="text/html;charset=utf-8"):
return "data:{0},{1}".format(mime, urllib.quote(doc)) return "data:{0},{1}".format(mime, urllib.quote(doc))
ELEMENT = "iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAVklEQVRoge3PMQ0AMAzAsPJHVWYbjEWTj/zx7O75oXk9AAISD6QWSC2QWiC1QGqB1AKpBVILpBZILZBaILVAaoHUAqkFUgukFkgtkFogtUBqgdT6BnIBMKa1DtYxhPkAAAAASUVORK5CYII=" box = inline("<body><div id='box'><p id='green' style='width: 50px; height: 50px; "
HIGHLIGHTED_ELEMENT = "iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAVklEQVRoge3PQRHAQAgAMfyrwhm1sb3JIwIyN3MvmJu53f01kRqRGpEakRqRGpEakRqRGpEakRqRGpEakRqRGpEakRqRGpEakRqRGpEakRqRmvciL/gAQgW/OxTpMPwAAAAASUVORK5CYII=" "background: silver;'></p></div></body>")
input = inline("<body><input id='text-input'></input></body>")
long = inline("<body style='height: 300vh'><p style='margin-top: 100vh'>foo</p></body>")
box = inline( short = inline("<body style='height: 10vh'></body>")
"<div id=green style='width: 50px; height: 50px; background: silver;'></div>") svg = inline("""
long = inline("<body style='height: 300vh'><p style='margin-top: 100vh'>foo") <svg xmlns="http://www.w3.org/2000/svg" height="20" width="20">
short = inline("<body style='height: 10vh'>") <rect height="20" width="20"/>
svg = inline("""<svg xmlns="http://www.w3.org/2000/svg" height="20" width="20"> </svg>""", mime="image/svg+xml")
<rect height="20" width="20"/>
</svg>""", mime="image/svg+xml")
class ScreenCaptureTestCase(MarionetteTestCase): class ScreenCaptureTestCase(MarionetteTestCase):
def assert_png(self, string):
"""Test that string is a Base64 encoded PNG file.""" def setUp(self):
image = base64.decodestring(string) super(ScreenCaptureTestCase, self).setUp()
self._device_pixel_ratio = None
@property
def device_pixel_ratio(self):
if self._device_pixel_ratio is None:
self._device_pixel_ratio = self.marionette.execute_script("""
return window.devicePixelRatio
""")
return self._device_pixel_ratio
@property
def document_element(self):
return self.marionette.find_element(By.CSS_SELECTOR, ":root")
@property
def page_y_offset(self):
return self.marionette.execute_script("return window.pageYOffset")
@property
def viewport_dimensions(self):
return self.marionette.execute_script("""
return [arguments[0].clientWidth,
arguments[0].clientHeight];
""", script_args=[self.document_element])
def assert_png(self, screenshot):
"""Test that screenshot is a Base64 encoded PNG file."""
image = base64.decodestring(screenshot)
self.assertEqual(imghdr.what("", image), "png") self.assertEqual(imghdr.what("", image), "png")
def get_image_dimensions(self, string): def assert_formats(self, element=None):
self.assert_png(string) if element is None:
image = base64.decodestring(string) element = self.document_element
screenshot_default = self.marionette.screenshot(element=element)
screenshot_image = self.marionette.screenshot(element=element, format="base64")
binary1 = self.marionette.screenshot(element=element, format="binary")
binary2 = self.marionette.screenshot(element=element, format="binary")
hash1 = self.marionette.screenshot(element=element, format="hash")
hash2 = self.marionette.screenshot(element=element, format="hash")
# Valid data should have been returned
self.assert_png(screenshot_image)
self.assertEqual(imghdr.what("", binary1), "png")
self.assertEqual(screenshot_image, base64.b64encode(binary1))
self.assertEqual(hash1, hashlib.sha256(screenshot_image).hexdigest())
# Different formats produce different data
self.assertNotEqual(screenshot_image, binary1)
self.assertNotEqual(screenshot_image, hash1)
self.assertNotEqual(binary1, hash1)
# A second capture should be identical
self.assertEqual(screenshot_image, screenshot_default)
self.assertEqual(binary1, binary2)
self.assertEqual(hash1, hash2)
def get_element_dimensions(self, element):
rect = element.rect
return rect["width"], rect["height"]
def get_image_dimensions(self, screenshot):
self.assert_png(screenshot)
image = base64.decodestring(screenshot)
width, height = struct.unpack(">LL", image[16:24]) width, height = struct.unpack(">LL", image[16:24])
return int(width), int(height) return int(width), int(height)
def scale(self, rect):
return (int(rect[0] * self.device_pixel_ratio),
int(rect[1] * self.device_pixel_ratio))
class TestScreenCaptureChrome(WindowManagerMixin, ScreenCaptureTestCase): class TestScreenCaptureChrome(WindowManagerMixin, ScreenCaptureTestCase):
@property
def primary_window_dimensions(self):
current_window = self.marionette.current_chrome_window_handle
self.marionette.switch_to_window(self.start_window)
with self.marionette.using_context("chrome"):
rv = tuple(self.marionette.execute_script("""
let el = document.documentElement;
let rect = el.getBoundingClientRect();
return [rect.width, rect.height];
"""))
self.marionette.switch_to_window(current_window)
return rv
def setUp(self): def setUp(self):
super(TestScreenCaptureChrome, self).setUp() super(TestScreenCaptureChrome, self).setUp()
@ -67,90 +117,245 @@ class TestScreenCaptureChrome(WindowManagerMixin, ScreenCaptureTestCase):
self.close_all_windows() self.close_all_windows()
super(TestScreenCaptureChrome, self).tearDown() super(TestScreenCaptureChrome, self).tearDown()
# A full chrome window screenshot is not the outer dimensions of @property
# the window, but instead the bounding box of the <window> inside def window_dimensions(self):
# <browser>. return tuple(self.marionette.execute_script("""
def test_window(self): let el = document.documentElement;
ss = self.marionette.screenshot() let rect = el.getBoundingClientRect();
self.assert_png(ss) return [rect.width, rect.height];
self.assertEqual(self.primary_window_dimensions, """))
self.get_image_dimensions(ss))
def test_chrome_delegation(self): def open_dialog(self, url=None, width=None, height=None):
with self.marionette.using_context("content"): if url is None:
content = self.marionette.screenshot() url = "chrome://marionette/content/test_dialog.xul"
chrome = self.marionette.screenshot()
self.assertNotEqual(content, chrome) def opener():
features = "chrome"
if height is not None:
features += ",height={}".format(height)
if width is not None:
features += ",width={}".format(width)
# This tests that GeckoDriver#takeScreenshot uses
# currentContext.document.documentElement instead of looking for a
# <window> element, which does not exist for all windows.
def test_secondary_windows(self):
def open_window_with_js():
self.marionette.execute_script(""" self.marionette.execute_script("""
window.open('chrome://marionette/content/test_dialog.xul', 'foo', window.open(arguments[0], "", arguments[1]);
'dialog,height=200,width=300'); """, script_args=[url, features])
""")
new_window = self.open_window(open_window_with_js) return self.open_window(opener)
self.marionette.switch_to_window(new_window)
ss = self.marionette.screenshot() def test_capture_different_context(self):
size = self.get_image_dimensions(ss) """Check that screenshots in content and chrome are different."""
self.assert_png(ss) with self.marionette.using_context("content"):
self.assertNotEqual(self.primary_window_dimensions, size) screenshot_content = self.marionette.screenshot()
screenshot_chrome = self.marionette.screenshot()
self.assertNotEqual(screenshot_content, screenshot_chrome)
@skip_if_mobile
def test_capture_element(self):
dialog = self.open_dialog()
self.marionette.switch_to_window(dialog)
# Ensure we only capture the element
el = self.marionette.find_element(By.ID, "test-list")
screenshot_element = self.marionette.screenshot(element=el)
self.assertEqual(self.scale(self.get_element_dimensions(el)),
self.get_image_dimensions(screenshot_element))
# Ensure we do not capture the full window
screenshot_dialog = self.marionette.screenshot()
self.assertNotEqual(screenshot_dialog, screenshot_element)
self.marionette.close_chrome_window() self.marionette.close_chrome_window()
self.marionette.switch_to_window(self.start_window) self.marionette.switch_to_window(self.start_window)
def test_capture_flags(self):
dialog = self.open_dialog()
self.marionette.switch_to_window(dialog)
class Content(ScreenCaptureTestCase): textbox = self.marionette.find_element(By.ID, "text-box")
@property textbox.send_keys("")
def body_scroll_dimensions(self): screenshot_focus = self.marionette.screenshot()
return tuple(self.marionette.execute_script(
"return [document.body.scrollWidth, document.body.scrollHeight]"))
@property self.marionette.execute_script("arguments[0].blur();", script_args=[textbox])
def viewport_dimensions(self): screenshot_no_focus = self.marionette.screenshot()
return tuple(self.marionette.execute_script("""
let docEl = document.documentElement;
return [docEl.clientWidth, docEl.clientHeight];"""))
@property self.marionette.close_chrome_window()
def document_element(self): self.marionette.switch_to_window(self.start_window)
return self.marionette.execute_script("return document.documentElement")
@property self.assertNotEqual(screenshot_focus, screenshot_no_focus)
def page_y_offset(self):
return self.marionette.execute_script("return window.pageYOffset") def test_capture_full_area(self):
# A full capture is not the outer dimensions of the window,
# but instead the bounding box of the window's root node (documentElement).
screenshot_full = self.marionette.screenshot()
screenshot_root = self.marionette.screenshot(element=self.document_element)
self.assert_png(screenshot_full)
self.assert_png(screenshot_root)
self.assertEqual(screenshot_root, screenshot_full)
self.assertEqual(self.scale(self.get_element_dimensions(self.document_element)),
self.get_image_dimensions(screenshot_full))
def test_capture_viewport(self):
# Load a HTML test page into the chrome window to get scrollbars
test_page = self.marionette.absolute_url("test.html")
dialog = self.open_dialog(url=test_page, width=50, height=50)
self.marionette.switch_to_window(dialog)
# Size of screenshot has to match viewport size
screenshot = self.marionette.screenshot(full=False)
self.assert_png(screenshot)
self.assertEqual(self.scale(self.viewport_dimensions),
self.get_image_dimensions(screenshot))
self.assertNotEqual(self.scale(self.window_dimensions),
self.get_image_dimensions(screenshot))
self.marionette.close_chrome_window()
self.marionette.switch_to_window(self.start_window)
@skip("https://bugzilla.mozilla.org/show_bug.cgi?id=1213875")
def test_capture_scroll_element_into_view(self):
pass
def test_capture_window_already_closed(self):
dialog = self.open_dialog()
self.marionette.switch_to_window(dialog)
self.marionette.close_chrome_window()
self.assertRaises(NoSuchWindowException, self.marionette.screenshot)
self.marionette.switch_to_window(self.start_window)
def test_formats(self):
dialog = self.open_dialog()
self.marionette.switch_to_window(dialog)
self.assert_formats()
self.marionette.close_chrome_window()
self.marionette.switch_to_window(self.start_window)
def test_format_unknown(self):
with self.assertRaises(ValueError):
self.marionette.screenshot(format="cheese")
@skip_if_mobile
def test_highlight_elements(self):
dialog = self.open_dialog()
self.marionette.switch_to_window(dialog)
# Highlighting the element itself shouldn't make the image larger
element = self.marionette.find_element(By.ID, "test-list")
screenshot_element = self.marionette.screenshot(element=element)
screenshot_highlight = self.marionette.screenshot(element=element,
highlights=[element])
self.assertEqual(self.scale(self.get_element_dimensions(element)),
self.get_image_dimensions(screenshot_element))
self.assertNotEqual(screenshot_element, screenshot_highlight)
# Highlighting a sub element
button = self.marionette.find_element(By.ID, "choose-button")
screenshot_highlight_button = self.marionette.screenshot(element=element,
highlights=[button])
self.assertNotEqual(screenshot_element, screenshot_highlight_button)
self.assertNotEqual(screenshot_highlight, screenshot_highlight_button)
self.marionette.close_chrome_window()
self.marionette.switch_to_window(self.start_window)
def test_highlight_element_not_seen(self):
"""Check that for not found elements an exception is raised."""
with self.marionette.using_context('content'):
self.marionette.navigate(box)
content_element = self.marionette.find_element(By.ID, "green")
self.assertRaisesRegexp(JavascriptException, "Element reference not seen before",
self.marionette.screenshot, highlights=[content_element])
chrome_document_element = self.document_element
with self.marionette.using_context('content'):
self.assertRaisesRegexp(JavascriptException, "Element reference not seen before",
self.marionette.screenshot,
highlights=[chrome_document_element])
class TestScreenCaptureContent(WindowManagerMixin, ScreenCaptureTestCase):
def setUp(self): def setUp(self):
ScreenCaptureTestCase.setUp(self) super(TestScreenCaptureContent, self).setUp()
self.marionette.set_context("content") self.marionette.set_context("content")
def test_html_document_element(self): def tearDown(self):
self.close_all_tabs()
super(TestScreenCaptureContent, self).tearDown()
@property
def scroll_dimensions(self):
return tuple(self.marionette.execute_script("""
return [document.body.scrollWidth, document.body.scrollHeight]
"""))
@skip_if_mobile # Needs application independent method to open a new tab
def test_capture_tab_already_closed(self):
tab = self.open_tab()
self.marionette.switch_to_window(tab)
self.marionette.close()
self.assertRaises(NoSuchWindowException, self.marionette.screenshot)
self.marionette.switch_to_window(self.start_tab)
def test_capture_element(self):
self.marionette.navigate(box)
el = self.marionette.find_element(By.TAG_NAME, "div")
screenshot = self.marionette.screenshot(element=el)
self.assert_png(screenshot)
self.assertEqual(self.scale(self.get_element_dimensions(el)),
self.get_image_dimensions(screenshot))
@skip("https://bugzilla.mozilla.org/show_bug.cgi?id=1213875")
def test_capture_element_scrolled_into_view(self):
self.marionette.navigate(long) self.marionette.navigate(long)
string = self.marionette.screenshot() el = self.marionette.find_element(By.TAG_NAME, "p")
self.assert_png(string) screenshot = self.marionette.screenshot(element=el)
self.assertEqual( self.assert_png(screenshot)
self.body_scroll_dimensions, self.get_image_dimensions(string)) self.assertEqual(self.scale(self.get_element_dimensions(el)),
self.get_image_dimensions(screenshot))
self.assertGreater(self.page_y_offset, 0)
def test_svg_document_element(self): def test_capture_flags(self):
self.marionette.navigate(input)
textbox = self.marionette.find_element(By.ID, "text-input")
textbox.send_keys("")
screenshot_focus = self.marionette.screenshot()
self.marionette.execute_script("arguments[0].blur();", script_args=[textbox])
screenshot_no_focus = self.marionette.screenshot()
self.assertNotEqual(screenshot_focus, screenshot_no_focus)
def test_capture_html_document_element(self):
self.marionette.navigate(long)
screenshot = self.marionette.screenshot()
self.assert_png(screenshot)
self.assertEqual(self.scale(self.scroll_dimensions),
self.get_image_dimensions(screenshot))
def test_capture_svg_document_element(self):
self.marionette.navigate(svg) self.marionette.navigate(svg)
doc_el = self.document_element screenshot = self.marionette.screenshot()
string = self.marionette.screenshot() self.assert_png(screenshot)
self.assert_png(string) self.assertEqual(self.scale(self.get_element_dimensions(self.document_element)),
self.assertEqual((doc_el.rect["width"], doc_el.rect["height"]), self.get_image_dimensions(screenshot))
self.get_image_dimensions(string))
def test_viewport(self): def test_capture_viewport(self):
url = self.marionette.absolute_url("clicks.html")
self.marionette.navigate(short) self.marionette.navigate(short)
string = self.marionette.screenshot(full=False) self.marionette.navigate(url)
self.assert_png(string) screenshot = self.marionette.screenshot(full=False)
self.assertEqual( self.assert_png(screenshot)
self.viewport_dimensions, self.get_image_dimensions(string)) self.assertEqual(self.scale(self.viewport_dimensions),
self.get_image_dimensions(screenshot))
def test_viewport_after_scroll(self): def test_capture_viewport_after_scroll(self):
self.marionette.navigate(long) self.marionette.navigate(long)
before = self.marionette.screenshot() before = self.marionette.screenshot()
el = self.marionette.find_element(By.TAG_NAME, "p") el = self.marionette.find_element(By.TAG_NAME, "p")
@ -160,48 +365,32 @@ class Content(ScreenCaptureTestCase):
self.assertNotEqual(before, after) self.assertNotEqual(before, after)
self.assertGreater(self.page_y_offset, 0) self.assertGreater(self.page_y_offset, 0)
def test_element(self): def test_formats(self):
self.marionette.navigate(box) self.marionette.navigate(box)
el = self.marionette.find_element(By.TAG_NAME, "div")
string = self.marionette.screenshot(element=el)
self.assert_png(string)
self.assertEqual(
(el.rect["width"], el.rect["height"]), self.get_image_dimensions(string))
self.assertEqual(ELEMENT, string)
@skip("https://bugzilla.mozilla.org/show_bug.cgi?id=1213875") # Use a smaller region to speed up the test
def test_element_scrolled_into_view(self): element = self.marionette.find_element(By.TAG_NAME, "div")
self.marionette.navigate(long) self.assert_formats(element=element)
el = self.marionette.find_element(By.TAG_NAME, "p")
string = self.marionette.screenshot(element=el)
self.assert_png(string)
self.assertEqual(
(el.rect["width"], el.rect["height"]), self.get_image_dimensions(string))
self.assertGreater(self.page_y_offset, 0)
def test_element_with_highlight(self): def test_format_unknown(self):
self.marionette.navigate(box)
el = self.marionette.find_element(By.TAG_NAME, "div")
string = self.marionette.screenshot(element=el, highlights=[el])
self.assert_png(string)
self.assertEqual(
(el.rect["width"], el.rect["height"]), self.get_image_dimensions(string))
self.assertEqual(HIGHLIGHTED_ELEMENT, string)
def test_binary_element(self):
self.marionette.navigate(box)
el = self.marionette.find_element(By.TAG_NAME, "div")
bin = self.marionette.screenshot(element=el, format="binary")
enc = base64.b64encode(bin)
self.assertEqual(ELEMENT, enc)
def test_unknown_format(self):
with self.assertRaises(ValueError): with self.assertRaises(ValueError):
self.marionette.screenshot(format="cheese") self.marionette.screenshot(format="cheese")
def test_hash_format(self): def test_highlight_elements(self):
self.marionette.navigate(box) self.marionette.navigate(box)
el = self.marionette.find_element(By.TAG_NAME, "div") element = self.marionette.find_element(By.TAG_NAME, "div")
content = self.marionette.screenshot(element=el, format="hash")
hash = hashlib.sha256(ELEMENT).hexdigest() # Highlighting the element itself shouldn't make the image larger
self.assertEqual(content, hash) screenshot_element = self.marionette.screenshot(element=element)
screenshot_highlight = self.marionette.screenshot(element=element,
highlights=[element])
self.assertEqual(self.scale(self.get_element_dimensions(element)),
self.get_image_dimensions(screenshot_highlight))
self.assertNotEqual(screenshot_element, screenshot_highlight)
# Highlighting a sub element
paragraph = self.marionette.find_element(By.ID, "green")
screenshot_highlight_paragraph = self.marionette.screenshot(element=element,
highlights=[paragraph])
self.assertNotEqual(screenshot_element, screenshot_highlight_paragraph)
self.assertNotEqual(screenshot_highlight, screenshot_highlight_paragraph)

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

@ -1673,7 +1673,7 @@ function screenshot(id, full=true, highlights=[]) {
// viewport // viewport
if (!id && !full) { if (!id && !full) {
canvas = capture.viewport(curContainer.frame.document, highlightEls); canvas = capture.viewport(curContainer.frame, highlightEls);
// element or full document element // element or full document element
} else { } else {

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

@ -62,7 +62,7 @@ class TabBar(UIBaseLib):
:return: Index of the selected tab. :return: Index of the selected tab.
""" """
return int(self.toolbar.get_attribute('selectedIndex')) return int(self.toolbar.get_property('selectedIndex'))
@property @property
def selected_tab(self): def selected_tab(self):

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

@ -98,7 +98,7 @@ class LocationBar(UIBaseLib):
self.focus('shortcut') self.focus('shortcut')
self.urlbar.send_keys(keys.Keys.DELETE) self.urlbar.send_keys(keys.Keys.DELETE)
Wait(self.marionette).until( Wait(self.marionette).until(
lambda _: self.urlbar.get_attribute('value') == '', lambda _: self.value == '',
message='Contents of location bar could not be cleared.') message='Contents of location bar could not be cleared.')
def close_context_menu(self): def close_context_menu(self):
@ -298,7 +298,7 @@ class LocationBar(UIBaseLib):
:returns: The urlbar value. :returns: The urlbar value.
""" """
return self.urlbar.get_attribute('value') return self.urlbar.get_property('value')
class AutocompleteResults(UIBaseLib): class AutocompleteResults(UIBaseLib):
@ -342,7 +342,7 @@ class AutocompleteResults(UIBaseLib):
{'class': 'ac-emphasize-text ac-emphasize-text-%s' % match_type} {'class': 'ac-emphasize-text ac-emphasize-text-%s' % match_type}
) )
return [node.get_attribute('textContent') for node in emphasized_nodes] return [node.get_property('textContent') for node in emphasized_nodes]
@property @property
def visible_results(self): def visible_results(self):
@ -350,7 +350,7 @@ class AutocompleteResults(UIBaseLib):
:returns: The list of visible results. :returns: The list of visible results.
""" """
match_count = self.element.get_attribute('_matchCount') match_count = self.element.get_property('_matchCount')
return self.marionette.execute_script(""" return self.marionette.execute_script("""
let rv = []; let rv = [];
@ -370,7 +370,7 @@ class AutocompleteResults(UIBaseLib):
:returns: True when the popup is open, otherwise false. :returns: True when the popup is open, otherwise false.
""" """
return self.element.get_attribute('state') == 'open' return self.element.get_property('state') == 'open'
@property @property
def is_complete(self): def is_complete(self):
@ -404,7 +404,7 @@ class AutocompleteResults(UIBaseLib):
:returns: The index. :returns: The index.
""" """
return self.results.get_attribute('selectedIndex') return self.results.get_property('selectedIndex')
class IdentityPopup(UIBaseLib): class IdentityPopup(UIBaseLib):
@ -421,7 +421,7 @@ class IdentityPopup(UIBaseLib):
:returns: True when the popup is open, otherwise false. :returns: True when the popup is open, otherwise false.
""" """
return self.element.get_attribute('state') == 'open' return self.element.get_property('state') == 'open'
def close(self, force=False): def close(self, force=False):
"""Closes the identity popup by hitting the escape key. """Closes the identity popup by hitting the escape key.

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

@ -98,7 +98,7 @@ class Deck(UIBaseLib):
:return: Index of the selected panel. :return: Index of the selected panel.
""" """
return int(self.element.get_attribute('selectedIndex')) return int(self.element.get_property('selectedIndex'))
@property @property
def selected_panel(self): def selected_panel(self):
@ -133,7 +133,7 @@ class PageInfoPanel(Panel):
:returns: Reference to the tab element. :returns: Reference to the tab element.
""" """
name = self.element.get_attribute('id').split('Panel')[0] name = self.element.get_property('id').split('Panel')[0]
return self.window.window_element.find_element(By.ID, name + 'Tab') return self.window.window_element.find_element(By.ID, name + 'Tab')

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

@ -48,12 +48,12 @@ class ShutdownLeaks(object):
def process(self): def process(self):
if not self.seenShutdown: if not self.seenShutdown:
self.logger.warning( self.logger.error(
"TEST-UNEXPECTED-FAIL | ShutdownLeaks | process() called before end of test suite") "TEST-UNEXPECTED-FAIL | ShutdownLeaks | process() called before end of test suite")
for test in self._parseLeakingTests(): for test in self._parseLeakingTests():
for url, count in self._zipLeakedWindows(test["leakedWindows"]): for url, count in self._zipLeakedWindows(test["leakedWindows"]):
self.logger.warning( self.logger.error(
"TEST-UNEXPECTED-FAIL | %s | leaked %d window(s) until shutdown " "TEST-UNEXPECTED-FAIL | %s | leaked %d window(s) until shutdown "
"[url = %s]" % (test["fileName"], count, url)) "[url = %s]" % (test["fileName"], count, url))
@ -62,9 +62,9 @@ class ShutdownLeaks(object):
(test["fileName"], test["leakedWindowsString"])) (test["fileName"], test["leakedWindowsString"]))
if test["leakedDocShells"]: if test["leakedDocShells"]:
self.logger.warning("TEST-UNEXPECTED-FAIL | %s | leaked %d docShell(s) until " self.logger.error("TEST-UNEXPECTED-FAIL | %s | leaked %d docShell(s) until "
"shutdown" % "shutdown" %
(test["fileName"], len(test["leakedDocShells"]))) (test["fileName"], len(test["leakedDocShells"])))
self.logger.info("TEST-INFO | %s | docShell(s) leaked: %s" % self.logger.info("TEST-INFO | %s | docShell(s) leaked: %s" %
(test["fileName"], ', '.join(["[pid = %s] [id = %s]" % (test["fileName"], ', '.join(["[pid = %s] [id = %s]" %
x for x in test["leakedDocShells"]] x for x in test["leakedDocShells"]]
@ -77,7 +77,7 @@ class ShutdownLeaks(object):
# log line has invalid format # log line has invalid format
if not pid or not serial: if not pid or not serial:
self.logger.warning( self.logger.error(
"TEST-UNEXPECTED-FAIL | ShutdownLeaks | failed to parse line <%s>" % line) "TEST-UNEXPECTED-FAIL | ShutdownLeaks | failed to parse line <%s>" % line)
return return
@ -99,7 +99,7 @@ class ShutdownLeaks(object):
# log line has invalid format # log line has invalid format
if not pid or not id: if not pid or not id:
self.logger.warning( self.logger.error(
"TEST-UNEXPECTED-FAIL | ShutdownLeaks | failed to parse line <%s>" % line) "TEST-UNEXPECTED-FAIL | ShutdownLeaks | failed to parse line <%s>" % line)
return return
@ -233,8 +233,8 @@ class LSANLeaks(object):
def process(self): def process(self):
if self.fatalError: if self.fatalError:
self.logger.warning("TEST-UNEXPECTED-FAIL | LeakSanitizer | LeakSanitizer " self.logger.error("TEST-UNEXPECTED-FAIL | LeakSanitizer | LeakSanitizer "
"has encountered a fatal error.") "has encountered a fatal error.")
if self.foundFrames: if self.foundFrames:
self.logger.info("TEST-INFO | LeakSanitizer | To show the " self.logger.info("TEST-INFO | LeakSanitizer | To show the "
@ -243,7 +243,7 @@ class LSANLeaks(object):
"in testing/mozbase/mozrunner/mozrunner/utils.py") "in testing/mozbase/mozrunner/mozrunner/utils.py")
for f in self.foundFrames: for f in self.foundFrames:
self.logger.warning( self.logger.error(
"TEST-UNEXPECTED-FAIL | LeakSanitizer | leak at " + f) "TEST-UNEXPECTED-FAIL | LeakSanitizer | leak at " + f)
def _finishStack(self): def _finishStack(self):

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

@ -275,6 +275,9 @@ def setup_argument_parser():
import imp import imp
path = os.path.join(build_obj.topobjdir, mochitest_dir, 'runtests.py') path = os.path.join(build_obj.topobjdir, mochitest_dir, 'runtests.py')
if not os.path.exists(path):
path = os.path.join(here, "runtests.py")
with open(path, 'r') as fh: with open(path, 'r') as fh:
imp.load_module('mochitest', fh, path, imp.load_module('mochitest', fh, path,
('.py', 'r', imp.PY_SOURCE)) ('.py', 'r', imp.PY_SOURCE))

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

@ -824,6 +824,7 @@ class MochitestDesktop(object):
self.result = {} self.result = {}
self.start_script = os.path.join(here, 'start_desktop.js') self.start_script = os.path.join(here, 'start_desktop.js')
self.disable_leak_checking = False
def update_mozinfo(self): def update_mozinfo(self):
"""walk up directories to find mozinfo.json update the info""" """walk up directories to find mozinfo.json update the info"""
@ -1495,7 +1496,8 @@ toolbar#nav-bar {
self.log.error(str(e)) self.log.error(str(e))
return None return None
browserEnv["XPCOM_MEM_BLOAT_LOG"] = self.leak_report_file if not self.disable_leak_checking:
browserEnv["XPCOM_MEM_BLOAT_LOG"] = self.leak_report_file
try: try:
gmp_path = self.getGMPPluginPath(options) gmp_path = self.getGMPPluginPath(options)
@ -1939,12 +1941,13 @@ toolbar#nav-bar {
args.append('-foreground') args.append('-foreground')
self.start_script_args.append(testUrl or 'about:blank') self.start_script_args.append(testUrl or 'about:blank')
if detectShutdownLeaks: if detectShutdownLeaks and not self.disable_leak_checking:
shutdownLeaks = ShutdownLeaks(self.log) shutdownLeaks = ShutdownLeaks(self.log)
else: else:
shutdownLeaks = None shutdownLeaks = None
if mozinfo.info["asan"] and (mozinfo.isLinux or mozinfo.isMac): if mozinfo.info["asan"] and (mozinfo.isLinux or mozinfo.isMac) \
and not self.disable_leak_checking:
lsanLeaks = LSANLeaks(self.log) lsanLeaks = LSANLeaks(self.log)
else: else:
lsanLeaks = None lsanLeaks = None
@ -2201,6 +2204,36 @@ toolbar#nav-bar {
result = 1 # default value, if no tests are run. result = 1 # default value, if no tests are run.
for d in dirs: for d in dirs:
print "dir: %s" % d print "dir: %s" % d
# BEGIN LEAKCHECK HACK
# Leak checking was broken in mochitest unnoticed for a length of time. During
# this time, several leaks slipped through. The leak checking was fixed by bug
# 1325148, but it couldn't land until all the regressions were also fixed or
# backed out. Rather than waiting and risking new regressions, in the meantime
# this code will selectively disable leak checking on flavors/directories where
# known regressions exist. At least this way we can prevent further damage while
# they get fixed.
info = mozinfo.info
skip_leak_conditions = [
(options.flavor in ('browser', 'chrome', 'plain') and d.startswith('toolkit/components/extensions/test/mochitest'), 'bug 1325158'), # noqa
(info['debug'] and options.flavor == 'browser' and d.startswith('browser/components/extensions/test/browser'), 'bug 1325141'), # noqa
(info['debug'] and options.flavor == 'plain' and d == 'dom/animation/test/css-animations', 'bug 1325277'), # noqa
(info['debug'] and options.flavor == 'plain' and d == 'dom/tests/mochitest/gamepad' and info['os'] == 'win', 'bug 1324592'), # noqa
(info['debug'] and options.flavor == 'plain' and d == 'toolkit/components/prompts/test' and info['os'] == 'mac', 'bug 1325275'), # noqa
(info['debug'] and options.flavor == 'plain' and d == 'tests/dom/xhr/tests', 'bug 1325438'), # noqa
]
for condition, reason in skip_leak_conditions:
if condition:
self.log.warning('WARNING | disabling leakcheck due to {}'.format(reason))
self.disable_leak_checking = True
break
else:
self.disable_leak_checking = False
# END LEAKCHECK HACK
tests_in_dir = [t for t in testsToRun if os.path.dirname(t) == d] tests_in_dir = [t for t in testsToRun if os.path.dirname(t) == d]
# If we are using --run-by-dir, we should not use the profile path (if) provided # If we are using --run-by-dir, we should not use the profile path (if) provided

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

@ -38,8 +38,6 @@ def process_single_leak_file(leakLogFileName, processType, leakThreshold,
processString = "%s process:" % processType processString = "%s process:" % processType
crashedOnPurpose = False crashedOnPurpose = False
totalBytesLeaked = None totalBytesLeaked = None
logAsWarning = False
leakAnalysis = []
leakedObjectAnalysis = [] leakedObjectAnalysis = []
leakedObjectNames = [] leakedObjectNames = []
recordLeakedObjects = False recordLeakedObjects = False
@ -68,9 +66,9 @@ def process_single_leak_file(leakLogFileName, processType, leakThreshold,
# log, particularly on B2G. Eventually, these should be split into multiple # log, particularly on B2G. Eventually, these should be split into multiple
# logs (bug 1068869), but for now, we report the largest leak. # logs (bug 1068869), but for now, we report the largest leak.
if totalBytesLeaked is not None: if totalBytesLeaked is not None:
leakAnalysis.append("WARNING | leakcheck | %s " log.warning("leakcheck | %s "
"multiple BloatView byte totals found" "multiple BloatView byte totals found"
% processString) % processString)
else: else:
totalBytesLeaked = 0 totalBytesLeaked = 0
if bytesLeaked > totalBytesLeaked: if bytesLeaked > totalBytesLeaked:
@ -83,22 +81,15 @@ def process_single_leak_file(leakLogFileName, processType, leakThreshold,
else: else:
recordLeakedObjects = False recordLeakedObjects = False
if size < 0 or bytesLeaked < 0 or numLeaked < 0: if size < 0 or bytesLeaked < 0 or numLeaked < 0:
leakAnalysis.append("TEST-UNEXPECTED-FAIL | leakcheck | %s negative leaks caught!" log.error("TEST-UNEXPECTED-FAIL | leakcheck | %s negative leaks caught!"
% processString) % processString)
logAsWarning = True
continue continue
if name != "TOTAL" and numLeaked != 0 and recordLeakedObjects: if name != "TOTAL" and numLeaked != 0 and recordLeakedObjects:
leakedObjectNames.append(name) leakedObjectNames.append(name)
leakedObjectAnalysis.append("TEST-INFO | leakcheck | %s leaked %d %s" leakedObjectAnalysis.append("TEST-INFO | leakcheck | %s leaked %d %s"
% (processString, numLeaked, name)) % (processString, numLeaked, name))
leakAnalysis.extend(leakedObjectAnalysis) log.info('\n'.join(leakedObjectAnalysis))
if logAsWarning:
log.warning('\n'.join(leakAnalysis))
else:
log.info('\n'.join(leakAnalysis))
logAsWarning = False
if totalBytesLeaked is None: if totalBytesLeaked is None:
# We didn't see a line with name 'TOTAL' # We didn't see a line with name 'TOTAL'
@ -109,8 +100,8 @@ def process_single_leak_file(leakLogFileName, processType, leakThreshold,
log.info("TEST-INFO | leakcheck | %s ignoring missing output line for total leaks" log.info("TEST-INFO | leakcheck | %s ignoring missing output line for total leaks"
% processString) % processString)
else: else:
log.info("TEST-UNEXPECTED-FAIL | leakcheck | %s missing output line for total leaks!" log.error("TEST-UNEXPECTED-FAIL | leakcheck | %s missing output line for total leaks!"
% processString) % processString)
log.info("TEST-INFO | leakcheck | missing output line from log file %s" log.info("TEST-INFO | leakcheck | missing output line from log file %s"
% leakLogFileName) % leakLogFileName)
return return
@ -120,12 +111,6 @@ def process_single_leak_file(leakLogFileName, processType, leakThreshold,
processString) processString)
return return
if totalBytesLeaked > leakThreshold:
logAsWarning = True
# Fail the run if we're over the threshold (which defaults to 0)
prefix = "TEST-UNEXPECTED-FAIL"
else:
prefix = "WARNING"
# Create a comma delimited string of the first N leaked objects found, # Create a comma delimited string of the first N leaked objects found,
# to aid with bug summary matching in TBPL. Note: The order of the objects # to aid with bug summary matching in TBPL. Note: The order of the objects
# had no significance (they're sorted alphabetically). # had no significance (they're sorted alphabetically).
@ -134,14 +119,15 @@ def process_single_leak_file(leakLogFileName, processType, leakThreshold,
if len(leakedObjectNames) > maxSummaryObjects: if len(leakedObjectNames) > maxSummaryObjects:
leakedObjectSummary += ', ...' leakedObjectSummary += ', ...'
message = "leakcheck | %s %d bytes leaked (%s)" % (
processString, totalBytesLeaked, leakedObjectSummary)
# totalBytesLeaked will include any expected leaks, so it can be off # totalBytesLeaked will include any expected leaks, so it can be off
# by a few thousand bytes. # by a few thousand bytes.
if logAsWarning: if totalBytesLeaked > leakThreshold:
log.warning("%s | leakcheck | %s %d bytes leaked (%s)" log.error("TEST-UNEXPECTED-FAIL | %s" % message)
% (prefix, processString, totalBytesLeaked, leakedObjectSummary))
else: else:
log.info("%s | leakcheck | %s %d bytes leaked (%s)" log.warning(message)
% (prefix, processString, totalBytesLeaked, leakedObjectSummary))
def process_leak_log(leak_log_file, leak_thresholds=None, def process_leak_log(leak_log_file, leak_thresholds=None,
@ -175,8 +161,8 @@ def process_leak_log(leak_log_file, leak_thresholds=None,
leakLogFile = leak_log_file leakLogFile = leak_log_file
if not os.path.exists(leakLogFile): if not os.path.exists(leakLogFile):
log.info( log.warning(
"WARNING | leakcheck | refcount logging is off, so leaks can't be detected!") "leakcheck | refcount logging is off, so leaks can't be detected!")
return return
leakThresholds = leak_thresholds or {} leakThresholds = leak_thresholds or {}
@ -192,8 +178,8 @@ def process_leak_log(leak_log_file, leak_thresholds=None,
for processType in leakThresholds: for processType in leakThresholds:
if processType not in knownProcessTypes: if processType not in knownProcessTypes:
log.info("TEST-UNEXPECTED-FAIL | leakcheck | Unknown process type %s in leakThresholds" log.error("TEST-UNEXPECTED-FAIL | leakcheck | "
% processType) "Unknown process type %s in leakThresholds" % processType)
(leakLogFileDir, leakFileBase) = os.path.split(leakLogFile) (leakLogFileDir, leakFileBase) = os.path.split(leakLogFile)
if leakFileBase[-4:] == ".log": if leakFileBase[-4:] == ".log":
@ -211,8 +197,8 @@ def process_leak_log(leak_log_file, leak_thresholds=None,
else: else:
processType = "default" processType = "default"
if processType not in knownProcessTypes: if processType not in knownProcessTypes:
log.info("TEST-UNEXPECTED-FAIL | leakcheck | Leak log with unknown process type %s" log.error("TEST-UNEXPECTED-FAIL | leakcheck | "
% processType) "Leak log with unknown process type %s" % processType)
leakThreshold = leakThresholds.get(processType, 0) leakThreshold = leakThresholds.get(processType, 0)
process_single_leak_file(thisFile, processType, leakThreshold, process_single_leak_file(thisFile, processType, leakThreshold,
processType in ignoreMissingLeaks, processType in ignoreMissingLeaks,

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

@ -314,7 +314,9 @@ stage-extensions: make-stage-dir
check:: check::
$(eval cores=$(shell $(PYTHON) -c 'import multiprocessing; print(multiprocessing.cpu_count())')) $(eval cores=$(shell $(PYTHON) -c 'import multiprocessing; print(multiprocessing.cpu_count())'))
@echo "Starting 'mach python-test' with -j$(cores)"
@$(topsrcdir)/mach --log-no-times python-test -j$(cores) @$(topsrcdir)/mach --log-no-times python-test -j$(cores)
@echo "Finished 'mach python-test' successfully"
.PHONY: \ .PHONY: \