2014-04-24 14:07:31 +02:00
|
|
|
import os
|
|
|
|
import time
|
|
|
|
|
2014-04-24 17:08:40 +02:00
|
|
|
import pytest
|
2014-04-24 15:48:01 +02:00
|
|
|
from requests.structures import CaseInsensitiveDict
|
|
|
|
|
2014-04-24 14:07:31 +02:00
|
|
|
from httpie.compat import urlopen
|
|
|
|
from httpie.downloads import (
|
2014-04-24 18:20:23 +02:00
|
|
|
parse_content_range, filename_from_content_disposition, filename_from_url,
|
2016-02-29 08:00:17 +01:00
|
|
|
get_unique_filename, ContentRangeError, Downloader,
|
2014-04-24 14:07:31 +02:00
|
|
|
)
|
2014-06-28 16:35:57 +02:00
|
|
|
from utils import http, TestEnvironment
|
2014-04-24 14:07:31 +02:00
|
|
|
|
|
|
|
|
2014-04-24 15:48:01 +02:00
|
|
|
class Response(object):
|
|
|
|
# noinspection PyDefaultArgument
|
|
|
|
def __init__(self, url, headers={}, status_code=200):
|
|
|
|
self.url = url
|
|
|
|
self.headers = CaseInsensitiveDict(headers)
|
|
|
|
self.status_code = status_code
|
2014-04-24 14:07:31 +02:00
|
|
|
|
|
|
|
|
2014-04-25 11:39:59 +02:00
|
|
|
class TestDownloadUtils:
|
2014-04-24 15:48:01 +02:00
|
|
|
def test_Content_Range_parsing(self):
|
2014-04-24 14:07:31 +02:00
|
|
|
parse = parse_content_range
|
|
|
|
|
2014-04-24 14:58:15 +02:00
|
|
|
assert parse('bytes 100-199/200', 100) == 200
|
|
|
|
assert parse('bytes 100-199/*', 100) == 200
|
2014-04-24 14:07:31 +02:00
|
|
|
|
|
|
|
# missing
|
2014-04-24 17:08:40 +02:00
|
|
|
pytest.raises(ContentRangeError, parse, None, 100)
|
2014-04-24 14:07:31 +02:00
|
|
|
|
|
|
|
# syntax error
|
2014-04-24 17:08:40 +02:00
|
|
|
pytest.raises(ContentRangeError, parse, 'beers 100-199/*', 100)
|
2014-04-24 14:07:31 +02:00
|
|
|
|
|
|
|
# unexpected range
|
2014-04-24 17:08:40 +02:00
|
|
|
pytest.raises(ContentRangeError, parse, 'bytes 100-199/*', 99)
|
2014-04-24 14:07:31 +02:00
|
|
|
|
|
|
|
# invalid instance-length
|
2014-04-24 17:08:40 +02:00
|
|
|
pytest.raises(ContentRangeError, parse, 'bytes 100-199/199', 100)
|
2014-04-24 14:07:31 +02:00
|
|
|
|
|
|
|
# invalid byte-range-resp-spec
|
2014-04-24 17:08:40 +02:00
|
|
|
pytest.raises(ContentRangeError, parse, 'bytes 100-99/199', 100)
|
2014-04-24 14:07:31 +02:00
|
|
|
|
|
|
|
# invalid byte-range-resp-spec
|
2014-04-24 17:08:40 +02:00
|
|
|
pytest.raises(ContentRangeError, parse, 'bytes 100-100/*', 100)
|
2014-04-24 14:07:31 +02:00
|
|
|
|
2014-04-25 11:39:59 +02:00
|
|
|
@pytest.mark.parametrize('header, expected_filename', [
|
|
|
|
('attachment; filename=hello-WORLD_123.txt', 'hello-WORLD_123.txt'),
|
|
|
|
('attachment; filename=".hello-WORLD_123.txt"', 'hello-WORLD_123.txt'),
|
|
|
|
('attachment; filename="white space.txt"', 'white space.txt'),
|
|
|
|
(r'attachment; filename="\"quotes\".txt"', '"quotes".txt'),
|
|
|
|
('attachment; filename=/etc/hosts', 'hosts'),
|
|
|
|
('attachment; filename=', None)
|
|
|
|
])
|
|
|
|
def test_Content_Disposition_parsing(self, header, expected_filename):
|
|
|
|
assert filename_from_content_disposition(header) == expected_filename
|
2014-04-24 14:07:31 +02:00
|
|
|
|
|
|
|
def test_filename_from_url(self):
|
2014-04-24 14:58:15 +02:00
|
|
|
assert 'foo.txt' == filename_from_url(
|
2014-04-24 14:07:31 +02:00
|
|
|
url='http://example.org/foo',
|
|
|
|
content_type='text/plain'
|
2014-04-24 14:58:15 +02:00
|
|
|
)
|
|
|
|
assert 'foo.html' == filename_from_url(
|
2014-04-24 14:07:31 +02:00
|
|
|
url='http://example.org/foo',
|
|
|
|
content_type='text/html; charset=utf8'
|
2014-04-24 14:58:15 +02:00
|
|
|
)
|
|
|
|
assert 'foo' == filename_from_url(
|
2014-04-24 14:07:31 +02:00
|
|
|
url='http://example.org/foo',
|
|
|
|
content_type=None
|
2014-04-24 14:58:15 +02:00
|
|
|
)
|
|
|
|
assert 'foo' == filename_from_url(
|
2014-04-24 14:07:31 +02:00
|
|
|
url='http://example.org/foo',
|
|
|
|
content_type='x-foo/bar'
|
2014-04-24 14:58:15 +02:00
|
|
|
)
|
2014-04-24 14:07:31 +02:00
|
|
|
|
|
|
|
def test_unique_filename(self):
|
2014-04-24 15:48:01 +02:00
|
|
|
def attempts(unique_on_attempt=0):
|
2014-04-24 14:07:31 +02:00
|
|
|
# noinspection PyUnresolvedReferences,PyUnusedLocal
|
|
|
|
def exists(filename):
|
|
|
|
if exists.attempt == unique_on_attempt:
|
|
|
|
return False
|
|
|
|
exists.attempt += 1
|
|
|
|
return True
|
2014-04-24 15:48:01 +02:00
|
|
|
|
2014-04-24 14:07:31 +02:00
|
|
|
exists.attempt = 0
|
|
|
|
return exists
|
|
|
|
|
2014-04-24 15:48:01 +02:00
|
|
|
assert 'foo.bar' == get_unique_filename('foo.bar', attempts(0))
|
|
|
|
assert 'foo.bar-1' == get_unique_filename('foo.bar', attempts(1))
|
|
|
|
assert 'foo.bar-10' == get_unique_filename('foo.bar', attempts(10))
|
2014-04-24 14:07:31 +02:00
|
|
|
|
|
|
|
|
2014-04-25 11:39:59 +02:00
|
|
|
class TestDownloads:
|
2014-04-24 14:07:31 +02:00
|
|
|
# TODO: more tests
|
|
|
|
|
2014-06-28 16:35:57 +02:00
|
|
|
def test_actual_download(self, httpbin):
|
|
|
|
url = httpbin.url + '/robots.txt'
|
2014-04-24 14:07:31 +02:00
|
|
|
body = urlopen(url).read().decode()
|
2014-04-24 15:48:01 +02:00
|
|
|
env = TestEnvironment(stdin_isatty=True, stdout_isatty=False)
|
|
|
|
r = http('--download', url, env=env)
|
2014-04-24 14:58:15 +02:00
|
|
|
assert 'Downloading' in r.stderr
|
|
|
|
assert '[K' in r.stderr
|
|
|
|
assert 'Done' in r.stderr
|
|
|
|
assert body == r
|
2014-04-24 14:07:31 +02:00
|
|
|
|
2014-06-28 16:35:57 +02:00
|
|
|
def test_download_with_Content_Length(self, httpbin):
|
2014-04-25 12:18:35 +02:00
|
|
|
devnull = open(os.devnull, 'w')
|
2016-02-29 08:00:17 +01:00
|
|
|
downloader = Downloader(output_file=devnull, progress_file=devnull)
|
|
|
|
downloader.start(Response(
|
2014-06-28 16:35:57 +02:00
|
|
|
url=httpbin.url + '/',
|
2014-04-24 15:48:01 +02:00
|
|
|
headers={'Content-Length': 10}
|
2014-04-24 14:07:31 +02:00
|
|
|
))
|
|
|
|
time.sleep(1.1)
|
2016-02-29 08:00:17 +01:00
|
|
|
downloader.chunk_downloaded(b'12345')
|
2014-04-24 14:07:31 +02:00
|
|
|
time.sleep(1.1)
|
2016-02-29 08:00:17 +01:00
|
|
|
downloader.chunk_downloaded(b'12345')
|
|
|
|
downloader.finish()
|
|
|
|
assert not downloader.interrupted
|
2014-04-24 14:07:31 +02:00
|
|
|
|
2014-06-28 16:35:57 +02:00
|
|
|
def test_download_no_Content_Length(self, httpbin):
|
2014-04-25 12:18:35 +02:00
|
|
|
devnull = open(os.devnull, 'w')
|
2016-02-29 08:00:17 +01:00
|
|
|
downloader = Downloader(output_file=devnull, progress_file=devnull)
|
|
|
|
downloader.start(Response(url=httpbin.url + '/'))
|
2014-04-24 14:07:31 +02:00
|
|
|
time.sleep(1.1)
|
2016-02-29 08:00:17 +01:00
|
|
|
downloader.chunk_downloaded(b'12345')
|
|
|
|
downloader.finish()
|
|
|
|
assert not downloader.interrupted
|
2014-04-24 14:07:31 +02:00
|
|
|
|
2014-06-28 16:35:57 +02:00
|
|
|
def test_download_interrupted(self, httpbin):
|
2014-04-25 12:18:35 +02:00
|
|
|
devnull = open(os.devnull, 'w')
|
2016-02-29 08:00:17 +01:00
|
|
|
downloader = Downloader(output_file=devnull, progress_file=devnull)
|
|
|
|
downloader.start(Response(
|
2014-06-28 16:35:57 +02:00
|
|
|
url=httpbin.url + '/',
|
2014-04-24 14:07:31 +02:00
|
|
|
headers={'Content-Length': 5}
|
|
|
|
))
|
2016-02-29 08:00:17 +01:00
|
|
|
downloader.chunk_downloaded(b'1234')
|
|
|
|
downloader.finish()
|
|
|
|
assert downloader.interrupted
|