2015-08-24 08:00:32 +02:00
|
|
|
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
|
|
|
|
|
2016-01-04 07:12:39 +01:00
|
|
|
# Copyright 2015-2016 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
2015-08-24 08:00:32 +02:00
|
|
|
#
|
|
|
|
# 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/>.
|
|
|
|
|
|
|
|
"""Tests for qutebrowser.misc.sessions."""
|
|
|
|
|
2015-08-26 09:38:54 +02:00
|
|
|
import os
|
2015-08-24 08:00:32 +02:00
|
|
|
import textwrap
|
2015-08-24 17:11:33 +02:00
|
|
|
import logging
|
2015-08-24 08:00:32 +02:00
|
|
|
|
|
|
|
import pytest
|
|
|
|
import yaml
|
2015-11-28 20:41:42 +01:00
|
|
|
from PyQt5.QtCore import QUrl, QPoint, QByteArray, QObject
|
2016-09-05 18:45:50 +02:00
|
|
|
QWebView = pytest.importorskip('PyQt5.QtWebKitWidgets').QWebView
|
2015-08-24 08:00:32 +02:00
|
|
|
|
|
|
|
from qutebrowser.misc import sessions
|
|
|
|
from qutebrowser.utils import objreg, qtutils
|
2016-06-13 10:49:58 +02:00
|
|
|
from qutebrowser.browser.webkit import tabhistory
|
|
|
|
from qutebrowser.browser.webkit.tabhistory import TabHistoryItem as Item
|
2015-08-24 17:11:33 +02:00
|
|
|
from qutebrowser.commands import cmdexc
|
2015-08-24 08:00:32 +02:00
|
|
|
|
|
|
|
|
2016-07-29 07:10:06 +02:00
|
|
|
pytestmark = pytest.mark.qt_log_ignore('QIODevice::read.*: device not open')
|
2015-08-24 08:00:32 +02:00
|
|
|
|
2016-07-07 13:35:13 +02:00
|
|
|
webengine_refactoring_xfail = pytest.mark.xfail(
|
|
|
|
True, reason='Broke during QtWebEngine refactoring, will be fixed after '
|
|
|
|
'sessions are refactored too.')
|
|
|
|
|
2016-01-21 18:07:56 +01:00
|
|
|
|
2015-08-24 08:00:32 +02:00
|
|
|
@pytest.fixture
|
|
|
|
def sess_man():
|
|
|
|
"""Fixture providing a SessionManager with no session dir."""
|
|
|
|
return sessions.SessionManager(base_path=None)
|
|
|
|
|
|
|
|
|
|
|
|
class TestInit:
|
|
|
|
|
2016-08-22 07:40:24 +02:00
|
|
|
@pytest.fixture(autouse=True)
|
2015-08-24 08:00:32 +02:00
|
|
|
def cleanup(self):
|
|
|
|
yield
|
|
|
|
objreg.delete('session-manager')
|
|
|
|
|
|
|
|
def test_no_standarddir(self, monkeypatch):
|
|
|
|
monkeypatch.setattr('qutebrowser.misc.sessions.standarddir.data',
|
|
|
|
lambda: None)
|
|
|
|
sessions.init()
|
|
|
|
manager = objreg.get('session-manager')
|
|
|
|
assert manager._base_path is None
|
|
|
|
|
|
|
|
@pytest.mark.parametrize('create_dir', [True, False])
|
|
|
|
def test_with_standarddir(self, tmpdir, monkeypatch, create_dir):
|
|
|
|
monkeypatch.setattr('qutebrowser.misc.sessions.standarddir.data',
|
|
|
|
lambda: str(tmpdir))
|
|
|
|
session_dir = tmpdir / 'sessions'
|
|
|
|
if create_dir:
|
|
|
|
session_dir.ensure(dir=True)
|
|
|
|
|
|
|
|
sessions.init()
|
|
|
|
manager = objreg.get('session-manager')
|
|
|
|
|
|
|
|
assert session_dir.exists()
|
|
|
|
assert manager._base_path == str(session_dir)
|
|
|
|
|
|
|
|
|
|
|
|
def test_did_not_load(sess_man):
|
|
|
|
assert not sess_man.did_load
|
|
|
|
|
|
|
|
|
|
|
|
class TestExists:
|
|
|
|
|
|
|
|
@pytest.mark.parametrize('absolute', [True, False])
|
2015-10-13 23:52:13 +02:00
|
|
|
def test_existent(self, tmpdir, absolute):
|
2015-08-24 08:00:32 +02:00
|
|
|
session_dir = tmpdir / 'sessions'
|
|
|
|
abs_session = tmpdir / 'foo.yml'
|
|
|
|
rel_session = session_dir / 'foo.yml'
|
|
|
|
|
|
|
|
session_dir.ensure(dir=True)
|
|
|
|
abs_session.ensure()
|
|
|
|
rel_session.ensure()
|
|
|
|
|
|
|
|
man = sessions.SessionManager(str(session_dir))
|
|
|
|
|
|
|
|
if absolute:
|
|
|
|
name = str(abs_session)
|
|
|
|
else:
|
|
|
|
name = 'foo'
|
|
|
|
|
|
|
|
assert man.exists(name)
|
|
|
|
|
|
|
|
@pytest.mark.parametrize('absolute', [True, False])
|
2015-10-04 15:41:42 +02:00
|
|
|
def test_inexistent(self, tmpdir, absolute):
|
2015-08-24 08:00:32 +02:00
|
|
|
man = sessions.SessionManager(str(tmpdir))
|
|
|
|
|
|
|
|
if absolute:
|
|
|
|
name = str(tmpdir / 'foo')
|
|
|
|
else:
|
|
|
|
name = 'foo'
|
|
|
|
|
|
|
|
assert not man.exists(name)
|
|
|
|
|
|
|
|
@pytest.mark.parametrize('absolute', [True, False])
|
|
|
|
def test_no_datadir(self, sess_man, tmpdir, absolute):
|
|
|
|
abs_session = tmpdir / 'foo.yml'
|
|
|
|
abs_session.ensure()
|
|
|
|
|
|
|
|
if absolute:
|
|
|
|
assert sess_man.exists(str(abs_session))
|
|
|
|
else:
|
|
|
|
assert not sess_man.exists('foo')
|
|
|
|
|
|
|
|
|
|
|
|
class HistTester:
|
|
|
|
|
|
|
|
"""Helper object for the hist_tester fixture.
|
|
|
|
|
|
|
|
Makes it possible to use tabhistory.TabHistoryItem objects to easily load
|
|
|
|
data into a QWebHistory, does some basic checks, and provides the
|
|
|
|
serialized history.
|
|
|
|
|
|
|
|
Attributes:
|
|
|
|
sess_man: The SessionManager which is used.
|
|
|
|
webview: The WebView where the history is loaded to.
|
|
|
|
"""
|
|
|
|
|
|
|
|
def __init__(self, webview):
|
|
|
|
self.sess_man = sessions.SessionManager(base_path=None)
|
|
|
|
self.webview = webview
|
|
|
|
|
|
|
|
def get(self, items):
|
|
|
|
"""Get the serialized history for the given items.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
items: A list of TabHistoryItems.
|
|
|
|
|
|
|
|
Return:
|
|
|
|
A list of serialized items, as dicts.
|
|
|
|
"""
|
|
|
|
history = self.webview.page().history()
|
|
|
|
|
|
|
|
stream, _data, user_data = tabhistory.serialize(items)
|
|
|
|
qtutils.deserialize_stream(stream, history)
|
|
|
|
for i, data in enumerate(user_data):
|
|
|
|
history.itemAt(i).setUserData(data)
|
|
|
|
|
|
|
|
d = self.sess_man._save_tab(self.webview, active=True)
|
|
|
|
new_history = d['history']
|
|
|
|
assert len(new_history) == len(items)
|
|
|
|
return new_history
|
|
|
|
|
|
|
|
def get_single(self, item):
|
|
|
|
"""Convenience method to use get() with a single item."""
|
|
|
|
ret = self.get([item])
|
|
|
|
assert len(ret) == 1
|
|
|
|
return ret[0]
|
|
|
|
|
|
|
|
|
2016-07-07 13:35:13 +02:00
|
|
|
@webengine_refactoring_xfail
|
2015-08-24 08:00:32 +02:00
|
|
|
class TestSaveTab:
|
|
|
|
|
|
|
|
@pytest.fixture
|
|
|
|
def hist_tester(self, webview):
|
|
|
|
"""Helper to test saving of history."""
|
|
|
|
return HistTester(webview)
|
|
|
|
|
|
|
|
@pytest.mark.parametrize('is_active', [True, False])
|
|
|
|
def test_active(self, sess_man, webview, is_active):
|
|
|
|
data = sess_man._save_tab(webview, is_active)
|
|
|
|
if is_active:
|
|
|
|
assert data['active']
|
|
|
|
else:
|
|
|
|
assert 'active' not in data
|
|
|
|
|
|
|
|
def test_no_history(self, sess_man, webview):
|
|
|
|
data = sess_man._save_tab(webview, active=False)
|
|
|
|
assert not data['history']
|
|
|
|
|
|
|
|
def test_single_item(self, hist_tester):
|
|
|
|
item = Item(url=QUrl('http://www.qutebrowser.org/'),
|
|
|
|
title='Test title',
|
|
|
|
active=True)
|
|
|
|
expected = {
|
|
|
|
'url': 'http://www.qutebrowser.org/',
|
|
|
|
'title': 'Test title',
|
|
|
|
'active': True,
|
|
|
|
'scroll-pos': {'x': 0, 'y': 0},
|
|
|
|
'zoom': 1.0
|
|
|
|
}
|
|
|
|
|
|
|
|
hist = hist_tester.get_single(item)
|
|
|
|
assert hist == expected
|
|
|
|
|
|
|
|
def test_original_url(self, hist_tester):
|
|
|
|
"""Test with an original-url which differs from the URL."""
|
|
|
|
item = Item(url=QUrl('http://www.example.com/'),
|
|
|
|
original_url=QUrl('http://www.example.org/'),
|
|
|
|
title='Test title',
|
|
|
|
active=True)
|
|
|
|
|
|
|
|
hist = hist_tester.get_single(item)
|
|
|
|
|
|
|
|
assert hist['url'] == 'http://www.example.com/'
|
|
|
|
assert hist['original-url'] == 'http://www.example.org/'
|
|
|
|
|
|
|
|
def test_multiple_items(self, hist_tester):
|
|
|
|
items = [
|
|
|
|
Item(url=QUrl('http://www.qutebrowser.org/'), title='test 1'),
|
|
|
|
Item(url=QUrl('http://www.example.com/'), title='test 2',
|
|
|
|
active=True),
|
|
|
|
Item(url=QUrl('http://www.example.org/'), title='test 3'),
|
|
|
|
]
|
|
|
|
|
|
|
|
expected = [
|
|
|
|
{
|
|
|
|
'url': 'http://www.qutebrowser.org/',
|
|
|
|
'title': 'test 1',
|
|
|
|
},
|
|
|
|
{
|
|
|
|
'url': 'http://www.example.com/',
|
|
|
|
'title': 'test 2',
|
|
|
|
'active': True,
|
|
|
|
'scroll-pos': {'x': 0, 'y': 0},
|
|
|
|
'zoom': 1.0
|
|
|
|
},
|
|
|
|
{
|
|
|
|
'url': 'http://www.example.org/',
|
|
|
|
'title': 'test 3',
|
|
|
|
},
|
|
|
|
]
|
|
|
|
|
|
|
|
hist = hist_tester.get(items)
|
|
|
|
assert hist == expected
|
|
|
|
|
|
|
|
@pytest.mark.parametrize('factor', [-1.0, 0.0, 1.5])
|
|
|
|
def test_zoom(self, hist_tester, factor):
|
|
|
|
"""Test zoom."""
|
|
|
|
items = [
|
|
|
|
Item(url=QUrl('http://www.example.com/'), title='Test title',
|
2016-01-21 18:07:56 +01:00
|
|
|
active=True),
|
2015-08-24 08:00:32 +02:00
|
|
|
Item(url=QUrl('http://www.example.com/'), title='Test title',
|
|
|
|
user_data={'zoom': factor}),
|
|
|
|
]
|
|
|
|
hist_tester.webview.setZoomFactor(factor)
|
|
|
|
hist = hist_tester.get(items)
|
|
|
|
assert hist[0]['zoom'] == factor
|
|
|
|
assert hist[1]['zoom'] == factor
|
|
|
|
|
|
|
|
@pytest.mark.parametrize('pos_x, pos_y', [
|
|
|
|
(0.0, 0.0), (1.0, 1.0), (0.0, 1.0), (1.0, 0.0),
|
|
|
|
])
|
|
|
|
def test_scroll_current(self, hist_tester, pos_x, pos_y):
|
|
|
|
"""Test scroll position on the current URL."""
|
|
|
|
items = [
|
|
|
|
Item(url=QUrl('http://www.example.com/'), title='Test title',
|
|
|
|
active=True),
|
|
|
|
Item(url=QUrl('http://www.example.com/'), title='Test title',
|
|
|
|
user_data={'scroll-pos': QPoint(pos_x, pos_y)}),
|
|
|
|
]
|
|
|
|
frame = hist_tester.webview.page().mainFrame()
|
|
|
|
|
|
|
|
text = '{}\n'.format('x' * 100) * 100
|
|
|
|
frame.setHtml('<html><body>{}</body></html>'.format(text))
|
|
|
|
frame.setScrollPosition(QPoint(pos_x, pos_y))
|
|
|
|
|
|
|
|
hist = hist_tester.get(items)
|
|
|
|
assert hist[0]['scroll-pos'] == {'x': pos_x, 'y': pos_y}
|
|
|
|
assert hist[1]['scroll-pos'] == {'x': pos_x, 'y': pos_y}
|
|
|
|
|
|
|
|
|
2015-11-28 20:41:42 +01:00
|
|
|
class FakeMainWindow(QObject):
|
2015-08-24 08:00:32 +02:00
|
|
|
|
|
|
|
"""Helper class for the fake_main_window fixture.
|
|
|
|
|
|
|
|
A fake MainWindow which provides a saveGeometry method.
|
2015-11-28 20:41:42 +01:00
|
|
|
|
|
|
|
Needs to be a QObject so sip.isdeleted works.
|
2015-08-24 08:00:32 +02:00
|
|
|
"""
|
|
|
|
|
2015-11-28 20:41:42 +01:00
|
|
|
def __init__(self, geometry, win_id, parent=None):
|
|
|
|
super().__init__(parent)
|
2015-08-24 08:00:32 +02:00
|
|
|
self._geometry = QByteArray(geometry)
|
|
|
|
self.win_id = win_id
|
|
|
|
|
|
|
|
def saveGeometry(self):
|
|
|
|
return self._geometry
|
|
|
|
|
|
|
|
|
|
|
|
class FakeTabbedBrowser:
|
|
|
|
|
|
|
|
"""A fake tabbed-browser which contains some widgets."""
|
|
|
|
|
|
|
|
def __init__(self, widgets):
|
|
|
|
self._widgets = widgets
|
|
|
|
|
|
|
|
def widgets(self):
|
|
|
|
return self._widgets
|
|
|
|
|
|
|
|
def currentIndex(self):
|
|
|
|
return 1
|
|
|
|
|
|
|
|
|
2016-08-22 07:40:24 +02:00
|
|
|
@pytest.fixture
|
2015-08-24 08:00:32 +02:00
|
|
|
def fake_windows(win_registry, stubs, monkeypatch, qtbot):
|
|
|
|
"""Fixture which provides two fake main windows and tabbedbrowsers."""
|
|
|
|
win_registry.add_window(1)
|
|
|
|
win0 = FakeMainWindow(b'fake-geometry-0', win_id=0)
|
|
|
|
win1 = FakeMainWindow(b'fake-geometry-1', win_id=1)
|
|
|
|
objreg.register('main-window', win0, scope='window', window=0)
|
|
|
|
objreg.register('main-window', win1, scope='window', window=1)
|
|
|
|
|
|
|
|
webview0 = QWebView()
|
|
|
|
qtbot.add_widget(webview0)
|
|
|
|
webview1 = QWebView()
|
|
|
|
qtbot.add_widget(webview1)
|
|
|
|
webview2 = QWebView()
|
|
|
|
qtbot.add_widget(webview2)
|
|
|
|
webview3 = QWebView()
|
|
|
|
qtbot.add_widget(webview3)
|
|
|
|
|
|
|
|
browser0 = FakeTabbedBrowser([webview0, webview1])
|
|
|
|
browser1 = FakeTabbedBrowser([webview2, webview3])
|
|
|
|
objreg.register('tabbed-browser', browser0, scope='window', window=0)
|
|
|
|
objreg.register('tabbed-browser', browser1, scope='window', window=1)
|
|
|
|
|
|
|
|
qapp = stubs.FakeQApplication(active_window=win0)
|
|
|
|
monkeypatch.setattr('qutebrowser.misc.sessions.QApplication', qapp)
|
|
|
|
|
|
|
|
yield browser0, browser1
|
|
|
|
|
|
|
|
objreg.delete('main-window', scope='window', window=0)
|
|
|
|
objreg.delete('main-window', scope='window', window=1)
|
|
|
|
objreg.delete('tabbed-browser', scope='window', window=0)
|
|
|
|
objreg.delete('tabbed-browser', scope='window', window=1)
|
|
|
|
|
|
|
|
|
|
|
|
class TestSaveAll:
|
|
|
|
|
|
|
|
def test_no_history(self, sess_man):
|
|
|
|
assert not objreg.window_registry
|
|
|
|
data = sess_man._save_all()
|
|
|
|
assert not data['windows']
|
|
|
|
|
2016-07-07 13:35:13 +02:00
|
|
|
@webengine_refactoring_xfail
|
2015-08-24 08:00:32 +02:00
|
|
|
def test_normal(self, fake_windows, sess_man):
|
|
|
|
"""Test with some windows and tabs set up."""
|
|
|
|
data = sess_man._save_all()
|
|
|
|
|
|
|
|
win1 = {
|
|
|
|
'active': True,
|
|
|
|
'geometry': b'fake-geometry-0',
|
|
|
|
'tabs': [
|
|
|
|
{'history': []},
|
|
|
|
{'active': True, 'history': []},
|
|
|
|
],
|
|
|
|
}
|
|
|
|
win2 = {
|
|
|
|
'geometry': b'fake-geometry-1',
|
|
|
|
'tabs': [
|
|
|
|
{'history': []},
|
|
|
|
{'active': True, 'history': []},
|
|
|
|
],
|
|
|
|
}
|
|
|
|
expected = {'windows': [win1, win2]}
|
|
|
|
assert data == expected
|
|
|
|
|
2016-07-07 13:35:13 +02:00
|
|
|
@webengine_refactoring_xfail
|
2015-08-24 08:00:32 +02:00
|
|
|
def test_no_active_window(self, sess_man, fake_windows, stubs,
|
|
|
|
monkeypatch):
|
|
|
|
qapp = stubs.FakeQApplication(active_window=None)
|
|
|
|
monkeypatch.setattr('qutebrowser.misc.sessions.QApplication', qapp)
|
|
|
|
sess_man._save_all()
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize('arg, config, current, expected', [
|
|
|
|
('foo', None, None, 'foo'),
|
|
|
|
(sessions.default, 'foo', None, 'foo'),
|
|
|
|
(sessions.default, None, 'foo', 'foo'),
|
|
|
|
(sessions.default, None, None, 'default'),
|
|
|
|
])
|
|
|
|
def test_get_session_name(config_stub, sess_man, arg, config, current,
|
|
|
|
expected):
|
|
|
|
config_stub.data = {'general': {'session-default-name': config}}
|
|
|
|
sess_man._current = current
|
|
|
|
assert sess_man._get_session_name(arg) == expected
|
|
|
|
|
|
|
|
|
|
|
|
class TestSave:
|
|
|
|
|
2016-08-22 07:40:24 +02:00
|
|
|
@pytest.fixture
|
2015-08-24 08:00:32 +02:00
|
|
|
def state_config(self):
|
|
|
|
state = {'general': {}}
|
|
|
|
objreg.register('state-config', state)
|
|
|
|
yield state
|
|
|
|
objreg.delete('state-config')
|
|
|
|
|
2016-08-22 07:40:24 +02:00
|
|
|
@pytest.fixture
|
2015-08-24 08:00:32 +02:00
|
|
|
def fake_history(self, win_registry, stubs, monkeypatch, webview):
|
|
|
|
"""Fixture which provides a window with a fake history."""
|
|
|
|
win = FakeMainWindow(b'fake-geometry-0', win_id=0)
|
|
|
|
objreg.register('main-window', win, scope='window', window=0)
|
|
|
|
browser = FakeTabbedBrowser([webview])
|
|
|
|
|
|
|
|
objreg.register('tabbed-browser', browser, scope='window', window=0)
|
|
|
|
qapp = stubs.FakeQApplication(active_window=win)
|
|
|
|
monkeypatch.setattr('qutebrowser.misc.sessions.QApplication', qapp)
|
|
|
|
|
|
|
|
def set_data(items):
|
|
|
|
history = browser.widgets()[0].page().history()
|
|
|
|
stream, _data, user_data = tabhistory.serialize(items)
|
|
|
|
qtutils.deserialize_stream(stream, history)
|
|
|
|
for i, data in enumerate(user_data):
|
|
|
|
history.itemAt(i).setUserData(data)
|
|
|
|
|
|
|
|
yield set_data
|
|
|
|
|
|
|
|
objreg.delete('main-window', scope='window', window=0)
|
|
|
|
objreg.delete('tabbed-browser', scope='window', window=0)
|
|
|
|
|
|
|
|
def test_no_config_storage(self, sess_man):
|
|
|
|
with pytest.raises(sessions.SessionError) as excinfo:
|
|
|
|
sess_man.save('foo')
|
|
|
|
assert str(excinfo.value) == "No data storage configured."
|
|
|
|
|
|
|
|
def test_simple_dump(self, sess_man, tmpdir):
|
|
|
|
session_path = tmpdir / 'foo.yml'
|
|
|
|
name = sess_man.save(str(session_path))
|
|
|
|
|
|
|
|
assert name == str(session_path)
|
2015-08-24 08:44:41 +02:00
|
|
|
data = session_path.read_text('utf-8')
|
2015-08-24 08:00:32 +02:00
|
|
|
assert data == 'windows: []\n'
|
|
|
|
|
|
|
|
def test_update_completion_signal(self, sess_man, tmpdir, qtbot):
|
|
|
|
session_path = tmpdir / 'foo.yml'
|
2016-01-08 09:49:06 +01:00
|
|
|
with qtbot.waitSignal(sess_man.update_completion):
|
|
|
|
sess_man.save(str(session_path))
|
2015-08-24 08:00:32 +02:00
|
|
|
|
|
|
|
def test_no_state_config(self, sess_man, tmpdir, state_config):
|
|
|
|
session_path = tmpdir / 'foo.yml'
|
|
|
|
sess_man.save(str(session_path))
|
|
|
|
assert 'session' not in state_config['general']
|
|
|
|
|
2016-04-10 18:22:58 +02:00
|
|
|
def test_last_window_session_none(self, caplog, sess_man, tmpdir):
|
2015-08-24 08:00:32 +02:00
|
|
|
session_path = tmpdir / 'foo.yml'
|
2016-04-10 18:22:58 +02:00
|
|
|
with caplog.at_level(logging.ERROR):
|
2015-08-24 08:00:32 +02:00
|
|
|
sess_man.save(str(session_path), last_window=True)
|
2016-04-10 18:22:58 +02:00
|
|
|
|
|
|
|
assert len(caplog.records) == 1
|
|
|
|
msg = "last_window_session is None while saving!"
|
|
|
|
assert caplog.records[0].msg == msg
|
2015-08-24 08:00:32 +02:00
|
|
|
assert not session_path.exists()
|
|
|
|
|
|
|
|
def test_last_window_session(self, sess_man, tmpdir):
|
|
|
|
sess_man.save_last_window_session()
|
|
|
|
session_path = tmpdir / 'foo.yml'
|
|
|
|
sess_man.save(str(session_path), last_window=True)
|
2015-08-24 08:44:41 +02:00
|
|
|
data = session_path.read_text('utf-8')
|
2015-08-24 08:00:32 +02:00
|
|
|
assert data == 'windows: []\n'
|
|
|
|
|
|
|
|
@pytest.mark.parametrize('exception', [
|
|
|
|
OSError('foo'), UnicodeEncodeError('ascii', '', 0, 2, 'foo'),
|
|
|
|
yaml.YAMLError('foo')])
|
|
|
|
def test_fake_exception(self, mocker, sess_man, tmpdir, exception):
|
|
|
|
mocker.patch('qutebrowser.misc.sessions.yaml.dump',
|
|
|
|
side_effect=exception)
|
|
|
|
|
|
|
|
with pytest.raises(sessions.SessionError) as excinfo:
|
|
|
|
sess_man.save(str(tmpdir / 'foo.yml'))
|
|
|
|
|
|
|
|
assert str(excinfo.value) == str(exception)
|
|
|
|
assert not tmpdir.listdir()
|
|
|
|
|
|
|
|
def test_directory(self, sess_man, tmpdir):
|
|
|
|
"""Test with a directory given as session file."""
|
|
|
|
with pytest.raises(sessions.SessionError) as excinfo:
|
|
|
|
sess_man.save(str(tmpdir))
|
|
|
|
|
2015-08-24 08:31:11 +02:00
|
|
|
assert str(excinfo.value) in ["Filename refers to a directory",
|
|
|
|
"Commit failed!"]
|
2015-08-24 08:00:32 +02:00
|
|
|
assert not tmpdir.listdir()
|
|
|
|
|
|
|
|
def test_load_next_time(self, tmpdir, state_config, sess_man):
|
|
|
|
session_path = tmpdir / 'foo.yml'
|
|
|
|
sess_man.save(str(session_path), load_next_time=True)
|
|
|
|
assert state_config['general']['session'] == str(session_path)
|
|
|
|
|
2016-07-07 13:35:13 +02:00
|
|
|
@webengine_refactoring_xfail
|
2015-08-24 08:00:32 +02:00
|
|
|
def test_utf_8_valid(self, tmpdir, sess_man, fake_history):
|
|
|
|
"""Make sure data containing valid UTF8 gets saved correctly."""
|
|
|
|
session_path = tmpdir / 'foo.yml'
|
|
|
|
fake_history([Item(QUrl('http://www.qutebrowser.org/'), 'foo☃bar',
|
|
|
|
active=True)])
|
|
|
|
|
|
|
|
sess_man.save(str(session_path))
|
|
|
|
|
2015-08-24 08:44:41 +02:00
|
|
|
data = session_path.read_text('utf-8')
|
2015-08-24 08:00:32 +02:00
|
|
|
assert 'title: foo☃bar' in data
|
|
|
|
|
2016-07-07 13:35:13 +02:00
|
|
|
@webengine_refactoring_xfail
|
2015-08-24 08:00:32 +02:00
|
|
|
def test_utf_8_invalid(self, tmpdir, sess_man, fake_history):
|
|
|
|
"""Make sure data containing invalid UTF8 raises SessionError."""
|
|
|
|
session_path = tmpdir / 'foo.yml'
|
|
|
|
fake_history([Item(QUrl('http://www.qutebrowser.org/'), '\ud800',
|
|
|
|
active=True)])
|
|
|
|
|
2015-08-24 08:31:11 +02:00
|
|
|
try:
|
2015-08-24 08:00:32 +02:00
|
|
|
sess_man.save(str(session_path))
|
2015-08-24 08:31:11 +02:00
|
|
|
except sessions.SessionError:
|
|
|
|
# This seems to happen on some systems only?!
|
|
|
|
pass
|
|
|
|
else:
|
2015-08-24 08:44:41 +02:00
|
|
|
data = session_path.read_text('utf-8')
|
2015-08-24 08:31:11 +02:00
|
|
|
assert r'title: "\uD800"' in data
|
2015-08-24 08:00:32 +02:00
|
|
|
|
|
|
|
def _set_data(self, browser, tab_id, items):
|
|
|
|
"""Helper function for test_long_output."""
|
|
|
|
history = browser.widgets()[tab_id].page().history()
|
|
|
|
stream, _data, user_data = tabhistory.serialize(items)
|
|
|
|
qtutils.deserialize_stream(stream, history)
|
|
|
|
for i, data in enumerate(user_data):
|
|
|
|
history.itemAt(i).setUserData(data)
|
|
|
|
|
2015-08-26 09:38:54 +02:00
|
|
|
@pytest.mark.skipif(
|
|
|
|
os.name == 'nt', reason="Test segfaults on Windows, see "
|
|
|
|
"https://github.com/The-Compiler/qutebrowser/issues/895")
|
2016-07-07 13:35:13 +02:00
|
|
|
@webengine_refactoring_xfail
|
2015-08-24 08:00:32 +02:00
|
|
|
def test_long_output(self, fake_windows, tmpdir, sess_man):
|
|
|
|
session_path = tmpdir / 'foo.yml'
|
|
|
|
|
|
|
|
items1 = [
|
|
|
|
Item(QUrl('http://www.qutebrowser.org/'), 'test title 1'),
|
|
|
|
Item(QUrl('http://www.example.org/'), 'test title 2',
|
|
|
|
original_url=QUrl('http://www.example.com/'), active=True),
|
|
|
|
]
|
|
|
|
items2 = [
|
|
|
|
Item(QUrl('http://www.example.com/?q=foo+bar'), 'test title 3'),
|
|
|
|
Item(QUrl('http://www.example.com/?q=test%20foo'), 'test title 4',
|
|
|
|
active=True),
|
|
|
|
]
|
|
|
|
items3 = []
|
|
|
|
items4 = [
|
|
|
|
Item(QUrl('http://www.github.com/The-Compiler/qutebrowser'),
|
|
|
|
'test title 5', active=True),
|
|
|
|
]
|
|
|
|
|
|
|
|
self._set_data(fake_windows[0], 0, items1)
|
|
|
|
self._set_data(fake_windows[0], 1, items2)
|
|
|
|
self._set_data(fake_windows[1], 0, items3)
|
|
|
|
self._set_data(fake_windows[1], 1, items4)
|
|
|
|
|
|
|
|
expected = """
|
|
|
|
windows:
|
|
|
|
- active: true
|
|
|
|
geometry: !!binary |
|
|
|
|
ZmFrZS1nZW9tZXRyeS0w
|
|
|
|
tabs:
|
|
|
|
- history:
|
|
|
|
- title: test title 1
|
|
|
|
url: http://www.qutebrowser.org/
|
|
|
|
- active: true
|
|
|
|
original-url: http://www.example.com/
|
|
|
|
scroll-pos:
|
|
|
|
x: 0
|
|
|
|
y: 0
|
|
|
|
title: test title 2
|
|
|
|
url: http://www.example.org/
|
|
|
|
zoom: 1.0
|
|
|
|
- active: true
|
|
|
|
history:
|
|
|
|
- title: test title 3
|
|
|
|
url: http://www.example.com/?q=foo+bar
|
|
|
|
- active: true
|
|
|
|
scroll-pos:
|
|
|
|
x: 0
|
|
|
|
y: 0
|
|
|
|
title: test title 4
|
|
|
|
url: http://www.example.com/?q=test%20foo
|
|
|
|
zoom: 1.0
|
|
|
|
- geometry: !!binary |
|
|
|
|
ZmFrZS1nZW9tZXRyeS0x
|
|
|
|
tabs:
|
|
|
|
- history: []
|
|
|
|
- active: true
|
|
|
|
history:
|
|
|
|
- active: true
|
|
|
|
scroll-pos:
|
|
|
|
x: 0
|
|
|
|
y: 0
|
|
|
|
title: test title 5
|
|
|
|
url: http://www.github.com/The-Compiler/qutebrowser
|
|
|
|
zoom: 1.0
|
|
|
|
"""
|
|
|
|
|
|
|
|
sess_man.save(str(session_path))
|
2015-08-24 08:44:41 +02:00
|
|
|
data = session_path.read_text('utf-8')
|
2015-08-24 08:00:32 +02:00
|
|
|
assert data == textwrap.dedent(expected.strip('\n'))
|
2015-08-24 17:11:33 +02:00
|
|
|
|
|
|
|
|
2015-08-24 20:25:40 +02:00
|
|
|
class FakeWebView:
|
|
|
|
|
|
|
|
"""A QWebView fake which provides a "page" with a load_history method.
|
|
|
|
|
|
|
|
Attributes:
|
|
|
|
loaded_history: The history which has been loaded by load_history, or
|
|
|
|
None.
|
|
|
|
raise_error: The exception to raise on load_history, or None.
|
|
|
|
"""
|
|
|
|
|
|
|
|
def __init__(self):
|
|
|
|
self.loaded_history = None
|
|
|
|
self.raise_error = None
|
|
|
|
|
|
|
|
def page(self):
|
|
|
|
return self
|
|
|
|
|
|
|
|
def load_history(self, data):
|
|
|
|
self.loaded_history = data
|
|
|
|
if self.raise_error is not None:
|
2015-08-25 21:24:58 +02:00
|
|
|
raise self.raise_error # pylint: disable=raising-bad-type
|
2015-08-24 20:25:40 +02:00
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture
|
|
|
|
def fake_webview():
|
|
|
|
return FakeWebView()
|
|
|
|
|
|
|
|
|
2016-07-07 13:35:13 +02:00
|
|
|
@webengine_refactoring_xfail
|
2015-08-24 20:25:40 +02:00
|
|
|
class TestLoadTab:
|
|
|
|
|
|
|
|
def test_no_history(self, sess_man, fake_webview):
|
|
|
|
sess_man._load_tab(fake_webview, {'history': []})
|
|
|
|
assert fake_webview.loaded_history == []
|
|
|
|
|
|
|
|
def test_load_fail(self, sess_man, fake_webview):
|
|
|
|
fake_webview.raise_error = ValueError
|
|
|
|
with pytest.raises(sessions.SessionError):
|
|
|
|
sess_man._load_tab(fake_webview, {'history': []})
|
|
|
|
|
|
|
|
@pytest.mark.parametrize('key, val, expected', [
|
|
|
|
('zoom', 1.23, 1.23),
|
|
|
|
('scroll-pos', {'x': 23, 'y': 42}, QPoint(23, 42)),
|
|
|
|
])
|
|
|
|
@pytest.mark.parametrize('in_main_data', [True, False])
|
|
|
|
def test_user_data(self, sess_man, fake_webview, key, val, expected,
|
|
|
|
in_main_data):
|
|
|
|
|
|
|
|
item = {'url': 'http://www.example.com/', 'title': 'foo'}
|
|
|
|
|
|
|
|
if in_main_data:
|
|
|
|
# This information got saved in the main data instead of saving it
|
|
|
|
# per item - make sure the old format can still be read
|
|
|
|
# https://github.com/The-Compiler/qutebrowser/issues/728
|
|
|
|
d = {'history': [item], key: val}
|
|
|
|
else:
|
|
|
|
item[key] = val
|
|
|
|
d = {'history': [item]}
|
|
|
|
|
|
|
|
sess_man._load_tab(fake_webview, d)
|
|
|
|
assert len(fake_webview.loaded_history) == 1
|
|
|
|
assert fake_webview.loaded_history[0].user_data[key] == expected
|
|
|
|
|
|
|
|
@pytest.mark.parametrize('original_url', ['http://example.org/', None])
|
|
|
|
def test_urls(self, sess_man, fake_webview, original_url):
|
|
|
|
url = 'http://www.example.com/'
|
|
|
|
item = {'url': url, 'title': 'foo'}
|
|
|
|
|
|
|
|
if original_url is None:
|
|
|
|
expected = QUrl(url)
|
|
|
|
else:
|
|
|
|
item['original-url'] = original_url
|
|
|
|
expected = QUrl(original_url)
|
|
|
|
|
|
|
|
d = {'history': [item]}
|
|
|
|
|
|
|
|
sess_man._load_tab(fake_webview, d)
|
|
|
|
assert len(fake_webview.loaded_history) == 1
|
|
|
|
loaded_item = fake_webview.loaded_history[0]
|
|
|
|
assert loaded_item.url == QUrl(url)
|
|
|
|
assert loaded_item.original_url == expected
|
|
|
|
|
|
|
|
|
2015-08-24 17:11:33 +02:00
|
|
|
class TestDelete:
|
|
|
|
|
|
|
|
def test_existing(self, sess_man, tmpdir):
|
|
|
|
sess = tmpdir / 'foo.yml'
|
|
|
|
sess.ensure()
|
|
|
|
sess_man.delete(str(sess))
|
|
|
|
assert not tmpdir.listdir()
|
|
|
|
|
|
|
|
def test_update_completion_signal(self, sess_man, qtbot, tmpdir):
|
|
|
|
sess = tmpdir / 'foo.yml'
|
|
|
|
sess.ensure()
|
|
|
|
|
2016-01-08 09:49:06 +01:00
|
|
|
with qtbot.waitSignal(sess_man.update_completion):
|
|
|
|
sess_man.delete(str(sess))
|
2015-08-24 17:11:33 +02:00
|
|
|
|
|
|
|
def test_not_existing(self, sess_man, qtbot, tmpdir):
|
|
|
|
sess = tmpdir / 'foo.yml'
|
|
|
|
|
|
|
|
with pytest.raises(sessions.SessionError):
|
|
|
|
sess_man.delete(str(sess))
|
|
|
|
|
|
|
|
|
|
|
|
class TestListSessions:
|
|
|
|
|
|
|
|
def test_no_base_path(self, sess_man):
|
|
|
|
assert not sess_man.list_sessions()
|
|
|
|
|
|
|
|
def test_no_sessions(self, tmpdir):
|
|
|
|
sess_man = sessions.SessionManager(str(tmpdir))
|
|
|
|
assert not sess_man.list_sessions()
|
|
|
|
|
|
|
|
def test_with_sessions(self, tmpdir):
|
|
|
|
(tmpdir / 'foo.yml').ensure()
|
|
|
|
(tmpdir / 'bar.yml').ensure()
|
|
|
|
sess_man = sessions.SessionManager(str(tmpdir))
|
|
|
|
assert sorted(sess_man.list_sessions()) == ['bar', 'foo']
|
|
|
|
|
|
|
|
def test_with_other_files(self, tmpdir):
|
|
|
|
(tmpdir / 'foo.yml').ensure()
|
|
|
|
(tmpdir / 'bar.html').ensure()
|
|
|
|
sess_man = sessions.SessionManager(str(tmpdir))
|
|
|
|
assert sess_man.list_sessions() == ['foo']
|
|
|
|
|
|
|
|
|
2015-08-24 20:25:40 +02:00
|
|
|
class TestSessionSave:
|
|
|
|
|
2016-07-07 13:35:13 +02:00
|
|
|
@webengine_refactoring_xfail
|
2015-08-24 20:25:40 +02:00
|
|
|
def test_normal_save(self, sess_man, tmpdir, fake_windows):
|
|
|
|
sess_file = tmpdir / 'foo.yml'
|
|
|
|
sess_man.session_save(0, str(sess_file), quiet=True)
|
|
|
|
assert sess_file.read_text('utf-8').startswith('windows:')
|
|
|
|
|
|
|
|
def test_internal_without_force(self, tmpdir):
|
|
|
|
sess_man = sessions.SessionManager(str(tmpdir))
|
|
|
|
with pytest.raises(cmdexc.CommandError) as excinfo:
|
|
|
|
sess_man.session_save(0, '_foo')
|
|
|
|
|
|
|
|
expected_text = ("_foo is an internal session, use --force to "
|
|
|
|
"save anyways.")
|
|
|
|
assert str(excinfo.value) == expected_text
|
|
|
|
assert not (tmpdir / '_foo.yml').exists()
|
|
|
|
|
2016-07-07 13:35:13 +02:00
|
|
|
@webengine_refactoring_xfail
|
2015-08-24 20:25:40 +02:00
|
|
|
def test_internal_with_force(self, tmpdir, fake_windows):
|
|
|
|
sess_man = sessions.SessionManager(str(tmpdir))
|
|
|
|
sess_man.session_save(0, '_foo', force=True, quiet=True)
|
|
|
|
assert (tmpdir / '_foo.yml').exists()
|
|
|
|
|
|
|
|
def test_current_unset(self, tmpdir):
|
|
|
|
sess_man = sessions.SessionManager(str(tmpdir))
|
|
|
|
|
|
|
|
with pytest.raises(cmdexc.CommandError) as excinfo:
|
|
|
|
sess_man.session_save(0, current=True)
|
|
|
|
|
|
|
|
assert str(excinfo.value) == "No session loaded currently!"
|
|
|
|
|
2016-07-07 13:35:13 +02:00
|
|
|
@webengine_refactoring_xfail
|
2015-08-24 20:25:40 +02:00
|
|
|
def test_current_set(self, tmpdir, fake_windows):
|
|
|
|
sess_man = sessions.SessionManager(str(tmpdir))
|
|
|
|
sess_man._current = 'foo'
|
|
|
|
sess_man.session_save(0, current=True, quiet=True)
|
|
|
|
assert (tmpdir / 'foo.yml').exists()
|
|
|
|
|
|
|
|
def test_saving_error(self, sess_man, tmpdir):
|
|
|
|
with pytest.raises(cmdexc.CommandError) as excinfo:
|
|
|
|
sess_man.session_save(0, str(tmpdir))
|
|
|
|
|
|
|
|
assert str(excinfo.value).startswith('Error while saving session: ')
|
|
|
|
|
2016-07-07 13:35:13 +02:00
|
|
|
@webengine_refactoring_xfail
|
2015-08-24 20:25:40 +02:00
|
|
|
def test_message(self, sess_man, tmpdir, message_mock, fake_windows):
|
|
|
|
message_mock.patch('qutebrowser.misc.sessions.message')
|
|
|
|
sess_path = str(tmpdir / 'foo.yml')
|
|
|
|
sess_man.session_save(0, sess_path)
|
|
|
|
expected_text = 'Saved session {}.'.format(sess_path)
|
|
|
|
assert message_mock.getmsg(immediate=True).text == expected_text
|
|
|
|
|
2016-07-07 13:35:13 +02:00
|
|
|
@webengine_refactoring_xfail
|
2015-08-24 20:25:40 +02:00
|
|
|
def test_message_quiet(self, sess_man, tmpdir, message_mock, fake_windows):
|
|
|
|
message_mock.patch('qutebrowser.misc.sessions.message')
|
|
|
|
sess_path = str(tmpdir / 'foo.yml')
|
|
|
|
sess_man.session_save(0, sess_path, quiet=True)
|
|
|
|
assert not message_mock.messages
|
|
|
|
|
|
|
|
|
2015-08-24 17:11:33 +02:00
|
|
|
class TestSessionDelete:
|
|
|
|
|
|
|
|
def test_internal_without_force(self, tmpdir):
|
|
|
|
sess_man = sessions.SessionManager(str(tmpdir))
|
|
|
|
sess_file = tmpdir / '_foo.yml'
|
|
|
|
sess_file.ensure()
|
|
|
|
|
|
|
|
with pytest.raises(cmdexc.CommandError) as excinfo:
|
|
|
|
sess_man.session_delete('_foo')
|
|
|
|
|
|
|
|
expected_text = ("_foo is an internal session, use --force to "
|
|
|
|
"delete anyways.")
|
|
|
|
assert str(excinfo.value) == expected_text
|
|
|
|
assert sess_file.exists()
|
|
|
|
|
|
|
|
def test_internal_with_force(self, tmpdir):
|
|
|
|
sess_man = sessions.SessionManager(str(tmpdir))
|
|
|
|
sess_file = tmpdir / '_foo.yml'
|
|
|
|
sess_file.ensure()
|
|
|
|
sess_man.session_delete('_foo', force=True)
|
|
|
|
assert not tmpdir.listdir()
|
|
|
|
|
|
|
|
def test_normal_delete(self, tmpdir):
|
|
|
|
sess_man = sessions.SessionManager(str(tmpdir))
|
|
|
|
sess_file = tmpdir / 'foo.yml'
|
|
|
|
sess_file.ensure()
|
|
|
|
sess_man.session_delete('foo')
|
|
|
|
assert not tmpdir.listdir()
|
|
|
|
|
|
|
|
def test_session_not_found(self, tmpdir):
|
|
|
|
sess_man = sessions.SessionManager(str(tmpdir))
|
|
|
|
|
|
|
|
with pytest.raises(cmdexc.CommandError) as excinfo:
|
|
|
|
sess_man.session_delete('foo')
|
|
|
|
|
|
|
|
assert str(excinfo.value) == "Session foo not found!"
|
|
|
|
|
|
|
|
@pytest.mark.posix
|
|
|
|
def test_deletion_error(self, caplog, tmpdir):
|
|
|
|
sess_man = sessions.SessionManager(str(tmpdir))
|
|
|
|
(tmpdir / 'foo.yml').ensure()
|
|
|
|
tmpdir.chmod(0o555) # unwritable
|
|
|
|
|
|
|
|
with pytest.raises(cmdexc.CommandError) as excinfo:
|
2015-11-11 19:57:03 +01:00
|
|
|
with caplog.at_level(logging.ERROR):
|
2015-08-24 17:11:33 +02:00
|
|
|
sess_man.session_delete('foo')
|
|
|
|
|
|
|
|
assert str(excinfo.value).startswith('Error while deleting session: ')
|
2015-11-11 19:57:03 +01:00
|
|
|
assert len(caplog.records) == 1
|
|
|
|
assert caplog.records[0].message == 'Error while deleting session!'
|