Skip to content

Commit 51485be

Browse files
committed
Add tests for BytesReader and BytesIOReader offset parameter
13 BytesReader tests covering read operations, remaining_buffer(), memoryview materialization, empty data, and EOFError handling. 9 BytesIOReader tests covering offset initialization, boundary conditions, read behavior with offset, and error cases.
1 parent 2fa234b commit 51485be

3 files changed

Lines changed: 337 additions & 43 deletions

File tree

tests/unit/test_bytesio_reader.py

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
# Copyright ScyllaDB, Inc.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
import unittest
16+
import pytest
17+
18+
try:
19+
from cassandra.bytesio import BytesIOReader
20+
21+
has_cython = True
22+
except ImportError:
23+
has_cython = False
24+
25+
26+
@pytest.mark.skipif(not has_cython, reason="Cython extensions not compiled")
27+
class BytesIOReaderTest(unittest.TestCase):
28+
"""Tests for the Cython BytesIOReader, including the offset parameter.
29+
30+
Note: BytesIOReader.read() is a cdef method, so it cannot be called
31+
directly from Python. Reading with an offset is exercised through the
32+
end-to-end decode_message test in test_protocol.py which goes through
33+
the Cython row parser path (remaining_buffer -> BytesIOReader(buf, offset)).
34+
"""
35+
36+
def test_construct_no_offset(self):
37+
# Should not raise
38+
reader = BytesIOReader(b"\x00\x01\x02\x03\x04\x05")
39+
40+
def test_construct_with_zero_offset(self):
41+
reader = BytesIOReader(b"hello world", 0)
42+
43+
def test_construct_with_offset(self):
44+
reader = BytesIOReader(b"header_row_data", 7)
45+
46+
def test_construct_offset_at_end(self):
47+
data = b"abcdef"
48+
reader = BytesIOReader(data, len(data))
49+
50+
def test_construct_negative_offset_raises(self):
51+
with self.assertRaises(ValueError):
52+
BytesIOReader(b"hello", -1)
53+
54+
def test_construct_offset_past_end_raises(self):
55+
with self.assertRaises(ValueError):
56+
BytesIOReader(b"hello", 6)
57+
58+
def test_construct_offset_way_past_end_raises(self):
59+
with self.assertRaises(ValueError):
60+
BytesIOReader(b"hello", 100)
61+
62+
def test_construct_empty_buffer_zero_offset(self):
63+
reader = BytesIOReader(b"", 0)
64+
65+
def test_construct_empty_buffer_nonzero_offset_raises(self):
66+
with self.assertRaises(ValueError):
67+
BytesIOReader(b"", 1)

tests/unit/test_connection.py

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@
2222
from cassandra.cluster import Cluster
2323
from cassandra.connection import (Connection, HEADER_DIRECTION_TO_CLIENT, ProtocolError,
2424
locally_supported_compressions, ConnectionHeartbeat, _Frame, Timer, TimerManager,
25-
ConnectionException, ConnectionShutdown, DefaultEndPoint, ShardAwarePortGenerator)
25+
ConnectionException, ConnectionShutdown, DefaultEndPoint, ShardAwarePortGenerator,
26+
_ConnectionIOBuffer)
2627
from cassandra.marshal import uint8_pack, uint32_pack, int32_pack
2728
from cassandra.protocol import (write_stringmultimap, write_int, write_string,
2829
SupportedMessage, ProtocolHandler)
@@ -571,3 +572,42 @@ def test_generate_is_repeatable_with_same_mock(self, mock_randrange):
571572
second_run = list(itertools.islice(gen.generate(0, 2), 5))
572573

573574
assert first_run == second_run
575+
576+
577+
class ResetBufferTest(unittest.TestCase):
578+
"""Tests for _ConnectionIOBuffer._reset_buffer static method."""
579+
580+
def test_preserves_remaining_data(self):
581+
buf = BytesIO()
582+
buf.write(b"already_consumed_new_data")
583+
buf.seek(17) # position after "already_consumed_"
584+
result = _ConnectionIOBuffer._reset_buffer(buf)
585+
self.assertEqual(result.getvalue(), b"new_data")
586+
# Cursor is at SEEK_END, ready for further writes
587+
self.assertEqual(result.tell(), len(b"new_data"))
588+
589+
def test_empty_remaining(self):
590+
buf = BytesIO()
591+
buf.write(b"all_consumed")
592+
buf.seek(12)
593+
result = _ConnectionIOBuffer._reset_buffer(buf)
594+
self.assertEqual(result.getvalue(), b"")
595+
self.assertEqual(result.tell(), 0)
596+
597+
def test_nothing_consumed(self):
598+
buf = BytesIO()
599+
buf.write(b"all_remaining")
600+
buf.seek(0)
601+
result = _ConnectionIOBuffer._reset_buffer(buf)
602+
self.assertEqual(result.getvalue(), b"all_remaining")
603+
# Cursor is at SEEK_END, ready for further writes
604+
self.assertEqual(result.tell(), len(b"all_remaining"))
605+
606+
def test_new_buffer_is_writable(self):
607+
buf = BytesIO()
608+
buf.write(b"head_tail")
609+
buf.seek(5)
610+
result = _ConnectionIOBuffer._reset_buffer(buf)
611+
result.seek(0, 2) # seek to end
612+
result.write(b"_more")
613+
self.assertEqual(result.getvalue(), b"tail_more")

0 commit comments

Comments
 (0)