From 6826a979101fa8e292c37875a2225ff26165b6ad Mon Sep 17 00:00:00 2001 From: avk Date: Mon, 22 Feb 2016 15:10:39 +0100 Subject: [PATCH 01/34] Tests for browser.network.pastebin --- tests/unit/browser/network/test_pastebin.py | 60 +++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 tests/unit/browser/network/test_pastebin.py diff --git a/tests/unit/browser/network/test_pastebin.py b/tests/unit/browser/network/test_pastebin.py new file mode 100644 index 000000000..e0999bc60 --- /dev/null +++ b/tests/unit/browser/network/test_pastebin.py @@ -0,0 +1,60 @@ +# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: + +# Copyright 2016 Anna Kobak (avk) : +# +# 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 . + +"""Tests for qutebrowser.browser.network""" + +import pytest +from PyQt5.QtCore import QUrl + +from qutebrowser.browser.network import pastebin +from qutebrowser.misc import httpclient + +DATA = [{"name" : "XYZ", "title" : "hello world", "text" : "xyz. 123 \n 172ANB", "reply" : "abc" }] + +class HTTPPostStub(httpclient.HTTPClient): + + """A stub class for HTTPClient. + + Attributes: + url: the last url send by post() + data: the last data send by post() + """ + + def __init__(self): + super().__init__() + + def post(self, url, data): + self.url = url + self.data = data + + + +def test_constructor(qapp): + client = pastebin.PastebinClient() + assert isinstance(client._client, httpclient.HTTPClient) + +@pytest.mark.parametrize('data', DATA) +def test_paste(data): + client = pastebin.PastebinClient() + http_stub = HTTPPostStub() + client._client = http_stub + client.paste(data["name"], data["title"], data["text"], data["reply"]) + assert http_stub.data == data + assert http_stub.url == QUrl('http://paste.the-compiler.org/api/create') + From 90c80782258c89a4c2612f73985fba15078803ad Mon Sep 17 00:00:00 2001 From: avk Date: Mon, 22 Feb 2016 16:09:57 +0100 Subject: [PATCH 02/34] More tests for browser.network.pastebin --- tests/unit/browser/network/test_pastebin.py | 41 +++++++++++++++++++-- 1 file changed, 38 insertions(+), 3 deletions(-) diff --git a/tests/unit/browser/network/test_pastebin.py b/tests/unit/browser/network/test_pastebin.py index e0999bc60..1e237d58c 100644 --- a/tests/unit/browser/network/test_pastebin.py +++ b/tests/unit/browser/network/test_pastebin.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2016 Anna Kobak (avk) : +# Copyright 2016 Anna Kobak (avk) : # # This file is part of qutebrowser. # @@ -25,7 +25,15 @@ from PyQt5.QtCore import QUrl from qutebrowser.browser.network import pastebin from qutebrowser.misc import httpclient -DATA = [{"name" : "XYZ", "title" : "hello world", "text" : "xyz. 123 \n 172ANB", "reply" : "abc" }] +DATA = [{"name" : "XYZ", "title" : "hello world", "text" : "xyz. 123 \n 172ANB", "reply" : "abc" }, + {"name" : "the name", "title" : "the title", "text" : "some Text", "reply" : "some parent"}] + +DATA_NOPARENT = [{"name" : "XYZ", "title" : "hello world", "text" : "xyz. 123 \n 172ANB"}, + {"name" : "the name", "title" : "the title", "text" : "some Text"}] + +HTTP_VALID = ["http://paste.the-compiler.org/view/ges83nt3", "http://paste.the-compiler.org/view/3gjnwg4"] + +HTTP_INVALID = ["http invalid", "http:/invalid.org"] class HTTPPostStub(httpclient.HTTPClient): @@ -50,7 +58,7 @@ def test_constructor(qapp): assert isinstance(client._client, httpclient.HTTPClient) @pytest.mark.parametrize('data', DATA) -def test_paste(data): +def test_paste_with_parent(data): client = pastebin.PastebinClient() http_stub = HTTPPostStub() client._client = http_stub @@ -58,3 +66,30 @@ def test_paste(data): assert http_stub.data == data assert http_stub.url == QUrl('http://paste.the-compiler.org/api/create') +@pytest.mark.parametrize('data', DATA_NOPARENT) +def test_paste_without_parent(data): + client = pastebin.PastebinClient() + http_stub = HTTPPostStub() + client._client = http_stub + client.paste(data["name"], data["title"], data["text"]) + assert http_stub.data == data + assert http_stub.url == QUrl('http://paste.the-compiler.org/api/create') + +@pytest.mark.parametrize('http', HTTP_VALID) +def test_on_client_success(http, qtbot): + client = pastebin.PastebinClient() + http_stub = HTTPPostStub() + client._client = http_stub + with qtbot.assertNotEmitted(client.error): + with qtbot.waitSignal(client.success): + client.on_client_success(http) + +@pytest.mark.parametrize('http', HTTP_INVALID) +def test_on_client_success_invalid_http(http, qtbot): + client = pastebin.PastebinClient() + http_stub = HTTPPostStub() + client._client = http_stub + with qtbot.assertNotEmitted(client.success): + with qtbot.waitSignal(client.error): + client.on_client_success(http) + From c9bb6d01119c53f250459a96505759e5ed5f56e3 Mon Sep 17 00:00:00 2001 From: avk Date: Mon, 22 Feb 2016 20:46:22 +0100 Subject: [PATCH 03/34] tests for browser.network.pastebin - code refactor --- scripts/dev/check_coverage.py | 2 + tests/unit/browser/network/test_pastebin.py | 114 ++++++++++++-------- 2 files changed, 71 insertions(+), 45 deletions(-) diff --git a/scripts/dev/check_coverage.py b/scripts/dev/check_coverage.py index ff5bc01f5..939fd5e71 100644 --- a/scripts/dev/check_coverage.py +++ b/scripts/dev/check_coverage.py @@ -65,6 +65,8 @@ PERFECT_FILES = [ 'qutebrowser/browser/network/filescheme.py'), ('tests/unit/browser/network/test_networkreply.py', 'qutebrowser/browser/network/networkreply.py'), + ('tests/unit/browser/network/test_pastebin.py', + 'qutebrowser/browser/network/pastebin.py'), ('tests/unit/browser/test_signalfilter.py', 'qutebrowser/browser/signalfilter.py'), diff --git a/tests/unit/browser/network/test_pastebin.py b/tests/unit/browser/network/test_pastebin.py index 1e237d58c..d72c9f6b8 100644 --- a/tests/unit/browser/network/test_pastebin.py +++ b/tests/unit/browser/network/test_pastebin.py @@ -17,7 +17,7 @@ # You should have received a copy of the GNU General Public License # along with qutebrowser. If not, see . -"""Tests for qutebrowser.browser.network""" +"""Tests for qutebrowser.browser.network.""" import pytest from PyQt5.QtCore import QUrl @@ -25,15 +25,6 @@ from PyQt5.QtCore import QUrl from qutebrowser.browser.network import pastebin from qutebrowser.misc import httpclient -DATA = [{"name" : "XYZ", "title" : "hello world", "text" : "xyz. 123 \n 172ANB", "reply" : "abc" }, - {"name" : "the name", "title" : "the title", "text" : "some Text", "reply" : "some parent"}] - -DATA_NOPARENT = [{"name" : "XYZ", "title" : "hello world", "text" : "xyz. 123 \n 172ANB"}, - {"name" : "the name", "title" : "the title", "text" : "some Text"}] - -HTTP_VALID = ["http://paste.the-compiler.org/view/ges83nt3", "http://paste.the-compiler.org/view/3gjnwg4"] - -HTTP_INVALID = ["http invalid", "http:/invalid.org"] class HTTPPostStub(httpclient.HTTPClient): @@ -46,50 +37,83 @@ class HTTPPostStub(httpclient.HTTPClient): def __init__(self): super().__init__() - - def post(self, url, data): + self.url = None + self.data = None + + def post(self, url, data=None): self.url = url self.data = data +@pytest.fixture +def pbclient(): + client = pastebin.PastebinClient() + http_stub = HTTPPostStub() + client._client = http_stub + return client + def test_constructor(qapp): - client = pastebin.PastebinClient() - assert isinstance(client._client, httpclient.HTTPClient) + pbclient = pastebin.PastebinClient() + assert isinstance(pbclient._client, httpclient.HTTPClient) -@pytest.mark.parametrize('data', DATA) -def test_paste_with_parent(data): - client = pastebin.PastebinClient() - http_stub = HTTPPostStub() - client._client = http_stub - client.paste(data["name"], data["title"], data["text"], data["reply"]) - assert http_stub.data == data - assert http_stub.url == QUrl('http://paste.the-compiler.org/api/create') - -@pytest.mark.parametrize('data', DATA_NOPARENT) -def test_paste_without_parent(data): - client = pastebin.PastebinClient() - http_stub = HTTPPostStub() - client._client = http_stub - client.paste(data["name"], data["title"], data["text"]) + +@pytest.mark.parametrize('data', [ + { + "name": "XYZ", + "title": "hello world", + "text": "xyz. 123 \n 172ANB", + "reply": "abc" + }, + { + "name": "the name", + "title": "the title", + "text": "some Text", + "reply": "some parent" + } +]) +def test_paste_with_parent(data, pbclient): + http_stub = pbclient._client + pbclient.paste(data["name"], data["title"], data["text"], data["reply"]) assert http_stub.data == data assert http_stub.url == QUrl('http://paste.the-compiler.org/api/create') -@pytest.mark.parametrize('http', HTTP_VALID) -def test_on_client_success(http, qtbot): - client = pastebin.PastebinClient() - http_stub = HTTPPostStub() - client._client = http_stub - with qtbot.assertNotEmitted(client.error): - with qtbot.waitSignal(client.success): - client.on_client_success(http) -@pytest.mark.parametrize('http', HTTP_INVALID) -def test_on_client_success_invalid_http(http, qtbot): - client = pastebin.PastebinClient() - http_stub = HTTPPostStub() - client._client = http_stub - with qtbot.assertNotEmitted(client.success): - with qtbot.waitSignal(client.error): - client.on_client_success(http) +@pytest.mark.parametrize('data', [ + { + "name": "XYZ", + "title": "hello world", + "text": "xyz. 123 \n 172ANB" + }, + { + "name": "the name", + "title": "the title", + "text": "some Text" + } +]) +def test_paste_without_parent(data, pbclient): + http_stub = pbclient._client + pbclient.paste(data["name"], data["title"], data["text"]) + assert pbclient._client.data == data + assert http_stub.url == QUrl('http://paste.the-compiler.org/api/create') + +@pytest.mark.parametrize('http', [ + "http://paste.the-compiler.org/view/ges83nt3", + "http://paste.the-compiler.org/view/3gjnwg4" +]) +def test_on_client_success(http, pbclient, qtbot): + with qtbot.assertNotEmitted(pbclient.error): + with qtbot.waitSignal(pbclient.success): + pbclient.on_client_success(http) + + +@pytest.mark.parametrize('http', [ + "http invalid", + "http:/invalid.org" + "http//invalid.com" +]) +def test_on_client_success_invalid_http(http, pbclient, qtbot): + with qtbot.assertNotEmitted(pbclient.success): + with qtbot.waitSignal(pbclient.error): + pbclient.on_client_success(http) From a78644a19922bb48e680a11ab21765ca16081edb Mon Sep 17 00:00:00 2001 From: Clayton Craft Date: Wed, 23 Mar 2016 22:14:16 -0700 Subject: [PATCH 04/34] Implement statusbar.url test --- scripts/dev/check_coverage.py | 2 + tests/unit/mainwindow/statusbar/test_url.py | 126 ++++++++++++++++++++ 2 files changed, 128 insertions(+) create mode 100644 tests/unit/mainwindow/statusbar/test_url.py diff --git a/scripts/dev/check_coverage.py b/scripts/dev/check_coverage.py index ff5bc01f5..18f7758b7 100644 --- a/scripts/dev/check_coverage.py +++ b/scripts/dev/check_coverage.py @@ -102,6 +102,8 @@ PERFECT_FILES = [ 'qutebrowser/mainwindow/statusbar/textbase.py'), ('tests/unit/mainwindow/statusbar/test_prompt.py', 'qutebrowser/mainwindow/statusbar/prompt.py'), + ('tests/unit/mainwindow/statusbar/test_url.py', + 'qutebrowser/mainwindow/statusbar/url.py'), ('tests/unit/config/test_configtypes.py', 'qutebrowser/config/configtypes.py'), diff --git a/tests/unit/mainwindow/statusbar/test_url.py b/tests/unit/mainwindow/statusbar/test_url.py new file mode 100644 index 000000000..15cff07b0 --- /dev/null +++ b/tests/unit/mainwindow/statusbar/test_url.py @@ -0,0 +1,126 @@ +# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: + +# Copyright 2014-2016 Florian Bruhin (The Compiler) +# +# 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 . + + +"""Test Statusbar url.""" + +import pytest + +from collections import namedtuple +from qutebrowser.mainwindow.statusbar.url import UrlText +from qutebrowser.utils import usertypes + + +UrlType = usertypes.enum('UrlType', ['success', 'success_https', 'error', + 'warn', 'hover', 'normal']) + + +@pytest.fixture +def tab_widget(): + """Fixture providing a fake tab widget.""" + tab = namedtuple('Tab', 'cur_url load_status') + tab.load_status = namedtuple('load_status', 'name') + tab.cur_url = namedtuple('cur_url', 'toDisplayString') + return tab + + +@pytest.fixture +def url_widget(qtbot, monkeypatch, config_stub): + """Fixture providing a Url widget.""" + config_stub.data = { + 'colors': { + 'statusbar.url.bg': 'white', + 'statusbar.url.fg': 'black', + 'statusbar.url.fg.success': 'yellow', + 'statusbar.url.fg.success.https': 'green', + 'statusbar.url.fg.error': 'red', + 'statusbar.url.fg.warn': 'orange', + 'statusbar.url.fg.hover': 'blue'}, + 'fonts': {}, + } + monkeypatch.setattr( + 'qutebrowser.mainwindow.statusbar.url.style.config', config_stub) + widget = UrlText() + qtbot.add_widget(widget) + assert not widget.isVisible() + return widget + + +@pytest.mark.parametrize('url', [ + ('http://abc123.com/this/awesome/url.html'), + ('https://supersecret.gov/nsa/files.txt'), + ('Th1$ i$ n0t @ n0rm@L uRL! P@n1c! <-->'), + (None) +]) +def test_set_url(url_widget, url): + """Test text displayed by the widget.""" + url_widget.set_url(url) + if url is not None: + assert url_widget.text() == url + else: + assert url_widget.text() == "" + + +@pytest.mark.parametrize('url, title, text', [ + ('http://abc123.com/this/awesome/url.html', 'Awesome site', 'click me!'), + ('https://supersecret.gov/nsa/files.txt', 'Secret area', None), + ('Th1$ i$ n0t @ n0rm@L uRL! P@n1c! <-->', 'Probably spam', 'definitely'), + (None, None, 'did I break?!') +]) +def test_set_hover_url(url_widget, url, title, text): + """Test text when hovering over a link.""" + url_widget.set_hover_url(url, title, text) + if url is not None: + assert url_widget.text() == url + else: + assert url_widget.text() == '' + + +@pytest.mark.parametrize('status, expected', [ + ('success', 'success'), + ('success_https', 'success_https'), + ('error', 'error'), + ('warn', 'warn') +]) +def test_on_load_status_changed(url_widget, status, expected): + """Test text when status is changed.""" + url_widget.set_url('www.example.com') + url_widget.on_load_status_changed(status) + assert url_widget._urltype.name == expected + + +@pytest.mark.parametrize('load_status, url', [ + ('success', 'http://abc123.com/this/awesome/url.html'), + ('success', 'http://reddit.com/r/linux'), + ('success_https', 'www.google.com'), + ('success_https', 'https://supersecret.gov/nsa/files.txt'), + ('warn', 'www.shadysite.org/some/path/to/a/file/that/has/issues.htm'), + ('error', 'Th1$ i$ n0t @ n0rm@L uRL! P@n1c! <-->'), + ('error', None) +]) +def test_on_tab_changed(url_widget, tab_widget, load_status, url): + tab_widget.load_status.name = load_status + tab_widget.cur_url.toDisplayString = lambda: url + url_widget.on_tab_changed(tab_widget) + if url is not None: + assert url_widget._urltype.name == load_status + assert url_widget.text() == url + else: + assert url_widget._urltype.name == 'normal' + assert url_widget.text() == '' From 81ce9b818f27b4362b133d9dea38d31e05772320 Mon Sep 17 00:00:00 2001 From: Clayton Craft Date: Thu, 24 Mar 2016 18:58:08 -0700 Subject: [PATCH 05/34] Fix importer script to output bookmarks in the right format for qutebrowser to use --- scripts/importer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/importer.py b/scripts/importer.py index 44453ea92..a30fd5ba8 100755 --- a/scripts/importer.py +++ b/scripts/importer.py @@ -61,7 +61,7 @@ def import_netscape_bookmarks(bookmarks_file): bookmarks = [] for tag in html_tags: if tag['href'] not in bookmarks: - bookmarks.append('{tag.string} {tag[href]}'.format(tag=tag)) + bookmarks.append('{tag[href]} {tag.string}'.format(tag=tag)) for bookmark in bookmarks: print(bookmark) From 8addc19d47855e4ba3bd44d72e4ab712ad217de1 Mon Sep 17 00:00:00 2001 From: Clayton Craft Date: Fri, 25 Mar 2016 13:01:21 -0700 Subject: [PATCH 06/34] Added option to output in "bookmark" format, clarified some "help" msgs --- scripts/importer.py | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/scripts/importer.py b/scripts/importer.py index a30fd5ba8..5e0883cd2 100755 --- a/scripts/importer.py +++ b/scripts/importer.py @@ -31,23 +31,27 @@ import argparse def main(): args = get_args() if args.browser in ['chromium', 'firefox', 'ie']: - import_netscape_bookmarks(args.bookmarks) + import_netscape_bookmarks(args.bookmarks, args.bookmark_format) def get_args(): """Get the argparse parser.""" parser = argparse.ArgumentParser( epilog="To import bookmarks from Chromium, Firefox or IE, " - "export them to HTML in your browsers bookmark manager.") - parser.add_argument('browser', help="Which browser?", + "export them to HTML in your browsers bookmark manager. " + "By default, this script will output in a quickmarks format.") + parser.add_argument('browser', help="Which browser? (chromium, firefox)", choices=['chromium', 'firefox', 'ie'], metavar='browser') - parser.add_argument('bookmarks', help="Bookmarks file") + parser.add_argument('-b', help="Output in bookmark format.", + dest='bookmark_format', action='store_true', + default=False, required=False) + parser.add_argument('bookmarks', help="Bookmarks file (html format)") args = parser.parse_args() return args -def import_netscape_bookmarks(bookmarks_file): +def import_netscape_bookmarks(bookmarks_file, is_bookmark_format): """Import bookmarks from a NETSCAPE-Bookmark-file v1. Generated by Chromium, Firefox, IE and possibly more browsers @@ -57,11 +61,15 @@ def import_netscape_bookmarks(bookmarks_file): soup = bs4.BeautifulSoup(f, 'html.parser') html_tags = soup.findAll('a') + if is_bookmark_format: + output_template = '{tag[href]} {tag.string}' + else: + output_template = '{tag.string} {tag[href]}' bookmarks = [] for tag in html_tags: if tag['href'] not in bookmarks: - bookmarks.append('{tag[href]} {tag.string}'.format(tag=tag)) + bookmarks.append(output_template.format(tag=tag)) for bookmark in bookmarks: print(bookmark) From af54255cee3bf72362a90a72a97c6b6355a995b0 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Fri, 25 Mar 2016 21:47:46 +0100 Subject: [PATCH 07/34] Regenerate authors --- README.asciidoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.asciidoc b/README.asciidoc index 181bd11aa..a9f5fb236 100644 --- a/README.asciidoc +++ b/README.asciidoc @@ -182,6 +182,7 @@ Contributors, sorted by the number of commits in descending order: * rikn00 * Michael Ilsaas * Martin Zimmermann +* Clayton Craft * Brian Jackson * sbinix * neeasade @@ -199,7 +200,6 @@ Contributors, sorted by the number of commits in descending order: * Fritz V155 Reichwald * Franz Fellner * Corentin Jule -* Clayton Craft * zwarag * xd1le * haxwithaxe From ebfe4763191f970b21257444c466baf5f640abe6 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Fri, 25 Mar 2016 22:04:48 +0100 Subject: [PATCH 08/34] Allow eslint to fail for now https://github.com/eslint/eslint/issues/5680 --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 4952fddcc..9148a5cda 100644 --- a/.travis.yml +++ b/.travis.yml @@ -42,6 +42,8 @@ matrix: allow_failures: - os: osx env: TESTENV=py35 + - os: linux + env: TESTENV=eslint cache: directories: From 4778ff6f9c96d447b0c6de50f8628f0151f82d00 Mon Sep 17 00:00:00 2001 From: Clayton Craft Date: Fri, 25 Mar 2016 15:56:39 -0700 Subject: [PATCH 09/34] Cleaned up url test and improved coveraged based on feedback --- tests/unit/mainwindow/statusbar/test_url.py | 92 ++++++++++----------- 1 file changed, 46 insertions(+), 46 deletions(-) diff --git a/tests/unit/mainwindow/statusbar/test_url.py b/tests/unit/mainwindow/statusbar/test_url.py index 15cff07b0..6587c6afa 100644 --- a/tests/unit/mainwindow/statusbar/test_url.py +++ b/tests/unit/mainwindow/statusbar/test_url.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2016 Clayton Craft (craftyguy) # # This file is part of qutebrowser. # @@ -21,22 +21,18 @@ """Test Statusbar url.""" import pytest +import collections -from collections import namedtuple -from qutebrowser.mainwindow.statusbar.url import UrlText +from qutebrowser.browser import webview +from qutebrowser.mainwindow.statusbar import url from qutebrowser.utils import usertypes -UrlType = usertypes.enum('UrlType', ['success', 'success_https', 'error', - 'warn', 'hover', 'normal']) - - @pytest.fixture def tab_widget(): """Fixture providing a fake tab widget.""" - tab = namedtuple('Tab', 'cur_url load_status') - tab.load_status = namedtuple('load_status', 'name') - tab.cur_url = namedtuple('cur_url', 'toDisplayString') + tab = collections.namedtuple('Tab', 'cur_url load_status') + tab.cur_url = collections.namedtuple('cur_url', 'toDisplayString') return tab @@ -56,71 +52,75 @@ def url_widget(qtbot, monkeypatch, config_stub): } monkeypatch.setattr( 'qutebrowser.mainwindow.statusbar.url.style.config', config_stub) - widget = UrlText() + widget = url.UrlText() qtbot.add_widget(widget) assert not widget.isVisible() return widget -@pytest.mark.parametrize('url', [ - ('http://abc123.com/this/awesome/url.html'), - ('https://supersecret.gov/nsa/files.txt'), - ('Th1$ i$ n0t @ n0rm@L uRL! P@n1c! <-->'), - (None) +@pytest.mark.parametrize('url_text', [ + 'http://abc123.com/this/awesome/url.html', + 'https://supersecret.gov/nsa/files.txt', + 'Th1$ i$ n0t @ n0rm@L uRL! P@n1c! <-->', + None ]) -def test_set_url(url_widget, url): +def test_set_url(url_widget, url_text): """Test text displayed by the widget.""" - url_widget.set_url(url) - if url is not None: - assert url_widget.text() == url + url_widget.set_url(url_text) + if url_text is not None: + assert url_widget.text() == url_text else: assert url_widget.text() == "" -@pytest.mark.parametrize('url, title, text', [ +@pytest.mark.parametrize('url_text, title, text', [ ('http://abc123.com/this/awesome/url.html', 'Awesome site', 'click me!'), ('https://supersecret.gov/nsa/files.txt', 'Secret area', None), ('Th1$ i$ n0t @ n0rm@L uRL! P@n1c! <-->', 'Probably spam', 'definitely'), (None, None, 'did I break?!') ]) -def test_set_hover_url(url_widget, url, title, text): +def test_set_hover_url(url_widget, url_text, title, text): """Test text when hovering over a link.""" - url_widget.set_hover_url(url, title, text) - if url is not None: - assert url_widget.text() == url + url_widget.set_hover_url(url_text, title, text) + if url_text is not None: + assert url_widget.text() == url_text + assert url_widget._urltype == url.UrlType.hover else: assert url_widget.text() == '' + assert url_widget._urltype == url.UrlType.normal @pytest.mark.parametrize('status, expected', [ - ('success', 'success'), - ('success_https', 'success_https'), - ('error', 'error'), - ('warn', 'warn') + (webview.LoadStatus.success, url.UrlType.success), + (webview.LoadStatus.success_https, url.UrlType.success_https), + (webview.LoadStatus.error, url.UrlType.error), + (webview.LoadStatus.warn, url.UrlType.warn), + (webview.LoadStatus.loading, url.UrlType.normal), + (webview.LoadStatus.none, url.UrlType.normal) ]) def test_on_load_status_changed(url_widget, status, expected): """Test text when status is changed.""" url_widget.set_url('www.example.com') - url_widget.on_load_status_changed(status) - assert url_widget._urltype.name == expected + url_widget.on_load_status_changed(status.name) + assert url_widget._urltype == expected -@pytest.mark.parametrize('load_status, url', [ - ('success', 'http://abc123.com/this/awesome/url.html'), - ('success', 'http://reddit.com/r/linux'), - ('success_https', 'www.google.com'), - ('success_https', 'https://supersecret.gov/nsa/files.txt'), - ('warn', 'www.shadysite.org/some/path/to/a/file/that/has/issues.htm'), - ('error', 'Th1$ i$ n0t @ n0rm@L uRL! P@n1c! <-->'), - ('error', None) +@pytest.mark.parametrize('load_status, url_text', [ + (url.UrlType.success, 'http://abc123.com/this/awesome/url.html'), + (url.UrlType.success, 'http://reddit.com/r/linux'), + (url.UrlType.success_https, 'www.google.com'), + (url.UrlType.success_https, 'https://supersecret.gov/nsa/files.txt'), + (url.UrlType.warn, 'www.shadysite.org/some/path/to/a/file/that/has/issues.htm'), + (url.UrlType.error, 'Th1$ i$ n0t @ n0rm@L uRL! P@n1c! <-->'), + (url.UrlType.error, None) ]) -def test_on_tab_changed(url_widget, tab_widget, load_status, url): - tab_widget.load_status.name = load_status - tab_widget.cur_url.toDisplayString = lambda: url +def test_on_tab_changed(url_widget, tab_widget, load_status, url_text): + tab_widget.load_status = load_status + tab_widget.cur_url.toDisplayString = lambda: url_text url_widget.on_tab_changed(tab_widget) - if url is not None: - assert url_widget._urltype.name == load_status - assert url_widget.text() == url + if url_text is not None: + assert url_widget._urltype == load_status + assert url_widget.text() == url_text else: - assert url_widget._urltype.name == 'normal' + assert url_widget._urltype == url.UrlType.normal assert url_widget.text() == '' From 9edc5a665e414ae2ec8f5bc04a99bce2bc4e6d33 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Fri, 25 Mar 2016 23:57:29 +0100 Subject: [PATCH 10/34] Handle jinja's UndefinedError in jinja.render We can get UndefinedError when a new function got added to the jinja env (and gets called from a template) and the user did update the on-disk templates but not restart qutebrowser yet. In this case, let's show a special error page to the user and tell them to do :report in the unlikely case it's actually a bug. Fixes #1362. See #1360. --- qutebrowser/html/undef_error.html | 22 ++++++++++++++++++++++ qutebrowser/utils/jinja.py | 13 ++++++++++--- tests/unit/utils/test_jinja.py | 20 +++++++++++++++++++- 3 files changed, 51 insertions(+), 4 deletions(-) create mode 100644 qutebrowser/html/undef_error.html diff --git a/qutebrowser/html/undef_error.html b/qutebrowser/html/undef_error.html new file mode 100644 index 000000000..55a47ca95 --- /dev/null +++ b/qutebrowser/html/undef_error.html @@ -0,0 +1,22 @@ + + + + + + + Error while rendering HTML + + +

