diff --git a/pldm-interface/src/control_context.rs b/pldm-interface/src/control_context.rs index f68e2a3..cae2cb5 100644 --- a/pldm-interface/src/control_context.rs +++ b/pldm-interface/src/control_context.rs @@ -319,3 +319,497 @@ impl CtrlCmdResponder for ControlContext<'_> { } } } + +#[cfg(test)] +mod test { + use super::*; + use pldm_common::protocol::base::PldmFailureResponse; + use pldm_common::protocol::base::{ + PldmControlCmd, PldmMsgType, PldmSupportedType, PLDM_FAILURE_RESP_LEN, + }; + use pldm_common::protocol::firmware_update::FwUpdateCmd; + + const PAY_LOAD_BUFFER_LEN: usize = 256; + const SUPPORTED_CTRL_CMDS: [u8; 5] = [ + PldmControlCmd::SetTid as u8, + PldmControlCmd::GetTid as u8, + PldmControlCmd::GetPldmCommands as u8, + PldmControlCmd::GetPldmVersion as u8, + PldmControlCmd::GetPldmTypes as u8, + ]; + const SUPPORTED_FW_UDPATE_CMDS: [u8; 12] = [ + FwUpdateCmd::QueryDeviceIdentifiers as u8, + FwUpdateCmd::GetFirmwareParameters as u8, + FwUpdateCmd::RequestUpdate as u8, + FwUpdateCmd::PassComponentTable as u8, + FwUpdateCmd::UpdateComponent as u8, + FwUpdateCmd::RequestFirmwareData as u8, + FwUpdateCmd::TransferComplete as u8, + FwUpdateCmd::VerifyComplete as u8, + FwUpdateCmd::ApplyComplete as u8, + FwUpdateCmd::ActivateFirmware as u8, + FwUpdateCmd::GetStatus as u8, + FwUpdateCmd::CancelUpdate as u8, + ]; + + static PLDM_PROTOCOL_CAPABILITIES: [ProtocolCapability<'static>; 2] = [ + ProtocolCapability { + pldm_type: PldmSupportedType::Base, + protocol_version: 0xF1F1F000, //"1.1.0" + supported_commands: &SUPPORTED_CTRL_CMDS, + }, + ProtocolCapability { + pldm_type: PldmSupportedType::FwUpdate, + protocol_version: 0xF1F3F000, // 1.3.0 + supported_commands: &SUPPORTED_FW_UDPATE_CMDS, + }, + ]; + + fn construct_request(buf: &mut [u8], request_msg: T) { + request_msg.encode(buf).unwrap(); + } + + fn validate_response(buf: &mut [u8], expected_rsp_msg: T) { + let rsp = T::decode(buf).unwrap(); + assert_eq!(rsp, expected_rsp_msg); + } + + #[test] + fn test_protocol_capability() { + let cap = ProtocolCapability::new(PldmSupportedType::Base, "1.1.0", &SUPPORTED_CTRL_CMDS); + assert!(cap.is_ok()); + let cap = cap.unwrap(); + assert_eq!(cap.pldm_type, PldmSupportedType::Base); + assert_eq!(cap.protocol_version, 0xF1F1F000); + assert_eq!(cap.supported_commands, SUPPORTED_CTRL_CMDS); + + let cap = ProtocolCapability::new( + PldmSupportedType::FwUpdate, + "1.3.0", + &SUPPORTED_FW_UDPATE_CMDS, + ); + assert!(cap.is_ok()); + let cap = cap.unwrap(); + assert_eq!(cap.pldm_type, PldmSupportedType::FwUpdate); + assert_eq!(cap.protocol_version, 0xF1F3F000); + assert_eq!(cap.supported_commands, SUPPORTED_FW_UDPATE_CMDS); + } + + #[test] + fn test_control_context() { + let protocol_capabilities: [ProtocolCapability; 4] = [ + ProtocolCapability::new(PldmSupportedType::Base, "1.1.0", &SUPPORTED_CTRL_CMDS) + .unwrap(), + ProtocolCapability::new(PldmSupportedType::Base, "1.0.0", &SUPPORTED_CTRL_CMDS) + .unwrap(), + ProtocolCapability::new( + PldmSupportedType::FwUpdate, + "1.3.0", + &SUPPORTED_FW_UDPATE_CMDS, + ) + .unwrap(), + ProtocolCapability::new( + PldmSupportedType::FwUpdate, + "1.2.0", + &SUPPORTED_FW_UDPATE_CMDS, + ) + .unwrap(), + ]; + + let ctrl_cxt = ControlContext::new(&protocol_capabilities); + assert_eq!(ctrl_cxt.get_tid(), UNASSIGNED_TID); + + ctrl_cxt.set_tid(1); + assert_eq!(ctrl_cxt.get_tid(), 1); + + let mut types = [0; 6]; + let count = ctrl_cxt.get_supported_types(&mut types); + assert_eq!(count, 2); + assert_eq!( + types[..count], + [ + PldmSupportedType::Base as u8, + PldmSupportedType::FwUpdate as u8 + ] + ); + + let mut versions = [0; 4]; + let count = ctrl_cxt.get_protocol_versions(PldmSupportedType::Base, &mut versions); + assert_eq!(count, 2); + assert_eq!(versions[..count], [0xF1F1F000, 0xF1F0F000]); + + versions.fill(0); + let count = ctrl_cxt.get_protocol_versions(PldmSupportedType::FwUpdate, &mut versions); + assert_eq!(count, 2); + assert_eq!(versions[..count], [0xF1F3F000, 0xF1F2F000]); + + assert!(ctrl_cxt.is_supported_type(PldmSupportedType::Base)); + assert!(ctrl_cxt.is_supported_type(PldmSupportedType::FwUpdate)); + assert!(!ctrl_cxt.is_supported_type(PldmSupportedType::Fru)); + assert!(ctrl_cxt.is_supported_version( + PldmSupportedType::Base, + 0xF1F1F000 // "1.1.0" + )); + assert!(ctrl_cxt.is_supported_version( + PldmSupportedType::Base, + 0xF1F0F000 // "1.0.0" + )); + assert!(ctrl_cxt.is_supported_version( + PldmSupportedType::FwUpdate, + 0xF1F3F000 // "1.3.0" + )); + + assert_eq!( + ctrl_cxt.get_supported_commands(PldmSupportedType::Base, 0xF1F1F000), + Some(&SUPPORTED_CTRL_CMDS[..]) + ); + assert_eq!( + ctrl_cxt.get_supported_commands(PldmSupportedType::FwUpdate, 0xF1F3F000), + Some(&SUPPORTED_FW_UDPATE_CMDS[..]) + ); + + assert!( + ctrl_cxt.is_supported_command(PldmSupportedType::Base, PldmControlCmd::SetTid as u8) + ); + assert!( + ctrl_cxt.is_supported_command(PldmSupportedType::Base, PldmControlCmd::GetTid as u8) + ); + assert!(ctrl_cxt.is_supported_command( + PldmSupportedType::Base, + PldmControlCmd::GetPldmCommands as u8 + )); + assert!(ctrl_cxt.is_supported_command( + PldmSupportedType::Base, + PldmControlCmd::GetPldmVersion as u8 + )); + assert!(ctrl_cxt + .is_supported_command(PldmSupportedType::Base, PldmControlCmd::GetPldmTypes as u8)); + + assert!(ctrl_cxt.is_supported_command( + PldmSupportedType::FwUpdate, + FwUpdateCmd::QueryDeviceIdentifiers as u8 + )); + assert!(ctrl_cxt.is_supported_command( + PldmSupportedType::FwUpdate, + FwUpdateCmd::GetFirmwareParameters as u8 + )); + assert!(ctrl_cxt.is_supported_command( + PldmSupportedType::FwUpdate, + FwUpdateCmd::RequestUpdate as u8 + )); + assert!(ctrl_cxt.is_supported_command( + PldmSupportedType::FwUpdate, + FwUpdateCmd::PassComponentTable as u8 + )); + assert!(ctrl_cxt.is_supported_command( + PldmSupportedType::FwUpdate, + FwUpdateCmd::UpdateComponent as u8 + )); + assert!(ctrl_cxt.is_supported_command( + PldmSupportedType::FwUpdate, + FwUpdateCmd::RequestFirmwareData as u8 + )); + assert!(ctrl_cxt.is_supported_command( + PldmSupportedType::FwUpdate, + FwUpdateCmd::TransferComplete as u8 + )); + assert!(ctrl_cxt.is_supported_command( + PldmSupportedType::FwUpdate, + FwUpdateCmd::VerifyComplete as u8 + )); + assert!(ctrl_cxt.is_supported_command( + PldmSupportedType::FwUpdate, + FwUpdateCmd::ApplyComplete as u8 + )); + assert!(ctrl_cxt.is_supported_command( + PldmSupportedType::FwUpdate, + FwUpdateCmd::ActivateFirmware as u8 + )); + assert!(ctrl_cxt + .is_supported_command(PldmSupportedType::FwUpdate, FwUpdateCmd::GetStatus as u8)); + assert!(ctrl_cxt + .is_supported_command(PldmSupportedType::FwUpdate, FwUpdateCmd::CancelUpdate as u8)); + + // Test unsupported command + assert!(!ctrl_cxt.is_supported_command(PldmSupportedType::Base, 0x06)); + assert!(!ctrl_cxt.is_supported_command(PldmSupportedType::FwUpdate, 0x1E)); + } + + #[test] + fn test_get_tid_responder() { + let ctrl_cxt = ControlContext::new(&PLDM_PROTOCOL_CAPABILITIES); + let mut msg_buf = [0u8; PAY_LOAD_BUFFER_LEN]; + construct_request(&mut msg_buf, GetTidRequest::new(0x01, PldmMsgType::Request)); + + let resp_len = ctrl_cxt.get_tid_rsp(&mut msg_buf).unwrap(); + validate_response( + &mut msg_buf[..resp_len], + GetTidResponse::new(0x01, UNASSIGNED_TID, PldmBaseCompletionCode::Success as u8), + ); + } + + #[test] + fn test_set_tid_responder() { + let ctrl_cxt = ControlContext::new(&PLDM_PROTOCOL_CAPABILITIES); + let mut msg_buf = [0u8; PAY_LOAD_BUFFER_LEN]; + let assigned_tid = 0x02; + + construct_request( + &mut msg_buf, + SetTidRequest::new(0x02, PldmMsgType::Request, assigned_tid), + ); + let resp_len = ctrl_cxt.set_tid_rsp(&mut msg_buf).unwrap(); + validate_response( + &mut msg_buf[..resp_len], + SetTidResponse::new(0x02, PldmBaseCompletionCode::Success as u8), + ); + assert_eq!(ctrl_cxt.get_tid(), assigned_tid); + + // Reset msg buf + msg_buf.fill(0); + + construct_request(&mut msg_buf, GetTidRequest::new(0x03, PldmMsgType::Request)); + let resp_len = ctrl_cxt.get_tid_rsp(&mut msg_buf).unwrap(); + validate_response( + &mut msg_buf[..resp_len], + GetTidResponse::new(0x03, assigned_tid, PldmBaseCompletionCode::Success as u8), + ); + } + + #[test] + fn test_get_pldm_types_responder() { + let ctrl_cxt = ControlContext::new(&PLDM_PROTOCOL_CAPABILITIES); + let mut msg_buf = [0u8; PAY_LOAD_BUFFER_LEN]; + + construct_request( + &mut msg_buf, + GetPldmTypeRequest::new(0x04, PldmMsgType::Request), + ); + let resp_len = ctrl_cxt.get_pldm_types_rsp(&mut msg_buf).unwrap(); + + validate_response( + &mut msg_buf[..resp_len], + GetPldmTypeResponse::new( + 0x04, + PldmBaseCompletionCode::Success as u8, + &[ + PldmSupportedType::Base as u8, + PldmSupportedType::FwUpdate as u8, + ], + ), + ); + } + + #[test] + fn test_get_pldm_commands_responder() { + let ctrl_cxt = ControlContext::new(&PLDM_PROTOCOL_CAPABILITIES); + let mut msg_buf = [0u8; PAY_LOAD_BUFFER_LEN]; + + construct_request( + &mut msg_buf, + GetPldmCommandsRequest::new( + 0x05, + PldmMsgType::Request, + PldmSupportedType::Base as u8, + "1.1.0", + ), + ); + let resp_len = ctrl_cxt.get_pldm_commands_rsp(&mut msg_buf).unwrap(); + validate_response( + &mut msg_buf[..resp_len], + GetPldmCommandsResponse::new( + 0x05, + PldmBaseCompletionCode::Success as u8, + &[ + PldmControlCmd::SetTid as u8, + PldmControlCmd::GetTid as u8, + PldmControlCmd::GetPldmCommands as u8, + PldmControlCmd::GetPldmVersion as u8, + PldmControlCmd::GetPldmTypes as u8, + ], + ), + ); + + msg_buf.fill(0); + + construct_request( + &mut msg_buf, + GetPldmCommandsRequest::new( + 0x06, + PldmMsgType::Request, + PldmSupportedType::FwUpdate as u8, + "1.3.0", + ), + ); + let resp_len = ctrl_cxt.get_pldm_commands_rsp(&mut msg_buf).unwrap(); + validate_response( + &mut msg_buf[..resp_len], + GetPldmCommandsResponse::new( + 0x06, + PldmBaseCompletionCode::Success as u8, + &[ + FwUpdateCmd::QueryDeviceIdentifiers as u8, + FwUpdateCmd::GetFirmwareParameters as u8, + FwUpdateCmd::RequestUpdate as u8, + FwUpdateCmd::PassComponentTable as u8, + FwUpdateCmd::UpdateComponent as u8, + FwUpdateCmd::RequestFirmwareData as u8, + FwUpdateCmd::TransferComplete as u8, + FwUpdateCmd::VerifyComplete as u8, + FwUpdateCmd::ApplyComplete as u8, + FwUpdateCmd::ActivateFirmware as u8, + FwUpdateCmd::GetStatus as u8, + FwUpdateCmd::CancelUpdate as u8, + ], + ), + ); + + // Test get pldm commands request with invalid pldm type + msg_buf.fill(0); + construct_request( + &mut msg_buf, + GetPldmCommandsRequest::new( + 0x09, + PldmMsgType::Request, + PldmSupportedType::Fru as u8, + "1.3.5", + ), + ); + let resp_len = ctrl_cxt.get_pldm_commands_rsp(&mut msg_buf).unwrap(); + validate_response( + &mut msg_buf[..resp_len], + PldmFailureResponse::new( + 0x09, + PldmSupportedType::Base, + PldmControlCmd::GetPldmCommands as u8, + PldmControlCompletionCode::InvalidPldmTypeInRequestData as u8, + ), + ); + + // Test get pldm commands request with invalid protocol version + msg_buf.fill(0); + construct_request( + &mut msg_buf, + GetPldmCommandsRequest::new( + 0x0A, + PldmMsgType::Request, + PldmSupportedType::Base as u8, + "1.2.0", + ), + ); + let resp_len = ctrl_cxt.get_pldm_commands_rsp(&mut msg_buf).unwrap(); + validate_response( + &mut msg_buf[..resp_len], + PldmFailureResponse::new( + 0x0A, + PldmSupportedType::Base, + PldmControlCmd::GetPldmCommands as u8, + PldmControlCompletionCode::InvalidPldmVersionInRequestData as u8, + ), + ); + + // Test invalid length + msg_buf.fill(0); + construct_request( + &mut msg_buf, + GetPldmCommandsRequest::new( + 0x0A, + PldmMsgType::Request, + PldmSupportedType::Base as u8, + "1.1.0", + ), + ); + let resp_len = ctrl_cxt + .get_pldm_commands_rsp(&mut msg_buf[..PLDM_FAILURE_RESP_LEN + 1]) + .unwrap(); + assert_eq!(resp_len, PLDM_FAILURE_RESP_LEN); + validate_response( + &mut msg_buf[..resp_len], + PldmFailureResponse::new( + 0x0A, + PldmSupportedType::Base, + PldmControlCmd::GetPldmCommands as u8, + PldmBaseCompletionCode::InvalidLength as u8, + ), + ); + } + + #[test] + fn test_get_version_responder() { + let ctrl_cxt = ControlContext::new(&PLDM_PROTOCOL_CAPABILITIES); + let mut msg_buf = [0u8; PAY_LOAD_BUFFER_LEN]; + + // Test base protocol version + construct_request( + &mut msg_buf, + GetPldmVersionRequest::new( + 0x07, + PldmMsgType::Request, + 0, + TransferOperationFlag::GetFirstPart, + PldmSupportedType::Base, + ), + ); + let resp_len = ctrl_cxt.get_pldm_version_rsp(&mut msg_buf).unwrap(); + validate_response( + &mut msg_buf[..resp_len], + GetPldmVersionResponse::new( + 0x07, + PldmBaseCompletionCode::Success as u8, + 0, + TransferRespFlag::StartAndEnd, + "1.1.0", + ) + .unwrap(), + ); + + msg_buf.fill(0); + + // Test firmware update protocol version + construct_request( + &mut msg_buf, + GetPldmVersionRequest::new( + 0x08, + PldmMsgType::Request, + 0, + TransferOperationFlag::GetFirstPart, + PldmSupportedType::FwUpdate, + ), + ); + let resp_len = ctrl_cxt.get_pldm_version_rsp(&mut msg_buf).unwrap(); + validate_response( + &mut msg_buf[..resp_len], + GetPldmVersionResponse::new( + 0x08, + PldmBaseCompletionCode::Success as u8, + 0, + TransferRespFlag::StartAndEnd, + "1.3.0", + ) + .unwrap(), + ); + + // Test get pldm version request with invalid transfer operation flag + msg_buf.fill(0); + construct_request( + &mut msg_buf, + GetPldmVersionRequest::new( + 0x0B, + PldmMsgType::Request, + 0, + TransferOperationFlag::GetNextPart, + PldmSupportedType::Base, + ), + ); + let resp_len = ctrl_cxt.get_pldm_version_rsp(&mut msg_buf).unwrap(); + validate_response( + &mut msg_buf[..resp_len], + PldmFailureResponse::new( + 0x0B, + PldmSupportedType::Base, + PldmControlCmd::GetPldmVersion as u8, + PldmControlCompletionCode::InvalidTransferOperationFlag as u8, + ), + ); + } +} diff --git a/pldm-interface/src/firmware_device/fd_context.rs b/pldm-interface/src/firmware_device/fd_context.rs index e41861f..52631b8 100644 --- a/pldm-interface/src/firmware_device/fd_context.rs +++ b/pldm-interface/src/firmware_device/fd_context.rs @@ -1030,3 +1030,391 @@ impl FirmwareDeviceContext { } } } + +#[cfg(test)] +mod tests { + use super::*; + use pldm_common::message::firmware_update::activate_fw::SelfContainedActivationRequest; + use pldm_common::protocol::base::{PldmMsgHeader, PldmMsgType}; + use pldm_common::protocol::firmware_update::{ + ComponentClassification, PldmFirmwareString, UpdateOptionFlags, VersionStringType, + PLDM_FWUP_IMAGE_SET_VER_STR_MAX_LEN, + }; + + #[test] + fn test_firmware_device_context_new() { + let fd_ctx = FirmwareDeviceContext::new(); + + // Verify initial state is Idle + assert_eq!(fd_ctx.internal.get_fd_state(), FirmwareDeviceState::Idle); + assert!(!fd_ctx.internal.is_update_mode()); + } + + #[test] + fn test_query_devid_rsp_basic() { + let fd_ctx = FirmwareDeviceContext::new(); + let mut buffer = [0u8; 256]; + + // Create a QueryDeviceIdentifiers request + let req = QueryDeviceIdentifiersRequest::new(0x01, PldmMsgType::Request); + req.encode(&mut buffer).unwrap(); + + // Process the request + let result = fd_ctx.query_devid_rsp(&mut buffer); + + // Should succeed or return error based on FdOps implementation + // The actual behavior depends on the FdOps mock/implementation + assert!(result.is_ok() || result.is_err()); + } + + #[test] + fn test_request_update_already_in_update_mode() { + let mut fd_ctx = FirmwareDeviceContext::new(); + let mut buffer = [0u8; 256]; + + let version_string: PldmFirmwareString = PldmFirmwareString { + str_type: VersionStringType::Unspecified as u8, + str_len: 0, + str_data: [0u8; PLDM_FWUP_IMAGE_SET_VER_STR_MAX_LEN], + }; + + // First request should succeed + let req = RequestUpdateRequest::new( + 0x01, + PldmMsgType::Request, + 256, // max_transfer_size + 1, // num_components + 5, // max_outstanding_transfer_req + 7, // comp_image_set_version_string_len + &version_string, + ); + req.encode(&mut buffer).unwrap(); + + let result = fd_ctx.request_update_rsp(&mut buffer); + if result.is_ok() { + // If first request succeeded, verify we're in LearnComponents state + assert_eq!( + fd_ctx.internal.get_fd_state(), + FirmwareDeviceState::LearnComponents + ); + + // Second request should fail with AlreadyInUpdateMode + buffer.fill(0); + req.encode(&mut buffer).unwrap(); + let result2 = fd_ctx.request_update_rsp(&mut buffer); + assert!(result2.is_ok()); // Returns Ok but with failure completion code + + // Decode response to check completion code + let resp_header = PldmMsgHeader::decode(&buffer).unwrap(); + assert!(!resp_header.is_request()); + let completion_code = buffer[3]; + assert_eq!( + completion_code, + FwUpdateCompletionCode::AlreadyInUpdateMode as u8 + ); + } + } + + #[test] + fn test_request_update_invalid_transfer_size() { + let mut fd_ctx = FirmwareDeviceContext::new(); + let mut buffer = [0u8; 256]; + + let version_string: PldmFirmwareString = PldmFirmwareString { + str_type: VersionStringType::Unspecified as u8, + str_len: 0, + str_data: [0u8; PLDM_FWUP_IMAGE_SET_VER_STR_MAX_LEN], + }; + + // Create request with too small transfer size + let req = RequestUpdateRequest::new( + 0x02, + PldmMsgType::Request, + 16, // max_transfer_size - too small, must be >= 32 + 1, // num_components + 5, // max_outstanding_transfer_req + 7, // comp_image_set_version_string_len + &version_string, + ); + req.encode(&mut buffer).unwrap(); + + let result = fd_ctx.request_update_rsp(&mut buffer); + assert!(result.is_ok()); // Returns Ok but with failure completion code + + // Check completion code + let completion_code = buffer[3]; + assert_eq!( + completion_code, + FwUpdateCompletionCode::InvalidTransferLength as u8 + ); + } + + #[test] + fn test_pass_component_invalid_state() { + let mut fd_ctx = FirmwareDeviceContext::new(); + let mut buffer = [0u8; 256]; + + // Try to pass component when not in LearnComponents state (currently Idle) + let comp_ver_str: PldmFirmwareString = PldmFirmwareString { + str_type: VersionStringType::Unspecified as u8, + str_len: 0, + str_data: [0u8; PLDM_FWUP_IMAGE_SET_VER_STR_MAX_LEN], + }; + + let req = PassComponentTableRequest::new( + 0x03, + PldmMsgType::Request, + TransferRespFlag::Start, + ComponentClassification::ApplicationSoftware, + 0x01, + 0, + 0x12345678, + &comp_ver_str, + ); + req.encode(&mut buffer).unwrap(); + + let result = fd_ctx.pass_component_rsp(&mut buffer); + assert!(result.is_ok()); // Returns Ok but with failure completion code + + // Check completion code + let completion_code = buffer[3]; + assert_eq!( + completion_code, + FwUpdateCompletionCode::InvalidStateForCommand as u8 + ); + } + + #[test] + fn test_update_component_invalid_state() { + let mut fd_ctx = FirmwareDeviceContext::new(); + let mut buffer = [0u8; 256]; + + // Try to pass component when not in LearnComponents state (currently Idle) + let comp_ver_str: PldmFirmwareString = PldmFirmwareString { + str_type: VersionStringType::Unspecified as u8, + str_len: 0, + str_data: [0u8; PLDM_FWUP_IMAGE_SET_VER_STR_MAX_LEN], + }; + + // Try to update component when not in ReadyXfer state (currently Idle) + let req = UpdateComponentRequest::new( + 0x04, + PldmMsgType::Request, + ComponentClassification::ApplicationSoftware, + 0x01, + 0, + 0x12345678, + 0x1000, + UpdateOptionFlags(0), + &comp_ver_str, + ); + req.encode(&mut buffer).unwrap(); + + let result = fd_ctx.update_component_rsp(&mut buffer); + assert!(result.is_ok()); // Returns Ok but with failure completion code + + // Check completion code + let completion_code = buffer[3]; + assert_eq!( + completion_code, + FwUpdateCompletionCode::InvalidStateForCommand as u8 + ); + } + + #[test] + fn test_activate_firmware_invalid_state() { + let mut fd_ctx = FirmwareDeviceContext::new(); + let mut buffer = [0u8; 256]; + + // Try to activate firmware when not in ReadyXfer state (currently Idle) + let req = ActivateFirmwareRequest::new( + 0x05, + PldmMsgType::Request, + SelfContainedActivationRequest::ActivateSelfContainedComponents, // self_contained_activation_req + ); + req.encode(&mut buffer).unwrap(); + + let result = fd_ctx.activate_firmware_rsp(&mut buffer); + assert!(result.is_ok()); // Returns Ok but with failure completion code + + // Check completion code + let completion_code = buffer[3]; + assert_eq!( + completion_code, + FwUpdateCompletionCode::InvalidStateForCommand as u8 + ); + } + + #[test] + fn test_cancel_update_component_not_in_update_mode() { + let mut fd_ctx = FirmwareDeviceContext::new(); + let mut buffer = [0u8; 256]; + + // Try to cancel when not in update mode (currently Idle) + let req = CancelUpdateComponentRequest::new(0x06, PldmMsgType::Request); + req.encode(&mut buffer).unwrap(); + + let result = fd_ctx.cancel_update_component_rsp(&mut buffer); + assert!(result.is_ok()); // Returns Ok but with failure completion code + + // Check completion code + let completion_code = buffer[3]; + assert_eq!( + completion_code, + FwUpdateCompletionCode::NotInUpdateMode as u8 + ); + } + + #[test] + fn test_get_status_initial_state() { + let mut fd_ctx = FirmwareDeviceContext::new(); + let mut buffer = [0u8; 256]; + + // Get status in initial Idle state + let req = GetStatusRequest::new(0x0A, PldmMsgType::Request); + req.encode(&mut buffer).unwrap(); + + let result = fd_ctx.get_status_rsp(&mut buffer); + assert!(result.is_ok()); + + // Verify response is properly encoded + let resp_header = PldmMsgHeader::decode(&buffer).unwrap(); + assert!(!resp_header.is_request()); + + // Basic validation - completion code should be at offset 3 + let completion_code = buffer[3]; + assert_eq!(completion_code, PldmBaseCompletionCode::Success as u8); + } + + #[test] + fn test_state_transitions() { + let mut fd_ctx = FirmwareDeviceContext::new(); + + // Initial state should be Idle + assert_eq!(fd_ctx.internal.get_fd_state(), FirmwareDeviceState::Idle); + assert!(!fd_ctx.internal.is_update_mode()); + + // Transition to LearnComponents + fd_ctx + .internal + .set_fd_state(FirmwareDeviceState::LearnComponents); + assert_eq!( + fd_ctx.internal.get_fd_state(), + FirmwareDeviceState::LearnComponents + ); + assert!(fd_ctx.internal.is_update_mode()); + + // Transition to ReadyXfer + fd_ctx.internal.set_fd_state(FirmwareDeviceState::ReadyXfer); + assert_eq!( + fd_ctx.internal.get_fd_state(), + FirmwareDeviceState::ReadyXfer + ); + assert!(fd_ctx.internal.is_update_mode()); + + // Transition to Download + fd_ctx.internal.set_fd_state(FirmwareDeviceState::Download); + assert_eq!( + fd_ctx.internal.get_fd_state(), + FirmwareDeviceState::Download + ); + assert!(fd_ctx.internal.is_update_mode()); + + // Transition to Verify + fd_ctx.internal.set_fd_state(FirmwareDeviceState::Verify); + assert_eq!(fd_ctx.internal.get_fd_state(), FirmwareDeviceState::Verify); + assert!(fd_ctx.internal.is_update_mode()); + + // Transition to Apply + fd_ctx.internal.set_fd_state(FirmwareDeviceState::Apply); + assert_eq!(fd_ctx.internal.get_fd_state(), FirmwareDeviceState::Apply); + assert!(fd_ctx.internal.is_update_mode()); + + // Transition back to Idle + fd_ctx.internal.set_fd_idle(GetStatusReasonCode::ActivateFw); + assert_eq!(fd_ctx.internal.get_fd_state(), FirmwareDeviceState::Idle); + assert!(!fd_ctx.internal.is_update_mode()); + } + + #[test] + fn test_should_send_fd_request_unused() { + let fd_ctx = FirmwareDeviceContext::new(); + + // Initial state should be Unused, shouldn't send request + assert!(!fd_ctx.should_send_fd_request()); + } + + #[test] + fn test_should_send_fd_request_ready() { + let mut fd_ctx = FirmwareDeviceContext::new(); + + // Set state to Ready + fd_ctx + .internal + .set_fd_req(FdReqState::Ready, false, None, None, None, None); + + // Should send request when Ready + assert!(fd_ctx.should_send_fd_request()); + } + + #[test] + fn test_should_send_fd_request_failed() { + let mut fd_ctx = FirmwareDeviceContext::new(); + + // Set state to Failed + fd_ctx + .internal + .set_fd_req(FdReqState::Failed, false, None, None, None, None); + + // Shouldn't send request when Failed + assert!(!fd_ctx.should_send_fd_request()); + } + + #[test] + fn test_transfer_size_management() { + let mut fd_ctx = FirmwareDeviceContext::new(); + + // Set a transfer size + fd_ctx.internal.set_xfer_size(1024); + + // Verify it can be retrieved (through internal state) + // Note: This tests the internal state management + assert_eq!(fd_ctx.internal.get_xfer_size(), 1024); + } + + #[test] + fn test_update_flags_management() { + let mut fd_ctx = FirmwareDeviceContext::new(); + + let mut flags = UpdateOptionFlags(0); + flags.set_request_force_update(true); + flags.set_component_opaque_data(true); + + fd_ctx.internal.set_update_flags(flags); + + let retrieved_flags = fd_ctx.internal.get_update_flags(); + assert!(retrieved_flags.request_force_update()); + assert!(retrieved_flags.component_opaque_data()); + } + + #[test] + fn test_component_storage() { + let mut fd_ctx = FirmwareDeviceContext::new(); + + let comp = FirmwareComponent::new( + 0x0A, + 0x01, + 0, + 0x12345678, + PldmFirmwareString::new("ASCII", "v1.0.0").unwrap(), + Some(0x1000), + Some(UpdateOptionFlags(0)), + ); + + fd_ctx.internal.set_component(&comp); + + let stored_comp = fd_ctx.internal.get_component(); + assert_eq!(stored_comp.comp_identifier, 0x01); + assert_eq!(stored_comp.comp_comparison_stamp, 0x12345678); + } +}