Skip to content

Commit 8501a93

Browse files
committed
Added test with restricted filestore
1 parent 994c066 commit 8501a93

File tree

3 files changed

+166
-15
lines changed

3 files changed

+166
-15
lines changed

src/cfdppy/handler/source.py

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -659,8 +659,8 @@ def _prepare_metadata_pdu(self) -> None:
659659
def _prepare_metadata_base_params_with_metadata(self) -> MetadataParams:
660660
assert self._params.remote_cfg is not None
661661
return MetadataParams(
662-
dest_file_name=self._put_req.dest_file.as_posix(), # type: ignore
663-
source_file_name=self._put_req.source_file.as_posix(), # type: ignore
662+
dest_file_name=self._put_req.dest_file.as_posix(),
663+
source_file_name=self._put_req.source_file.as_posix(),
664664
checksum_type=self._params.remote_cfg.crc_type,
665665
closure_requested=self._params.closure_requested,
666666
file_size=self._params.fp.file_size,
@@ -900,16 +900,17 @@ def _prepare_file_data_pdu(self, offset: int, read_len: int) -> None:
900900
re-transmit file data PDUs of segments which were already sent."""
901901
assert self._put_req is not None
902902
assert self._put_req.source_file is not None
903-
with open(self._put_req.source_file, "rb") as of:
904-
file_data = self.user.vfs.read_from_opened_file(of, offset, read_len)
905-
# TODO: Support for record continuation state not implemented yet. Segment metadata
906-
# flag is therefore always set to False. Segment metadata support also omitted
907-
# for now. Implementing those generically could be done in form of a callback,
908-
# e.g. abstractmethod of this handler as a first way, another one being
909-
# to expect the user to supply some helper class to split up a file
910-
fd_params = FileDataParams(file_data=file_data, offset=offset, segment_metadata=None)
911-
file_data_pdu = FileDataPdu(pdu_conf=self._params.pdu_conf, params=fd_params)
912-
self._add_packet_to_be_sent(file_data_pdu)
903+
file_data = self.user.vfs.read_data(
904+
file=self._put_req.source_file, offset=offset, read_len=read_len
905+
)
906+
# TODO: Support for record continuation state not implemented yet. Segment metadata
907+
# flag is therefore always set to False. Segment metadata support also omitted
908+
# for now. Implementing those generically could be done in form of a callback,
909+
# e.g. abstractmethod of this handler as a first way, another one being
910+
# to expect the user to supply some helper class to split up a file
911+
fd_params = FileDataParams(file_data=file_data, offset=offset, segment_metadata=None)
912+
file_data_pdu = FileDataPdu(pdu_conf=self._params.pdu_conf, params=fd_params)
913+
self._add_packet_to_be_sent(file_data_pdu)
913914

914915
def _prepare_eof_pdu(self, checksum: bytes) -> None:
915916
assert self._params.cond_code_eof is not None

tests/cfdp_user_mock.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from spacepackets.cfdp import ConditionCode
22

3-
from cfdppy import CfdpUserBase, TransactionId
3+
from cfdppy import CfdpUserBase, TransactionId, VirtualFilestore
44
from cfdppy.user import (
55
FileSegmentRecvdParams,
66
MetadataRecvParams,
@@ -10,8 +10,8 @@
1010

1111

1212
class CfdpUser(CfdpUserBase):
13-
def __init__(self):
14-
super().__init__()
13+
def __init__(self, vfs: VirtualFilestore | None = None):
14+
super().__init__(vfs=vfs)
1515

1616
def transaction_indication(self, transaction_params: TransactionParams):
1717
pass
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
import copy
2+
import shutil
3+
import tempfile
4+
from pathlib import Path
5+
from unittest import TestCase
6+
from unittest.mock import MagicMock
7+
8+
from spacepackets.cfdp import (
9+
ChecksumType,
10+
ConditionCode,
11+
CrcFlag,
12+
Direction,
13+
DirectiveType,
14+
FinishedParams,
15+
LargeFileFlag,
16+
PduConfig,
17+
SegmentationControl,
18+
TransmissionMode,
19+
)
20+
from spacepackets.cfdp.pdu import (
21+
AckPdu,
22+
EofPdu,
23+
FileDataPdu,
24+
FinishedPdu,
25+
MetadataPdu,
26+
TransactionStatus,
27+
)
28+
from spacepackets.seqcount import SeqCountProvider
29+
from spacepackets.util import ByteFieldU16
30+
31+
from cfdppy import (
32+
CfdpState,
33+
IndicationCfg,
34+
LocalEntityCfg,
35+
PutRequest,
36+
RemoteEntityCfg,
37+
RemoteEntityCfgTable,
38+
RestrictedFilestore,
39+
)
40+
from cfdppy.handler import SourceHandler
41+
from tests.cfdp_fault_handler_mock import FaultHandler
42+
from tests.cfdp_user_mock import CfdpUser
43+
from tests.common import CheckTimerProviderForTest
44+
45+
46+
class TestSrcHandlerRestrictedFileStore(TestCase):
47+
def setUp(self):
48+
super().setUp()
49+
self.temp_dir = Path(tempfile.mkdtemp())
50+
self.closure_requested = False
51+
self.indication_cfg = IndicationCfg(True, True, True, True, True, True)
52+
self.fault_handler = MagicMock()
53+
self.fault_handler.mock_add_spec(spec=FaultHandler, spec_set=True)
54+
self.local_cfg = LocalEntityCfg(ByteFieldU16(1), self.indication_cfg, self.fault_handler)
55+
self.cfdp_user = CfdpUser(vfs=RestrictedFilestore(self.temp_dir))
56+
self.seq_num_provider = SeqCountProvider(bit_width=8)
57+
self.expected_seq_num = 0
58+
self.source_id = ByteFieldU16(1)
59+
self.dest_id = ByteFieldU16(2)
60+
self.alternative_dest_id = ByteFieldU16(3)
61+
self.file_segment_len = 64
62+
self.max_packet_len = 256
63+
self.positive_ack_intvl_seconds = 0.02
64+
self.default_remote_cfg = RemoteEntityCfg(
65+
entity_id=self.dest_id,
66+
max_packet_len=self.max_packet_len,
67+
max_file_segment_len=self.file_segment_len,
68+
closure_requested=self.closure_requested,
69+
crc_on_transmission=False,
70+
default_transmission_mode=TransmissionMode.ACKNOWLEDGED,
71+
positive_ack_timer_interval_seconds=self.positive_ack_intvl_seconds,
72+
positive_ack_timer_expiration_limit=2,
73+
crc_type=ChecksumType.CRC_32,
74+
check_limit=2,
75+
)
76+
self.alternative_remote_cfg = copy.copy(self.default_remote_cfg)
77+
self.alternative_remote_cfg.entity_id = self.alternative_dest_id
78+
self.remote_cfg_table = RemoteEntityCfgTable()
79+
self.remote_cfg_table.add_config(self.default_remote_cfg)
80+
self.remote_cfg_table.add_config(self.alternative_remote_cfg)
81+
# Create an empty file and send it via CFDP
82+
self.source_handler = SourceHandler(
83+
cfg=self.local_cfg,
84+
user=self.cfdp_user,
85+
remote_cfg_table=self.remote_cfg_table,
86+
seq_num_provider=self.seq_num_provider,
87+
check_timer_provider=CheckTimerProviderForTest(),
88+
)
89+
90+
def tearDown(self):
91+
shutil.rmtree(self.temp_dir)
92+
93+
def test_src_handler_restricted(self):
94+
file_content = "Hello, World!"
95+
with open(self.temp_dir.joinpath("hello.txt"), "w") as f:
96+
f.write(file_content)
97+
source_path = Path("hello.txt")
98+
dest_path = Path("hello_copy.txt")
99+
self.seq_num_provider.get_and_increment = MagicMock(return_value=self.expected_seq_num)
100+
self.source_handler.entity_id = self.source_id
101+
put_req = PutRequest(
102+
destination_id=self.dest_id,
103+
source_file=source_path,
104+
dest_file=dest_path,
105+
# Let the transmission mode be auto-determined by the remote MIB
106+
trans_mode=TransmissionMode.ACKNOWLEDGED,
107+
closure_requested=True,
108+
)
109+
self.source_handler.put_request(put_req)
110+
111+
fsm = self.source_handler.state_machine()
112+
self.assertTrue(fsm.states.packets_ready)
113+
self.assertEqual(fsm.states.num_packets_ready, 1)
114+
next_pdu = self.source_handler.get_next_packet()
115+
self.assertIsInstance(next_pdu.base, MetadataPdu)
116+
fsm = self.source_handler.state_machine()
117+
self.assertTrue(fsm.states.packets_ready)
118+
file_data = self.source_handler.get_next_packet()
119+
self.assertIsInstance(file_data.base, FileDataPdu)
120+
fsm = self.source_handler.state_machine()
121+
self.assertTrue(fsm.states.packets_ready)
122+
eof_data = self.source_handler.get_next_packet()
123+
self.assertIsInstance(eof_data.base, EofPdu)
124+
# Send ACK
125+
pdu_conf = PduConfig(
126+
direction=Direction.TOWARDS_SENDER,
127+
transaction_seq_num=eof_data.base.transaction_seq_num,
128+
source_entity_id=self.source_id,
129+
dest_entity_id=self.dest_id,
130+
trans_mode=TransmissionMode.ACKNOWLEDGED,
131+
file_flag=LargeFileFlag.NORMAL,
132+
crc_flag=CrcFlag.NO_CRC,
133+
seg_ctrl=SegmentationControl.NO_RECORD_BOUNDARIES_PRESERVATION,
134+
)
135+
eof_ack = AckPdu(
136+
pdu_conf=pdu_conf,
137+
directive_code_of_acked_pdu=DirectiveType.EOF_PDU,
138+
condition_code_of_acked_pdu=ConditionCode.NO_ERROR,
139+
transaction_status=TransactionStatus.ACTIVE,
140+
)
141+
fsm = self.source_handler.state_machine(packet=eof_ack)
142+
self.assertFalse(fsm.states.packets_ready)
143+
144+
finished = FinishedPdu(pdu_conf=pdu_conf, params=FinishedParams.success_params())
145+
fsm = self.source_handler.state_machine(packet=finished)
146+
self.assertTrue(fsm.states.packets_ready)
147+
finished_ack = self.source_handler.get_next_packet()
148+
self.assertIsInstance(finished_ack.base, AckPdu)
149+
fsm = self.source_handler.state_machine()
150+
self.assertEqual(fsm.states.state, CfdpState.IDLE)

0 commit comments

Comments
 (0)