Skip to content

Commit 6a557f7

Browse files
authored
Merge pull request #34 from sgaisser/source_handler_check
Added test with restricted filestore
2 parents 994c066 + 72a456a commit 6a557f7

File tree

4 files changed

+182
-22
lines changed

4 files changed

+182
-22
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
1818
- Correction for `InvalidDestinationId` exception arguments in destination handler.
1919
- Destination handler now only checks entity ID values when checking inserted packets.
2020
- Source handler used an incorrect check if the file exists without the virtual filestore.
21+
- Source handler opened files without the virtual filestore
2122

2223
# [v0.4.0] 2024-11-08
2324

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: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,23 @@
1-
from spacepackets.cfdp import ConditionCode
1+
from __future__ import annotations # Python 3.9 compatibility for | syntax
22

3-
from cfdppy import CfdpUserBase, TransactionId
4-
from cfdppy.user import (
5-
FileSegmentRecvdParams,
6-
MetadataRecvParams,
7-
TransactionFinishedParams,
8-
TransactionParams,
9-
)
3+
from typing import TYPE_CHECKING
4+
5+
from cfdppy import CfdpUserBase, TransactionId, VirtualFilestore
6+
7+
if TYPE_CHECKING:
8+
from spacepackets.cfdp import ConditionCode
9+
10+
from cfdppy.user import (
11+
FileSegmentRecvdParams,
12+
MetadataRecvParams,
13+
TransactionFinishedParams,
14+
TransactionParams,
15+
)
1016

1117

1218
class CfdpUser(CfdpUserBase):
13-
def __init__(self):
14-
super().__init__()
19+
def __init__(self, vfs: VirtualFilestore | None = None):
20+
super().__init__(vfs=vfs)
1521

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

0 commit comments

Comments
 (0)