Error while rendering internal qutebrowser page

+

There was an error while rendering {pagename}.

+ +

This most likely happened because you updated qutebrowser but didn't restart yet.

+ +

If you believe this isn't the case and this is a bug, please do :report.

+ +

Traceback

+
{traceback}
+ + diff --git a/qutebrowser/utils/jinja.py b/qutebrowser/utils/jinja.py index 5c9827b21..7dee80249 100644 --- a/qutebrowser/utils/jinja.py +++ b/qutebrowser/utils/jinja.py @@ -21,10 +21,12 @@ import os import os.path +import traceback import jinja2 +import jinja2.exceptions -from qutebrowser.utils import utils +from qutebrowser.utils import utils, log from PyQt5.QtCore import QUrl @@ -74,8 +76,13 @@ def resource_url(path): def render(template, **kwargs): """Render the given template and pass the given arguments to it.""" - return _env.get_template(template).render(**kwargs) - + try: + return _env.get_template(template).render(**kwargs) + except jinja2.exceptions.UndefinedError: + log.misc.exception("UndefinedError while rendering " + template) + err_template = utils.read_file(os.path.join('html', 'undef_error.html')) + tb = traceback.format_exc() + return err_template.format(pagename=template, traceback=tb) _env = jinja2.Environment(loader=Loader('html'), autoescape=_guess_autoescape) _env.globals['resource_url'] = resource_url diff --git a/tests/unit/utils/test_jinja.py b/tests/unit/utils/test_jinja.py index 99aecf551..6852e4051 100644 --- a/tests/unit/utils/test_jinja.py +++ b/tests/unit/utils/test_jinja.py @@ -23,21 +23,27 @@ import os import os.path import pytest +import logging import jinja2 from PyQt5.QtCore import QUrl -from qutebrowser.utils import jinja +from qutebrowser.utils import utils, jinja @pytest.fixture(autouse=True) def patch_read_file(monkeypatch): """pytest fixture to patch utils.read_file.""" + real_read_file = utils.read_file def _read_file(path): """A read_file which returns a simple template if the path is right.""" if path == os.path.join('html', 'test.html'): return """Hello {{var}}""" elif path == os.path.join('html', 'test2.html'): return """{{ resource_url('utils/testfile') }}""" + elif path == os.path.join('html', 'undef.html'): + return """{{ does_not_exist() }}""" + elif path == os.path.join('html', 'undef_error.html'): + return real_read_file(path) else: raise IOError("Invalid path {}!".format(path)) @@ -87,6 +93,18 @@ def test_utf8(): assert data == "Hello \u2603" +def test_undefined_function(caplog): + """Make sure we don't crash if an undefined function is called.""" + with caplog.at_level(logging.ERROR): + data = jinja.render('undef.html') + assert 'There was an error while rendering undef.html' in data + assert "'does_not_exist' is undefined" in data + assert data.startswith('') + + assert len(caplog.records) == 1 + assert caplog.records[0].msg == "UndefinedError while rendering undef.html" + + @pytest.mark.parametrize('name, expected', [ (None, False), ('foo', False), From 387e35d3e51a2b05578974e16e98517ace7eb441 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Sat, 26 Mar 2016 00:24:54 +0100 Subject: [PATCH 11/34] Fix lint --- qutebrowser/utils/jinja.py | 3 ++- tests/unit/utils/test_jinja.py | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/qutebrowser/utils/jinja.py b/qutebrowser/utils/jinja.py index 7dee80249..e6ccd9c6c 100644 --- a/qutebrowser/utils/jinja.py +++ b/qutebrowser/utils/jinja.py @@ -80,7 +80,8 @@ def render(template, **kwargs): return _env.get_template(template).render(**kwargs) except jinja2.exceptions.UndefinedError: log.misc.exception("UndefinedError while rendering " + template) - err_template = utils.read_file(os.path.join('html', 'undef_error.html')) + err_path = os.path.join('html', 'undef_error.html') + err_template = utils.read_file(err_path) tb = traceback.format_exc() return err_template.format(pagename=template, traceback=tb) diff --git a/tests/unit/utils/test_jinja.py b/tests/unit/utils/test_jinja.py index 6852e4051..cea237d22 100644 --- a/tests/unit/utils/test_jinja.py +++ b/tests/unit/utils/test_jinja.py @@ -34,6 +34,7 @@ from qutebrowser.utils import utils, jinja def patch_read_file(monkeypatch): """pytest fixture to patch utils.read_file.""" real_read_file = utils.read_file + def _read_file(path): """A read_file which returns a simple template if the path is right.""" if path == os.path.join('html', 'test.html'): From 32304f36dd53aff487b466b4685f9f9cac7ece96 Mon Sep 17 00:00:00 2001 From: Clayton Craft Date: Fri, 25 Mar 2016 16:35:49 -0700 Subject: [PATCH 12/34] fix pylint failure --- tests/unit/mainwindow/statusbar/test_url.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/mainwindow/statusbar/test_url.py b/tests/unit/mainwindow/statusbar/test_url.py index 6587c6afa..530c67965 100644 --- a/tests/unit/mainwindow/statusbar/test_url.py +++ b/tests/unit/mainwindow/statusbar/test_url.py @@ -110,7 +110,7 @@ def test_on_load_status_changed(url_widget, status, expected): (url.UrlType.success, 'http://reddit.com/r/linux'), (url.UrlType.success_https, 'www.google.com'), (url.UrlType.success_https, 'https://supersecret.gov/nsa/files.txt'), - (url.UrlType.warn, 'www.shadysite.org/some/path/to/a/file/that/has/issues.htm'), + (url.UrlType.warn, 'www.shadysite.org/some/path/to/file/with/issues.htm'), (url.UrlType.error, 'Th1$ i$ n0t @ n0rm@L uRL! P@n1c! <-->'), (url.UrlType.error, None) ]) From 3ccbf3ce1f0c0d08f8d0201daa1f80f1b65e1b4c Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Sat, 26 Mar 2016 12:15:56 +0100 Subject: [PATCH 13/34] tox: Use -m pytest instead of -m py.test Using -m py.test seems to cause some issues, see e.g. https://github.com/The-Compiler/qutebrowser/pull/1317#issuecomment-196923033 --- tox.ini | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tox.ini b/tox.ini index 49d59e616..38aff5cc9 100644 --- a/tox.ini +++ b/tox.ini @@ -47,7 +47,7 @@ deps = cherrypy==5.1.0 commands = {envpython} scripts/link_pyqt.py --tox {envdir} - {envpython} -m py.test {posargs:tests} + {envpython} -m pytest {posargs:tests} [testenv:py35-cov] basepython = python3.5 @@ -56,7 +56,7 @@ passenv = {[testenv]passenv} deps = {[testenv]deps} commands = {envpython} scripts/link_pyqt.py --tox {envdir} - {envpython} -m py.test --cov --cov-report xml --cov-report=html --cov-report= {posargs:tests} + {envpython} -m pytest --cov --cov-report xml --cov-report=html --cov-report= {posargs:tests} {envpython} scripts/dev/check_coverage.py {posargs} [testenv:py34-cov] @@ -66,7 +66,7 @@ passenv = {[testenv]passenv} deps = {[testenv]deps} commands = {envpython} scripts/link_pyqt.py --tox {envdir} - {envpython} -m py.test --cov --cov-report xml --cov-report=html --cov-report= {posargs:tests} + {envpython} -m pytest --cov --cov-report xml --cov-report=html --cov-report= {posargs:tests} {envpython} scripts/dev/check_coverage.py {posargs} [testenv:mkvenv] @@ -99,7 +99,7 @@ setenv = QUTE_NO_DISPLAY=1 commands = {envpython} scripts/link_pyqt.py --tox {envdir} - {envpython} -m py.test {posargs:tests} + {envpython} -m pytest {posargs:tests} [testenv:misc] ignore_errors = true From cb9ff97edbf5d8be2a2502382b649d27750cfefb Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Sat, 26 Mar 2016 14:36:42 +0100 Subject: [PATCH 14/34] Mention to clone repo in INSTALL.asciidoc --- INSTALL.asciidoc | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/INSTALL.asciidoc b/INSTALL.asciidoc index 0d143624e..71b46d09a 100644 --- a/INSTALL.asciidoc +++ b/INSTALL.asciidoc @@ -214,7 +214,16 @@ it as part of the packaging process. Installing qutebrowser with tox ------------------------------- -Run tox inside the qutebrowser repository to set up a +First of all, clone the repository using http://git-scm.org/[git] and switch +into the repository folder: + +---- +$ git clone https://github.com/The-Compiler/qutebrowser.git +$ cd qutebrowser +---- + + +Then run tox inside the qutebrowser repository to set up a https://docs.python.org/3/library/venv.html[virtual environment]: ---- From e0475bf4e106cae1715cf89d6449d9cc3c9b8e63 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Sat, 26 Mar 2016 17:28:08 +0100 Subject: [PATCH 15/34] Revert "Allow eslint to fail for now" This reverts commit ebfe4763191f970b21257444c466baf5f640abe6. --- .travis.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 9148a5cda..4952fddcc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -42,8 +42,6 @@ matrix: allow_failures: - os: osx env: TESTENV=py35 - - os: linux - env: TESTENV=eslint cache: directories: From 4cd49d42cc855acaa2731a85035d8cd875e9e933 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Sun, 27 Mar 2016 12:53:53 +0200 Subject: [PATCH 16/34] Fix pdfjs error page after jinja.render changes --- qutebrowser/browser/webpage.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/qutebrowser/browser/webpage.py b/qutebrowser/browser/webpage.py index 30582579f..60e3abb67 100644 --- a/qutebrowser/browser/webpage.py +++ b/qutebrowser/browser/webpage.py @@ -219,14 +219,14 @@ class BrowserPage(QWebPage): def _show_pdfjs(self, reply): """Show the reply with pdfjs.""" try: - page = pdfjs.generate_pdfjs_page(reply.url()).encode('utf-8') + page = pdfjs.generate_pdfjs_page(reply.url()) except pdfjs.PDFJSNotFound: # pylint: disable=no-member # WORKAROUND for https://bitbucket.org/logilab/pylint/issue/490/ - page = (jinja.env.get_template('no_pdfjs.html') - .render(url=reply.url().toDisplayString()) - .encode('utf-8')) - self.mainFrame().setContent(page, 'text/html', reply.url()) + page = jinja.render('no_pdfjs.html', + url=reply.url().toDisplayString()) + self.mainFrame().setContent(page.encode('utf-8'), 'text/html', + reply.url()) reply.deleteLater() def shutdown(self): From 952daf0479a7a525bd13d4c211ba0c0a9b2ffca9 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Sun, 27 Mar 2016 22:50:55 +0200 Subject: [PATCH 17/34] Well, this is embarassing. Fix segfault reports. --- CHANGELOG.asciidoc | 1 + qutebrowser/misc/crashsignal.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc index b42ec2279..150aa92d7 100644 --- a/CHANGELOG.asciidoc +++ b/CHANGELOG.asciidoc @@ -62,6 +62,7 @@ Fixed - Fixed very long filenames when downloading `data://`-URLs. - Fixed ugly UI fonts on Windows when Liberation Mono is installed - Fixed crash when unbinding key from a section which doesn't exist in the config +- Fixed report window after a segfault v0.5.1 ------ diff --git a/qutebrowser/misc/crashsignal.py b/qutebrowser/misc/crashsignal.py index 22ed19d79..8d78b1e9a 100644 --- a/qutebrowser/misc/crashsignal.py +++ b/qutebrowser/misc/crashsignal.py @@ -72,7 +72,7 @@ class CrashHandler(QObject): def handle_segfault(self): """Handle a segfault from a previous run.""" - data_dir = None + data_dir = standarddir.data() if data_dir is None: return logname = os.path.join(data_dir, 'crash.log') From bec8bd028518a4446affc16bcd5da6dfed4f0f25 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Sun, 27 Mar 2016 23:11:45 +0200 Subject: [PATCH 18/34] Mention QtWebEngine in segfault report dialog --- qutebrowser/misc/crashdialog.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/qutebrowser/misc/crashdialog.py b/qutebrowser/misc/crashdialog.py index 161ce9308..ed4e47519 100644 --- a/qutebrowser/misc/crashdialog.py +++ b/qutebrowser/misc/crashdialog.py @@ -508,11 +508,23 @@ class FatalCrashDialog(_CrashDialog): def _init_text(self): super()._init_text() text = ("qutebrowser was restarted after a fatal crash.
" - "
Note: Crash reports for fatal crashes sometimes don't " + "QTWEBENGINE_NOTE" + "
Crash reports for fatal crashes sometimes don't " "contain the information necessary to fix an issue. Please " "follow the steps in " "stacktrace.asciidoc to submit a stacktrace.
") + + if datetime.datetime.now() < datetime.datetime(2016, 4, 23): + note = ("
Fatal crashes like this are often caused by the " + "current QtWebKit backend.
I'm currently running a " + "crowdfunding for the new QtWebEngine backend, based on " + "Chromium: " + "igg.me/at/qutebrowser
") + text = text.replace('QTWEBENGINE_NOTE', note) + else: + text = text.replace('QTWEBENGINE_NOTE', '') + self._lbl.setText(text) def _init_checkboxes(self): From 654d2ac6763d0982b532ae4ede3c2bef0b5edbdf Mon Sep 17 00:00:00 2001 From: issue Date: Mon, 28 Mar 2016 14:40:35 +0200 Subject: [PATCH 19/34] Added openSUSE repo --- INSTALL.asciidoc | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/INSTALL.asciidoc b/INSTALL.asciidoc index 71b46d09a..2d2fb6494 100644 --- a/INSTALL.asciidoc +++ b/INSTALL.asciidoc @@ -135,6 +135,21 @@ it with: $ nix-env -i qutebrowser ---- +On openSUSE +----------- + +There are prebuilt RPMs available for Tumbleweed and Leap 42.1: + +http://software.opensuse.org/download.html?project=home%3Aarpraher&package=qutebrowser[One Click Install] + +Or add the repo manually: + +---- +# zypper addrepo http://download.opensuse.org/repositories/home:arpraher/openSUSE_Tumbleweed/home:arpraher.repo +# zypper refresh +# zypper install qutebrowser +---- + On Windows ---------- From 6d190fc16e56d34d62f1d1a9a9c51f3b2926c71f Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Mon, 28 Mar 2016 19:59:18 +0200 Subject: [PATCH 20/34] Regenerate authors --- README.asciidoc | 1 + 1 file changed, 1 insertion(+) diff --git a/README.asciidoc b/README.asciidoc index a9f5fb236..c1ac5ff6f 100644 --- a/README.asciidoc +++ b/README.asciidoc @@ -202,6 +202,7 @@ Contributors, sorted by the number of commits in descending order: * Corentin Jule * zwarag * xd1le +* issue * haxwithaxe * evan * dylan araps From c0f9ab9b2bacf782b638293a4201bf6d6f82a22f Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Mon, 28 Mar 2016 20:14:15 +0200 Subject: [PATCH 21/34] tox: Update Werkzeug to 0.11.5 - werkzeug.serving: Fix crash when attempting SSL connection to HTTP server. --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 38aff5cc9..09834c2c8 100644 --- a/tox.ini +++ b/tox.ini @@ -42,7 +42,7 @@ deps = six==1.10.0 termcolor==1.1.0 vulture==0.8.1 - Werkzeug==0.11.4 + Werkzeug==0.11.5 wheel==0.29.0 cherrypy==5.1.0 commands = From e0c0613db6abf752c2c1c262a073758ce3bed08b Mon Sep 17 00:00:00 2001 From: Clayton Craft Date: Mon, 28 Mar 2016 15:12:16 -0700 Subject: [PATCH 22/34] Added new test for simulating loading page and hover/unhover URL --- tests/unit/mainwindow/statusbar/test_url.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/tests/unit/mainwindow/statusbar/test_url.py b/tests/unit/mainwindow/statusbar/test_url.py index 530c67965..d9115b28e 100644 --- a/tests/unit/mainwindow/statusbar/test_url.py +++ b/tests/unit/mainwindow/statusbar/test_url.py @@ -124,3 +124,23 @@ def test_on_tab_changed(url_widget, tab_widget, load_status, url_text): else: assert url_widget._urltype == url.UrlType.normal assert url_widget.text() == '' + + +@pytest.mark.parametrize('url_text, load_status, expected_status', [ + ('http://abc123.com/this/awesome/url.html', webview.LoadStatus.success, + url.UrlType.success), + ('https://supersecret.gov/nsa/files.txt', webview.LoadStatus.success_https, + url.UrlType.success_https), + ('Th1$ i$ n0t @ n0rm@L uRL! P@n1c! <-->', webview.LoadStatus.error, + url.UrlType.error), + ('http://www.qutebrowser.org/CONTRIBUTING.html', webview.LoadStatus.loading, + url.UrlType.normal), + ('www.whatisthisurl.com', webview.LoadStatus.warn, url.UrlType.warn) +]) +def test_normal_url(url_widget, url_text, load_status, expected_status): + url_widget.set_url(url_text) + url_widget.on_load_status_changed(load_status.name) + url_widget.set_hover_url(url_text, "", "") + url_widget.set_hover_url("", "", "") + assert url_widget.text() == url_text + assert url_widget._urltype == expected_status From c811db5424ba7ae34af3e9ddd41ecfa2a6e3f191 Mon Sep 17 00:00:00 2001 From: Clayton Craft Date: Mon, 28 Mar 2016 15:31:28 -0700 Subject: [PATCH 23/34] remove unused import --- tests/unit/mainwindow/statusbar/test_url.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/unit/mainwindow/statusbar/test_url.py b/tests/unit/mainwindow/statusbar/test_url.py index d9115b28e..5ac0600d5 100644 --- a/tests/unit/mainwindow/statusbar/test_url.py +++ b/tests/unit/mainwindow/statusbar/test_url.py @@ -25,7 +25,6 @@ import collections from qutebrowser.browser import webview from qutebrowser.mainwindow.statusbar import url -from qutebrowser.utils import usertypes @pytest.fixture From 2aaf22df764eb2edc6f8c7dc1d3c1a8e52207f08 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Tue, 29 Mar 2016 06:59:23 +0200 Subject: [PATCH 24/34] Also read blocked hosts from ~/.config --- CHANGELOG.asciidoc | 2 + doc/help/commands.asciidoc | 2 + qutebrowser/browser/adblock.py | 76 ++++++++++++++++++++++++++-------- 3 files changed, 63 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc index 150aa92d7..59d28aa08 100644 --- a/CHANGELOG.asciidoc +++ b/CHANGELOG.asciidoc @@ -44,6 +44,8 @@ Changed - Improved detection of URLs/search terms when pasting multiple lines. - Don't remove `qutebrowser-editor-*` temporary file if editor subprocess crashed - Userscripts are also searched in `/usr/share/qutebrowser/userscripts`. +- Blocked hosts are now also read from a `blocked-hosts` file in the config dir + (e.g. `~/.config/qutebrowser/blocked-hosts`). Fixed ~~~~~ diff --git a/doc/help/commands.asciidoc b/doc/help/commands.asciidoc index 79e0549dd..d1875c645 100644 --- a/doc/help/commands.asciidoc +++ b/doc/help/commands.asciidoc @@ -72,6 +72,8 @@ === adblock-update Update the adblock block lists. +This updates ~/.local/share/qutebrowser/blocked-hosts with downloaded host lists and re-reads ~/.config/qutebrowser/blocked-hosts. + [[back]] === back Syntax: +:back [*--tab*] [*--bg*] [*--window*]+ diff --git a/qutebrowser/browser/adblock.py b/qutebrowser/browser/adblock.py index 2de66691f..df7595a20 100644 --- a/qutebrowser/browser/adblock.py +++ b/qutebrowser/browser/adblock.py @@ -92,9 +92,11 @@ class HostBlocker: Attributes: _blocked_hosts: A set of blocked hosts. + _config_blocked_hosts: A set of blocked hosts from ~/.config. _in_progress: The DownloadItems which are currently downloading. _done_count: How many files have been read successfully. - _hosts_file: The path to the blocked-hosts file. + _local_hosts_file: The path to the blocked-hosts file. + _config_hosts_file: The path to a blocked-hosts in ~/.config Class attributes: WHITELISTED: Hosts which never should be blocked. @@ -105,13 +107,22 @@ class HostBlocker: def __init__(self): self._blocked_hosts = set() + self._config_blocked_hosts = set() self._in_progress = [] self._done_count = 0 + data_dir = standarddir.data() if data_dir is None: - self._hosts_file = None + self._local_hosts_file = None else: - self._hosts_file = os.path.join(data_dir, 'blocked-hosts') + self._local_hosts_file = os.path.join(data_dir, 'blocked-hosts') + + config_dir = standarddir.config() + if config_dir is None: + self._config_hosts_file = None + else: + self._config_hosts_file = os.path.join(config_dir, 'blocked-hosts') + objreg.get('config').changed.connect(self.on_config_changed) def is_blocked(self, url): @@ -119,21 +130,46 @@ class HostBlocker: if not config.get('content', 'host-blocking-enabled'): return False host = url.host() - return host in self._blocked_hosts and not is_whitelisted_host(host) + return ((host in self._blocked_hosts or + host in self._config_blocked_hosts) and + not is_whitelisted_host(host)) + + def _read_hosts_file(self, filename, target): + """Read hosts from the given filename. + + Args: + filename: The file to read. + target: The set to store the hosts in. + + Return: + True if a read was attempted, False otherwise + """ + if not os.path.exists(filename): + return False + + try: + with open(filename, 'r', encoding='utf-8') as f: + for line in f: + target.add(line.strip()) + except OSError: + log.misc.exception("Failed to read host blocklist!") + + return True def read_hosts(self): """Read hosts from the existing blocked-hosts file.""" self._blocked_hosts = set() - if self._hosts_file is None: + + if self._local_hosts_file is None: return - if os.path.exists(self._hosts_file): - try: - with open(self._hosts_file, 'r', encoding='utf-8') as f: - for line in f: - self._blocked_hosts.add(line.strip()) - except OSError: - log.misc.exception("Failed to read host blocklist!") - else: + + self._read_hosts_file(self._config_hosts_file, + self._config_blocked_hosts) + + found = self._read_hosts_file(self._local_hosts_file, + self._blocked_hosts) + + if not found: args = objreg.get('args') if (config.get('content', 'host-block-lists') is not None and args.basedir is None): @@ -142,8 +178,14 @@ class HostBlocker: @cmdutils.register(instance='host-blocker', win_id='win_id') def adblock_update(self, win_id): - """Update the adblock block lists.""" - if self._hosts_file is None: + """Update the adblock block lists. + + This updates ~/.local/share/qutebrowser/blocked-hosts with downloaded + host lists and re-reads ~/.config/qutebrowser/blocked-hosts. + """ + self._read_hosts_file(self._config_hosts_file, + self._config_blocked_hosts) + if self._local_hosts_file is None: raise cmdexc.CommandError("No data storage is configured!") self._blocked_hosts = set() self._done_count = 0 @@ -221,7 +263,7 @@ class HostBlocker: def on_lists_downloaded(self): """Install block lists after files have been downloaded.""" - with open(self._hosts_file, 'w', encoding='utf-8') as f: + with open(self._local_hosts_file, 'w', encoding='utf-8') as f: for host in sorted(self._blocked_hosts): f.write(host + '\n') message.info('current', "adblock: Read {} hosts from {} sources." @@ -233,7 +275,7 @@ class HostBlocker: urls = config.get('content', 'host-block-lists') if urls is None: try: - os.remove(self._hosts_file) + os.remove(self._local_hosts_file) except OSError: log.misc.exception("Failed to delete hosts file.") From 3eb782b20431094a9d57f20763814e99b73efabb Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Tue, 29 Mar 2016 07:06:32 +0200 Subject: [PATCH 25/34] pylint: Remove now useless no-member suppression This is now useless since introducing the jinja.render wrapper. --- qutebrowser/browser/webpage.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/qutebrowser/browser/webpage.py b/qutebrowser/browser/webpage.py index 60e3abb67..a8bce0e07 100644 --- a/qutebrowser/browser/webpage.py +++ b/qutebrowser/browser/webpage.py @@ -221,8 +221,6 @@ class BrowserPage(QWebPage): try: page = pdfjs.generate_pdfjs_page(reply.url()) except pdfjs.PDFJSNotFound: - # pylint: disable=no-member - # WORKAROUND for https://bitbucket.org/logilab/pylint/issue/490/ page = jinja.render('no_pdfjs.html', url=reply.url().toDisplayString()) self.mainFrame().setContent(page.encode('utf-8'), 'text/html', From cce1747e3e65cd33af0b1a5ac1633c342e3bdc47 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Tue, 29 Mar 2016 07:08:48 +0200 Subject: [PATCH 26/34] Regenerate authors --- README.asciidoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.asciidoc b/README.asciidoc index c1ac5ff6f..fe93da6df 100644 --- a/README.asciidoc +++ b/README.asciidoc @@ -169,6 +169,7 @@ Contributors, sorted by the number of commits in descending order: * Milan Svoboda * John ShaggyTwoDope Jenkins * Peter Vilim +* Clayton Craft * Oliver Caldwell * Philipp Hansch * Jonas Schürmann @@ -182,7 +183,6 @@ Contributors, sorted by the number of commits in descending order: * rikn00 * Michael Ilsaas * Martin Zimmermann -* Clayton Craft * Brian Jackson * sbinix * neeasade From f15fb16ad4db371321c973f2790d4a2205ea599d Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Tue, 29 Mar 2016 07:09:17 +0200 Subject: [PATCH 27/34] Fix small nitpick --- tests/unit/mainwindow/statusbar/test_url.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/unit/mainwindow/statusbar/test_url.py b/tests/unit/mainwindow/statusbar/test_url.py index 5ac0600d5..1d5878a01 100644 --- a/tests/unit/mainwindow/statusbar/test_url.py +++ b/tests/unit/mainwindow/statusbar/test_url.py @@ -46,7 +46,8 @@ def url_widget(qtbot, monkeypatch, config_stub): 'statusbar.url.fg.success.https': 'green', 'statusbar.url.fg.error': 'red', 'statusbar.url.fg.warn': 'orange', - 'statusbar.url.fg.hover': 'blue'}, + 'statusbar.url.fg.hover': 'blue' + }, 'fonts': {}, } monkeypatch.setattr( From 32c9f2ac94795fd3cee0e7c663bf26a23dc9fee4 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Tue, 29 Mar 2016 07:43:11 +0200 Subject: [PATCH 28/34] Fix starting with -c "" --- qutebrowser/browser/adblock.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qutebrowser/browser/adblock.py b/qutebrowser/browser/adblock.py index df7595a20..e6d1eab76 100644 --- a/qutebrowser/browser/adblock.py +++ b/qutebrowser/browser/adblock.py @@ -144,7 +144,7 @@ class HostBlocker: Return: True if a read was attempted, False otherwise """ - if not os.path.exists(filename): + if filename is None or not os.path.exists(filename): return False try: From 95d1f48b014deb338ac48ae0a02c82d6b38faf03 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Tue, 29 Mar 2016 07:31:11 +0200 Subject: [PATCH 29/34] docker: Use non-debug packages for Archlinux Upstream Arch now updated to Qt 5.6 (which means it works with Xvfb), and using debug packages means the image is a lot bigger and builds slower, so let's use the normal packages from [extra]. --- misc/docker/archlinux/Dockerfile | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/misc/docker/archlinux/Dockerfile b/misc/docker/archlinux/Dockerfile index 37038b0a6..273bdb134 100644 --- a/misc/docker/archlinux/Dockerfile +++ b/misc/docker/archlinux/Dockerfile @@ -4,19 +4,15 @@ MAINTAINER Florian Bruhin RUN echo 'Server = http://mirror.de.leaseweb.net/archlinux/$repo/os/$arch' > /etc/pacman.d/mirrorlist RUN pacman-key --init && pacman-key --populate archlinux && pacman -Sy --noconfirm archlinux-keyring -RUN pacman-key -r 0xD6A1C70FE80A0C82 && \ - pacman-key --lsign-key 0xD6A1C70FE80A0C82 && \ - echo -e '[qt-debug]\nServer = http://qutebrowser.org/qt-debug/$arch' >> /etc/pacman.conf - RUN pacman -Suyy --noconfirm RUN pacman-db-upgrade RUN pacman -S --noconfirm \ git \ python-tox \ - qt5-base-debug \ - qt5-webkit-debug \ - python-pyqt5-debug \ + qt5-base \ + qt5-webkit \ + python-pyqt5 \ xorg-xinit \ herbstluftwm \ xorg-server-xvfb From 89a061c5364acfbbe0462c87e28bc233717aa4e8 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Tue, 29 Mar 2016 18:47:54 +0200 Subject: [PATCH 30/34] INSTALL: Mention gstreamer packages for Archlinux --- INSTALL.asciidoc | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/INSTALL.asciidoc b/INSTALL.asciidoc index 2d2fb6494..99abe55d1 100644 --- a/INSTALL.asciidoc +++ b/INSTALL.asciidoc @@ -102,6 +102,12 @@ $ rm -r qutebrowser-git or you could use an AUR helper, e.g. `yaourt -S qutebrowser-git`. +If video or sound don't seem to work, try installing the gstreamer plugins: + +---- +# pacman -S gst-plugins-{base,good,bad,ugly} gst-libav +---- + On Gentoo --------- From b704c7167b3eb4eff8608526ade18d852a0e7df9 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Tue, 29 Mar 2016 18:48:33 +0200 Subject: [PATCH 31/34] tox: Update pytest-rerunfailures to 1.0.2 - Add support for --resultlog option by parsing reruns accordingly. --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 09834c2c8..1cf3deb42 100644 --- a/tox.ini +++ b/tox.ini @@ -37,7 +37,7 @@ deps = pytest-instafail==0.3.0 pytest-travis-fold==1.2.0 pytest-repeat==0.2 - pytest-rerunfailures==1.0.1 + pytest-rerunfailures==1.0.2 pytest-xvfb==0.2.0 six==1.10.0 termcolor==1.1.0 From 86ab33c558c5d0dcae605b53c0eb9ea1f3dead23 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Tue, 29 Mar 2016 19:21:15 +0200 Subject: [PATCH 32/34] Pass HTTPClient to PastebinClient as argument --- qutebrowser/browser/network/pastebin.py | 16 ++++++++++------ qutebrowser/misc/crashdialog.py | 5 +++-- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/qutebrowser/browser/network/pastebin.py b/qutebrowser/browser/network/pastebin.py index 84976e9a0..977fffe4a 100644 --- a/qutebrowser/browser/network/pastebin.py +++ b/qutebrowser/browser/network/pastebin.py @@ -23,8 +23,6 @@ import urllib.parse from PyQt5.QtCore import pyqtSignal, pyqtSlot, QObject, QUrl -from qutebrowser.misc import httpclient - class PastebinClient(QObject): @@ -47,11 +45,17 @@ class PastebinClient(QObject): success = pyqtSignal(str) error = pyqtSignal(str) - def __init__(self, parent=None): + def __init__(self, client, parent=None): + """Constructor. + + Args: + client: The HTTPClient to use. Will be reparented. + """ super().__init__(parent) - self._client = httpclient.HTTPClient(self) - self._client.error.connect(self.error) - self._client.success.connect(self.on_client_success) + client.setParent(self) + client.error.connect(self.error) + client.success.connect(self.on_client_success) + self._client = client def paste(self, name, title, text, parent=None): """Paste the text into a pastebin and return the URL. diff --git a/qutebrowser/misc/crashdialog.py b/qutebrowser/misc/crashdialog.py index ed4e47519..3c4ab03d4 100644 --- a/qutebrowser/misc/crashdialog.py +++ b/qutebrowser/misc/crashdialog.py @@ -36,7 +36,7 @@ from PyQt5.QtWidgets import (QDialog, QLabel, QTextEdit, QPushButton, import qutebrowser from qutebrowser.utils import version, log, utils, objreg, qtutils -from qutebrowser.misc import miscwidgets, autoupdate, msgbox +from qutebrowser.misc import miscwidgets, autoupdate, msgbox, httpclient from qutebrowser.browser.network import pastebin from qutebrowser.config import config @@ -141,7 +141,8 @@ class _CrashDialog(QDialog): self.setWindowTitle("Whoops!") self.resize(QSize(640, 600)) self._vbox = QVBoxLayout(self) - self._paste_client = pastebin.PastebinClient(self) + http_client = httpclient.HTTPClient() + self._paste_client = pastebin.PastebinClient(http_client, self) self._pypi_client = autoupdate.PyPIVersionClient(self) self._init_text() From f43cbed72f535bc9b7e71dcd6a741f86c6faf90d Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Tue, 29 Mar 2016 19:22:28 +0200 Subject: [PATCH 33/34] tests: Inherit HTTPPostStub from QObject This way we can test the actual signals instead of calling on_client_success by hand. --- tests/unit/browser/network/test_pastebin.py | 32 ++++++++++++--------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/tests/unit/browser/network/test_pastebin.py b/tests/unit/browser/network/test_pastebin.py index d72c9f6b8..56f988e94 100644 --- a/tests/unit/browser/network/test_pastebin.py +++ b/tests/unit/browser/network/test_pastebin.py @@ -17,16 +17,14 @@ # You should have received a copy of the GNU General Public License # along with qutebrowser. If not, see . -"""Tests for qutebrowser.browser.network.""" - import pytest -from PyQt5.QtCore import QUrl +from PyQt5.QtCore import pyqtSignal, QUrl, QObject from qutebrowser.browser.network import pastebin from qutebrowser.misc import httpclient -class HTTPPostStub(httpclient.HTTPClient): +class HTTPPostStub(QObject): """A stub class for HTTPClient. @@ -35,8 +33,11 @@ class HTTPPostStub(httpclient.HTTPClient): data: the last data send by post() """ - def __init__(self): - super().__init__() + success = pyqtSignal(str) + error = pyqtSignal(str) + + def __init__(self, parent=None): + super().__init__(parent) self.url = None self.data = None @@ -47,15 +48,14 @@ class HTTPPostStub(httpclient.HTTPClient): @pytest.fixture def pbclient(): - client = pastebin.PastebinClient() http_stub = HTTPPostStub() - client._client = http_stub + client = pastebin.PastebinClient(http_stub) return client def test_constructor(qapp): - pbclient = pastebin.PastebinClient() - assert isinstance(pbclient._client, httpclient.HTTPClient) + http_client = httpclient.HTTPClient() + pbclient = pastebin.PastebinClient(http_client) @pytest.mark.parametrize('data', [ @@ -105,7 +105,7 @@ def test_paste_without_parent(data, pbclient): def test_on_client_success(http, pbclient, qtbot): with qtbot.assertNotEmitted(pbclient.error): with qtbot.waitSignal(pbclient.success): - pbclient.on_client_success(http) + pbclient._client.success.emit(http) @pytest.mark.parametrize('http', [ @@ -113,7 +113,13 @@ def test_on_client_success(http, pbclient, qtbot): "http:/invalid.org" "http//invalid.com" ]) -def test_on_client_success_invalid_http(http, pbclient, qtbot): +def test_client_success_invalid_http(http, pbclient, qtbot): with qtbot.assertNotEmitted(pbclient.success): with qtbot.waitSignal(pbclient.error): - pbclient.on_client_success(http) + pbclient._client.success.emit(http) + + +def test_client_error(pbclient, qtbot): + with qtbot.assertNotEmitted(pbclient.success): + with qtbot.waitSignal(pbclient.error): + pbclient._client.error.emit("msg") From c9f1f11489a4605a2e8e28df6e8be4eb3d6ed9cb Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Tue, 29 Mar 2016 19:23:21 +0200 Subject: [PATCH 34/34] Regenerate authors --- README.asciidoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.asciidoc b/README.asciidoc index fe93da6df..8fc09e84e 100644 --- a/README.asciidoc +++ b/README.asciidoc @@ -165,6 +165,7 @@ Contributors, sorted by the number of commits in descending order: * Thorsten Wißmann * Austin Anderson * Alexey "Averrin" Nabrodov +* avk * ZDarian * Milan Svoboda * John ShaggyTwoDope Jenkins @@ -192,7 +193,6 @@ Contributors, sorted by the number of commits in descending order: * Link * Larry Hynes * Johannes Altmanninger -* avk * Samir Benmendil * Regina Hug * Mathias Fussenegger