Compare commits

...

28 Commits
nsis ... v0.4.1

Author SHA1 Message Date
Florian Bruhin
8d9e9851f1 Release v0.4.1. 2015-09-30 18:23:35 +02:00
Florian Bruhin
c1ab3ebc71 Update changelog. 2015-09-30 18:22:59 +02:00
Florian Bruhin
8376b21f92 Also get ci_install.py from master. 2015-09-30 18:14:04 +02:00
Florian Bruhin
9a9b0643a5 Update CI files from master. 2015-09-30 18:07:06 +02:00
Florian Bruhin
715157a7d0 Fix lint.
Conflicts:
	tox.ini
2015-09-30 07:50:37 +02:00
Florian Bruhin
f88036e780 Adjust pylint ignores for py.path.
Since db513aa pylint can now import py, but fails because it can't infer that
there's a py.path.
2015-09-30 07:48:44 +02:00
Florian Bruhin
aefac1ad19 Update authors. 2015-09-30 07:40:11 +02:00
Florian Bruhin
1654bc4448 tests: Skip test_stale_legacy_server when frozen. 2015-09-30 07:38:14 +02:00
Florian Bruhin
8a3a785c19 Update changelog. 2015-09-30 07:34:41 +02:00
Florian Bruhin
d615b49e60 Fix StopIteration handling in _init_late_modules.
This caused a PendingDeprecationWarning with Python 3.5:

Traceback (most recent call last):
  File "/home/florian/proj/qutebrowser/git/qutebrowser/utils/debug.py", line 237, in log_time
    yield
  File "/home/florian/proj/qutebrowser/git/qutebrowser/app.py", line 433, in _init_late_modules
    next(reader)
StopIteration

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/florian/proj/qutebrowser/git/qutebrowser/app.py", line 433, in _init_late_modules
    next(reader)
  File "/usr/lib64/python3.5/contextlib.py", line 77, in __exit__
    self.gen.throw(type, value, traceback)
PendingDeprecationWarning: generator 'log_time' raised StopIteration
2015-09-30 07:34:41 +02:00
Florian Bruhin
4a5f243ff0 Allow unittests-nodisp to run w/o DISPLAY on CI. 2015-09-30 07:08:30 +02:00
neeasade
7a33dff986 Empty osver for linux, adjust test to match this. 2015-09-30 07:07:36 +02:00
Florian Bruhin
e62a18f8e8 Adjust check_coverage.py for coverage 4.0. 2015-09-30 06:51:00 +02:00
Florian Bruhin
83203820ad Fetch changelog and tox.ini from master. 2015-09-30 06:46:45 +02:00
Florian Bruhin
376d7f2986 Add maxlen argument to ErrorNetworkReply.readData.
This was missing before, causing a (hidden) exception with Python < 3.5, and
this with 3.5:

    TypeError: readData() takes 1 positional argument but 2 were given

    During handling of the above exception, another exception occurred:

    SystemError: PyEval_EvalFrameEx returned a result with an error set

Fixes #969.
2015-09-30 06:35:06 +02:00
Florian Bruhin
03b5248ce6 Add fallback argument to ConfigManager.get.
This is needed for interpolation since this change in Python 3.4:

https://hg.python.org/cpython/rev/267422f7c927

This broke qutebrowser in Debian experimental when updating python from
3.4.3-8 to 3.4.3-9 as they pulled from hg.

Fixes #968.
2015-09-30 06:35:06 +02:00
Florian Bruhin
25cc0534e6 Fix tests without DISPLAY. 2015-09-30 06:35:05 +02:00
Florian Bruhin
6fe89a3800 Don't skip all tests without DISPLAY set.
We used qapp in a session scoped fixture, which means testing without DISPLAY
skipped all tests.
2015-09-30 06:33:48 +02:00
Florian Bruhin
239b25cc8a Save search parameters in tabbed_browser. 2015-09-30 06:31:23 +02:00
Florian Bruhin
7fc054d12c Also re-highlight text when restarting search. 2015-09-30 06:31:23 +02:00
Florian Bruhin
5679649fcf Cleanups 2015-09-30 06:31:23 +02:00
Martin Tournoij
2e0f8a22f0 Use a single search term per-window
Previously, every tab had its own search term. This sets single search term per
window. using `/hello`, `gt`, and `n` will search for `hello` in the 2nd tab.

