Vehicle-Anti-Theft-Face-Rec.../venv/Lib/site-packages/gcloud/streaming/test_transfer.py

1900 lines
74 KiB
Python

import unittest2
class Test__Transfer(unittest2.TestCase):
URL = 'http://example.com/api'
def _getTargetClass(self):
from gcloud.streaming.transfer import _Transfer
return _Transfer
def _makeOne(self, *args, **kw):
return self._getTargetClass()(*args, **kw)
def test_ctor_defaults(self):
from gcloud.streaming.transfer import _DEFAULT_CHUNKSIZE
stream = _Stream()
xfer = self._makeOne(stream)
self.assertTrue(xfer.stream is stream)
self.assertFalse(xfer.close_stream)
self.assertEqual(xfer.chunksize, _DEFAULT_CHUNKSIZE)
self.assertTrue(xfer.auto_transfer)
self.assertTrue(xfer.bytes_http is None)
self.assertTrue(xfer.http is None)
self.assertEqual(xfer.num_retries, 5)
self.assertTrue(xfer.url is None)
self.assertFalse(xfer.initialized)
def test_ctor_explicit(self):
stream = _Stream()
HTTP = object()
CHUNK_SIZE = 1 << 18
NUM_RETRIES = 8
xfer = self._makeOne(stream,
close_stream=True,
chunksize=CHUNK_SIZE,
auto_transfer=False,
http=HTTP,
num_retries=NUM_RETRIES)
self.assertTrue(xfer.stream is stream)
self.assertTrue(xfer.close_stream)
self.assertEqual(xfer.chunksize, CHUNK_SIZE)
self.assertFalse(xfer.auto_transfer)
self.assertTrue(xfer.bytes_http is HTTP)
self.assertTrue(xfer.http is HTTP)
self.assertEqual(xfer.num_retries, NUM_RETRIES)
def test_bytes_http_fallback_to_http(self):
stream = _Stream()
HTTP = object()
xfer = self._makeOne(stream, http=HTTP)
self.assertTrue(xfer.bytes_http is HTTP)
def test_bytes_http_setter(self):
stream = _Stream()
HTTP = object()
BYTES_HTTP = object()
xfer = self._makeOne(stream, http=HTTP)
xfer.bytes_http = BYTES_HTTP
self.assertTrue(xfer.bytes_http is BYTES_HTTP)
def test_num_retries_setter_invalid(self):
stream = _Stream()
xfer = self._makeOne(stream)
with self.assertRaises(ValueError):
xfer.num_retries = object()
def test_num_retries_setter_negative(self):
stream = _Stream()
xfer = self._makeOne(stream)
with self.assertRaises(ValueError):
xfer.num_retries = -1
def test__initialize_not_already_initialized_w_http(self):
HTTP = object()
stream = _Stream()
xfer = self._makeOne(stream)
xfer._initialize(HTTP, self.URL)
self.assertTrue(xfer.initialized)
self.assertTrue(xfer.http is HTTP)
self.assertTrue(xfer.url is self.URL)
def test__initialize_not_already_initialized_wo_http(self):
from httplib2 import Http
stream = _Stream()
xfer = self._makeOne(stream)
xfer._initialize(None, self.URL)
self.assertTrue(xfer.initialized)
self.assertTrue(isinstance(xfer.http, Http))
self.assertTrue(xfer.url is self.URL)
def test__initialize_w_existing_http(self):
HTTP_1, HTTP_2 = object(), object()
stream = _Stream()
xfer = self._makeOne(stream, http=HTTP_1)
xfer._initialize(HTTP_2, self.URL)
self.assertTrue(xfer.initialized)
self.assertTrue(xfer.http is HTTP_1)
self.assertTrue(xfer.url is self.URL)
def test__initialize_already_initialized(self):
from gcloud.streaming.exceptions import TransferInvalidError
URL_2 = 'http://example.com/other'
HTTP_1, HTTP_2 = object(), object()
stream = _Stream()
xfer = self._makeOne(stream)
xfer._initialize(HTTP_1, self.URL)
with self.assertRaises(TransferInvalidError):
xfer._initialize(HTTP_2, URL_2)
def test__ensure_initialized_hit(self):
HTTP = object()
stream = _Stream()
xfer = self._makeOne(stream)
xfer._initialize(HTTP, self.URL)
xfer._ensure_initialized() # no raise
def test__ensure_initialized_miss(self):
from gcloud.streaming.exceptions import TransferInvalidError
stream = _Stream()
xfer = self._makeOne(stream)
with self.assertRaises(TransferInvalidError):
xfer._ensure_initialized()
def test__ensure_uninitialized_hit(self):
stream = _Stream()
xfer = self._makeOne(stream)
xfer._ensure_uninitialized() # no raise
def test__ensure_uninitialized_miss(self):
from gcloud.streaming.exceptions import TransferInvalidError
stream = _Stream()
HTTP = object()
xfer = self._makeOne(stream)
xfer._initialize(HTTP, self.URL)
with self.assertRaises(TransferInvalidError):
xfer._ensure_uninitialized()
def test___del___closes_stream(self):
stream = _Stream()
xfer = self._makeOne(stream, close_stream=True)
self.assertFalse(stream._closed)
del xfer
self.assertTrue(stream._closed)
class Test_Download(unittest2.TestCase):
URL = "http://example.com/api"
def _getTargetClass(self):
from gcloud.streaming.transfer import Download
return Download
def _makeOne(self, *args, **kw):
return self._getTargetClass()(*args, **kw)
def test_ctor_defaults(self):
stream = _Stream()
download = self._makeOne(stream)
self.assertTrue(download.stream is stream)
self.assertTrue(download._initial_response is None)
self.assertEqual(download.progress, 0)
self.assertTrue(download.total_size is None)
self.assertTrue(download.encoding is None)
def test_ctor_w_kwds(self):
stream = _Stream()
CHUNK_SIZE = 123
download = self._makeOne(stream, chunksize=CHUNK_SIZE)
self.assertTrue(download.stream is stream)
self.assertEqual(download.chunksize, CHUNK_SIZE)
def test_ctor_w_total_size(self):
stream = _Stream()
SIZE = 123
download = self._makeOne(stream, total_size=SIZE)
self.assertTrue(download.stream is stream)
self.assertEqual(download.total_size, SIZE)
def test_from_file_w_existing_file_no_override(self):
import os
klass = self._getTargetClass()
with _tempdir() as tempdir:
filename = os.path.join(tempdir, 'file.out')
with open(filename, 'w') as fileobj:
fileobj.write('EXISTING FILE')
with self.assertRaises(ValueError):
klass.from_file(filename)
def test_from_file_w_existing_file_w_override_wo_auto_transfer(self):
import os
klass = self._getTargetClass()
with _tempdir() as tempdir:
filename = os.path.join(tempdir, 'file.out')
with open(filename, 'w') as fileobj:
fileobj.write('EXISTING FILE')
download = klass.from_file(filename, overwrite=True,
auto_transfer=False)
self.assertFalse(download.auto_transfer)
del download # closes stream
with open(filename, 'rb') as fileobj:
self.assertEqual(fileobj.read(), b'')
def test_from_stream_defaults(self):
stream = _Stream()
klass = self._getTargetClass()
download = klass.from_stream(stream)
self.assertTrue(download.stream is stream)
self.assertTrue(download.auto_transfer)
self.assertTrue(download.total_size is None)
def test_from_stream_explicit(self):
CHUNK_SIZE = 1 << 18
SIZE = 123
stream = _Stream()
klass = self._getTargetClass()
download = klass.from_stream(stream, auto_transfer=False,
total_size=SIZE, chunksize=CHUNK_SIZE)
self.assertTrue(download.stream is stream)
self.assertFalse(download.auto_transfer)
self.assertEqual(download.total_size, SIZE)
self.assertEqual(download.chunksize, CHUNK_SIZE)
def test_configure_request(self):
CHUNK_SIZE = 100
download = self._makeOne(_Stream(), chunksize=CHUNK_SIZE)
request = _Dummy(headers={})
url_builder = _Dummy(query_params={})
download.configure_request(request, url_builder)
self.assertEqual(request.headers, {'Range': 'bytes=0-99'})
self.assertEqual(url_builder.query_params, {'alt': 'media'})
def test__set_total_wo_content_range_wo_existing_total(self):
info = {}
download = self._makeOne(_Stream())
download._set_total(info)
self.assertEqual(download.total_size, 0)
def test__set_total_wo_content_range_w_existing_total(self):
SIZE = 123
info = {}
download = self._makeOne(_Stream(), total_size=SIZE)
download._set_total(info)
self.assertEqual(download.total_size, SIZE)
def test__set_total_w_content_range_w_existing_total(self):
SIZE = 123
info = {'content-range': 'bytes 123-234/4567'}
download = self._makeOne(_Stream(), total_size=SIZE)
download._set_total(info)
self.assertEqual(download.total_size, 4567)
def test__set_total_w_content_range_w_asterisk_total(self):
info = {'content-range': 'bytes 123-234/*'}
download = self._makeOne(_Stream())
download._set_total(info)
self.assertEqual(download.total_size, 0)
def test_initialize_download_already_initialized(self):
from gcloud.streaming.exceptions import TransferInvalidError
request = _Request()
download = self._makeOne(_Stream())
download._initialize(None, self.URL)
with self.assertRaises(TransferInvalidError):
download.initialize_download(request, http=object())
def test_initialize_download_wo_autotransfer(self):
request = _Request()
http = object()
download = self._makeOne(_Stream(), auto_transfer=False)
download.initialize_download(request, http)
self.assertTrue(download.http is http)
self.assertEqual(download.url, request.url)
def test_initialize_download_w_autotransfer_failing(self):
from six.moves import http_client
from gcloud._testing import _Monkey
from gcloud.streaming import transfer as MUT
from gcloud.streaming.exceptions import HttpError
request = _Request()
http = object()
download = self._makeOne(_Stream(), auto_transfer=True)
response = _makeResponse(http_client.BAD_REQUEST)
requester = _MakeRequest(response)
with _Monkey(MUT, make_api_request=requester):
with self.assertRaises(HttpError):
download.initialize_download(request, http)
self.assertTrue(len(requester._requested), 1)
self.assertTrue(requester._requested[0][0] is request)
def test_initialize_download_w_autotransfer_w_content_location(self):
from six.moves import http_client
from gcloud._testing import _Monkey
from gcloud.streaming import transfer as MUT
REDIRECT_URL = 'http://example.com/other'
request = _Request()
http = object()
info = {'content-location': REDIRECT_URL}
download = self._makeOne(_Stream(), auto_transfer=True)
response = _makeResponse(http_client.NO_CONTENT, info)
requester = _MakeRequest(response)
with _Monkey(MUT, make_api_request=requester):
download.initialize_download(request, http)
self.assertTrue(download._initial_response is None)
self.assertEqual(download.total_size, 0)
self.assertTrue(download.http is http)
self.assertEqual(download.url, REDIRECT_URL)
self.assertTrue(len(requester._requested), 1)
self.assertTrue(requester._requested[0][0] is request)
def test__normalize_start_end_w_end_w_start_lt_0(self):
from gcloud.streaming.exceptions import TransferInvalidError
download = self._makeOne(_Stream())
with self.assertRaises(TransferInvalidError):
download._normalize_start_end(-1, 0)
def test__normalize_start_end_w_end_w_start_gt_total(self):
from gcloud.streaming.exceptions import TransferInvalidError
download = self._makeOne(_Stream())
download._set_total({'content-range': 'bytes 0-1/2'})
with self.assertRaises(TransferInvalidError):
download._normalize_start_end(3, 0)
def test__normalize_start_end_w_end_lt_start(self):
from gcloud.streaming.exceptions import TransferInvalidError
download = self._makeOne(_Stream())
download._set_total({'content-range': 'bytes 0-1/2'})
with self.assertRaises(TransferInvalidError):
download._normalize_start_end(1, 0)
def test__normalize_start_end_w_end_gt_start(self):
download = self._makeOne(_Stream())
download._set_total({'content-range': 'bytes 0-1/2'})
self.assertEqual(download._normalize_start_end(1, 2), (1, 1))
def test__normalize_start_end_wo_end_w_start_lt_0(self):
download = self._makeOne(_Stream())
download._set_total({'content-range': 'bytes 0-1/2'})
self.assertEqual(download._normalize_start_end(-2), (0, 1))
self.assertEqual(download._normalize_start_end(-1), (1, 1))
def test__normalize_start_end_wo_end_w_start_ge_0(self):
download = self._makeOne(_Stream())
download._set_total({'content-range': 'bytes 0-1/100'})
self.assertEqual(download._normalize_start_end(0), (0, 99))
self.assertEqual(download._normalize_start_end(1), (1, 99))
def test__set_range_header_w_start_lt_0(self):
request = _Request()
download = self._makeOne(_Stream())
download._set_range_header(request, -1)
self.assertEqual(request.headers['range'], 'bytes=-1')
def test__set_range_header_w_start_ge_0_wo_end(self):
request = _Request()
download = self._makeOne(_Stream())
download._set_range_header(request, 0)
self.assertEqual(request.headers['range'], 'bytes=0-')
def test__set_range_header_w_start_ge_0_w_end(self):
request = _Request()
download = self._makeOne(_Stream())
download._set_range_header(request, 0, 1)
self.assertEqual(request.headers['range'], 'bytes=0-1')
def test__compute_end_byte_w_start_lt_0_w_end(self):
download = self._makeOne(_Stream())
self.assertEqual(download._compute_end_byte(-1, 1), 1)
def test__compute_end_byte_w_start_ge_0_wo_end_w_use_chunks(self):
CHUNK_SIZE = 5
download = self._makeOne(_Stream(), chunksize=CHUNK_SIZE)
self.assertEqual(download._compute_end_byte(0, use_chunks=True), 4)
def test__compute_end_byte_w_start_ge_0_w_end_w_use_chunks(self):
CHUNK_SIZE = 5
download = self._makeOne(_Stream(), chunksize=CHUNK_SIZE)
self.assertEqual(download._compute_end_byte(0, 3, use_chunks=True), 3)
self.assertEqual(download._compute_end_byte(0, 5, use_chunks=True), 4)
def test__compute_end_byte_w_start_ge_0_w_end_w_total_size(self):
CHUNK_SIZE = 50
download = self._makeOne(_Stream(), chunksize=CHUNK_SIZE)
download._set_total({'content-range': 'bytes 0-1/10'})
self.assertEqual(download._compute_end_byte(0, 100, use_chunks=False),
9)
self.assertEqual(download._compute_end_byte(0, 8, use_chunks=False), 8)
def test__compute_end_byte_w_start_ge_0_wo_end_w_total_size(self):
CHUNK_SIZE = 50
download = self._makeOne(_Stream(), chunksize=CHUNK_SIZE)
download._set_total({'content-range': 'bytes 0-1/10'})
self.assertEqual(download._compute_end_byte(0, use_chunks=False), 9)
def test__get_chunk_not_initialized(self):
from gcloud.streaming.exceptions import TransferInvalidError
download = self._makeOne(_Stream())
with self.assertRaises(TransferInvalidError):
download._get_chunk(0, 10)
def test__get_chunk(self):
from six.moves import http_client
from gcloud._testing import _Monkey
from gcloud.streaming import transfer as MUT
http = object()
download = self._makeOne(_Stream())
download._initialize(http, self.URL)
response = _makeResponse(http_client.OK)
requester = _MakeRequest(response)
with _Monkey(MUT,
Request=_Request,
make_api_request=requester):
found = download._get_chunk(0, 10)
self.assertTrue(found is response)
self.assertTrue(len(requester._requested), 1)
request = requester._requested[0][0]
self.assertEqual(request.headers['range'], 'bytes=0-10')
def test__process_response_w_FORBIDDEN(self):
from gcloud.streaming.exceptions import HttpError
from six.moves import http_client
download = self._makeOne(_Stream())
response = _makeResponse(http_client.FORBIDDEN)
with self.assertRaises(HttpError):
download._process_response(response)
def test__process_response_w_NOT_FOUND(self):
from gcloud.streaming.exceptions import HttpError
from six.moves import http_client
download = self._makeOne(_Stream())
response = _makeResponse(http_client.NOT_FOUND)
with self.assertRaises(HttpError):
download._process_response(response)
def test__process_response_w_other_error(self):
from gcloud.streaming.exceptions import TransferRetryError
from six.moves import http_client
download = self._makeOne(_Stream())
response = _makeResponse(http_client.BAD_REQUEST)
with self.assertRaises(TransferRetryError):
download._process_response(response)
def test__process_response_w_OK_wo_encoding(self):
from six.moves import http_client
stream = _Stream()
download = self._makeOne(stream)
response = _makeResponse(http_client.OK, content='OK')
found = download._process_response(response)
self.assertTrue(found is response)
self.assertEqual(stream._written, ['OK'])
self.assertEqual(download.progress, 2)
self.assertEqual(download.encoding, None)
def test__process_response_w_PARTIAL_CONTENT_w_encoding(self):
from six.moves import http_client
stream = _Stream()
download = self._makeOne(stream)
info = {'content-encoding': 'blah'}
response = _makeResponse(http_client.OK, info, 'PARTIAL')
found = download._process_response(response)
self.assertTrue(found is response)
self.assertEqual(stream._written, ['PARTIAL'])
self.assertEqual(download.progress, 7)
self.assertEqual(download.encoding, 'blah')
def test__process_response_w_REQUESTED_RANGE_NOT_SATISFIABLE(self):
from six.moves import http_client
stream = _Stream()
download = self._makeOne(stream)
response = _makeResponse(
http_client.REQUESTED_RANGE_NOT_SATISFIABLE)
found = download._process_response(response)
self.assertTrue(found is response)
self.assertEqual(stream._written, [])
self.assertEqual(download.progress, 0)
self.assertEqual(download.encoding, None)
def test__process_response_w_NO_CONTENT(self):
from six.moves import http_client
stream = _Stream()
download = self._makeOne(stream)
response = _makeResponse(status_code=http_client.NO_CONTENT)
found = download._process_response(response)
self.assertTrue(found is response)
self.assertEqual(stream._written, [''])
self.assertEqual(download.progress, 0)
self.assertEqual(download.encoding, None)
def test_get_range_not_initialized(self):
from gcloud.streaming.exceptions import TransferInvalidError
download = self._makeOne(_Stream())
with self.assertRaises(TransferInvalidError):
download.get_range(0, 10)
def test_get_range_wo_total_size_complete(self):
from six.moves import http_client
from gcloud._testing import _Monkey
from gcloud.streaming import transfer as MUT
CONTENT = b'ABCDEFGHIJ'
LEN = len(CONTENT)
REQ_RANGE = 'bytes=0-%d' % (LEN,)
RESP_RANGE = 'bytes 0-%d/%d' % (LEN - 1, LEN)
http = object()
stream = _Stream()
download = self._makeOne(stream)
download._initialize(http, self.URL)
info = {'content-range': RESP_RANGE}
response = _makeResponse(http_client.OK, info, CONTENT)
requester = _MakeRequest(response)
with _Monkey(MUT,
Request=_Request,
make_api_request=requester):
download.get_range(0, LEN)
self.assertTrue(len(requester._requested), 1)
request = requester._requested[0][0]
self.assertEqual(request.headers, {'range': REQ_RANGE})
self.assertEqual(stream._written, [CONTENT])
self.assertEqual(download.total_size, LEN)
def test_get_range_wo_total_size_wo_end(self):
from six.moves import http_client
from gcloud._testing import _Monkey
from gcloud.streaming import transfer as MUT
CONTENT = b'ABCDEFGHIJ'
LEN = len(CONTENT)
START = 5
CHUNK_SIZE = 123
REQ_RANGE = 'bytes=%d-%d' % (START, START + CHUNK_SIZE - 1,)
RESP_RANGE = 'bytes %d-%d/%d' % (START, LEN - 1, LEN)
http = object()
stream = _Stream()
download = self._makeOne(stream, chunksize=CHUNK_SIZE)
download._initialize(http, self.URL)
info = {'content-range': RESP_RANGE}
response = _makeResponse(http_client.OK, info, CONTENT[START:])
requester = _MakeRequest(response)
with _Monkey(MUT,
Request=_Request,
make_api_request=requester):
download.get_range(START)
self.assertTrue(len(requester._requested), 1)
request = requester._requested[0][0]
self.assertEqual(request.headers, {'range': REQ_RANGE})
self.assertEqual(stream._written, [CONTENT[START:]])
self.assertEqual(download.total_size, LEN)
def test_get_range_w_total_size_partial(self):
from six.moves import http_client
from gcloud._testing import _Monkey
from gcloud.streaming import transfer as MUT
CONTENT = b'ABCDEFGHIJ'
LEN = len(CONTENT)
PARTIAL_LEN = 5
REQ_RANGE = 'bytes=0-%d' % (PARTIAL_LEN,)
RESP_RANGE = 'bytes 0-%d/%d' % (PARTIAL_LEN, LEN,)
http = object()
stream = _Stream()
download = self._makeOne(stream, total_size=LEN)
download._initialize(http, self.URL)
info = {'content-range': RESP_RANGE}
response = _makeResponse(http_client.OK, info, CONTENT[:PARTIAL_LEN])
response.length = LEN
requester = _MakeRequest(response)
with _Monkey(MUT,
Request=_Request,
make_api_request=requester):
download.get_range(0, PARTIAL_LEN)
self.assertTrue(len(requester._requested), 1)
request = requester._requested[0][0]
self.assertEqual(request.headers, {'range': REQ_RANGE})
self.assertEqual(stream._written, [CONTENT[:PARTIAL_LEN]])
self.assertEqual(download.total_size, LEN)
def test_get_range_w_empty_chunk(self):
from six.moves import http_client
from gcloud._testing import _Monkey
from gcloud.streaming import transfer as MUT
from gcloud.streaming.exceptions import TransferRetryError
CONTENT = b'ABCDEFGHIJ'
LEN = len(CONTENT)
START = 5
CHUNK_SIZE = 123
REQ_RANGE = 'bytes=%d-%d' % (START, START + CHUNK_SIZE - 1,)
RESP_RANGE = 'bytes %d-%d/%d' % (START, LEN - 1, LEN)
http = object()
stream = _Stream()
download = self._makeOne(stream, chunksize=CHUNK_SIZE)
download._initialize(http, self.URL)
info = {'content-range': RESP_RANGE}
response = _makeResponse(http_client.OK, info)
requester = _MakeRequest(response)
with _Monkey(MUT,
Request=_Request,
make_api_request=requester):
with self.assertRaises(TransferRetryError):
download.get_range(START)
self.assertTrue(len(requester._requested), 1)
request = requester._requested[0][0]
self.assertEqual(request.headers, {'range': REQ_RANGE})
self.assertEqual(stream._written, [''])
self.assertEqual(download.total_size, LEN)
def test_get_range_w_total_size_wo_use_chunks(self):
from six.moves import http_client
from gcloud._testing import _Monkey
from gcloud.streaming import transfer as MUT
CONTENT = b'ABCDEFGHIJ'
LEN = len(CONTENT)
CHUNK_SIZE = 3
REQ_RANGE = 'bytes=0-%d' % (LEN - 1,)
RESP_RANGE = 'bytes 0-%d/%d' % (LEN - 1, LEN,)
http = object()
stream = _Stream()
download = self._makeOne(stream, total_size=LEN, chunksize=CHUNK_SIZE)
download._initialize(http, self.URL)
info = {'content-range': RESP_RANGE}
response = _makeResponse(http_client.OK, info, CONTENT)
requester = _MakeRequest(response)
with _Monkey(MUT,
Request=_Request,
make_api_request=requester):
download.get_range(0, use_chunks=False)
self.assertTrue(len(requester._requested), 1)
request = requester._requested[0][0]
self.assertEqual(request.headers, {'range': REQ_RANGE})
self.assertEqual(stream._written, [CONTENT])
self.assertEqual(download.total_size, LEN)
def test_get_range_w_multiple_chunks(self):
from six.moves import http_client
from gcloud._testing import _Monkey
from gcloud.streaming import transfer as MUT
CONTENT = b'ABCDE'
LEN = len(CONTENT)
CHUNK_SIZE = 3
REQ_RANGE_1 = 'bytes=0-%d' % (CHUNK_SIZE - 1,)
RESP_RANGE_1 = 'bytes 0-%d/%d' % (CHUNK_SIZE - 1, LEN)
REQ_RANGE_2 = 'bytes=%d-%d' % (CHUNK_SIZE, LEN - 1)
RESP_RANGE_2 = 'bytes %d-%d/%d' % (CHUNK_SIZE, LEN - 1, LEN)
http = object()
stream = _Stream()
download = self._makeOne(stream, chunksize=CHUNK_SIZE)
download._initialize(http, self.URL)
info_1 = {'content-range': RESP_RANGE_1}
response_1 = _makeResponse(http_client.PARTIAL_CONTENT, info_1,
CONTENT[:CHUNK_SIZE])
info_2 = {'content-range': RESP_RANGE_2}
response_2 = _makeResponse(http_client.OK, info_2,
CONTENT[CHUNK_SIZE:])
requester = _MakeRequest(response_1, response_2)
with _Monkey(MUT,
Request=_Request,
make_api_request=requester):
download.get_range(0)
self.assertTrue(len(requester._requested), 2)
request_1 = requester._requested[0][0]
self.assertEqual(request_1.headers, {'range': REQ_RANGE_1})
request_2 = requester._requested[1][0]
self.assertEqual(request_2.headers, {'range': REQ_RANGE_2})
self.assertEqual(stream._written, [b'ABC', b'DE'])
self.assertEqual(download.total_size, LEN)
def test_stream_file_not_initialized(self):
from gcloud.streaming.exceptions import TransferInvalidError
download = self._makeOne(_Stream())
with self.assertRaises(TransferInvalidError):
download.stream_file()
def test_stream_file_w_initial_response_complete(self):
from six.moves import http_client
CONTENT = b'ABCDEFGHIJ'
LEN = len(CONTENT)
RESP_RANGE = 'bytes 0-%d/%d' % (LEN - 1, LEN,)
stream = _Stream()
download = self._makeOne(stream, total_size=LEN)
info = {'content-range': RESP_RANGE}
download._initial_response = _makeResponse(
http_client.OK, info, CONTENT)
http = object()
download._initialize(http, _Request.URL)
download.stream_file()
self.assertEqual(stream._written, [CONTENT])
self.assertEqual(download.total_size, LEN)
def test_stream_file_w_initial_response_incomplete(self):
from six.moves import http_client
from gcloud._testing import _Monkey
from gcloud.streaming import transfer as MUT
CHUNK_SIZE = 3
CONTENT = b'ABCDEF'
LEN = len(CONTENT)
RESP_RANGE_1 = 'bytes 0-%d/%d' % (CHUNK_SIZE - 1, LEN,)
REQ_RANGE_2 = 'bytes=%d-%d' % (CHUNK_SIZE, LEN - 1)
RESP_RANGE_2 = 'bytes %d-%d/%d' % (CHUNK_SIZE, LEN - 1, LEN,)
stream = _Stream()
http = object()
download = self._makeOne(stream, chunksize=CHUNK_SIZE)
info_1 = {'content-range': RESP_RANGE_1}
download._initial_response = _makeResponse(
http_client.PARTIAL_CONTENT, info_1, CONTENT[:CHUNK_SIZE])
info_2 = {'content-range': RESP_RANGE_2}
response_2 = _makeResponse(
http_client.OK, info_2, CONTENT[CHUNK_SIZE:])
requester = _MakeRequest(response_2)
download._initialize(http, _Request.URL)
request = _Request()
with _Monkey(MUT,
Request=_Request,
make_api_request=requester):
download.stream_file()
self.assertTrue(len(requester._requested), 1)
request = requester._requested[0][0]
self.assertEqual(request.headers, {'range': REQ_RANGE_2})
self.assertEqual(stream._written,
[CONTENT[:CHUNK_SIZE], CONTENT[CHUNK_SIZE:]])
self.assertEqual(download.total_size, LEN)
def test_stream_file_wo_initial_response_wo_total_size(self):
from six.moves import http_client
from gcloud._testing import _Monkey
from gcloud.streaming import transfer as MUT
CONTENT = b'ABCDEFGHIJ'
LEN = len(CONTENT)
CHUNK_SIZE = 123
REQ_RANGE = 'bytes=0-%d' % (CHUNK_SIZE - 1)
RESP_RANGE = 'bytes 0-%d/%d' % (LEN - 1, LEN,)
stream = _Stream()
http = object()
download = self._makeOne(stream, chunksize=CHUNK_SIZE)
info = {'content-range': RESP_RANGE}
response = _makeResponse(http_client.OK, info, CONTENT)
requester = _MakeRequest(response)
download._initialize(http, _Request.URL)
request = _Request()
with _Monkey(MUT,
Request=_Request,
make_api_request=requester):
download.stream_file()
self.assertTrue(len(requester._requested), 1)
request = requester._requested[0][0]
self.assertEqual(request.headers, {'range': REQ_RANGE})
self.assertEqual(stream._written, [CONTENT])
self.assertEqual(download.total_size, LEN)
class Test_Upload(unittest2.TestCase):
URL = "http://example.com/api"
MIME_TYPE = 'application/octet-stream'
UPLOAD_URL = 'http://example.com/upload/id=foobar'
def _getTargetClass(self):
from gcloud.streaming.transfer import Upload
return Upload
def _makeOne(self, stream, mime_type=MIME_TYPE, *args, **kw):
return self._getTargetClass()(stream, mime_type, *args, **kw)
def test_ctor_defaults(self):
from gcloud.streaming.transfer import _DEFAULT_CHUNKSIZE
stream = _Stream()
upload = self._makeOne(stream)
self.assertTrue(upload.stream is stream)
self.assertTrue(upload._final_response is None)
self.assertTrue(upload._server_chunk_granularity is None)
self.assertFalse(upload.complete)
self.assertEqual(upload.mime_type, self.MIME_TYPE)
self.assertEqual(upload.progress, 0)
self.assertTrue(upload.strategy is None)
self.assertTrue(upload.total_size is None)
self.assertEqual(upload.chunksize, _DEFAULT_CHUNKSIZE)
def test_ctor_w_kwds(self):
stream = _Stream()
CHUNK_SIZE = 123
upload = self._makeOne(stream, chunksize=CHUNK_SIZE)
self.assertTrue(upload.stream is stream)
self.assertEqual(upload.mime_type, self.MIME_TYPE)
self.assertEqual(upload.chunksize, CHUNK_SIZE)
def test_from_file_w_nonesuch_file(self):
klass = self._getTargetClass()
filename = '~nosuchuser/file.txt'
with self.assertRaises(OSError):
klass.from_file(filename)
def test_from_file_wo_mimetype_w_unguessable_filename(self):
import os
klass = self._getTargetClass()
CONTENT = b'EXISTING FILE W/ UNGUESSABLE MIMETYPE'
with _tempdir() as tempdir:
filename = os.path.join(tempdir, 'file.unguessable')
with open(filename, 'wb') as fileobj:
fileobj.write(CONTENT)
with self.assertRaises(ValueError):
klass.from_file(filename)
def test_from_file_wo_mimetype_w_guessable_filename(self):
import os
klass = self._getTargetClass()
CONTENT = b'EXISTING FILE W/ GUESSABLE MIMETYPE'
with _tempdir() as tempdir:
filename = os.path.join(tempdir, 'file.txt')
with open(filename, 'wb') as fileobj:
fileobj.write(CONTENT)
upload = klass.from_file(filename)
self.assertEqual(upload.mime_type, 'text/plain')
self.assertTrue(upload.auto_transfer)
self.assertEqual(upload.total_size, len(CONTENT))
upload._stream.close()
def test_from_file_w_mimetype_w_auto_transfer_w_kwds(self):
import os
klass = self._getTargetClass()
CONTENT = b'EXISTING FILE W/ GUESSABLE MIMETYPE'
CHUNK_SIZE = 3
with _tempdir() as tempdir:
filename = os.path.join(tempdir, 'file.unguessable')
with open(filename, 'wb') as fileobj:
fileobj.write(CONTENT)
upload = klass.from_file(
filename,
mime_type=self.MIME_TYPE,
auto_transfer=False,
chunksize=CHUNK_SIZE)
self.assertEqual(upload.mime_type, self.MIME_TYPE)
self.assertFalse(upload.auto_transfer)
self.assertEqual(upload.total_size, len(CONTENT))
self.assertEqual(upload.chunksize, CHUNK_SIZE)
upload._stream.close()
def test_from_stream_wo_mimetype(self):
klass = self._getTargetClass()
stream = _Stream()
with self.assertRaises(ValueError):
klass.from_stream(stream, mime_type=None)
def test_from_stream_defaults(self):
klass = self._getTargetClass()
stream = _Stream()
upload = klass.from_stream(stream, mime_type=self.MIME_TYPE)
self.assertEqual(upload.mime_type, self.MIME_TYPE)
self.assertTrue(upload.auto_transfer)
self.assertEqual(upload.total_size, None)
def test_from_stream_explicit(self):
klass = self._getTargetClass()
stream = _Stream()
SIZE = 10
CHUNK_SIZE = 3
upload = klass.from_stream(
stream,
mime_type=self.MIME_TYPE,
auto_transfer=False,
total_size=SIZE,
chunksize=CHUNK_SIZE)
self.assertEqual(upload.mime_type, self.MIME_TYPE)
self.assertFalse(upload.auto_transfer)
self.assertEqual(upload.total_size, SIZE)
self.assertEqual(upload.chunksize, CHUNK_SIZE)
def test_strategy_setter_invalid(self):
upload = self._makeOne(_Stream())
with self.assertRaises(ValueError):
upload.strategy = object()
with self.assertRaises(ValueError):
upload.strategy = 'unknown'
def test_strategy_setter_SIMPLE_UPLOAD(self):
from gcloud.streaming.transfer import SIMPLE_UPLOAD
upload = self._makeOne(_Stream())
upload.strategy = SIMPLE_UPLOAD
self.assertEqual(upload.strategy, SIMPLE_UPLOAD)
def test_strategy_setter_RESUMABLE_UPLOAD(self):
from gcloud.streaming.transfer import RESUMABLE_UPLOAD
upload = self._makeOne(_Stream())
upload.strategy = RESUMABLE_UPLOAD
self.assertEqual(upload.strategy, RESUMABLE_UPLOAD)
def test_total_size_setter_initialized(self):
from gcloud.streaming.exceptions import TransferInvalidError
SIZE = 123
upload = self._makeOne(_Stream)
http = object()
upload._initialize(http, _Request.URL)
with self.assertRaises(TransferInvalidError):
upload.total_size = SIZE
def test_total_size_setter_not_initialized(self):
SIZE = 123
upload = self._makeOne(_Stream)
upload.total_size = SIZE
self.assertEqual(upload.total_size, SIZE)
def test__set_default_strategy_w_existing_strategy(self):
from gcloud.streaming.transfer import RESUMABLE_UPLOAD
config = _Dummy(
resumable_path='/resumable/endpoint',
simple_multipart=True,
simple_path='/upload/endpoint',
)
request = _Request()
upload = self._makeOne(_Stream)
upload.strategy = RESUMABLE_UPLOAD
upload._set_default_strategy(config, request)
self.assertEqual(upload.strategy, RESUMABLE_UPLOAD)
def test__set_default_strategy_wo_resumable_path(self):
from gcloud.streaming.transfer import SIMPLE_UPLOAD
config = _Dummy(
resumable_path=None,
simple_multipart=True,
simple_path='/upload/endpoint',
)
request = _Request()
upload = self._makeOne(_Stream())
upload._set_default_strategy(config, request)
self.assertEqual(upload.strategy, SIMPLE_UPLOAD)
def test__set_default_strategy_w_total_size_gt_threshhold(self):
from gcloud.streaming.transfer import RESUMABLE_UPLOAD_THRESHOLD
from gcloud.streaming.transfer import RESUMABLE_UPLOAD
config = _UploadConfig()
request = _Request()
upload = self._makeOne(
_Stream(), total_size=RESUMABLE_UPLOAD_THRESHOLD + 1)
upload._set_default_strategy(config, request)
self.assertEqual(upload.strategy, RESUMABLE_UPLOAD)
def test__set_default_strategy_w_body_wo_multipart(self):
from gcloud.streaming.transfer import RESUMABLE_UPLOAD
CONTENT = b'ABCDEFGHIJ'
config = _UploadConfig()
config.simple_multipart = False
request = _Request(body=CONTENT)
upload = self._makeOne(_Stream(), total_size=len(CONTENT))
upload._set_default_strategy(config, request)
self.assertEqual(upload.strategy, RESUMABLE_UPLOAD)
def test__set_default_strategy_w_body_w_multipart_wo_simple_path(self):
from gcloud.streaming.transfer import RESUMABLE_UPLOAD
CONTENT = b'ABCDEFGHIJ'
config = _UploadConfig()
config.simple_path = None
request = _Request(body=CONTENT)
upload = self._makeOne(_Stream(), total_size=len(CONTENT))
upload._set_default_strategy(config, request)
self.assertEqual(upload.strategy, RESUMABLE_UPLOAD)
def test__set_default_strategy_w_body_w_multipart_w_simple_path(self):
from gcloud.streaming.transfer import SIMPLE_UPLOAD
CONTENT = b'ABCDEFGHIJ'
config = _UploadConfig()
request = _Request(body=CONTENT)
upload = self._makeOne(_Stream(), total_size=len(CONTENT))
upload._set_default_strategy(config, request)
self.assertEqual(upload.strategy, SIMPLE_UPLOAD)
def test_configure_request_w_total_size_gt_max_size(self):
MAX_SIZE = 1000
config = _UploadConfig()
config.max_size = MAX_SIZE
request = _Request()
url_builder = _Dummy()
upload = self._makeOne(_Stream(), total_size=MAX_SIZE + 1)
with self.assertRaises(ValueError):
upload.configure_request(config, request, url_builder)
def test_configure_request_w_invalid_mimetype(self):
config = _UploadConfig()
config.accept = ('text/*',)
request = _Request()
url_builder = _Dummy()
upload = self._makeOne(_Stream())
with self.assertRaises(ValueError):
upload.configure_request(config, request, url_builder)
def test_configure_request_w_simple_wo_body(self):
from gcloud.streaming.transfer import SIMPLE_UPLOAD
CONTENT = b'CONTENT'
config = _UploadConfig()
request = _Request()
url_builder = _Dummy(query_params={})
upload = self._makeOne(_Stream(CONTENT))
upload.strategy = SIMPLE_UPLOAD
upload.configure_request(config, request, url_builder)
self.assertEqual(url_builder.query_params, {'uploadType': 'media'})
self.assertEqual(url_builder.relative_path, config.simple_path)
self.assertEqual(request.headers, {'content-type': self.MIME_TYPE})
self.assertEqual(request.body, CONTENT)
self.assertEqual(request.loggable_body, '<media body>')
def test_configure_request_w_simple_w_body(self):
from gcloud._helpers import _to_bytes
from gcloud.streaming.transfer import SIMPLE_UPLOAD
CONTENT = b'CONTENT'
BODY = b'BODY'
config = _UploadConfig()
request = _Request(body=BODY)
request.headers['content-type'] = 'text/plain'
url_builder = _Dummy(query_params={})
upload = self._makeOne(_Stream(CONTENT))
upload.strategy = SIMPLE_UPLOAD
upload.configure_request(config, request, url_builder)
self.assertEqual(url_builder.query_params, {'uploadType': 'multipart'})
self.assertEqual(url_builder.relative_path, config.simple_path)
self.assertEqual(list(request.headers), ['content-type'])
ctype, boundary = [x.strip()
for x in request.headers['content-type'].split(';')]
self.assertEqual(ctype, 'multipart/related')
self.assertTrue(boundary.startswith('boundary="=='))
self.assertTrue(boundary.endswith('=="'))
divider = b'--' + _to_bytes(boundary[len('boundary="'):-1])
chunks = request.body.split(divider)[1:-1] # discard prolog / epilog
self.assertEqual(len(chunks), 2)
parse_chunk = _email_chunk_parser()
text_msg = parse_chunk(chunks[0].strip())
self.assertEqual(dict(text_msg._headers),
{'Content-Type': 'text/plain',
'MIME-Version': '1.0'})
self.assertEqual(text_msg._payload, BODY.decode('ascii'))
app_msg = parse_chunk(chunks[1].strip())
self.assertEqual(dict(app_msg._headers),
{'Content-Type': self.MIME_TYPE,
'Content-Transfer-Encoding': 'binary',
'MIME-Version': '1.0'})
self.assertEqual(app_msg._payload, CONTENT.decode('ascii'))
self.assertTrue(b'<media body>' in request.loggable_body)
def test_configure_request_w_resumable_wo_total_size(self):
from gcloud.streaming.transfer import RESUMABLE_UPLOAD
CONTENT = b'CONTENT'
config = _UploadConfig()
request = _Request()
url_builder = _Dummy(query_params={})
upload = self._makeOne(_Stream(CONTENT))
upload.strategy = RESUMABLE_UPLOAD
upload.configure_request(config, request, url_builder)
self.assertEqual(url_builder.query_params, {'uploadType': 'resumable'})
self.assertEqual(url_builder.relative_path, config.resumable_path)
self.assertEqual(request.headers,
{'X-Upload-Content-Type': self.MIME_TYPE})
def test_configure_request_w_resumable_w_total_size(self):
from gcloud.streaming.transfer import RESUMABLE_UPLOAD
CONTENT = b'CONTENT'
LEN = len(CONTENT)
config = _UploadConfig()
request = _Request()
url_builder = _Dummy(query_params={})
upload = self._makeOne(_Stream(CONTENT))
upload.total_size = LEN
upload.strategy = RESUMABLE_UPLOAD
upload.configure_request(config, request, url_builder)
self.assertEqual(url_builder.query_params, {'uploadType': 'resumable'})
self.assertEqual(url_builder.relative_path, config.resumable_path)
self.assertEqual(request.headers,
{'X-Upload-Content-Type': self.MIME_TYPE,
'X-Upload-Content-Length': '%d' % (LEN,)})
def test_refresh_upload_state_w_simple_strategy(self):
from gcloud.streaming.transfer import SIMPLE_UPLOAD
upload = self._makeOne(_Stream())
upload.strategy = SIMPLE_UPLOAD
upload.refresh_upload_state() # no-op
def test_refresh_upload_state_not_initialized(self):
from gcloud.streaming.exceptions import TransferInvalidError
from gcloud.streaming.transfer import RESUMABLE_UPLOAD
upload = self._makeOne(_Stream())
upload.strategy = RESUMABLE_UPLOAD
with self.assertRaises(TransferInvalidError):
upload.refresh_upload_state()
def test_refresh_upload_state_w_OK(self):
from six.moves import http_client
from gcloud._testing import _Monkey
from gcloud.streaming import transfer as MUT
from gcloud.streaming.transfer import RESUMABLE_UPLOAD
CONTENT = b'ABCDEFGHIJ'
LEN = len(CONTENT)
RESP_RANGE = 'bytes 0-%d/%d' % (LEN - 1, LEN,)
http = object()
stream = _Stream()
upload = self._makeOne(stream, total_size=LEN)
upload.strategy = RESUMABLE_UPLOAD
upload._initialize(http, _Request.URL)
info = {'content-range': RESP_RANGE}
response = _makeResponse(http_client.OK, info, CONTENT)
requester = _MakeRequest(response)
with _Monkey(MUT,
Request=_Request,
make_api_request=requester):
upload.refresh_upload_state()
self.assertTrue(upload.complete)
self.assertEqual(upload.progress, LEN)
self.assertEqual(stream.tell(), LEN)
self.assertTrue(upload._final_response is response)
def test_refresh_upload_state_w_CREATED(self):
from six.moves import http_client
from gcloud._testing import _Monkey
from gcloud.streaming import transfer as MUT
from gcloud.streaming.transfer import RESUMABLE_UPLOAD
CONTENT = b'ABCDEFGHIJ'
LEN = len(CONTENT)
RESP_RANGE = 'bytes 0-%d/%d' % (LEN - 1, LEN,)
http = object()
stream = _Stream()
upload = self._makeOne(stream, total_size=LEN)
upload.strategy = RESUMABLE_UPLOAD
upload._initialize(http, _Request.URL)
info = {'content-range': RESP_RANGE}
response = _makeResponse(http_client.CREATED, info, CONTENT)
requester = _MakeRequest(response)
with _Monkey(MUT,
Request=_Request,
make_api_request=requester):
upload.refresh_upload_state()
self.assertTrue(upload.complete)
self.assertEqual(upload.progress, LEN)
self.assertEqual(stream.tell(), LEN)
self.assertTrue(upload._final_response is response)
def test_refresh_upload_state_w_RESUME_INCOMPLETE_w_range(self):
from gcloud.streaming import transfer as MUT
from gcloud.streaming.http_wrapper import RESUME_INCOMPLETE
from gcloud._testing import _Monkey
from gcloud.streaming.transfer import RESUMABLE_UPLOAD
CONTENT = b'ABCDEFGHIJ'
LEN = len(CONTENT)
LAST = 5
http = object()
stream = _Stream()
upload = self._makeOne(stream, total_size=LEN)
upload.strategy = RESUMABLE_UPLOAD
upload._initialize(http, _Request.URL)
info = {'range': '0-%d' % (LAST - 1,)}
response = _makeResponse(RESUME_INCOMPLETE, info, CONTENT)
requester = _MakeRequest(response)
with _Monkey(MUT,
Request=_Request,
make_api_request=requester):
upload.refresh_upload_state()
self.assertFalse(upload.complete)
self.assertEqual(upload.progress, LAST)
self.assertEqual(stream.tell(), LAST)
self.assertFalse(upload._final_response is response)
def test_refresh_upload_state_w_RESUME_INCOMPLETE_wo_range(self):
from gcloud.streaming import transfer as MUT
from gcloud.streaming.http_wrapper import RESUME_INCOMPLETE
from gcloud._testing import _Monkey
from gcloud.streaming.transfer import RESUMABLE_UPLOAD
CONTENT = b'ABCDEFGHIJ'
LEN = len(CONTENT)
http = object()
stream = _Stream()
upload = self._makeOne(stream, total_size=LEN)
upload.strategy = RESUMABLE_UPLOAD
upload._initialize(http, _Request.URL)
response = _makeResponse(RESUME_INCOMPLETE, content=CONTENT)
requester = _MakeRequest(response)
with _Monkey(MUT,
Request=_Request,
make_api_request=requester):
upload.refresh_upload_state()
self.assertFalse(upload.complete)
self.assertEqual(upload.progress, 0)
self.assertEqual(stream.tell(), 0)
self.assertFalse(upload._final_response is response)
def test_refresh_upload_state_w_error(self):
from six.moves import http_client
from gcloud._testing import _Monkey
from gcloud.streaming import transfer as MUT
from gcloud.streaming.exceptions import HttpError
from gcloud.streaming.transfer import RESUMABLE_UPLOAD
CONTENT = b'ABCDEFGHIJ'
LEN = len(CONTENT)
http = object()
stream = _Stream()
upload = self._makeOne(stream, total_size=LEN)
upload.strategy = RESUMABLE_UPLOAD
upload._initialize(http, _Request.URL)
response = _makeResponse(http_client.FORBIDDEN)
requester = _MakeRequest(response)
with _Monkey(MUT,
Request=_Request,
make_api_request=requester):
with self.assertRaises(HttpError):
upload.refresh_upload_state()
def test__get_range_header_miss(self):
upload = self._makeOne(_Stream())
response = _makeResponse(None)
self.assertTrue(upload._get_range_header(response) is None)
def test__get_range_header_w_Range(self):
upload = self._makeOne(_Stream())
response = _makeResponse(None, {'Range': '123'})
self.assertEqual(upload._get_range_header(response), '123')
def test__get_range_header_w_range(self):
upload = self._makeOne(_Stream())
response = _makeResponse(None, {'range': '123'})
self.assertEqual(upload._get_range_header(response), '123')
def test_initialize_upload_no_strategy(self):
request = _Request()
upload = self._makeOne(_Stream())
with self.assertRaises(ValueError):
upload.initialize_upload(request, http=object())
def test_initialize_upload_simple_w_http(self):
from gcloud.streaming.transfer import SIMPLE_UPLOAD
request = _Request()
upload = self._makeOne(_Stream())
upload.strategy = SIMPLE_UPLOAD
upload.initialize_upload(request, http=object()) # no-op
def test_initialize_upload_resumable_already_initialized(self):
from gcloud.streaming.exceptions import TransferInvalidError
from gcloud.streaming.transfer import RESUMABLE_UPLOAD
request = _Request()
upload = self._makeOne(_Stream())
upload.strategy = RESUMABLE_UPLOAD
upload._initialize(None, self.URL)
with self.assertRaises(TransferInvalidError):
upload.initialize_upload(request, http=object())
def test_initialize_upload_w_http_resumable_not_initialized_w_error(self):
from six.moves import http_client
from gcloud._testing import _Monkey
from gcloud.streaming import transfer as MUT
from gcloud.streaming.exceptions import HttpError
from gcloud.streaming.transfer import RESUMABLE_UPLOAD
request = _Request()
upload = self._makeOne(_Stream())
upload.strategy = RESUMABLE_UPLOAD
response = _makeResponse(http_client.FORBIDDEN)
requester = _MakeRequest(response)
with _Monkey(MUT, make_api_request=requester):
with self.assertRaises(HttpError):
upload.initialize_upload(request, http=object())
def test_initialize_upload_w_http_wo_auto_transfer_w_OK(self):
from six.moves import http_client
from gcloud._testing import _Monkey
from gcloud.streaming import transfer as MUT
from gcloud.streaming.transfer import RESUMABLE_UPLOAD
request = _Request()
upload = self._makeOne(_Stream(), auto_transfer=False)
upload.strategy = RESUMABLE_UPLOAD
info = {'location': self.UPLOAD_URL}
response = _makeResponse(http_client.OK, info)
requester = _MakeRequest(response)
with _Monkey(MUT, make_api_request=requester):
upload.initialize_upload(request, http=object())
self.assertEqual(upload._server_chunk_granularity, None)
self.assertEqual(upload.url, self.UPLOAD_URL)
self.assertEqual(requester._responses, [])
self.assertEqual(len(requester._requested), 1)
self.assertTrue(requester._requested[0][0] is request)
def test_initialize_upload_w_granularity_w_auto_transfer_w_OK(self):
from six.moves import http_client
from gcloud._testing import _Monkey
from gcloud.streaming import transfer as MUT
from gcloud.streaming.transfer import RESUMABLE_UPLOAD
CONTENT = b'ABCDEFGHIJ'
http = object()
request = _Request()
upload = self._makeOne(_Stream(CONTENT), chunksize=1000)
upload.strategy = RESUMABLE_UPLOAD
info = {'X-Goog-Upload-Chunk-Granularity': '100',
'location': self.UPLOAD_URL}
response = _makeResponse(http_client.OK, info)
chunk_response = _makeResponse(http_client.OK)
requester = _MakeRequest(response, chunk_response)
with _Monkey(MUT,
Request=_Request,
make_api_request=requester):
upload.initialize_upload(request, http)
self.assertEqual(upload._server_chunk_granularity, 100)
self.assertEqual(upload.url, self.UPLOAD_URL)
self.assertEqual(requester._responses, [])
self.assertEqual(len(requester._requested), 2)
self.assertTrue(requester._requested[0][0] is request)
chunk_request = requester._requested[1][0]
self.assertTrue(isinstance(chunk_request, _Request))
self.assertEqual(chunk_request.url, self.UPLOAD_URL)
self.assertEqual(chunk_request.http_method, 'PUT')
self.assertEqual(chunk_request.body, CONTENT)
def test__last_byte(self):
upload = self._makeOne(_Stream())
self.assertEqual(upload._last_byte('123-456'), 456)
def test__validate_chunksize_wo__server_chunk_granularity(self):
upload = self._makeOne(_Stream())
upload._validate_chunksize(123) # no-op
def test__validate_chunksize_w__server_chunk_granularity_miss(self):
upload = self._makeOne(_Stream())
upload._server_chunk_granularity = 100
with self.assertRaises(ValueError):
upload._validate_chunksize(123)
def test__validate_chunksize_w__server_chunk_granularity_hit(self):
upload = self._makeOne(_Stream())
upload._server_chunk_granularity = 100
upload._validate_chunksize(400)
def test_stream_file_w_simple_strategy(self):
from gcloud.streaming.transfer import SIMPLE_UPLOAD
upload = self._makeOne(_Stream())
upload.strategy = SIMPLE_UPLOAD
with self.assertRaises(ValueError):
upload.stream_file()
def test_stream_file_w_use_chunks_invalid_chunk_size(self):
from gcloud.streaming.transfer import RESUMABLE_UPLOAD
upload = self._makeOne(_Stream(), chunksize=1024)
upload.strategy = RESUMABLE_UPLOAD
upload._server_chunk_granularity = 100
with self.assertRaises(ValueError):
upload.stream_file(use_chunks=True)
def test_stream_file_not_initialized(self):
from gcloud.streaming.exceptions import TransferInvalidError
from gcloud.streaming.transfer import RESUMABLE_UPLOAD
upload = self._makeOne(_Stream(), chunksize=1024)
upload.strategy = RESUMABLE_UPLOAD
upload._server_chunk_granularity = 128
with self.assertRaises(TransferInvalidError):
upload.stream_file()
def test_stream_file_already_complete_w_unseekable_stream(self):
from gcloud.streaming.transfer import RESUMABLE_UPLOAD
http = object()
stream = object()
response = object()
upload = self._makeOne(stream, chunksize=1024)
upload.strategy = RESUMABLE_UPLOAD
upload._server_chunk_granularity = 128
upload._initialize(http, _Request.URL)
upload._final_response = response
upload._complete = True
self.assertTrue(upload.stream_file() is response)
def test_stream_file_already_complete_w_seekable_stream_unsynced(self):
from gcloud.streaming.exceptions import CommunicationError
from gcloud.streaming.transfer import RESUMABLE_UPLOAD
CONTENT = b'ABCDEFGHIJ'
http = object()
stream = _Stream(CONTENT)
response = object()
upload = self._makeOne(stream, chunksize=1024)
upload.strategy = RESUMABLE_UPLOAD
upload._server_chunk_granularity = 128
upload._initialize(http, _Request.URL)
upload._final_response = response
upload._complete = True
with self.assertRaises(CommunicationError):
upload.stream_file()
def test_stream_file_already_complete_w_seekable_stream_synced(self):
import os
from gcloud.streaming.transfer import RESUMABLE_UPLOAD
CONTENT = b'ABCDEFGHIJ'
http = object()
stream = _Stream(CONTENT)
stream.seek(0, os.SEEK_END)
response = object()
upload = self._makeOne(stream, chunksize=1024)
upload.strategy = RESUMABLE_UPLOAD
upload._server_chunk_granularity = 128
upload._initialize(http, _Request.URL)
upload._final_response = response
upload._complete = True
self.assertTrue(upload.stream_file(use_chunks=False) is response)
def test_stream_file_incomplete(self):
from six.moves import http_client
from gcloud._testing import _Monkey
from gcloud.streaming import transfer as MUT
from gcloud.streaming.http_wrapper import RESUME_INCOMPLETE
from gcloud.streaming.transfer import RESUMABLE_UPLOAD
CONTENT = b'ABCDEFGHIJ'
http = object()
stream = _Stream(CONTENT)
upload = self._makeOne(stream, chunksize=6)
upload.strategy = RESUMABLE_UPLOAD
upload._server_chunk_granularity = 6
upload._initialize(http, self.UPLOAD_URL)
info_1 = {'content-length': '0', 'range': 'bytes=0-5'}
response_1 = _makeResponse(RESUME_INCOMPLETE, info_1)
info_2 = {'content-length': '0', 'range': 'bytes=6-9'}
response_2 = _makeResponse(http_client.OK, info_2)
requester = _MakeRequest(response_1, response_2)
with _Monkey(MUT,
Request=_Request,
make_api_request=requester):
response = upload.stream_file()
self.assertTrue(response is response_2)
self.assertEqual(len(requester._responses), 0)
self.assertEqual(len(requester._requested), 2)
request_1 = requester._requested[0][0]
self.assertEqual(request_1.url, self.UPLOAD_URL)
self.assertEqual(request_1.http_method, 'PUT')
self.assertEqual(request_1.headers,
{'Content-Range': 'bytes 0-5/*',
'Content-Type': self.MIME_TYPE})
self.assertEqual(request_1.body, CONTENT[:6])
request_2 = requester._requested[1][0]
self.assertEqual(request_2.url, self.UPLOAD_URL)
self.assertEqual(request_2.http_method, 'PUT')
self.assertEqual(request_2.headers,
{'Content-Range': 'bytes 6-9/10',
'Content-Type': self.MIME_TYPE})
self.assertEqual(request_2.body, CONTENT[6:])
def test_stream_file_incomplete_w_transfer_error(self):
from gcloud._testing import _Monkey
from gcloud.streaming import transfer as MUT
from gcloud.streaming.exceptions import CommunicationError
from gcloud.streaming.http_wrapper import RESUME_INCOMPLETE
from gcloud.streaming.transfer import RESUMABLE_UPLOAD
CONTENT = b'ABCDEFGHIJ'
http = object()
stream = _Stream(CONTENT)
upload = self._makeOne(stream, chunksize=6)
upload.strategy = RESUMABLE_UPLOAD
upload._server_chunk_granularity = 6
upload._initialize(http, self.UPLOAD_URL)
info = {
'content-length': '0',
'range': 'bytes=0-4', # simulate error, s.b. '0-5'
}
response = _makeResponse(RESUME_INCOMPLETE, info)
requester = _MakeRequest(response)
with _Monkey(MUT,
Request=_Request,
make_api_request=requester):
with self.assertRaises(CommunicationError):
upload.stream_file()
self.assertEqual(len(requester._responses), 0)
self.assertEqual(len(requester._requested), 1)
request = requester._requested[0][0]
self.assertEqual(request.url, self.UPLOAD_URL)
self.assertEqual(request.http_method, 'PUT')
self.assertEqual(request.headers,
{'Content-Range': 'bytes 0-5/*',
'Content-Type': self.MIME_TYPE})
self.assertEqual(request.body, CONTENT[:6])
def test__send_media_request_wo_error(self):
from gcloud._testing import _Monkey
from gcloud.streaming import transfer as MUT
from gcloud.streaming.http_wrapper import RESUME_INCOMPLETE
CONTENT = b'ABCDEFGHIJ'
bytes_http = object()
stream = _Stream(CONTENT)
upload = self._makeOne(stream)
upload.bytes_http = bytes_http
headers = {'Content-Range': 'bytes 0-9/10',
'Content-Type': self.MIME_TYPE}
request = _Request(self.UPLOAD_URL, 'PUT', CONTENT, headers)
info = {'content-length': '0', 'range': 'bytes=0-4'}
response = _makeResponse(RESUME_INCOMPLETE, info)
requester = _MakeRequest(response)
with _Monkey(MUT, make_api_request=requester):
upload._send_media_request(request, 9)
self.assertEqual(len(requester._responses), 0)
self.assertEqual(len(requester._requested), 1)
used_request, used_http, _ = requester._requested[0]
self.assertTrue(used_request is request)
self.assertTrue(used_http is bytes_http)
self.assertEqual(stream.tell(), 4)
def test__send_media_request_w_error(self):
from six.moves import http_client
from gcloud._testing import _Monkey
from gcloud.streaming import transfer as MUT
from gcloud.streaming.exceptions import HttpError
from gcloud.streaming.http_wrapper import RESUME_INCOMPLETE
from gcloud.streaming.transfer import RESUMABLE_UPLOAD
CONTENT = b'ABCDEFGHIJ'
bytes_http = object()
http = object()
stream = _Stream(CONTENT)
upload = self._makeOne(stream)
upload.strategy = RESUMABLE_UPLOAD
upload._initialize(http, self.UPLOAD_URL)
upload.bytes_http = bytes_http
headers = {'Content-Range': 'bytes 0-9/10',
'Content-Type': self.MIME_TYPE}
request = _Request(self.UPLOAD_URL, 'PUT', CONTENT, headers)
info_1 = {'content-length': '0', 'range': 'bytes=0-4'}
response_1 = _makeResponse(http_client.FORBIDDEN, info_1)
info_2 = {'Content-Length': '0', 'Range': 'bytes=0-4'}
response_2 = _makeResponse(RESUME_INCOMPLETE, info_2)
requester = _MakeRequest(response_1, response_2)
with _Monkey(MUT, Request=_Request, make_api_request=requester):
with self.assertRaises(HttpError):
upload._send_media_request(request, 9)
self.assertEqual(len(requester._responses), 0)
self.assertEqual(len(requester._requested), 2)
first_request, first_http, _ = requester._requested[0]
self.assertTrue(first_request is request)
self.assertTrue(first_http is bytes_http)
second_request, second_http, _ = requester._requested[1]
self.assertEqual(second_request.url, self.UPLOAD_URL)
self.assertEqual(second_request.http_method, 'PUT') # ACK!
self.assertEqual(second_request.headers,
{'Content-Range': 'bytes */*'})
self.assertTrue(second_http is http)
def test__send_media_body_not_initialized(self):
from gcloud.streaming.exceptions import TransferInvalidError
upload = self._makeOne(_Stream())
with self.assertRaises(TransferInvalidError):
upload._send_media_body(0)
def test__send_media_body_wo_total_size(self):
from gcloud.streaming.exceptions import TransferInvalidError
http = object()
upload = self._makeOne(_Stream())
upload._initialize(http, _Request.URL)
with self.assertRaises(TransferInvalidError):
upload._send_media_body(0)
def test__send_media_body_start_lt_total_size(self):
from gcloud.streaming.stream_slice import StreamSlice
SIZE = 1234
http = object()
stream = _Stream()
upload = self._makeOne(stream, total_size=SIZE)
upload._initialize(http, self.UPLOAD_URL)
response = object()
streamer = _MediaStreamer(response)
upload._send_media_request = streamer
found = upload._send_media_body(0)
self.assertTrue(found is response)
request, end = streamer._called_with
self.assertEqual(request.url, self.UPLOAD_URL)
self.assertEqual(request.http_method, 'PUT')
body_stream = request.body
self.assertTrue(isinstance(body_stream, StreamSlice))
self.assertTrue(body_stream._stream is stream)
self.assertEqual(len(body_stream), SIZE)
self.assertEqual(request.headers,
{'content-length': '%d' % (SIZE,), # speling!
'Content-Type': self.MIME_TYPE,
'Content-Range': 'bytes 0-%d/%d' % (SIZE - 1, SIZE)})
self.assertEqual(end, SIZE)
def test__send_media_body_start_eq_total_size(self):
from gcloud.streaming.stream_slice import StreamSlice
SIZE = 1234
http = object()
stream = _Stream()
upload = self._makeOne(stream, total_size=SIZE)
upload._initialize(http, self.UPLOAD_URL)
response = object()
streamer = _MediaStreamer(response)
upload._send_media_request = streamer
found = upload._send_media_body(SIZE)
self.assertTrue(found is response)
request, end = streamer._called_with
self.assertEqual(request.url, self.UPLOAD_URL)
self.assertEqual(request.http_method, 'PUT')
body_stream = request.body
self.assertTrue(isinstance(body_stream, StreamSlice))
self.assertTrue(body_stream._stream is stream)
self.assertEqual(len(body_stream), 0)
self.assertEqual(request.headers,
{'content-length': '0', # speling!
'Content-Type': self.MIME_TYPE,
'Content-Range': 'bytes */%d' % (SIZE,)})
self.assertEqual(end, SIZE)
def test__send_chunk_not_initialized(self):
from gcloud.streaming.exceptions import TransferInvalidError
upload = self._makeOne(_Stream())
with self.assertRaises(TransferInvalidError):
upload._send_chunk(0)
def test__send_chunk_wo_total_size_stream_exhausted(self):
CONTENT = b'ABCDEFGHIJ'
SIZE = len(CONTENT)
http = object()
upload = self._makeOne(_Stream(CONTENT), chunksize=1000)
upload._initialize(http, self.UPLOAD_URL)
response = object()
streamer = _MediaStreamer(response)
upload._send_media_request = streamer
self.assertEqual(upload.total_size, None)
found = upload._send_chunk(0)
self.assertTrue(found is response)
self.assertEqual(upload.total_size, SIZE)
request, end = streamer._called_with
self.assertEqual(request.url, self.UPLOAD_URL)
self.assertEqual(request.http_method, 'PUT')
self.assertEqual(request.body, CONTENT)
self.assertEqual(request.headers,
{'content-length': '%d' % SIZE, # speling!
'Content-Type': self.MIME_TYPE,
'Content-Range': 'bytes 0-%d/%d' % (SIZE - 1, SIZE)})
self.assertEqual(end, SIZE)
def test__send_chunk_wo_total_size_stream_not_exhausted(self):
CONTENT = b'ABCDEFGHIJ'
SIZE = len(CONTENT)
CHUNK_SIZE = SIZE - 5
http = object()
upload = self._makeOne(_Stream(CONTENT), chunksize=CHUNK_SIZE)
upload._initialize(http, self.UPLOAD_URL)
response = object()
streamer = _MediaStreamer(response)
upload._send_media_request = streamer
self.assertEqual(upload.total_size, None)
found = upload._send_chunk(0)
self.assertTrue(found is response)
self.assertEqual(upload.total_size, None)
request, end = streamer._called_with
self.assertEqual(request.url, self.UPLOAD_URL)
self.assertEqual(request.http_method, 'PUT')
self.assertEqual(request.body, CONTENT[:CHUNK_SIZE])
expected_headers = {
'content-length': '%d' % CHUNK_SIZE, # speling!
'Content-Type': self.MIME_TYPE,
'Content-Range': 'bytes 0-%d/*' % (CHUNK_SIZE - 1,),
}
self.assertEqual(request.headers, expected_headers)
self.assertEqual(end, CHUNK_SIZE)
def test__send_chunk_w_total_size_stream_not_exhausted(self):
from gcloud.streaming.stream_slice import StreamSlice
CONTENT = b'ABCDEFGHIJ'
SIZE = len(CONTENT)
CHUNK_SIZE = SIZE - 5
http = object()
stream = _Stream(CONTENT)
upload = self._makeOne(stream, total_size=SIZE, chunksize=CHUNK_SIZE)
upload._initialize(http, self.UPLOAD_URL)
response = object()
streamer = _MediaStreamer(response)
upload._send_media_request = streamer
found = upload._send_chunk(0)
self.assertTrue(found is response)
request, end = streamer._called_with
self.assertEqual(request.url, self.UPLOAD_URL)
self.assertEqual(request.http_method, 'PUT')
body_stream = request.body
self.assertTrue(isinstance(body_stream, StreamSlice))
self.assertTrue(body_stream._stream is stream)
self.assertEqual(len(body_stream), CHUNK_SIZE)
expected_headers = {
'content-length': '%d' % CHUNK_SIZE, # speling!
'Content-Type': self.MIME_TYPE,
'Content-Range': 'bytes 0-%d/%d' % (CHUNK_SIZE - 1, SIZE),
}
self.assertEqual(request.headers, expected_headers)
self.assertEqual(end, CHUNK_SIZE)
def test__send_chunk_w_total_size_stream_exhausted(self):
from gcloud.streaming.stream_slice import StreamSlice
CONTENT = b'ABCDEFGHIJ'
SIZE = len(CONTENT)
CHUNK_SIZE = 1000
http = object()
stream = _Stream(CONTENT)
upload = self._makeOne(stream, total_size=SIZE, chunksize=CHUNK_SIZE)
upload._initialize(http, self.UPLOAD_URL)
response = object()
streamer = _MediaStreamer(response)
upload._send_media_request = streamer
found = upload._send_chunk(SIZE)
self.assertTrue(found is response)
request, end = streamer._called_with
self.assertEqual(request.url, self.UPLOAD_URL)
self.assertEqual(request.http_method, 'PUT')
body_stream = request.body
self.assertTrue(isinstance(body_stream, StreamSlice))
self.assertTrue(body_stream._stream is stream)
self.assertEqual(len(body_stream), 0)
self.assertEqual(request.headers,
{'content-length': '0', # speling!
'Content-Type': self.MIME_TYPE,
'Content-Range': 'bytes */%d' % (SIZE,)})
self.assertEqual(end, SIZE)
def _email_chunk_parser():
import six
if six.PY3: # pragma: NO COVER Python3
from email.parser import BytesParser
parser = BytesParser()
return parser.parsebytes
else:
from email.parser import Parser
parser = Parser()
return parser.parsestr
class _Dummy(object):
def __init__(self, **kw):
self.__dict__.update(kw)
class _UploadConfig(object):
accept = ('*/*',)
max_size = None
resumable_path = '/resumable/endpoint'
simple_multipart = True
simple_path = '/upload/endpoint'
class _Stream(object):
_closed = False
def __init__(self, to_read=b''):
import io
self._written = []
self._to_read = io.BytesIO(to_read)
def write(self, to_write):
self._written.append(to_write)
def seek(self, offset, whence=0):
self._to_read.seek(offset, whence)
def read(self, size=None):
if size is not None:
return self._to_read.read(size)
return self._to_read.read()
def tell(self):
return self._to_read.tell()
def close(self):
self._closed = True
class _Request(object):
__slots__ = ('url', 'http_method', 'body', 'headers', 'loggable_body')
URL = 'http://example.com/api'
def __init__(self, url=URL, http_method='GET', body='', headers=None):
self.url = url
self.http_method = http_method
self.body = self.loggable_body = body
if headers is None:
headers = {}
self.headers = headers
class _MakeRequest(object):
def __init__(self, *responses):
self._responses = list(responses)
self._requested = []
def __call__(self, http, request, **kw):
self._requested.append((request, http, kw))
return self._responses.pop(0)
def _makeResponse(status_code, info=None, content='',
request_url=_Request.URL):
if info is None:
info = {}
return _Dummy(status_code=status_code,
info=info,
content=content,
length=len(content),
request_url=request_url)
class _MediaStreamer(object):
_called_with = None
def __init__(self, response):
self._response = response
def __call__(self, request, end):
assert self._called_with is None
self._called_with = (request, end)
return self._response
def _tempdir_maker():
import contextlib
import shutil
import tempfile
@contextlib.contextmanager
def _tempdir_mgr():
temp_dir = tempfile.mkdtemp()
yield temp_dir
shutil.rmtree(temp_dir)
return _tempdir_mgr
_tempdir = _tempdir_maker()
del _tempdir_maker