diff --git a/qutebrowser/misc/lineparser.py b/qutebrowser/misc/lineparser.py index b9300a392..4999c2e22 100644 --- a/qutebrowser/misc/lineparser.py +++ b/qutebrowser/misc/lineparser.py @@ -92,6 +92,12 @@ class BaseLineParser(QObject): Args: mode: The mode to use ('a'/'r'/'w') + + Raises: + IOError: if the file is already open + + Yields: + a file object for the config file """ assert self._configfile is not None if self._opened: diff --git a/tests/unit/misc/test_lineparser.py b/tests/unit/misc/test_lineparser.py index 7cf7a73b7..a37a5fe27 100644 --- a/tests/unit/misc/test_lineparser.py +++ b/tests/unit/misc/test_lineparser.py @@ -21,6 +21,7 @@ import os import pytest +from unittest import mock from qutebrowser.misc import lineparser as lineparsermod @@ -53,9 +54,49 @@ class TestBaseLineParser: lineparser._prepare_save() os_mock.makedirs.assert_called_with(self.CONFDIR, 0o755) + def test_prepare_save_no_config(self, mocker): + """Test if _prepare_save doesn't create a None config dir.""" + os_mock = mocker.patch('qutebrowser.misc.lineparser.os') + os_mock.path.exists.return_value = True + + lineparser = lineparsermod.BaseLineParser(None, self.FILENAME) + assert not lineparser._prepare_save() + assert not os_mock.makedirs.called + + def test_double_open(self, mocker, lineparser): + """Test if _open refuses reentry.""" + mocker.patch('qutebrowser.misc.lineparser.open', mock.mock_open()) + + with lineparser._open('r'): + with pytest.raises(IOError): + with lineparser._open('r'): + pass + + def test_binary(self, mocker): + """Test if _open and _write correctly handle binary files.""" + open_mock = mock.mock_open() + mocker.patch('qutebrowser.misc.lineparser.open', open_mock) + + testdata = b'\xf0\xff' + + lineparser = lineparsermod.BaseLineParser( + self.CONFDIR, self.FILENAME, binary=True) + with lineparser._open('r') as f: + lineparser._write(f, [testdata]) + + open_mock.assert_called_once_with( + os.path.join(self.CONFDIR, self.FILENAME), 'rb') + + open_mock().write.assert_has_calls([ + mock.call(testdata), + mock.call(b'\n') + ]) + class TestLineParser: + """Tests for LineParser.""" + @pytest.fixture def lineparser(self, tmpdir): """Fixture to get a LineParser for tests.""" @@ -63,7 +104,18 @@ class TestLineParser: lp.save() return lp + def test_init(self, tmpdir): + """Test if creating a line parser correctly reads its file.""" + (tmpdir / 'file').write('one\ntwo\n') + lineparser = lineparsermod.LineParser(str(tmpdir), 'file') + assert lineparser.data == ['one', 'two'] + + (tmpdir / 'file').write_binary(b'\xfe\n\xff\n') + lineparser = lineparsermod.LineParser(str(tmpdir), 'file', binary=True) + assert lineparser.data == [b'\xfe', b'\xff'] + def test_clear(self, tmpdir, lineparser): + """Test if clear() empties its file.""" lineparser.data = ['one', 'two'] lineparser.save() assert (tmpdir / 'file').read() == 'one\ntwo\n' @@ -71,6 +123,20 @@ class TestLineParser: assert not lineparser.data assert (tmpdir / 'file').read() == '' + def test_double_open(self, lineparser): + """Test if save() bails on an already open file.""" + with lineparser._open('r'): + with pytest.raises(IOError): + lineparser.save() + + def test_prepare_save(self, tmpdir, lineparser): + """Test if save() bails when _prepare_save() returns False.""" + (tmpdir / 'file').write('pristine\n') + lineparser.data = ['changed'] + lineparser._prepare_save = lambda: False + lineparser.save() + assert (tmpdir / 'file').read() == 'pristine\n' + class TestAppendLineParser: @@ -97,7 +163,17 @@ class TestAppendLineParser: lineparser.save() assert (tmpdir / 'file').read() == self._get_expected(new_data) + def test_save_without_configdir(self, tmpdir, lineparser): + """Test save() failing because no configdir was set.""" + new_data = ['new data 1', 'new data 2'] + lineparser.new_data = new_data + lineparser._configdir = None + assert not lineparser.save() + # make sure new data is still there + assert lineparser.new_data == new_data + def test_clear(self, tmpdir, lineparser): + """Check if calling clear() empties both pending and persisted data.""" lineparser.new_data = ['one', 'two'] lineparser.save() assert (tmpdir / 'file').read() == "old data 1\nold data 2\none\ntwo\n" @@ -108,6 +184,14 @@ class TestAppendLineParser: assert not lineparser.new_data assert (tmpdir / 'file').read() == "" + def test_clear_without_configdir(self, tmpdir, lineparser): + """Test clear() failing because no configdir was set.""" + new_data = ['new data 1', 'new data 2'] + lineparser.new_data = new_data + lineparser._configdir = None + assert not lineparser.clear() + assert lineparser.new_data == new_data + def test_iter_without_open(self, lineparser): """Test __iter__ without having called open().""" with pytest.raises(ValueError):