This fixes issue #940
2015-09-30 06:31:23 +02:00
Antoni Boucher
b8998e7741 Fixed style. 2015-09-30 06:30:05 +02:00
Antoni Boucher
2e228703b5 Fixed issue #934. 2015-09-30 06:30:05 +02:00
Florian Bruhin
8ce7f76b16 Ignore first QWheelEvent for webview.
Hopefully fixes #395.
2015-09-30 06:29:33 +02:00
Florian Bruhin
b603b7e20a ipc: Fix atime timer interval. 2015-09-30 06:29:12 +02:00
Claude
b9e8aedf8b making userid wildcardish 2015-09-30 06:28:35 +02:00
Claude
43bab89bdd workaround for debian, need additional permissions 2015-09-30 06:28:35 +02:00
26 changed files with 254 additions and 111 deletions

View File

@@ -14,5 +14,4 @@ test_script:
- C:\Python34\Scripts\tox -e py34
- C:\Python34\Scripts\tox -e py34-integration
- C:\Python34\Scripts\tox -e unittests-frozen
- C:\Python34\Scripts\tox -e smoke-frozen
- C:\Python34\Scripts\tox -e pylint

View File

@@ -1,4 +1,5 @@
dist: trusty
# So we get Ubuntu Trusty - using "dist: trusty" breaks OS X.
services: docker
os:
- linux
@@ -12,22 +13,21 @@ cache:
- $HOME/.cache/pip
- $HOME/build/The-Compiler/qutebrowser/.cache
env:
- PATH=/home/travis/bin:/home/travis/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
install:
- python scripts/dev/ci_install.py
script:
- xvfb-run -s "-screen 0 640x480x16" tox -e py34,py34-integration
- tox -e misc
- tox -e pep257
- tox -e pyflakes
- tox -e pep8
- tox -e mccabe
- tox -e pylint
- tox -e pyroma
- tox -e check-manifest
# Travis bug - OS X builds get routed to Ubuntu Trusty if "dist: trusty" is
# given.
matrix:
allow_failures:
- os: osx
- '[[ $TRAVIS_OS_NAME == linux ]] && tox -e unittests-nodisp || true'
- '[[ $TRAVIS_OS_NAME == linux ]] && tox -e misc || true'
- '[[ $TRAVIS_OS_NAME == linux ]] && tox -e pep257 || true'
- '[[ $TRAVIS_OS_NAME == linux ]] && tox -e pyflakes || true'
- '[[ $TRAVIS_OS_NAME == linux ]] && tox -e pep8 || true'
- '[[ $TRAVIS_OS_NAME == linux ]] && tox -e mccabe || true'
- '[[ $TRAVIS_OS_NAME == linux ]] && tox -e pyroma || true'
- '[[ $TRAVIS_OS_NAME == linux ]] && tox -e check-manifest || true'
- '[[ $TRAVIS_OS_NAME == linux ]] && tox -e pylint || true'

View File

@@ -14,6 +14,47 @@ This project adheres to http://semver.org/[Semantic Versioning].
// `Fixed` for any bug fixes.
// `Security` to invite users to upgrade in case of vulnerabilities.
v0.5.0 (unreleased)
-------------------
Added
~~~~~
- New setting `ui -> hide-wayland-decoration` to hide the window decoration
when using wayland.
- New userscripts in `misc/userscripts`:
- `open_download` to easily open a file in your downloads folder.
- `view_in_mpv` to open a video in mpv and remove it from the page.
- New setting `content -> host-blocking-whitelist` to whitelist certain domains
from the adblocker.
Changed
~~~~~~~
- The `colors -> tabs.bg/fg.selected` option got split into
`tabs.bg/fg.selected.odd/even`.
v0.4.1
------
Fixed
~~~~~
- Adjusted AppArmor config for the IPC changes in v0.4.0.
- Fixed atime update frequency for IPC file.
- Worked around a Qt issue where middle-clicking caused scrolling with a
touchpad to restart at the beginning of the page.
- The `completion -> web-history-max-items` setting is now also respected for
items added after starting qutebrowser.
- Search terms are now shared between different tabs again
- Tests (a reduced subset of them) now run correctly again when DISPLAY is not
set.
- Fixed an issue causing qutebrowser to crash with Python 3.5 as soon as an ad
was blocked.
- Fixed an issue causing qutebrowser to not start with more recent Python 3.4
versions (e.g. on Debian experimental).
- Fixed various `PendingDeprecationWarnings` shown with Python 3.5.
v0.4.0
------

