@@ -1317,6 +1317,128 @@ void PrmDbTester::runPrmFileLoadIllegal() {
13171317 ASSERT_EVENTS_PrmIdAdded_SIZE (1 );
13181318}
13191319
1320+ void PrmDbTester::runShorterSaveDoesNotCorrupt () {
1321+ // NOTE: test generated by AI
1322+ const FwPrmIdType ID_A = 0xA0 ;
1323+ const FwPrmIdType ID_B = 0xA1 ;
1324+ const FwPrmIdType ID_C = 0xA2 ;
1325+ const U32 INITIAL_VAL = 0x11223344 ;
1326+ const U32 UPDATED_VAL = 0x99 ;
1327+
1328+ Fw::ParamBuffer pBuff;
1329+ Fw::QueuedComponentBase::MsgDispatchStatus dispatchStat;
1330+
1331+ // -------------------------------------------------------------------
1332+ // Phase 1: populate with 3 parameters and save (the "longer" file)
1333+ // -------------------------------------------------------------------
1334+ this ->m_impl .clearDb (PrmDbType::DB_ACTIVE);
1335+ this ->clearEvents ();
1336+
1337+ const FwPrmIdType ids[3 ] = {ID_A, ID_B, ID_C};
1338+ for (FwSizeType i = 0 ; i < 3 ; i++) {
1339+ pBuff.resetSer ();
1340+ ASSERT_EQ (Fw::FW_SERIALIZE_OK, pBuff.serializeFrom (INITIAL_VAL));
1341+ this ->invoke_to_setPrm (0 , ids[i], pBuff);
1342+ dispatchStat = this ->m_impl .doDispatch ();
1343+ EXPECT_EQ (Fw::QueuedComponentBase::MSG_DISPATCH_OK, dispatchStat);
1344+ }
1345+ ASSERT_EVENTS_PrmIdAdded_SIZE (3 );
1346+
1347+ Os::Stub::File::Test::StaticData::setWriteResult (m_io_data, sizeof m_io_data);
1348+ Os::Stub::File::Test::StaticData::setNextStatus (Os::File::OP_OK);
1349+ this ->clearEvents ();
1350+ this ->clearHistory ();
1351+ this ->sendCmd_PRM_SAVE_FILE (0 , 12 );
1352+ dispatchStat = this ->m_impl .doDispatch ();
1353+ EXPECT_EQ (Fw::QueuedComponentBase::MSG_DISPATCH_OK, dispatchStat);
1354+ ASSERT_CMD_RESPONSE_SIZE (1 );
1355+ ASSERT_CMD_RESPONSE (0 , PrmDbImpl::OPCODE_PRM_SAVE_FILE, 12 , Fw::CmdResponse::OK);
1356+ ASSERT_EVENTS_SIZE (1 );
1357+ ASSERT_EVENTS_PrmFileSaveComplete_SIZE (1 );
1358+ ASSERT_EVENTS_PrmFileSaveComplete (0 , 3 );
1359+
1360+ // Record how many bytes the 3-parameter save produced.
1361+ const FwSizeType longWriteSize = Os::Stub::File::Test::StaticData::data.pointer ;
1362+ ASSERT_GT (longWriteSize, static_cast <FwSizeType>(0 ));
1363+
1364+ // -------------------------------------------------------------------
1365+ // Phase 2: replace with 1 parameter and save (the "shorter" file)
1366+ //
1367+ // m_io_data is intentionally NOT zeroed between saves so that the bytes
1368+ // at positions [shortWriteSize, longWriteSize) still hold the old
1369+ // content — exactly what a POSIX file looks like without O_TRUNC.
1370+ // -------------------------------------------------------------------
1371+ this ->m_impl .clearDb (PrmDbType::DB_ACTIVE);
1372+ this ->clearEvents ();
1373+
1374+ pBuff.resetSer ();
1375+ ASSERT_EQ (Fw::FW_SERIALIZE_OK, pBuff.serializeFrom (UPDATED_VAL));
1376+ this ->invoke_to_setPrm (0 , ID_A, pBuff);
1377+ dispatchStat = this ->m_impl .doDispatch ();
1378+ EXPECT_EQ (Fw::QueuedComponentBase::MSG_DISPATCH_OK, dispatchStat);
1379+ ASSERT_EVENTS_PrmIdAdded_SIZE (1 );
1380+
1381+ // Reset the write pointer but leave the buffer contents intact.
1382+ Os::Stub::File::Test::StaticData::setWriteResult (m_io_data, sizeof m_io_data);
1383+ Os::Stub::File::Test::StaticData::setNextStatus (Os::File::OP_OK);
1384+ this ->clearEvents ();
1385+ this ->clearHistory ();
1386+ this ->sendCmd_PRM_SAVE_FILE (0 , 13 );
1387+ dispatchStat = this ->m_impl .doDispatch ();
1388+ EXPECT_EQ (Fw::QueuedComponentBase::MSG_DISPATCH_OK, dispatchStat);
1389+ ASSERT_CMD_RESPONSE_SIZE (1 );
1390+ ASSERT_CMD_RESPONSE (0 , PrmDbImpl::OPCODE_PRM_SAVE_FILE, 13 , Fw::CmdResponse::OK);
1391+ ASSERT_EVENTS_SIZE (1 );
1392+ ASSERT_EVENTS_PrmFileSaveComplete_SIZE (1 );
1393+ ASSERT_EVENTS_PrmFileSaveComplete (0 , 1 );
1394+
1395+ const FwSizeType shortWriteSize = Os::Stub::File::Test::StaticData::data.pointer ;
1396+
1397+ // Confirm the precondition: the second save must be strictly smaller.
1398+ // If this fires, the test is misconfigured — the two saves must differ
1399+ // in total byte count for the truncation scenario to be meaningful.
1400+ ASSERT_LT (shortWriteSize, longWriteSize);
1401+
1402+ // -------------------------------------------------------------------
1403+ // Phase 3: reload using only the shorter image — must not emit
1404+ // PrmFileBadCrc and must return the updated value.
1405+ //
1406+ // We read back exactly shortWriteSize bytes (the content produced by
1407+ // the second save). This mirrors what the fixed implementation provides
1408+ // on a real filesystem: only the new, shorter content exists on disk
1409+ // because the file was properly truncated on open.
1410+ //
1411+ // Without the fix the read would span longWriteSize bytes (the full
1412+ // untruncated file), the CRC recomputed over those bytes would not match
1413+ // the header value, and PrmFileBadCrc would fire instead.
1414+ // -------------------------------------------------------------------
1415+ this ->m_impl .clearDb (PrmDbType::DB_ACTIVE);
1416+ Os::Stub::File::Test::StaticData::setReadResult (m_io_data, shortWriteSize);
1417+ Os::Stub::File::Test::StaticData::setNextStatus (Os::File::OP_OK);
1418+ this ->clearEvents ();
1419+
1420+ this ->m_impl .readParamFile ();
1421+
1422+ // The load must succeed — no CRC mismatch event.
1423+ ASSERT_EVENTS_PrmFileBadCrc_SIZE (0 );
1424+ ASSERT_EVENTS_SIZE (1 );
1425+ ASSERT_EVENTS_PrmFileLoadComplete_SIZE (1 );
1426+ ASSERT_EVENTS_PrmFileLoadComplete (0 , " ACTIVE" , 1 , 1 , 0 );
1427+
1428+ // The updated (shorter) value must be present.
1429+ pBuff.resetSer ();
1430+ EXPECT_EQ (Fw::ParamValid::VALID, this ->invoke_to_getPrm (0 , ID_A, pBuff).e );
1431+ U32 readVal = 0 ;
1432+ ASSERT_EQ (Fw::FW_SERIALIZE_OK, pBuff.deserializeTo (readVal));
1433+ EXPECT_EQ (UPDATED_VAL, readVal);
1434+
1435+ // ID_B and ID_C were not in the second save and must not be present.
1436+ pBuff.resetSer ();
1437+ EXPECT_EQ (Fw::ParamValid::INVALID, this ->invoke_to_getPrm (0 , ID_B, pBuff).e );
1438+ pBuff.resetSer ();
1439+ EXPECT_EQ (Fw::ParamValid::INVALID, this ->invoke_to_getPrm (0 , ID_C, pBuff).e );
1440+ }
1441+
13201442PrmDbTester* PrmDbTester::PrmDbTestFile::s_tester = nullptr ;
13211443
13221444void PrmDbTester::PrmDbTestFile::setTester (Svc::PrmDbTester* tester) {
0 commit comments