10000 Source handler check by sgaisser · Pull Request #1 · sgaisser/cfdp-py · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Source handler check #1

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Dec 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- Correction for `InvalidDestinationId` exception arguments in destination handler.
- Destination handler now only checks entity ID values when checking inserted packets.
- Source handler used an incorrect check if the file exists without the virtual filestore.
- Source handler opened files without the virtual filestore

# [v0.4.0] 2024-11-08

Expand Down
25 changes: 13 additions & 12 deletions src/cfdppy/handler/source.py
Original file line number Diff line number Diff line change
Expand Up @@ -659,8 +659,8 @@ def _prepare_metadata_pdu(self) -> None:
def _prepare_metadata_base_params_with_metadata(self) -> MetadataParams:
assert self._params.remote_cfg is not None
return MetadataParams(
dest_file_name=self._put_req.dest_file.as_posix(), # type: ignore
source_file_name=self._put_req.source_file.as_posix(), # type: ignore
dest_file_name=self._put_req.dest_file.as_posix(),
source_file_name=self._put_req.source_file.as_posix(),
checksum_type=self._params.remote_cfg.crc_type,
closure_requested=self._params.closure_requested,
file_size=self._params.fp.file_size,
Expand Down Expand Up @@ -900,16 +900,17 @@ def _prepare_file_data_pdu(self, offset: int, read_len: int) -> None:
re-transmit file data PDUs of segments which were already sent."""
assert self._put_req is not None
assert self._put_req.source_file is not None
with open(self._put_req.source_file, "rb") as of:
file_data = self.user.vfs.read_from_opened_file(of, offset, read_len)
# TODO: Support for record continuation state not implemented yet. Segment metadata
# flag is therefore always set to False. Segment metadata support also omitted
# for now. Implementing those generically could be done in form of a callback,
# e.g. abstractmethod of this handler as a first way, another one being
# to expect the user to supply some helper class to split up a file
fd_params = FileDataParams(file_data=file_data, offset=offset, segment_metadata=None)
file_data_pdu = FileDataPdu(pdu_conf=self._params.pdu_conf, params=fd_params)
self._add_packet_to_be_sent(file_data_pdu)
file_data = self.user.vfs.read_data(
file=self._put_req.source_file, offset=offset, read_len=read_len
)
# TODO: Support for record continuation state not implemented yet. Segment metadata
# flag is therefore always set to False. Segment metadata support also omitted
# for now. Implementing those generically could be done in form of a callback,
# e.g. abstractmethod of this handler as a first way, another one being
# to expect the user to supply some helper class to split up a file
fd_params = FileDataParams(file_data=file_data, offset=offset, segment_metadata=None)
file_data_pdu = FileDataPdu(pdu_conf=self._params.pdu_conf, params=fd_params)
self._add_packet_to_be_sent(file_data_pdu)

def _prepare_eof_pdu(self, checksum: bytes) -> None:
assert self._params.cond_code_eof is not None
Expand Down
26 changes: 16 additions & 10 deletions tests/cfdp_user_mock.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,23 @@
from spacepackets.cfdp import ConditionCode
from __future__ import annotations # Python 3.9 compatibility for | syntax

from cfdppy import CfdpUserBase, TransactionId
from cfdppy.user import (
FileSegmentRecvdParams,
MetadataRecvParams,
TransactionFinishedParams,
TransactionParams,
)
from typing import TYPE_CHECKING

from cfdppy import CfdpUserBase, TransactionId, VirtualFilestore

if TYPE_CHECKING:
from spacepackets.cfdp import ConditionCode

from cfdppy.user import (
FileSegmentRecvdParams,
MetadataRecvParams,
TransactionFinishedParams,
TransactionParams,
)


class CfdpUser(CfdpUserBase):
def __init__(self):
super().__init__()
def __init__(self, vfs: VirtualFilestore | None = None):
super().__init__(vfs=vfs)

def transaction_indication(self, transaction_params: TransactionParams):
pass
Expand Down
152 changes: 152 additions & 0 deletions tests/test_src_handler_restricted.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
from __future__ import annotations # Python 3.9 compatibility for | syntax

import copy
import shutil
import tempfile
from pathlib import Path
from unittest import TestCase
from unittest.mock import MagicMock

from spacepackets.cfdp import (
ChecksumType,
ConditionCode,
CrcFlag,
Direction,
DirectiveType,
FinishedParams,
LargeFileFlag,
PduConfig,
SegmentationControl,
TransmissionMode,
)
from spacepackets.cfdp.pdu import (
AckPdu,
EofPdu,
FileDataPdu,
FinishedPdu,
MetadataPdu,
TransactionStatus,
)
from spacepackets.seqcount import SeqCountProvider
from spacepackets.util import ByteFieldU16

from cfdppy import (
CfdpState,
IndicationCfg,
LocalEntityCfg,
PutRequest,
RemoteEntityCfg,
RemoteEntityCfgTable,
RestrictedFilestore,
)
from cfdppy.handler import SourceHandler
from tests.cfdp_fault_handler_mock import FaultHandler
from tests.cfdp_user_mock import CfdpUser
from tests.common import CheckTimerProviderForTest


class TestSrcHandlerRestrictedFileStore(TestCase):
def setUp(self):
super().setUp()
self.temp_dir = Path(tempfile.mkdtemp())
self.closure_requested = False
self.indication_cfg = IndicationCfg(True, True, True, True, True, True)
self.fault_handler = MagicMock()
self.fault_handler.mock_add_spec(spec=FaultHandler, spec_set=True)
self.local_cfg = LocalEntityCfg(ByteFieldU16(1), self.indication_cfg, self.fault_handler)
self.cfdp_user = CfdpUser(vfs=RestrictedFilestore(self.temp_dir))
self.seq_num_provider = SeqCountProvider(bit_width=8)
self.expected_seq_num = 0
self.source_id = ByteFieldU16(1)
self.dest_id = ByteFieldU16(2)
self.alternative_dest_id = ByteFieldU16(3)
self.file_segment_len = 64
self.max_packet_len = 256
self.positive_ack_intvl_seconds = 0.02
self.default_remote_cfg = RemoteEntityCfg(
entity_id=self.dest_id,
max_packet_len=self.max_packet_len,
max_file_segment_len=self.file_segment_len,
closure_requested=self.closure_requested,
crc_on_transmission=False,
default_transmission_mode=TransmissionMode.ACKNOWLEDGED,
positive_ack_timer_interval_seconds=self.positive_ack_intvl_seconds,
positive_ack_timer_expiration_limit=2,
crc_type=ChecksumType.CRC_32,
check_limit=2,
)
self.alternative_remote_cfg = copy.copy(self.default_remote_cfg)
self.alternative_remote_cfg.entity_id = self.alternative_dest_id
self.remote_cfg_table = RemoteEntityCfgTable()
self.remote_cfg_table.add_config(self.default_remote_cfg)
self.remote_cfg_table.add_config(self.alternative_remote_cfg)
# Create an empty file and send it via CFDP
self.source_handler = SourceHandler(
cfg=self.local_cfg,
user=self.cfdp_user,
remote_cfg_table=self.remote_cfg_table,
seq_num_provider=self.seq_num_provider,
check_timer_provider=CheckTimerProviderForTest(),
)

def tearDown(self):
shutil.rmtree(self.temp_dir)

def test_src_handler_restricted(self):
file_content = "Hello, World!"
with open(self.temp_dir.joinpath("hello.txt"), "w") as f:
f.write(file_content)
source_path = Path("hello.txt")
dest_path = Path("hello_copy.txt")
self.seq_num_provider.get_and_increment = MagicMock(return_value=self.expected_seq_num)
self.source_handler.entity_id = self.source_id
put_req = PutRequest(
destination_id=self.dest_id,
source_file=source_path,
dest_file=dest_path,
# Let the transmission mode be auto-determined by the remote MIB
trans_mode=TransmissionMode.ACKNOWLEDGED,
closure_requested=True,
)
self.source_handler.put_request(put_req)

fsm = self.source_handler.state_machine()
self.assertTrue(fsm.states.packets_ready)
self.assertEqual(fsm.states.num_packets_ready, 1)
next_pdu = self.source_handler.get_next_packet()
self.assertIsInstance(next_pdu.base, MetadataPdu)
fsm = self.source_handler.state_machine()
self.assertTrue(fsm.states.packets_ready)
file_data = self.source_handler.get_next_packet()
self.assertIsInstance(file_data.base, FileDataPdu)
fsm = self.source_handler.state_machine()
self.assertTrue(fsm.states.packets_ready)
eof_data = self.source_handler.get_next_packet()
self.assertIsInstance(eof_data.base, EofPdu)
# Send ACK
pdu_conf = PduConfig(
direction=Direction.TOWARDS_SENDER,
transaction_seq_num=eof_data.base.transaction_seq_num,
source_entity_id=self.source_id,
dest_entity_id=self.dest_id,
trans_mode=TransmissionMode.ACKNOWLEDGED,
file_flag=LargeFileFlag.NORMAL,
crc_flag=CrcFlag.NO_CRC,
seg_ctrl=SegmentationControl.NO_RECORD_BOUNDARIES_PRESERVATION,
)
eof_ack = AckPdu(
pdu_conf=pdu_conf,
directive_code_of_acked_pdu=DirectiveType.EOF_PDU,
condition_code_of_acked_pdu=ConditionCode.NO_ERROR,
transaction_status=TransactionStatus.ACTIVE,
)
fsm = self.source_handler.state_machine(packet=eof_ack)
self.assertFalse(fsm.states.packets_ready)

finished = FinishedPdu(pdu_conf=pdu_conf, params=FinishedParams.success_params())
fsm = self.source_handler.state_machine(packet=finished)
self.assertTrue(fsm.states.packets_ready)
finished_ack = self.source_handler.get_next_packet()
self.assertIsInstance(finished_ack.base, AckPdu)
fsm = self.source_handler.state_machine()
self.assertEqual(fsm.states.state, CfdpState.IDLE)
Loading
0