mirror of
https://github.com/markqvist/Reticulum.git
synced 2025-08-03 20:14:20 -04:00
Buffer: send and receive binary data over Channel
(also some minor fixes in channel)
This commit is contained in:
parent
58004d7c05
commit
aac2b9f987
8 changed files with 835 additions and 13 deletions
115
tests/channel.py
115
tests/channel.py
|
@ -2,6 +2,7 @@ from __future__ import annotations
|
|||
import threading
|
||||
import RNS
|
||||
from RNS.Channel import MessageState, ChannelOutletBase, Channel, MessageBase
|
||||
import RNS.Buffer
|
||||
from RNS.vendor import umsgpack
|
||||
from typing import Callable
|
||||
import contextlib
|
||||
|
@ -91,17 +92,20 @@ class ChannelOutletTest(ChannelOutletBase):
|
|||
self._rtt = rtt
|
||||
self._usable = True
|
||||
self.packets = []
|
||||
self.lock = threading.RLock()
|
||||
self.packet_callback: Callable[[ChannelOutletBase, bytes], None] | None = None
|
||||
|
||||
def send(self, raw: bytes) -> Packet:
|
||||
packet = Packet(raw)
|
||||
packet.send()
|
||||
self.packets.append(packet)
|
||||
return packet
|
||||
with self.lock:
|
||||
packet = Packet(raw)
|
||||
packet.send()
|
||||
self.packets.append(packet)
|
||||
return packet
|
||||
|
||||
def resend(self, packet: Packet) -> Packet:
|
||||
packet.send()
|
||||
return packet
|
||||
with self.lock:
|
||||
packet.send()
|
||||
return packet
|
||||
|
||||
@property
|
||||
def mdu(self):
|
||||
|
@ -370,6 +374,105 @@ class TestChannel(unittest.TestCase):
|
|||
|
||||
self.eat_own_dog_food(message, check)
|
||||
|
||||
def test_buffer_small_bidirectional(self):
|
||||
data = "Hello\n"
|
||||
with RNS.Buffer.create_bidirectional_buffer(0, 0, self.h.channel) as buffer:
|
||||
count = buffer.write(data.encode("utf-8"))
|
||||
buffer.flush()
|
||||
|
||||
self.assertEqual(len(data), count)
|
||||
self.assertEqual(1, len(self.h.outlet.packets))
|
||||
|
||||
packet = self.h.outlet.packets[0]
|
||||
self.h.channel._receive(packet.raw)
|
||||
result = buffer.readline()
|
||||
|
||||
self.assertIsNotNone(result)
|
||||
self.assertEqual(len(result), len(data))
|
||||
|
||||
decoded = result.decode("utf-8")
|
||||
|
||||
self.assertEqual(data, decoded)
|
||||
|
||||
def test_buffer_big(self):
|
||||
writer = RNS.Buffer.create_writer(15, self.h.channel)
|
||||
reader = RNS.Buffer.create_reader(15, self.h.channel)
|
||||
data = "01234556789"*1024 # 10 KB
|
||||
count = 0
|
||||
write_finished = False
|
||||
|
||||
def write_thread():
|
||||
nonlocal count, write_finished
|
||||
count = writer.write(data.encode("utf-8"))
|
||||
writer.flush()
|
||||
writer.close()
|
||||
write_finished = True
|
||||
threading.Thread(target=write_thread, name="Write Thread", daemon=True).start()
|
||||
|
||||
while not write_finished or next(filter(lambda x: x.state != MessageState.MSGSTATE_DELIVERED,
|
||||
self.h.outlet.packets), None) is not None:
|
||||
with self.h.outlet.lock:
|
||||
for packet in self.h.outlet.packets:
|
||||
if packet.state != MessageState.MSGSTATE_DELIVERED:
|
||||
self.h.channel._receive(packet.raw)
|
||||
packet.delivered()
|
||||
time.sleep(0.0001)
|
||||
|
||||
self.assertEqual(len(data), count)
|
||||
|
||||
read_finished = False
|
||||
result = bytes()
|
||||
|
||||
def read_thread():
|
||||
nonlocal read_finished, result
|
||||
result = reader.read()
|
||||
read_finished = True
|
||||
threading.Thread(target=read_thread, name="Read Thread", daemon=True).start()
|
||||
|
||||
timeout_at = time.time() + 7
|
||||
while not read_finished and time.time() < timeout_at:
|
||||
time.sleep(0.001)
|
||||
|
||||
self.assertTrue(read_finished)
|
||||
self.assertEqual(len(data), len(result))
|
||||
|
||||
decoded = result.decode("utf-8")
|
||||
|
||||
self.assertSequenceEqual(data, decoded)
|
||||
|
||||
def test_buffer_small_with_callback(self):
|
||||
callbacks = 0
|
||||
last_cb_value = None
|
||||
|
||||
def callback(ready: int):
|
||||
nonlocal callbacks, last_cb_value
|
||||
callbacks += 1
|
||||
last_cb_value = ready
|
||||
|
||||
data = "Hello\n"
|
||||
with RNS.RawChannelWriter(0, self.h.channel) as writer, RNS.RawChannelReader(0, self.h.channel) as reader:
|
||||
reader.add_ready_callback(callback)
|
||||
count = writer.write(data.encode("utf-8"))
|
||||
writer.flush()
|
||||
|
||||
self.assertEqual(len(data), count)
|
||||
self.assertEqual(1, len(self.h.outlet.packets))
|
||||
|
||||
packet = self.h.outlet.packets[0]
|
||||
self.h.channel._receive(packet.raw)
|
||||
|
||||
self.assertEqual(1, callbacks)
|
||||
self.assertEqual(len(data), last_cb_value)
|
||||
|
||||
result = reader.readline()
|
||||
|
||||
self.assertIsNotNone(result)
|
||||
self.assertEqual(len(result), len(data))
|
||||
|
||||
decoded = result.decode("utf-8")
|
||||
|
||||
self.assertEqual(data, decoded)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(verbosity=2)
|
||||
|
|
|
@ -396,6 +396,48 @@ class TestLink(unittest.TestCase):
|
|||
self.assertEqual(l1.status, RNS.Link.CLOSED)
|
||||
self.assertEqual(0, len(l1._channel._rx_ring))
|
||||
|
||||
def test_11_buffer_round_trip(self):
|
||||
global c_rns
|
||||
init_rns(self)
|
||||
print("")
|
||||
print("Buffer round trip test")
|
||||
|
||||
# TODO: Load this from public bytes only
|
||||
id1 = RNS.Identity.from_bytes(bytes.fromhex(fixed_keys[0][0]))
|
||||
self.assertEqual(id1.hash, bytes.fromhex(fixed_keys[0][1]))
|
||||
|
||||
dest = RNS.Destination(id1, RNS.Destination.OUT, RNS.Destination.SINGLE, APP_NAME, "link", "establish")
|
||||
|
||||
self.assertEqual(dest.hash, bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d"))
|
||||
|
||||
l1 = RNS.Link(dest)
|
||||
time.sleep(1)
|
||||
self.assertEqual(l1.status, RNS.Link.ACTIVE)
|
||||
buffer = None
|
||||
|
||||
received = []
|
||||
def handle_data(ready_bytes: int):
|
||||
data = buffer.read(ready_bytes)
|
||||
received.append(data)
|
||||
|
||||
channel = l1.get_channel()
|
||||
buffer = RNS.Buffer.create_bidirectional_buffer(0, 0, channel, handle_data)
|
||||
|
||||
buffer.write("Hi there".encode("utf-8"))
|
||||
buffer.flush()
|
||||
|
||||
time.sleep(0.5)
|
||||
|
||||
self.assertEqual(1 , len(received))
|
||||
|
||||
rx_message = received[0].decode("utf-8")
|
||||
|
||||
self.assertEqual("Hi there back at you", rx_message)
|
||||
|
||||
l1.teardown()
|
||||
time.sleep(0.5)
|
||||
self.assertEqual(l1.status, RNS.Link.CLOSED)
|
||||
|
||||
|
||||
def size_str(self, num, suffix='B'):
|
||||
units = ['','K','M','G','T','P','E','Z']
|
||||
|
@ -462,6 +504,15 @@ def targets(yp=False):
|
|||
channel.register_message_type(MessageTest)
|
||||
channel.add_message_handler(handle_message)
|
||||
|
||||
buffer = None
|
||||
|
||||
def handle_buffer(ready_bytes: int):
|
||||
data = buffer.read(ready_bytes)
|
||||
buffer.write((data.decode("utf-8") + " back at you").encode("utf-8"))
|
||||
buffer.flush()
|
||||
|
||||
buffer = RNS.Buffer.create_bidirectional_buffer(0, 0, channel, handle_buffer)
|
||||
|
||||
m_rns = RNS.Reticulum("./tests/rnsconfig")
|
||||
id1 = RNS.Identity.from_bytes(bytes.fromhex(fixed_keys[0][0]))
|
||||
d1 = RNS.Destination(id1, RNS.Destination.IN, RNS.Destination.SINGLE, APP_NAME, "link", "establish")
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue