1# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: 2 3# Copyright 2018-2021 Florian Bruhin (The Compiler) <mail@qutebrowser.org> 4# 5# This file is part of qutebrowser. 6# 7# qutebrowser is free software: you can redistribute it and/or modify 8# it under the terms of the GNU General Public License as published by 9# the Free Software Foundation, either version 3 of the License, or 10# (at your option) any later version. 11# 12# qutebrowser is distributed in the hope that it will be useful, 13# but WITHOUT ANY WARRANTY; without even the implied warranty of 14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15# GNU General Public License for more details. 16# 17# You should have received a copy of the GNU General Public License 18# along with qutebrowser. If not, see <https://www.gnu.org/licenses/>. 19 20"""Tests for qutebrowser.components.misccommands.""" 21 22import signal 23import contextlib 24import time 25 26import pytest 27 28from qutebrowser.api import cmdutils 29from qutebrowser.utils import utils 30from qutebrowser.components import misccommands 31 32 33@contextlib.contextmanager 34def _trapped_segv(handler): 35 """Temporarily install given signal handler for SIGSEGV.""" 36 old_handler = signal.signal(signal.SIGSEGV, handler) 37 yield 38 if old_handler is not None: 39 signal.signal(signal.SIGSEGV, old_handler) 40 41 42def test_debug_crash_exception(): 43 """Verify that debug_crash crashes as intended.""" 44 with pytest.raises(Exception, match="Forced crash"): 45 misccommands.debug_crash(typ='exception') 46 47 48@pytest.mark.skipif(utils.is_windows, 49 reason="current CPython/win can't recover from SIGSEGV") 50def test_debug_crash_segfault(): 51 """Verify that debug_crash crashes as intended.""" 52 caught = False 53 54 def _handler(num, frame): 55 """Temporary handler for segfault.""" 56 nonlocal caught 57 caught = num == signal.SIGSEGV 58 59 with _trapped_segv(_handler): 60 # since we handle the segfault, execution will continue and run into 61 # the "Segfault failed (wat.)" Exception 62 with pytest.raises(Exception, match="Segfault failed"): 63 misccommands.debug_crash(typ='segfault') 64 time.sleep(0.001) 65 assert caught 66 67 68def test_debug_trace(mocker): 69 """Check if hunter.trace is properly called.""" 70 # but only if hunter is available 71 pytest.importorskip('hunter') 72 hunter_mock = mocker.patch.object(misccommands, 'hunter') 73 misccommands.debug_trace(1) 74 hunter_mock.trace.assert_called_with(1) 75 76 77def test_debug_trace_exception(mocker): 78 """Check that exceptions thrown by hunter.trace are handled.""" 79 def _mock_exception(): 80 """Side effect for testing debug_trace's reraise.""" 81 raise Exception('message') 82 83 hunter_mock = mocker.patch.object(misccommands, 'hunter') 84 hunter_mock.trace.side_effect = _mock_exception 85 with pytest.raises(cmdutils.CommandError, match='Exception: message'): 86 misccommands.debug_trace() 87 88 89def test_debug_trace_no_hunter(monkeypatch): 90 """Test that an error is shown if debug_trace is called without hunter.""" 91 monkeypatch.setattr(misccommands, 'hunter', None) 92 with pytest.raises(cmdutils.CommandError, match="You need to install " 93 "'hunter' to use this command!"): 94 misccommands.debug_trace() 95