def test_blob_read_triggers_further_loading( _make_read_lob_request, type_code, lob_header, bin_lob_data, lob_data, lob_data_empty ): """Test that reading beyond currently available data (> 1024 items) triggers a READLOB request""" return_value = lob_data[1024 : 1024 + 100] _ExpectedLobClass = lobs.LOB_TYPE_CODE_MAP[type_code] enc_return_value = return_value.encode(_ExpectedLobClass.encoding) if _ExpectedLobClass.encoding else return_value _make_read_lob_request.return_value = enc_return_value payload = io.BytesIO(lob_header + bin_lob_data) lob = lobs.from_payload(type_code, payload, None) lob_len = len(lob.data.getvalue()) assert lob._current_lob_length == lob_len assert repr(lob) == "<%s length: %d (currently loaded from hana: %d)>" % ( _ExpectedLobClass.__name__, lob.length, lob._current_lob_length, ) lob.read(lob_len + 100) # read 100 items (chars/bytes) more than available # Reading extra 100 items should have triggered _read_missing_lob_data_from_db(): _make_read_lob_request.assert_called_once_with(1024, 100) assert lob.getvalue() == lob_data[: 1024 + 100] assert lob._current_lob_length == lob_len + 100 assert repr(lob) == "<%s length: %d (currently loaded from hana: %d)>" % ( _ExpectedLobClass.__name__, lob.length, lob._current_lob_length, )
def test_parse_null_blob(): """Parse a BLOB which is NULL""" payload = io.BytesIO(NULL_BLOB_HEADER) lob = lobs.from_payload(type_codes.BLOB, payload, None) assert isinstance(lob, lobs.Blob) # check for correct instance assert lob.lob_header.lob_type == lob.lob_header.BLOB_TYPE assert lob.lob_header.options & lob.lob_header.LOB_OPTION_ISNULL
def test_blob_read_triggers_further_loading(_make_read_lob_request, type_code, lob_header, bin_lob_data, lob_data, lob_data_empty): """Test that reading beyond currently available data (> 1024 items) triggers a READLOB request""" return_value = lob_data[1024:1024 + 100] _ExpectedLobClass = lobs.LOB_TYPE_CODE_MAP[type_code] enc_return_value = return_value.encode( _ExpectedLobClass.encoding ) if _ExpectedLobClass.encoding else return_value _make_read_lob_request.return_value = enc_return_value payload = io.BytesIO(lob_header + bin_lob_data) lob = lobs.from_payload(type_code, payload, None) lob_len = len(lob.data.getvalue()) assert lob._current_lob_length == lob_len assert repr(lob) == '<%s length: %d (currently loaded from hana: %d)>' % \ (_ExpectedLobClass.__name__, lob.length, lob._current_lob_length) lob.read(lob_len + 100) # read 100 items (chars/bytes) more than available # Reading extra 100 items should have triggered _read_missing_lob_data_from_db(): _make_read_lob_request.assert_called_once_with(1024, 100) assert lob.getvalue() == lob_data[:1024 + 100] assert lob._current_lob_length == lob_len + 100 assert repr(lob) == '<%s length: %d (currently loaded from hana: %d)>' % \ (_ExpectedLobClass.__name__, lob.length, lob._current_lob_length)
def test_read_lob__str__method(): """Read/parse a LOB with given payload (data) and check ___str__ method""" payload = io.BytesIO(BLOB_HEADER + BLOB_DATA) lob = lobs.from_payload(type_codes.BLOB, payload, None) len = lob._lob_header.byte_length assert str(lob._lob_header) == "<ReadLobHeader type: 1, options 2 (data_included), charlength: %d, bytelength: " \ "%d, locator_id: '\\x00\\x00\\x00\\x00\\xb2\\xb9\\x04\\x00', chunklength: 1024>" % \ (len, len)
def test_blob_read_triggers_further_loading(_read_missing_lob_data_from_db): """Test that reading beyond currently available data (> 1024 items) triggers a READLOB request""" payload = io.BytesIO(BLOB_HEADER + BLOB_DATA) lob = lobs.from_payload(type_codes.BLOB, payload, None) lob_len = len(lob.data.getvalue()) lob.read(lob_len + 100) # read 100 items (chars/bytes) more than available # Reading extra 100 items should have triggered _read_missing_lob_data_from_db(): _read_missing_lob_data_from_db.assert_called_once_with(1024, 100)
def test_null_blob_io_functions(): """Test that io functionality (read/getvalue()/...) of NULL blob also behave""" payload = io.BytesIO(NULL_BLOB_HEADER) lob = lobs.from_payload(type_codes.BLOB, payload, None) assert lob.tell() is None assert lob.read(10) is None lob.seek(20) # is accepted, but ignored assert lob.tell() is None
def test_blob_seek_triggers_further_loading(_read_missing_lob_data_from_db): """Test that seeking beyond currently available data (> 1024 items) triggers a READLOB request""" payload = io.BytesIO(BLOB_HEADER + BLOB_DATA) lob = lobs.from_payload(type_codes.BLOB, payload, None) lob_len = len(lob.data.getvalue()) lob.seek(lob_len + 100) # seek to position 100 items (bytes/chars) after what is available # This should have triggered _read_missing_lob_data_from_db(). # Since seek() makes the assumption that the user wants to read data from the new position # another EXTRA_NUM_ITEMS_TO_READ_AFTER_SEEK are read in addition to the 100 beyond the current num items: _read_missing_lob_data_from_db.assert_called_once_with(1024, 100 + lobs.Lob.EXTRA_NUM_ITEMS_TO_READ_AFTER_SEEK)
def test_parse_blob(): """Parse a BLOB with 2000 random items (bytes/chars)""" payload = io.BytesIO(BLOB_HEADER + BLOB_DATA) lob = lobs.from_payload(type_codes.BLOB, payload, None) assert isinstance(lob, lobs.Blob) # check for correct instance assert lob.lob_header.lob_type == lob.lob_header.BLOB_TYPE assert lob.lob_header.options & lob.lob_header.LOB_OPTION_DATAINCLUDED assert lob.lob_header.char_length == len(BLOB_DATA) assert lob.lob_header.byte_length == len(BLOB_DATA) assert lob.lob_header.locator_id == BLOB_HEADER[20:28] assert lob.lob_header.chunk_length == 1024 # default length of lob data read initially assert lob.lob_header.total_lob_length == len(BLOB_DATA) assert lob.data.getvalue() == BLOB_DATA[:1024]
def test_blob_io_functions(type_code, lob_header, bin_lob_data, lob_data): """Test that io functionality (read/seek/getvalue()/...) works fine Stay below the 1024 item range when reading to avoid lazy loading of additional lob data from DB. This feature is tested in a separate unittest. """ payload = io.BytesIO(lob_header + bin_lob_data) lob = lobs.from_payload(type_code, payload, None) assert lob.tell() == 0 # should be at start of lob initially assert lob.read(10) == lob_data[:10] assert lob.tell() == 10 lob.seek(20) assert lob.tell() == 20 assert lob.read(10) == lob_data[20:30] assert lob.read(10) == lob_data[30:40] assert lob.tell() == 40
def test_blob_io_functions(): """Test that io functionality (read/seek/getvalue()/...) works fine Stay below the 1024 item range when reading to avoid loading of additional lob data from DB. This feature is tested in a separate unittest below. """ payload = io.BytesIO(BLOB_HEADER + BLOB_DATA) lob = lobs.from_payload(type_codes.BLOB, payload, None) assert lob.tell() == 0 # should be at start of lob initially assert lob.read(10) == BLOB_DATA[:10] assert lob.tell() == 10 lob.seek(20) assert lob.tell() == 20 assert lob.read(10) == BLOB_DATA[20:30] assert lob.read(10) == BLOB_DATA[30:40] assert lob.tell() == 40
def test_read_lob(type_code, lob_header, bin_lob_data, lob_data): """Read/parse a LOB with given payload (data)""" payload = io.BytesIO(lob_header + bin_lob_data) lob = lobs.from_payload(type_code, payload, None) _ExpectedLobClass = lobs.LOB_TYPE_CODE_MAP[type_code] assert isinstance(lob, _ExpectedLobClass) # check for correct instance assert lob._lob_header.lob_type in (0, lob._lob_header.LOB_TYPES[type_code]) assert lob._lob_header.options & lob._lob_header.LOB_OPTION_DATAINCLUDED assert lob._lob_header.char_length == len(lob_data) assert lob._lob_header.byte_length == len(bin_lob_data) assert lob._lob_header.locator_id == lob_header[20:28] # assert lob._lob_header.chunk_length == min(len(bin_lob_data), MAX_LOB_DATA_LENGTH) - chunklength can vary ... assert lob._lob_header.total_lob_length == lob.length == len(lob_data) assert lob._current_lob_length == len(lob.data.getvalue()) assert lob.data.getvalue() == lob_data[:1024] assert repr(lob) == '<%s length: %d (currently loaded from hana: %d)>' % \ (_ExpectedLobClass.__name__, lob.length, lob._current_lob_length)
def test_blob_seek_triggers_further_loading(_make_read_lob_request): """Test that seeking beyond currently available data (> 1024 items) triggers a READLOB request""" # Since the actual size of the blob is smaller than the look ahead we are planning for, calculate # the correct number of items to be read: num_items_to_read = min(100 + lobs.Lob.EXTRA_NUM_ITEMS_TO_READ_AFTER_SEEK, len(BLOB_DATA) - 1024) _make_read_lob_request.return_value = BLOB_DATA[1024:1024 + num_items_to_read] payload = io.BytesIO(BLOB_HEADER + BLOB_DATA) lob = lobs.from_payload(type_codes.BLOB, payload, None) lob_len = len(lob.data.getvalue()) lob.seek(lob_len + 100) # seek to position 100 items (bytes/chars) after what is available # This should have triggered _read_missing_lob_data_from_db(). # Since seek() makes the assumption that the user wants to read data from the new position # another EXTRA_NUM_ITEMS_TO_READ_AFTER_SEEK are read in addition to the 100 beyond the current num items: _make_read_lob_request.assert_called_once_with(1024, num_items_to_read) assert lob.getvalue() == BLOB_DATA[:1024 + num_items_to_read] assert lob.tell() == lob_len + 100
def test_parse_null_blob(type_code, null_lob_header): """Parse a BLOB which is NULL -> a None object is expected""" payload = io.BytesIO(null_lob_header) lob = lobs.from_payload(type_code, payload, None) assert lob is None