1# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: 2 3# Copyright 2015-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 20import os 21import sys 22import shlex 23 24import pytest 25import pytest_bdd as bdd 26from PyQt5.QtNetwork import QSslSocket 27bdd.scenarios('downloads.feature') 28 29 30PROMPT_MSG = ("Asking question <qutebrowser.utils.usertypes.Question " 31 "default={!r} mode=<PromptMode.download: 5> option=None " 32 "text=* title='Save file to:'>, *") 33 34 35@pytest.fixture 36def download_dir(tmpdir): 37 downloads = tmpdir / 'downloads' 38 downloads.ensure(dir=True) 39 (downloads / 'subdir').ensure(dir=True) 40 try: 41 os.mkfifo(str(downloads / 'fifo')) 42 except AttributeError: 43 pass 44 unwritable = downloads / 'unwritable' 45 unwritable.ensure(dir=True) 46 unwritable.chmod(0) 47 48 yield downloads 49 50 unwritable.chmod(0o755) 51 52 53@bdd.given("I set up a temporary download dir") 54def temporary_download_dir(quteproc, download_dir): 55 quteproc.set_setting('downloads.location.prompt', 'false') 56 quteproc.set_setting('downloads.location.remember', 'false') 57 quteproc.set_setting('downloads.location.directory', str(download_dir)) 58 59 60@bdd.given("I clean old downloads") 61def clean_old_downloads(quteproc): 62 quteproc.send_cmd(':download-cancel --all') 63 quteproc.send_cmd(':download-clear') 64 65 66@bdd.when("SSL is supported") 67def check_ssl(): 68 if not QSslSocket.supportsSsl(): 69 pytest.skip("QtNetwork SSL not supported") 70 71 72@bdd.when("the unwritable dir is unwritable") 73def check_unwritable(tmpdir): 74 unwritable = tmpdir / 'downloads' / 'unwritable' 75 if os.access(str(unwritable), os.W_OK): 76 # Docker container or similar 77 pytest.skip("Unwritable dir was writable") 78 79 80@bdd.when("I wait until the download is finished") 81def wait_for_download_finished(quteproc): 82 quteproc.wait_for(category='downloads', message='Download * finished') 83 84 85@bdd.when(bdd.parsers.parse("I wait until the download {name} is finished")) 86def wait_for_download_finished_name(quteproc, name): 87 quteproc.wait_for(category='downloads', 88 message='Download {} finished'.format(name)) 89 90 91@bdd.when(bdd.parsers.parse('I wait for the download prompt for "{path}"')) 92def wait_for_download_prompt(tmpdir, quteproc, path): 93 full_path = path.replace('(tmpdir)', str(tmpdir)).replace('/', os.sep) 94 quteproc.wait_for(message=PROMPT_MSG.format(full_path)) 95 quteproc.wait_for(message="Entering mode KeyMode.prompt " 96 "(reason: question asked)") 97 98 99@bdd.then(bdd.parsers.parse("The downloaded file {filename} should not exist")) 100def download_should_not_exist(filename, tmpdir): 101 path = tmpdir / 'downloads' / filename 102 assert not path.check() 103 104 105@bdd.then(bdd.parsers.parse("The downloaded file {filename} should exist")) 106def download_should_exist(filename, tmpdir): 107 path = tmpdir / 'downloads' / filename 108 assert path.check() 109 110 111@bdd.then(bdd.parsers.parse("The downloaded file {filename} should be " 112 "{size} bytes big")) 113def download_size(filename, size, tmpdir): 114 path = tmpdir / 'downloads' / filename 115 assert path.size() == int(size) 116 117 118@bdd.then(bdd.parsers.parse("The downloaded file {filename} should contain " 119 "{text}")) 120def download_contents(filename, text, tmpdir): 121 path = tmpdir / 'downloads' / filename 122 assert text in path.read() 123 124 125@bdd.then(bdd.parsers.parse('The download prompt should be shown with ' 126 '"{path}"')) 127def download_prompt(tmpdir, quteproc, path): 128 full_path = path.replace('(tmpdir)', str(tmpdir)).replace('/', os.sep) 129 quteproc.wait_for(message=PROMPT_MSG.format(full_path)) 130 quteproc.send_cmd(':mode-leave') 131 132 133@bdd.when("I set a test python open_dispatcher") 134def default_open_dispatcher_python(quteproc, tmpdir): 135 cmd = '{} -c "import sys; print(sys.argv[1])"'.format( 136 shlex.quote(sys.executable)) 137 quteproc.set_setting('downloads.open_dispatcher', cmd) 138 139 140@bdd.when("I open the download") 141def download_open(quteproc): 142 cmd = '{} -c "import sys; print(sys.argv[1])"'.format( 143 shlex.quote(sys.executable)) 144 quteproc.send_cmd(':download-open {}'.format(cmd)) 145 146 147@bdd.when("I open the download with a placeholder") 148def download_open_placeholder(quteproc): 149 cmd = '{} -c "import sys; print(sys.argv[1])"'.format( 150 shlex.quote(sys.executable)) 151 quteproc.send_cmd(':download-open {} {{}}'.format(cmd)) 152 153 154@bdd.when("I directly open the download") 155def download_open_with_prompt(quteproc): 156 cmd = '{} -c pass'.format(shlex.quote(sys.executable)) 157 quteproc.send_cmd(':prompt-open-download {}'.format(cmd)) 158 159 160@bdd.when(bdd.parsers.parse("I delete the downloaded file {filename}")) 161def delete_file(tmpdir, filename): 162 (tmpdir / 'downloads' / filename).remove() 163 164 165@bdd.then("the FIFO should still be a FIFO") 166def fifo_should_be_fifo(tmpdir): 167 download_dir = tmpdir / 'downloads' 168 assert download_dir.exists() 169 assert not os.path.isfile(str(download_dir / 'fifo')) 170