View File

@@ -168,6 +168,7 @@ Contributors, sorted by the number of commits in descending order:
* Fritz V155 Reichwald
* Franz Fellner
* zwarag
* neeasade
* meles5
* error800
* Tim Harder

View File

@@ -29,6 +29,8 @@ profile qutebrowser /usr/{local/,}bin/qutebrowser {
/proc/*/mounts r,
owner /tmp/** rwkl,
owner /run/user/*/ rw,
owner /run/user/*/** krw,
@{HOME}/.config/qutebrowser/** krw,
@{HOME}/.local/share/qutebrowser/** krw,

View File

@@ -28,7 +28,7 @@ __copyright__ = "Copyright 2014-2015 Florian Bruhin (The Compiler)"
__license__ = "GPL"
__maintainer__ = __author__
__email__ = "mail@qutebrowser.org"
__version_info__ = (0, 4, 0)
__version_info__ = (0, 4, 1)
__version__ = '.'.join(map(str, __version_info__))
__description__ = "A keyboard-driven, vim-like browser based on PyQt5 and QtWebKit."

View File

@@ -424,19 +424,19 @@ def _init_modules(args, crash_handler):
def _init_late_modules(args):
"""Initialize modules which can be inited after the window is shown."""
try:
log.init.debug("Reading web history...")
reader = objreg.get('web-history').async_read()
with debug.log_time(log.init, 'Reading history'):
while True:
QApplication.processEvents()
log.init.debug("Reading web history...")
reader = objreg.get('web-history').async_read()
with debug.log_time(log.init, 'Reading history'):
while True:
QApplication.processEvents()
try:
next(reader)
except StopIteration:
pass
except (OSError, UnicodeDecodeError) as e:
error.handle_fatal_exc(e, args, "Error while initializing!",
pre_text="Error while initializing")
sys.exit(usertypes.Exit.err_init)
except StopIteration:
break
except (OSError, UnicodeDecodeError) as e:
error.handle_fatal_exc(e, args, "Error while initializing!",
pre_text="Error while initializing")
sys.exit(usertypes.Exit.err_init)
class Quitter:

View File

@@ -1269,6 +1269,17 @@ class CommandDispatcher:
except webelem.IsNullError:
raise cmdexc.CommandError("Element vanished while editing!")
def _clear_search(self, view, text):
"""Clear search string/highlights for the given view.
This does nothing if the view's search text is the same as the given
text.
"""
if view.search_text is not None and view.search_text != text:
# We first clear the marked text, then the highlights
view.search('', 0)
view.search('', QWebPage.HighlightAllOccurrences)
@cmdutils.register(instance='command-dispatcher', scope='window',
maxsplit=0)
def search(self, text="", reverse=False):
@@ -1279,11 +1290,7 @@ class CommandDispatcher:
reverse: Reverse search direction.
"""
view = self._current_widget()
if view.search_text is not None and view.search_text != text:
# We first clear the marked text, then the highlights
view.search('', 0)
view.search('', QWebPage.HighlightAllOccurrences)
self._clear_search(view, text)
flags = 0
ignore_case = config.get('general', 'ignore-case')
if ignore_case == 'smart':
@@ -1301,6 +1308,8 @@ class CommandDispatcher:
view.search(text, flags | QWebPage.HighlightAllOccurrences)
view.search_text = text
view.search_flags = flags
self._tabbed_browser.search_text = text
self._tabbed_browser.search_flags = flags
@cmdutils.register(instance='command-dispatcher', hide=True,
scope='window', count='count')
@@ -1311,7 +1320,14 @@ class CommandDispatcher:
count: How many elements to ignore.
"""
view = self._current_widget()
if view.search_text is not None:
self._clear_search(view, self._tabbed_browser.search_text)
if self._tabbed_browser.search_text is not None:
view.search_text = self._tabbed_browser.search_text
view.search_flags = self._tabbed_browser.search_flags
view.search(view.search_text,
view.search_flags | QWebPage.HighlightAllOccurrences)
for _ in range(count):
view.search(view.search_text, view.search_flags)
@@ -1324,8 +1340,13 @@ class CommandDispatcher:
count: How many elements to ignore.
"""
view = self._current_widget()
if view.search_text is None:
return
self._clear_search(view, self._tabbed_browser.search_text)
if self._tabbed_browser.search_text is not None:
view.search_text = self._tabbed_browser.search_text
view.search_flags = self._tabbed_browser.search_flags
view.search(view.search_text,
view.search_flags | QWebPage.HighlightAllOccurrences)
# The int() here serves as a QFlags constructor to create a copy of the
# QFlags instance rather as a reference. I don't know why it works this
# way, but it does.

View File

@@ -127,7 +127,7 @@ class ErrorNetworkReply(QNetworkReply):
"""We always have 0 bytes available."""
return 0
def readData(self):
def readData(self, _maxlen):
"""No data available."""
return bytes()

View File

@@ -71,6 +71,8 @@ class WebView(QWebView):
_check_insertmode: If True, in mouseReleaseEvent we should check if we
need to enter/leave insert mode.
_default_zoom_changed: Whether the zoom was changed from the default.
_ignore_wheel_event: Ignore the next wheel event.
See https://github.com/The-Compiler/qutebrowser/issues/395
Signals:
scroll_pos_changed: Scroll percentage of current tab changed.
@@ -103,6 +105,7 @@ class WebView(QWebView):
self._old_scroll_pos = (-1, -1)
self._zoom = None
self._has_ssl_errors = False
self._ignore_wheel_event = False
self.keep_icon = False
self.search_text = None
self.search_flags = 0
@@ -616,6 +619,7 @@ class WebView(QWebView):
return
self._mousepress_insertmode(e)
self._mousepress_opentarget(e)
self._ignore_wheel_event = True
super().mousePressEvent(e)
def mouseReleaseEvent(self, e):
@@ -638,6 +642,10 @@ class WebView(QWebView):
Args:
e: The QWheelEvent.
"""
if self._ignore_wheel_event:
self._ignore_wheel_event = False
# See https://github.com/The-Compiler/qutebrowser/issues/395
return
if e.modifiers() & Qt.ControlModifier:
e.accept()
divider = config.get('input', 'mouse-zoom-divider')

View File

@@ -69,8 +69,8 @@ class UrlCompletionModel(base.BaseCompletionModel):
bookmark_manager.removed.connect(self.on_bookmark_removed)
self._history = objreg.get('web-history')
max_history = config.get('completion', 'web-history-max-items')
history = utils.newest_slice(self._history, max_history)
self._max_history = config.get('completion', 'web-history-max-items')
history = utils.newest_slice(self._history, self._max_history)
for entry in history:
self._add_history_entry(entry)
self._history.add_completion_item.connect(
@@ -92,12 +92,19 @@ class UrlCompletionModel(base.BaseCompletionModel):
else:
return dt.strftime(fmt)
def _remove_oldest_history(self):
"""Remove the oldest history entry."""
self._history_cat.removeRow(0)
def _add_history_entry(self, entry):
"""Add a new history entry to the completion."""
self.new_item(self._history_cat, entry.url.toDisplayString(), "",
self._fmt_atime(entry.atime), sort=int(entry.atime),
userdata=entry.url)
if self._history_cat.rowCount() > self._max_history:
self._remove_oldest_history()
@config.change_filter('completion', 'timestamp-format')
def reformat_timestamps(self):
"""Reformat the timestamps if the config option was changed."""

View File

@@ -42,6 +42,9 @@ from qutebrowser.utils import (message, objreg, utils, standarddir, log,
from qutebrowser.utils.usertypes import Completion
UNSET = object()
class change_filter: # pylint: disable=invalid-name
"""Decorator to filter calls based on a config section/option matching.
@@ -619,9 +622,13 @@ class ConfigManager(QObject):
return existed
@functools.lru_cache()
def get(self, sectname, optname, raw=False, transformed=True):
def get(self, sectname, optname, raw=False, transformed=True,
fallback=UNSET):
"""Get the value from a section/option.
We don't support the vars argument from configparser.get as it's not
hashable.
Args:
sectname: The section to get the option from.
optname: The option name
@@ -634,13 +641,18 @@ class ConfigManager(QObject):
if not self._initialized:
raise Exception("get got called before initialization was "
"complete!")
try:
sect = self.sections[sectname]
except KeyError:
if fallback is not UNSET:
return fallback
raise configexc.NoSectionError(sectname)
try:
val = sect[optname]
except KeyError:
if fallback is not UNSET:
return fallback
raise configexc.NoOptionError(optname, sectname)
if raw:
return val.value()

View File

@@ -54,6 +54,8 @@ class TabbedBrowser(tabwidget.TabWidget):
emitted if the signal occurred in the current tab.
Attributes:
search_text/search_flags: Search parameters which are shared between
all tabs.
_win_id: The window ID this tabbedbrowser is associated with.
_filter: A SignalFilter instance.
_now_focused: The tab which is focused now.
@@ -108,6 +110,8 @@ class TabbedBrowser(tabwidget.TabWidget):
self._undo_stack = []
self._filter = signalfilter.SignalFilter(win_id, self)
self._now_focused = None
self.search_text = None
self.search_flags = 0
objreg.get('config').changed.connect(self.update_favicons)
objreg.get('config').changed.connect(self.update_window_title)
objreg.get('config').changed.connect(self.update_tab_titles)

View File

@@ -179,7 +179,7 @@ class IPCServer(QObject):
self._atime_timer = None
else:
self._atime_timer = usertypes.Timer(self, 'ipc-atime')
self._atime_timer.setInterval(READ_TIMEOUT)
self._atime_timer.setInterval(ATIME_INTERVAL)
self._atime_timer.timeout.connect(self.update_atime)
self._atime_timer.setTimerType(Qt.VeryCoarseTimer)

View File

@@ -162,7 +162,7 @@ def _os_info():
lines = []
releaseinfo = None
if sys.platform == 'linux':
osver = ', '.join([e for e in platform.dist() if e])
osver = ''
releaseinfo = _release_info()
elif sys.platform == 'win32':
osver = ', '.join(platform.win32_ver())

View File

@@ -119,12 +119,7 @@ def check(fileobj, perfect_files):
assert 0 <= branch_cov <= 100, branch_cov
assert '\\' not in filename, filename
# Files without any branches have 0% coverage
has_branches = klass.find('./lines/line[@branch="true"]') is not None
if branch_cov < 100 and has_branches:
is_bad = True
else:
is_bad = line_cov < 100
is_bad = line_cov < 100 or branch_cov < 100
if filename in perfect_files and is_bad:
messages.append(("{} has {}% line and {}% branch coverage!".format(

View File

@@ -48,7 +48,7 @@ def brew(args, silent=False):
with open(os.devnull, 'w') as f:
subprocess.check_call(['brew'] + args, stdout=f)
else:
subprocess.check_call(['brew'] + args)
subprocess.check_call(['brew'] + args + ['--verbose'])
def check_setup(executable):
@@ -97,7 +97,7 @@ elif os.environ.get('TRAVIS_OS_NAME', None) == 'osx':
brew(['install', 'python3', 'pyqt5'])
print("Installing tox...")
subprocess.check_call(['sudo', 'pip3.4', 'install', 'tox'])
subprocess.check_call(['sudo', 'pip3', 'install', 'tox'])
check_setup('python3')

View File

@@ -61,7 +61,8 @@ def pytest_collection_modifyitems(items):
if 'qapp' in getattr(item, 'fixturenames', ()):
item.add_marker('gui')
if sys.platform == 'linux' and not os.environ.get('DISPLAY', ''):
if 'CI' in os.environ:
if ('CI' in os.environ and
not os.environ.get('QUTE_NO_DISPLAY_OK', '')):
raise Exception("No display available on CI!")
skip_marker = pytest.mark.skipif(
True, reason="No DISPLAY available")
@@ -96,10 +97,11 @@ def pytest_runtest_setup(item):
pytest.skip("Can only run when frozen!")
@pytest.fixture(autouse=True, scope='session')
def change_qapp_name(qapp):
@pytest.fixture(scope='session')
def qapp(qapp):
"""Change the name of the QApplication instance."""
qapp.setApplicationName('qute_test')
return qapp
class WinRegistryHelper:

30
tests/test_conftest.py Normal file
View File

@@ -0,0 +1,30 @@
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
# Copyright 2015 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
#
# This file is part of qutebrowser.
#
# qutebrowser is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# qutebrowser is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>.
"""Various meta-tests for conftest.py."""
def test_qapp_name(qapp):
"""Make sure the QApplication name is changed when we use qapp."""
assert qapp.applicationName() == 'qute_test'
def test_foo(request):
"""Make sure a test without qapp doesn't use qapp (via autouse)."""
assert 'qapp' not in request.fixturenames

View File

@@ -88,6 +88,6 @@ def test_error_network_reply(qtbot, req):
assert reply.isFinished()
assert not reply.isRunning()
assert reply.bytesAvailable() == 0
assert reply.readData() == b''
assert reply.readData(1) == b''
assert reply.error() == QNetworkReply.UnknownNetworkError
assert reply.errorString() == "This is an error"

View File

@@ -66,6 +66,9 @@ class TestConfigParser:
def test_transformed_option_old(self):
"""Test a transformed option with the old name."""
# WORKAROUND
# Instance of 'object' has no 'name' member (no-member)
# pylint: disable=no-member
self.cp.read_dict({'colors': {'tab.fg.odd': 'pink'}})
self.cfg._from_cp(self.cp)
actual = self.cfg.get('colors', 'tabs.fg.odd').name()
@@ -74,6 +77,9 @@ class TestConfigParser:
def test_transformed_option_new(self):
"""Test a transformed section with the new name."""
# WORKAROUND
# Instance of 'object' has no 'name' member (no-member)
# pylint: disable=no-member
self.cp.read_dict({'colors': {'tabs.fg.odd': 'pink'}})
self.cfg._from_cp(self.cp)
actual = self.cfg.get('colors', 'tabs.fg.odd').name()
@@ -154,6 +160,27 @@ class TestConfigParser:
with pytest.raises(configexc.NoOptionError):
self.cfg.get('general', 'bar') # pylint: disable=bad-config-call
def test_fallback(self):
"""Test getting an option with fallback.
This is done during interpolation in later Python 3.4 versions.
See https://github.com/The-Compiler/qutebrowser/issues/968
"""
# pylint: disable=bad-config-call
assert self.cfg.get('general', 'blabla', fallback='blub') == 'blub'
def test_sectionproxy(self):
"""Test getting an option via the section proxy."""
self.cp.read_dict({'general': {'ignore-case': 'false'}})
self.cfg._from_cp(self.cp)
assert not self.cfg['general'].get('ignore-case')
def test_sectionproxy_keyerror(self):
"""Test getting an inexistent option via the section proxy."""
with pytest.raises(configexc.NoOptionError):
self.cfg['general'].get('blahblahblub')
class TestKeyConfigParser:

View File

@@ -18,6 +18,8 @@
"""Hypothesis tests for qutebrowser.config.configtypes."""
import os
import sys
import inspect
import functools
@@ -44,6 +46,11 @@ def gen_classes():
@hypothesis.given(strategies.text())
@hypothesis.example('\x00')
def test_configtypes_hypothesis(klass, s):
if (klass in [configtypes.File, configtypes.UserStyleSheet] and
sys.platform == 'linux' and
not os.environ.get('DISPLAY', '')):
pytest.skip("No DISPLAY available")
try:
klass().validate(s)
except configexc.ValidationError:

View File

@@ -31,7 +31,7 @@ import subprocess
from unittest import mock
import pytest
import py.path # pylint: disable=import-error
import py.path # pylint: disable=no-name-in-module,import-error
from PyQt5.QtCore import pyqtSignal, QObject
from PyQt5.QtNetwork import QLocalServer, QLocalSocket, QAbstractSocket
from PyQt5.QtTest import QSignalSpy
@@ -45,10 +45,13 @@ from helpers import stubs # pylint: disable=import-error
Args = collections.namedtuple('Args', 'basedir')
pytestmark = pytest.mark.usefixtures('qapp')
@pytest.yield_fixture()
def short_tmpdir():
with tempfile.TemporaryDirectory() as tdir:
yield py.path.local(tdir)
yield py.path.local(tdir) # pylint: disable=no-member
@pytest.yield_fixture
@@ -676,6 +679,7 @@ class TestSendOrListen:
assert "Connecting to {}".format(legacy_server._socketname) in msgs
@pytest.mark.posix # Unneeded on Windows
@pytest.mark.not_frozen
def test_stale_legacy_server(self, caplog, qtbot, args, legacy_server,
ipc_server, py_proc):
legacy_name = ipc._get_socketname(args.basedir, legacy=True)

View File

@@ -126,7 +126,7 @@ def test_untested(covtest):
pass
""")
covtest.run()
expected = 'module.py has 75.0% line and 0.0% branch coverage!'
expected = 'module.py has 75.0% line and 100.0% branch coverage!'
assert covtest.check() == [expected]

View File

@@ -451,25 +451,16 @@ class TestOsInfo:
"""Tests for _os_info."""
@pytest.mark.parametrize('dist, dist_str', [
(('x', '', 'y'), 'x, y'),
(('a', 'b', 'c'), 'a, b, c'),
(('', '', ''), ''),
])
def test_linux_fake(self, monkeypatch, dist, dist_str):
def test_linux_fake(self, monkeypatch):
"""Test with a fake Linux.
Args:
dist: The value to set platform.dist() to.
dist_str: The expected distribution string in version._os_info().
No args because osver is set to '' if the OS is linux.
"""
monkeypatch.setattr('qutebrowser.utils.version.sys.platform', 'linux')
monkeypatch.setattr('qutebrowser.utils.version._release_info',
lambda: [('releaseinfo', 'Hello World')])
monkeypatch.setattr('qutebrowser.utils.version.platform.dist',
lambda: dist)
ret = version._os_info()
expected = ['OS Version: {}'.format(dist_str), '',
expected = ['OS Version: ', '',
'--- releaseinfo ---', 'Hello World']
assert ret == expected

74
tox.ini
View File

@@ -16,16 +16,16 @@ passenv = PYTHON DISPLAY XAUTHORITY HOME USERNAME USER CI
deps =
-r{toxinidir}/requirements.txt
py==1.4.30
pytest==2.7.2
pytest==2.7.3 # rq.filter: <2.8.0
pytest-capturelog==0.7
pytest-qt==1.6.0
pytest-mock==0.7.0
pytest-qt==1.7.0
pytest-mock==0.8.1
pytest-html==1.6
hypothesis==1.11.0
hypothesis-pytest==0.18.1
coverage==3.7.1
hypothesis==1.11.4
coverage==4.0.0
pytest-cov==2.1.0
beautifulsoup4==4.4.0
beautifulsoup4==4.4.1
commands =
{envpython} scripts/link_pyqt.py --tox {envdir}
{envpython} -m py.test --strict -rfEsw -m 'not integration' --cov qutebrowser --cov-report xml --cov-report=html --cov-report= {posargs:tests}
@@ -35,7 +35,11 @@ commands =
basepython = python3
setenv = {[testenv]setenv}
passenv = {[testenv]passenv}
deps = {[testenv]deps}
deps =
{[testenv]deps}
httpbin==0.3.0
itsdangerous==0.24
Werkzeug==0.10.4
commands =
{envpython} scripts/link_pyqt.py --tox {envdir}
{envpython} -m py.test --strict -rfEsw -m 'integration' {posargs:tests}
@@ -46,29 +50,29 @@ commands = {envpython} scripts/link_pyqt.py --tox {envdir}
envdir = {toxinidir}/.venv
usedevelop = true
[testenv:unittests-watch]
basepython = python3
passenv = {[testenv]passenv}
deps =
{[testenv]deps}
pytest-testmon==0.6
pytest-watch==3.2.0
commands =
{envpython} scripts/link_pyqt.py --tox {envdir}
{envdir}/bin/ptw -- --testmon --strict -rfEsw {posargs:tests}
[testenv:unittests-frozen]
basepython = python3
passenv = {[testenv]passenv}
skip_install = true
deps =
{[testenv]deps}
{[testenv:py34-integration]deps}
cx_Freeze==4.3.4
commands =
{envpython} scripts/link_pyqt.py --tox {envdir}
{envpython} scripts/dev/freeze_tests.py build_exe -b {envdir}/build
{envdir}/build/run-frozen-tests --strict -rfEsw {posargs}
[testenv:unittests-nodisp]
basepython = python3
passenv = {[testenv]passenv}
deps = {[testenv]deps}
setenv =
DISPLAY=
QUTE_NO_DISPLAY_OK=1
commands =
{envpython} scripts/link_pyqt.py --tox {envdir}
{envpython} -m py.test --strict -rfEw -m 'not integration' {posargs:tests}
[testenv:misc]
basepython = python3
# For global .gitignore files
@@ -85,11 +89,9 @@ skip_install = true
setenv = PYTHONPATH={toxinidir}/scripts/dev
passenv =
deps =
-r{toxinidir}/requirements.txt
{[testenv:py34-integration]deps}
astroid==1.3.8
beautifulsoup4==4.4.0
pylint==1.4.4
hypothesis==1.11.0
logilab-common==1.0.2
six==1.9.0
commands =
@@ -117,9 +119,9 @@ passenv =
deps =
-r{toxinidir}/requirements.txt
py==1.4.30
pytest==2.7.2
pyflakes==0.9.2
pytest-flakes==1.0.0
pytest==2.7.3 # rq.filter: <2.8.0
pyflakes==1.0.0
pytest-flakes==1.0.1
commands =
{envpython} scripts/link_pyqt.py --tox {envdir}
{envpython} -m py.test -q --flakes --ignore=tests
@@ -130,7 +132,7 @@ passenv =
deps =
-r{toxinidir}/requirements.txt
py==1.4.30
pytest==2.7.2
pytest==2.7.3 # rq.filter: <2.8.0
pep8==1.6.2
pytest-pep8==1.0.6
commands =
@@ -143,7 +145,7 @@ passenv =
deps =
-r{toxinidir}/requirements.txt
py==1.4.30
pytest==2.7.2
pytest==2.7.3 # rq.filter: <2.8.0
mccabe==0.3.1
pytest-mccabe==0.1
commands =
@@ -184,24 +186,14 @@ commands =
git --no-pager diff --exit-code --stat
{envpython} scripts/asciidoc2html.py {posargs}
[testenv:smoke-frozen]
basepython = python3
passenv = {[testenv]passenv}
skip_install = true
deps =
{[testenv]deps}
cx_Freeze==4.3.4
commands =
{envpython} scripts/link_pyqt.py --tox {envdir}
{envpython} scripts/dev/freeze.py build_exe --qute-skip-html -b {envdir}/build
{envdir}/build/qutebrowser --no-err-windows --nowindow --temp-basedir about:blank ":later 500 quit"
[testenv:cxfreeze-windows]
# PYTHON is actually required when using this env, but the entire tox.ini would
# fail if we didn't have a fallback defined.
basepython = {env:PYTHON:}/python.exe
skip_install = true
deps = {[testenv:smoke-frozen]deps}
deps =
-r{toxinidir}/requirements.txt
cx_Freeze==4.3.4
commands =
{envpython} scripts/link_pyqt.py --tox {envdir}
{envpython} scripts/dev/freeze.py {posargs}