Merged
test(drivers): synchronize asynchronous lifecycle fixtures #1334
liuxingsenAdd on 25 days ago
test(drivers): synchronize asynchronous lifecycle fixtures #1334
Merged
Total: 11 files changed+443-222
| @@ -210,7 +210,7 @@ MC_REFLECT(drivers::pcie_nic_card::hisi::hi1822_fc::test::test_chip_object, ((m_ | |||
| 210 | 210 | ||
| 211 | using namespace drivers::pcie_nic_card::hisi::hi1822_fc::test; | 211 | using namespace drivers::pcie_nic_card::hisi::hi1822_fc::test; |
| 212 | 212 | ||
| 213 | -TEST_F(Hi1822FcCardTest, DISABLED_Start_WithPorts) | 213 | +TEST_F(Hi1822FcCardTest, Start_WithPorts) |
| 214 | { | 214 | { |
| 215 | static int port_counter = 0; | 215 | static int port_counter = 0; |
| 216 | port_counter++; | 216 | port_counter++; |
| @@ -231,24 +231,33 @@ TEST_F(Hi1822FcCardTest, DISABLED_Start_WithPorts) | |||
| 231 | port2->set_position(port2_position.c_str()); | 231 | port2->set_position(port2_position.c_str()); |
| 232 | port2->set_parent(m_card.get()); | 232 | port2->set_parent(m_card.get()); |
| 233 | 233 | ||
| 234 | - auto csr1 = create_test_csr(); | 234 | + auto csr1 = create_test_csr(); |
| 235 | - csr1["PortId"] = port_counter; | 235 | + csr1["bmc.dev.NetworkPort"] = mc::dict{{"PortId", uint8_t{0}}}; |
| 236 | - auto connector = create_test_connector(); | 236 | + auto connector = create_test_connector(); |
| 237 | port1->init(csr1, connector); | 237 | port1->init(csr1, connector); |
| 238 | 238 | ||
| 239 | - auto csr2 = create_test_csr(); | 239 | + auto csr2 = create_test_csr(); |
| 240 | - csr2["PortId"] = port_counter + 1; | 240 | + csr2["bmc.dev.NetworkPort"] = mc::dict{{"PortId", uint8_t{1}}}; |
| 241 | port2->init(csr2, connector); | 241 | port2->init(csr2, connector); |
| 242 | 242 | ||
| 243 | m_test_service->register_object(port1); | 243 | m_test_service->register_object(port1); |
| 244 | m_test_service->register_object(port2); | 244 | m_test_service->register_object(port2); |
| 245 | 245 | ||
| 246 | - m_card->init_network_ports(); | 246 | + auto optical1 = mc::make_shared<dev::hi1822_fc_optical_module>(); |
| 247 | + optical1->set_object_name("OpticalTransceiver_1"); | ||
| 248 | + optical1->set_parent(port1.get()); | ||
| 249 | + auto optical2 = mc::make_shared<dev::hi1822_fc_optical_module>(); | ||
| 250 | + optical2->set_object_name("OpticalTransceiver_2"); | ||
| 251 | + optical2->set_parent(port2.get()); | ||
| 247 | 252 | ||
| 248 | auto card_csr = create_test_csr(); | 253 | auto card_csr = create_test_csr(); |
| 249 | m_card->init(card_csr, connector); | 254 | m_card->init(card_csr, connector); |
| 250 | bool result = m_card->start(); | 255 | bool result = m_card->start(); |
| 251 | EXPECT_TRUE(result); | 256 | EXPECT_TRUE(result); |
| 257 | + EXPECT_EQ(port1->m_optical_module, optical1.get()); | ||
| 258 | + EXPECT_EQ(port2->m_optical_module, optical2.get()); | ||
| 259 | + EXPECT_EQ(optical1->m_optical_module.get_port_id(), 0); | ||
| 260 | + EXPECT_EQ(optical2->m_optical_module.get_port_id(), 1); | ||
| 252 | 261 | ||
| 253 | // 等待 start() 中的异步操作完成,包括端口启动和 IMU 协议初始化 | 262 | // 等待 start() 中的异步操作完成,包括端口启动和 IMU 协议初始化 |
| 254 | std::this_thread::sleep_for(std::chrono::milliseconds(30)); | 263 | std::this_thread::sleep_for(std::chrono::milliseconds(30)); |
| @@ -685,7 +685,7 @@ TEST_F(network_adapter_fault_status_test, check_netCard_Health_by_smbus_empty_re | |||
| 685 | m_fault_status->stop_smbus_update_task(); | 685 | m_fault_status->stop_smbus_update_task(); |
| 686 | } | 686 | } |
| 687 | 687 | ||
| 688 | -TEST_F(network_adapter_fault_status_test, DISABLED_updata_fault_code_by_smbus_success) | 688 | +TEST_F(network_adapter_fault_status_test, updata_fault_code_by_smbus_success) |
| 689 | { | 689 | { |
| 690 | std::string health_response; | 690 | std::string health_response; |
| 691 | uint8_t health = 1; | 691 | uint8_t health = 1; |
| @@ -703,18 +703,26 @@ TEST_F(network_adapter_fault_status_test, DISABLED_updata_fault_code_by_smbus_su | |||
| 703 | m_fault_status->start_smbus_update_task(m_smbus, mc::milliseconds(100)); | 703 | m_fault_status->start_smbus_update_task(m_smbus, mc::milliseconds(100)); |
| 704 | m_fault_status->check_netCard_Health_by_smbus(); | 704 | m_fault_status->check_netCard_Health_by_smbus(); |
| 705 | m_fault_status->updata_fault_code_by_smbus(); | 705 | m_fault_status->updata_fault_code_by_smbus(); |
| 706 | - // 方法没有返回值,主要验证不崩溃 | 706 | + EXPECT_EQ(m_fault_status->FaultCode.get_value().as<std::string>(), "4097, 4098"); |
| 707 | 707 | ||
| 708 | // 显式停止定时器,避免在 TearDown 时定时器仍在运行 | 708 | // 显式停止定时器,避免在 TearDown 时定时器仍在运行 |
| 709 | m_fault_status->stop_smbus_update_task(); | 709 | m_fault_status->stop_smbus_update_task(); |
| 710 | } | 710 | } |
| 711 | 711 | ||
| 712 | -TEST_F(network_adapter_fault_status_test, DISABLED_updata_fault_code_by_smbus_empty_response) | 712 | +TEST_F(network_adapter_fault_status_test, updata_fault_code_by_smbus_empty_response) |
| 713 | { | 713 | { |
| 714 | - m_smbus->set_send_request_result(""); | 714 | + auto oem = mc::make_shared<dev::NetworkAdapter_Oem>(); |
| 715 | - m_fault_status->start_smbus_update_task(m_smbus, mc::milliseconds(100)); | 715 | + oem->SmBiosStatus.set_value(uint8_t{0}); |
| 716 | - | 716 | + m_fault_status->set_network_adapter_oem(oem.get()); |
| 717 | + m_fault_status->FaultCode.set_value("stale fault"); | ||
| 718 | + m_smbus->set_send_request_result_for_opcode(0x01, std::string(1, '\1')); | ||
| 719 | + m_smbus->set_send_request_result_for_opcode(0x02, ""); | ||
| 720 | + m_fault_status->start_smbus_update_task(m_smbus, mc::milliseconds(60000)); | ||
| 721 | + m_fault_status->check_netCard_Health_by_smbus(); | ||
| 717 | m_fault_status->updata_fault_code_by_smbus(); | 722 | m_fault_status->updata_fault_code_by_smbus(); |
| 723 | + EXPECT_EQ(m_fault_status->FaultCode.get_value().as<std::string>(), "N/A"); | ||
| 724 | + m_fault_status->stop_smbus_update_task(); | ||
| 725 | + m_fault_status->set_network_adapter_oem(nullptr); | ||
| 718 | } | 726 | } |
| 719 | 727 | ||
| 720 | TEST_F(network_adapter_fault_status_test, start_smbus_update_task_with_null_pointer) | 728 | TEST_F(network_adapter_fault_status_test, start_smbus_update_task_with_null_pointer) |
| @@ -17,6 +17,7 @@ | |||
| 17 | #include <functional> | 17 | #include <functional> |
| 18 | #include <future> | 18 | #include <future> |
| 19 | #include <memory> | 19 | #include <memory> |
| 20 | +#include <mutex> | ||
| 20 | #include <string> | 21 | #include <string> |
| 21 | #include <thread> | 22 | #include <thread> |
| 22 | #include <vector> | 23 | #include <vector> |
| @@ -2149,7 +2150,7 @@ TEST_F(network_port_link_info_test, update_link_status_by_smbus_success) | |||
| 2149 | m_link_info->stop_smbus_update_task(); | 2150 | m_link_info->stop_smbus_update_task(); |
| 2150 | } | 2151 | } |
| 2151 | 2152 | ||
| 2152 | -TEST_F(network_port_link_info_test, DISABLED_update_link_status_by_smbus_port_0_up) | 2153 | +TEST_F(network_port_link_info_test, update_link_status_by_smbus_port_0_up) |
| 2153 | { | 2154 | { |
| 2154 | auto mock_smbus_obj = mc::make_shared<mock_smbus>(); | 2155 | auto mock_smbus_obj = mc::make_shared<mock_smbus>(); |
| 2155 | std::string response_data; | 2156 | std::string response_data; |
| @@ -2177,7 +2178,7 @@ TEST_F(network_port_link_info_test, update_link_status_by_smbus_empty_response) | |||
| 2177 | m_link_info->stop_smbus_update_task(); | 2178 | m_link_info->stop_smbus_update_task(); |
| 2178 | } | 2179 | } |
| 2179 | 2180 | ||
| 2180 | -TEST_F(network_port_link_info_test, DISABLED_update_link_status_by_smbus_invalid_status_value) | 2181 | +TEST_F(network_port_link_info_test, update_link_status_by_smbus_invalid_status_value) |
| 2181 | { | 2182 | { |
| 2182 | auto mock_smbus_obj = mc::make_shared<mock_smbus>(); | 2183 | auto mock_smbus_obj = mc::make_shared<mock_smbus>(); |
| 2183 | std::string response_data; | 2184 | std::string response_data; |
| @@ -2192,14 +2193,14 @@ TEST_F(network_port_link_info_test, DISABLED_update_link_status_by_smbus_invalid | |||
| 2192 | m_link_info->stop_smbus_update_task(); | 2193 | m_link_info->stop_smbus_update_task(); |
| 2193 | } | 2194 | } |
| 2194 | 2195 | ||
| 2195 | -TEST_F(network_port_link_info_test, DISABLED_update_link_status_by_smbus_not_initialized) | 2196 | +TEST_F(network_port_link_info_test, update_link_status_by_smbus_not_initialized) |
| 2196 | { | 2197 | { |
| 2197 | // 不设置 smbus_obj | 2198 | // 不设置 smbus_obj |
| 2198 | bool result = m_link_info->update_link_status_by_smbus(1); | 2199 | bool result = m_link_info->update_link_status_by_smbus(1); |
| 2199 | EXPECT_FALSE(result); | 2200 | EXPECT_FALSE(result); |
| 2200 | } | 2201 | } |
| 2201 | 2202 | ||
| 2202 | -TEST_F(network_port_link_info_test, DISABLED_start_smbus_update_task_nullptr) | 2203 | +TEST_F(network_port_link_info_test, start_smbus_update_task_nullptr) |
| 2203 | { | 2204 | { |
| 2204 | // 传入 nullptr 应该不会崩溃 | 2205 | // 传入 nullptr 应该不会崩溃 |
| 2205 | m_link_info->start_smbus_update_task(nullptr, mc::milliseconds(1000), 1); | 2206 | m_link_info->start_smbus_update_task(nullptr, mc::milliseconds(1000), 1); |
| @@ -2263,21 +2264,27 @@ TEST_F(network_port_link_info_test, ncsi_timer_callback_updates_status) | |||
| 2263 | } | 2264 | } |
| 2264 | 2265 | ||
| 2265 | // 测试定时器回调 - SMBus | 2266 | // 测试定时器回调 - SMBus |
| 2266 | -TEST_F(network_port_link_info_test, DISABLED_smbus_timer_callback_updates_status) | 2267 | +TEST_F(network_port_link_info_test, smbus_timer_callback_updates_status) |
| 2267 | { | 2268 | { |
| 2268 | auto mock_smbus_obj = mc::make_shared<mock_smbus>(); | 2269 | auto mock_smbus_obj = mc::make_shared<mock_smbus>(); |
| 2269 | std::string response_data; | 2270 | std::string response_data; |
| 2270 | response_data += static_cast<char>(0x01); // port_id=0: UP | 2271 | response_data += static_cast<char>(0x01); // port_id=0: UP |
| 2271 | mock_smbus_obj->set_send_request_result_for_opcode(0x0403, response_data); | 2272 | mock_smbus_obj->set_send_request_result_for_opcode(0x0403, response_data); |
| 2272 | 2273 | ||
| 2273 | - m_link_info->start_smbus_update_task(mock_smbus_obj, mc::milliseconds(100), 0); | 2274 | + auto requested = std::make_shared<std::promise<void>>(); |
| 2274 | - | 2275 | + auto ready = requested->get_future(); |
| 2275 | - // 直接调用一次 update_link_status_by_smbus(同步),确保状态更新且 send_request 被触发 | 2276 | + auto once = std::make_shared<std::once_flag>(); |
| 2276 | - EXPECT_TRUE(m_link_info->update_link_status_by_smbus(0)); | 2277 | + mock_smbus_obj->set_send_request_callback([requested, once]() { |
| 2277 | - EXPECT_EQ(m_link_info->LinkStatus.get_value().as<uint8_t>(), 1); | 2278 | + std::call_once(*once, [requested]() { |
| 2278 | - | 2279 | + requested->set_value(); |
| 2279 | - // 停止定时器,确保测试结束后不会有回调执行 | 2280 | + }); |
| 2281 | + }); | ||
| 2282 | + m_link_info->LinkStatus.set_value(uint8_t{0}); | ||
| 2283 | + m_link_info->start_smbus_update_task(mock_smbus_obj, mc::milliseconds(10), 0); | ||
| 2284 | + EXPECT_EQ(ready.wait_for(std::chrono::seconds(2)), std::future_status::ready); | ||
O | |||
| 2285 | + // Join the actual callback before reading the property it updates. | ||
| 2280 | m_link_info->stop_smbus_update_task(); | 2286 | m_link_info->stop_smbus_update_task(); |
| 2287 | + EXPECT_EQ(m_link_info->LinkStatus.get_value().as<uint8_t>(), 1); | ||
| 2281 | } | 2288 | } |
| 2282 | 2289 | ||
| 2283 | // ========== PLDM 相关测试 ========== | 2290 | // ========== PLDM 相关测试 ========== |
| @@ -2488,7 +2495,7 @@ TEST_F(network_port_link_info_test, pldm_timer_callback_updates_status) | |||
| 2488 | // ========== 边界场景测试 ========== | 2495 | // ========== 边界场景测试 ========== |
| 2489 | 2496 | ||
| 2490 | // 测试 update_link_status_by_smbus - port_id 超出范围 - 源码已注释 | 2497 | // 测试 update_link_status_by_smbus - port_id 超出范围 - 源码已注释 |
| 2491 | -TEST_F(network_port_link_info_test, DISABLED_update_link_status_by_smbus_port_id_out_of_range) | 2498 | +TEST_F(network_port_link_info_test, update_link_status_by_smbus_port_id_out_of_range) |
| 2492 | { | 2499 | { |
| 2493 | auto mock_smbus_obj = mc::make_shared<mock_smbus>(); | 2500 | auto mock_smbus_obj = mc::make_shared<mock_smbus>(); |
| 2494 | std::string response_data; | 2501 | std::string response_data; |
| @@ -1153,7 +1153,7 @@ TEST_F(optical_module_power_test, update_info_by_ncsi_failure) | |||
| 1153 | 1153 | ||
| 1154 | // ========== OpticalModule update_presence_info_by_smbus 测试 ========== | 1154 | // ========== OpticalModule update_presence_info_by_smbus 测试 ========== |
| 1155 | 1155 | ||
| 1156 | -TEST_F(optical_module_test, DISABLED_update_presence_info_by_smbus_presence) | 1156 | +TEST_F(optical_module_test, update_presence_info_by_smbus_presence) |
| 1157 | { | 1157 | { |
| 1158 | auto mock_smbus_obj = mc::make_shared<mock_smbus>(); | 1158 | auto mock_smbus_obj = mc::make_shared<mock_smbus>(); |
| 1159 | std::string response_data; | 1159 | std::string response_data; |
| @@ -1698,44 +1698,49 @@ TEST_F(optical_module_test, set_network_adapter_oem) | |||
| 1698 | EXPECT_TRUE(true); | 1698 | EXPECT_TRUE(true); |
| 1699 | } | 1699 | } |
| 1700 | 1700 | ||
| 1701 | -TEST_F(optical_module_test, DISABLED_set_optical_module_cooling) | 1701 | +TEST_F(optical_module_test, set_optical_module_cooling) |
| 1702 | { | 1702 | { |
| 1703 | // 目标:验证set_optical_module_cooling方法 | 1703 | // 目标:验证set_optical_module_cooling方法 |
| 1704 | auto cooling = mc::make_shared<dev::OpticalModule_TemperatureCelsius>(); | 1704 | auto cooling = mc::make_shared<dev::OpticalModule_TemperatureCelsius>(); |
| 1705 | m_optical_module->set_optical_module_cooling(cooling.get()); | 1705 | m_optical_module->set_optical_module_cooling(cooling.get()); |
| 1706 | - EXPECT_TRUE(true); | 1706 | + EXPECT_EQ(m_optical_module->m_optical_module_temperature_celsius, cooling.get()); |
| 1707 | + m_optical_module->set_optical_module_cooling(nullptr); | ||
| 1707 | } | 1708 | } |
| 1708 | 1709 | ||
| 1709 | -TEST_F(optical_module_test, DISABLED_set_optical_module_voltage) | 1710 | +TEST_F(optical_module_test, set_optical_module_voltage) |
| 1710 | { | 1711 | { |
| 1711 | // 目标:验证set_optical_module_voltage方法 | 1712 | // 目标:验证set_optical_module_voltage方法 |
| 1712 | auto voltage = mc::make_shared<dev::OpticalModule_Voltage>(); | 1713 | auto voltage = mc::make_shared<dev::OpticalModule_Voltage>(); |
| 1713 | m_optical_module->set_optical_module_voltage(voltage.get()); | 1714 | m_optical_module->set_optical_module_voltage(voltage.get()); |
| 1714 | - EXPECT_TRUE(true); | 1715 | + EXPECT_EQ(m_optical_module->m_optical_module_voltage, voltage.get()); |
| 1716 | + m_optical_module->set_optical_module_voltage(nullptr); | ||
| 1715 | } | 1717 | } |
| 1716 | 1718 | ||
| 1717 | -TEST_F(optical_module_test, DISABLED_set_optical_module_power) | 1719 | +TEST_F(optical_module_test, set_optical_module_power) |
| 1718 | { | 1720 | { |
| 1719 | // 目标:验证set_optical_module_power方法 | 1721 | // 目标:验证set_optical_module_power方法 |
| 1720 | auto power = mc::make_shared<dev::OpticalModule_Power>(); | 1722 | auto power = mc::make_shared<dev::OpticalModule_Power>(); |
| 1721 | m_optical_module->set_optical_module_power(power.get()); | 1723 | m_optical_module->set_optical_module_power(power.get()); |
| 1722 | - EXPECT_TRUE(true); | 1724 | + EXPECT_EQ(m_optical_module->m_optical_module_power, power.get()); |
| 1725 | + m_optical_module->set_optical_module_power(nullptr); | ||
| 1723 | } | 1726 | } |
| 1724 | 1727 | ||
| 1725 | -TEST_F(optical_module_test, DISABLED_set_optical_module_current) | 1728 | +TEST_F(optical_module_test, set_optical_module_current) |
| 1726 | { | 1729 | { |
| 1727 | // 目标:验证set_optical_module_current方法 | 1730 | // 目标:验证set_optical_module_current方法 |
| 1728 | auto current = mc::make_shared<dev::OpticalModule_Current>(); | 1731 | auto current = mc::make_shared<dev::OpticalModule_Current>(); |
| 1729 | m_optical_module->set_optical_module_current(current.get()); | 1732 | m_optical_module->set_optical_module_current(current.get()); |
| 1730 | - EXPECT_TRUE(true); | 1733 | + EXPECT_EQ(m_optical_module->m_optical_module_current, current.get()); |
| 1734 | + m_optical_module->set_optical_module_current(nullptr); | ||
| 1731 | } | 1735 | } |
| 1732 | 1736 | ||
| 1733 | -TEST_F(optical_module_test, DISABLED_set_optical_module_status) | 1737 | +TEST_F(optical_module_test, set_optical_module_status) |
| 1734 | { | 1738 | { |
| 1735 | // 目标:验证set_optical_module_status方法 | 1739 | // 目标:验证set_optical_module_status方法 |
| 1736 | auto status = mc::make_shared<dev::OpticalModule_Status>(); | 1740 | auto status = mc::make_shared<dev::OpticalModule_Status>(); |
| 1737 | m_optical_module->set_optical_module_status(status.get()); | 1741 | m_optical_module->set_optical_module_status(status.get()); |
| 1738 | - EXPECT_TRUE(true); | 1742 | + EXPECT_EQ(m_optical_module->m_optical_module_status, status.get()); |
| 1743 | + m_optical_module->set_optical_module_status(nullptr); | ||
| 1739 | } | 1744 | } |
| 1740 | 1745 | ||
| 1741 | // ========== 边界值测试 ========== | 1746 | // ========== 边界值测试 ========== |
| @@ -342,6 +342,9 @@ TEST_F(MucseInterfaceTest, PCIeDeviceSmbusTaskValidatesDependencies) | |||
| 342 | }); | 342 | }); |
| 343 | EXPECT_NO_THROW(device->start_smbus_update_task(smbus, mc::milliseconds(60000))); | 343 | EXPECT_NO_THROW(device->start_smbus_update_task(smbus, mc::milliseconds(60000))); |
| 344 | EXPECT_NO_THROW(device->stop_smbus_update_task()); | 344 | EXPECT_NO_THROW(device->stop_smbus_update_task()); |
| 345 | + | ||
| 346 | + oem.reset(); | ||
| 347 | + EXPECT_NO_THROW(device.reset()); | ||
| 345 | } | 348 | } |
| 346 | 349 | ||
| 347 | TEST_F(MucseInterfaceTest, PCIeDeviceSmbusReadsHandleFailuresAndEmptyFields) | 350 | TEST_F(MucseInterfaceTest, PCIeDeviceSmbusReadsHandleFailuresAndEmptyFields) |
| @@ -183,6 +183,47 @@ TEST_F(N10AbiTest, PortDelegatesNcsiAndSmbusTasksToChildren) | |||
| 183 | EXPECT_NO_THROW(port->stop_smbus_update_task()); | 183 | EXPECT_NO_THROW(port->stop_smbus_update_task()); |
| 184 | } | 184 | } |
| 185 | 185 | ||
| 186 | +TEST_F(N10AbiTest, PortNcsiTaskCancellationDoesNotOutliveParent) | ||
| 187 | +{ | ||
| 188 | + auto mctp_object = mc::make_shared<dev::mctp>(nullptr, 0, dev::MCTP_MESSAGE_TYPE::MCTP_MESSAGE_TYPE_NCSI, ""); | ||
| 189 | + auto ncsi = std::make_shared<dev::ncsi_over_mctp_mucse>(mctp_object); | ||
| 190 | + | ||
| 191 | + for (int iteration = 0; iteration < 128; ++iteration) { | ||
| 192 | + auto port = mc::make_shared<dev::n10_port>(); | ||
| 193 | + auto module = mc::make_shared<dev::n10_optical_module>(); | ||
| 194 | + port->set_service(&m_test_service); | ||
| 195 | + module->set_service(&m_test_service); | ||
| 196 | + port->set_name("NicPort_cancel"); | ||
| 197 | + module->set_name("OpticalTransceiver_cancel"); | ||
| 198 | + module->set_parent(port.get()); | ||
| 199 | + ASSERT_TRUE(port->init_optical_module()); | ||
| 200 | + | ||
| 201 | + ASSERT_TRUE(port->start_ncsi_update_task(ncsi, mc::milliseconds(100000))); | ||
| 202 | + ASSERT_TRUE(port->start_ncsi_update_task(ncsi, mc::milliseconds(100000))); | ||
| 203 | + port->stop_ncsi_update_task(); | ||
| 204 | + } | ||
| 205 | +} | ||
| 206 | + | ||
| 207 | +TEST_F(N10AbiTest, PortSmbusTaskCancellationDoesNotOutliveParent) | ||
| 208 | +{ | ||
| 209 | + auto bus = mc::make_shared<dev::smbus>(nullptr); | ||
| 210 | + | ||
| 211 | + for (int iteration = 0; iteration < 128; ++iteration) { | ||
| 212 | + auto port = mc::make_shared<dev::n10_port>(); | ||
| 213 | + auto module = mc::make_shared<dev::n10_optical_module>(); | ||
| 214 | + port->set_service(&m_test_service); | ||
| 215 | + module->set_service(&m_test_service); | ||
| 216 | + port->set_name("NicPort_cancel"); | ||
| 217 | + module->set_name("OpticalTransceiver_cancel"); | ||
| 218 | + module->set_parent(port.get()); | ||
| 219 | + ASSERT_TRUE(port->init_optical_module()); | ||
| 220 | + | ||
| 221 | + ASSERT_TRUE(port->start_smbus_update_task(bus, mc::milliseconds(100000))); | ||
| 222 | + ASSERT_TRUE(port->start_smbus_update_task(bus, mc::milliseconds(100000))); | ||
| 223 | + port->stop_smbus_update_task(); | ||
| 224 | + } | ||
| 225 | +} | ||
| 226 | + | ||
| 186 | class N10PortIdTest : public ::testing::TestWithParam<uint8_t> {}; | 227 | class N10PortIdTest : public ::testing::TestWithParam<uint8_t> {}; |
| 187 | 228 | ||
| 188 | TEST_P(N10PortIdTest, PortAcceptsPortIdBoundaryValues) | 229 | TEST_P(N10PortIdTest, PortAcceptsPortIdBoundaryValues) |
| @@ -183,6 +183,47 @@ TEST_F(N20AbiTest, PortDelegatesNcsiAndSmbusTasksToChildren) | |||
| 183 | EXPECT_NO_THROW(port->stop_smbus_update_task()); | 183 | EXPECT_NO_THROW(port->stop_smbus_update_task()); |
| 184 | } | 184 | } |
| 185 | 185 | ||
| 186 | +TEST_F(N20AbiTest, PortNcsiTaskCancellationDoesNotOutliveParent) | ||
| 187 | +{ | ||
| 188 | + auto mctp_object = mc::make_shared<dev::mctp>(nullptr, 0, dev::MCTP_MESSAGE_TYPE::MCTP_MESSAGE_TYPE_NCSI, ""); | ||
| 189 | + auto ncsi = std::make_shared<dev::ncsi_over_mctp_mucse>(mctp_object); | ||
| 190 | + | ||
| 191 | + for (int iteration = 0; iteration < 128; ++iteration) { | ||
| 192 | + auto port = mc::make_shared<dev::n20_port>(); | ||
| 193 | + auto module = mc::make_shared<dev::n20_optical_module>(); | ||
| 194 | + port->set_service(&m_test_service); | ||
| 195 | + module->set_service(&m_test_service); | ||
| 196 | + port->set_name("NicPort_cancel"); | ||
| 197 | + module->set_name("OpticalTransceiver_cancel"); | ||
| 198 | + module->set_parent(port.get()); | ||
| 199 | + ASSERT_TRUE(port->init_optical_module()); | ||
| 200 | + | ||
| 201 | + ASSERT_TRUE(port->start_ncsi_update_task(ncsi, mc::milliseconds(100000))); | ||
| 202 | + ASSERT_TRUE(port->start_ncsi_update_task(ncsi, mc::milliseconds(100000))); | ||
| 203 | + port->stop_ncsi_update_task(); | ||
| 204 | + } | ||
| 205 | +} | ||
| 206 | + | ||
| 207 | +TEST_F(N20AbiTest, PortSmbusTaskCancellationDoesNotOutliveParent) | ||
| 208 | +{ | ||
| 209 | + auto bus = mc::make_shared<dev::smbus>(nullptr); | ||
| 210 | + | ||
| 211 | + for (int iteration = 0; iteration < 128; ++iteration) { | ||
| 212 | + auto port = mc::make_shared<dev::n20_port>(); | ||
| 213 | + auto module = mc::make_shared<dev::n20_optical_module>(); | ||
| 214 | + port->set_service(&m_test_service); | ||
| 215 | + module->set_service(&m_test_service); | ||
| 216 | + port->set_name("NicPort_cancel"); | ||
| 217 | + module->set_name("OpticalTransceiver_cancel"); | ||
| 218 | + module->set_parent(port.get()); | ||
| 219 | + ASSERT_TRUE(port->init_optical_module()); | ||
| 220 | + | ||
| 221 | + ASSERT_TRUE(port->start_smbus_update_task(bus, mc::milliseconds(100000))); | ||
| 222 | + ASSERT_TRUE(port->start_smbus_update_task(bus, mc::milliseconds(100000))); | ||
| 223 | + port->stop_smbus_update_task(); | ||
| 224 | + } | ||
| 225 | +} | ||
| 226 | + | ||
| 186 | class N20PortIdTest : public ::testing::TestWithParam<uint8_t> {}; | 227 | class N20PortIdTest : public ::testing::TestWithParam<uint8_t> {}; |
| 187 | 228 | ||
| 188 | TEST_P(N20PortIdTest, PortAcceptsPortIdBoundaryValues) | 229 | TEST_P(N20PortIdTest, PortAcceptsPortIdBoundaryValues) |
| @@ -11,9 +11,11 @@ | |||
| 11 | */ | 11 | */ |
| 12 | 12 | ||
| 13 | #include <algorithm> | 13 | #include <algorithm> |
| 14 | +#include <atomic> | ||
| 14 | #include <chrono> | 15 | #include <chrono> |
| 15 | #include <cstring> | 16 | #include <cstring> |
| 16 | #include <functional> | 17 | #include <functional> |
| 18 | +#include <future> | ||
| 17 | #include <mc/common.h> | 19 | #include <mc/common.h> |
| 18 | #include <mc/engine.h> | 20 | #include <mc/engine.h> |
| 19 | #include <mctp/mctp.h> | 21 | #include <mctp/mctp.h> |
| @@ -495,7 +497,19 @@ TEST_F(NVMeNVMeMITest, GetTelemetryData_ReturnTaskId) | |||
| 495 | TEST_F(NVMeNVMeMITest, GetTelemetryData_AlreadyRunning) | 497 | TEST_F(NVMeNVMeMITest, GetTelemetryData_AlreadyRunning) |
| 496 | { | 498 | { |
| 497 | // 目标:遥测采集已在运行(标志位为 true)时再次调用返回 0 | 499 | // 目标:遥测采集已在运行(标志位为 true)时再次调用返回 0 |
| 498 | - dev::set_mctp_request_handler([](const mc::dict&, const std::vector<uint8_t>&, std::string& rsp) { | 500 | + auto request_entered = std::make_shared<std::promise<void>>(); |
| 501 | + auto request_entered_future = request_entered->get_future(); | ||
| 502 | + auto release_request = std::make_shared<std::promise<void>>(); | ||
| 503 | + auto release_request_future = release_request->get_future().share(); | ||
| 504 | + auto request_entered_signaled = std::make_shared<std::atomic<bool>>(false); | ||
| 505 | + dev::set_mctp_request_handler([request_entered, release_request_future, request_entered_signaled]( | ||
| 506 | + const mc::dict&, const std::vector<uint8_t>&, std::string& rsp) { | ||
| 507 | + if (!request_entered_signaled->exchange(true)) { | ||
| 508 | + request_entered->set_value(); | ||
| 509 | + } | ||
| 510 | + if (release_request_future.wait_for(std::chrono::seconds(5)) != std::future_status::ready) { | ||
| 511 | + return false; | ||
| 512 | + } | ||
| 499 | rsp.resize(1 + 15 + 512); | 513 | rsp.resize(1 + 15 + 512); |
| 500 | rsp[0] = NVME_MI_PROTOCOL_STATUS_SUCCESS; | 514 | rsp[0] = NVME_MI_PROTOCOL_STATUS_SUCCESS; |
| 501 | std::fill(rsp.begin() + 16, rsp.end(), 0x00); | 515 | std::fill(rsp.begin() + 16, rsp.end(), 0x00); |
| @@ -510,6 +524,7 @@ TEST_F(NVMeNVMeMITest, GetTelemetryData_AlreadyRunning) | |||
| 510 | auto owner = mc::make_shared<DeraNvmeMiTestOwner>(); | 524 | auto owner = mc::make_shared<DeraNvmeMiTestOwner>(); |
| 511 | owner->set_service(app_svc.get()); | 525 | owner->set_service(app_svc.get()); |
| 512 | m_nvme_mi->set_owner(owner.get()); | 526 | m_nvme_mi->set_owner(owner.get()); |
| 527 | + m_nvme_mi->bind_executor(mc::runtime::get_runtime_context().create_work_strand()); | ||
| 513 | 528 | ||
| 514 | auto [mctp_obj, nvme_mi_obj] = CreateMockNvmeMi(); | 529 | auto [mctp_obj, nvme_mi_obj] = CreateMockNvmeMi(); |
| 515 | m_nvme_mi->set_nvme_mi_obj(nvme_mi_obj); | 530 | m_nvme_mi->set_nvme_mi_obj(nvme_mi_obj); |
| @@ -518,14 +533,21 @@ TEST_F(NVMeNVMeMITest, GetTelemetryData_AlreadyRunning) | |||
| 518 | oem->DriveObjectPath.set_value("/bmc/Storage/Drives/Drive_0_000000"); | 533 | oem->DriveObjectPath.set_value("/bmc/Storage/Drives/Drive_0_000000"); |
| 519 | m_nvme_mi->set_nvme_oem(oem.get()); | 534 | m_nvme_mi->set_nvme_oem(oem.get()); |
| 520 | 535 | ||
| 521 | - // 第一次调用会设置 m_telemetry_running,异步任务尚未完成 | 536 | + // Block the first request inside the asynchronous collector so the second |
| 537 | + // call deterministically observes m_telemetry_running == true. | ||
| 522 | auto first = m_nvme_mi->GetTelemetryData("Host"); | 538 | auto first = m_nvme_mi->GetTelemetryData("Host"); |
| 523 | EXPECT_GT(first, 0u); | 539 | EXPECT_GT(first, 0u); |
| 540 | + EXPECT_EQ(request_entered_future.wait_for(std::chrono::seconds(5)), std::future_status::ready); | ||
| 524 | 541 | ||
| 525 | - // 立即再次调用应命中 running 标志返回 0 | ||
| 526 | EXPECT_EQ(m_nvme_mi->GetTelemetryData("Host"), 0u); | 542 | EXPECT_EQ(m_nvme_mi->GetTelemetryData("Host"), 0u); |
| 527 | 543 | ||
| 528 | - std::this_thread::sleep_for(std::chrono::milliseconds(200)); | 544 | + release_request->set_value(); |
| 545 | + auto collector_finished = std::make_shared<std::promise<void>>(); | ||
| 546 | + auto collector_finished_future = collector_finished->get_future(); | ||
| 547 | + m_nvme_mi->executor().post([collector_finished]() { | ||
| 548 | + collector_finished->set_value(); | ||
| 549 | + }); | ||
| 550 | + EXPECT_EQ(collector_finished_future.wait_for(std::chrono::seconds(5)), std::future_status::ready); | ||
| 529 | m_nvme_mi->set_owner(nullptr); | 551 | m_nvme_mi->set_owner(nullptr); |
| 530 | app_svc->stop(); | 552 | app_svc->stop(); |
| 531 | } | 553 | } |
| @@ -20,6 +20,7 @@ | |||
| 20 | #include <test_utilities/test_base.h> | 20 | #include <test_utilities/test_base.h> |
| 21 | #include <gtest/gtest.h> | 21 | #include <gtest/gtest.h> |
| 22 | 22 | ||
| 23 | +#include "../../../test_utils/device_lifecycle.h" | ||
| 23 | #include "interface/storage/bbu.h" | 24 | #include "interface/storage/bbu.h" |
| 24 | #include "interface/storage/bbu/status.h" | 25 | #include "interface/storage/bbu/status.h" |
| 25 | #include "interface/storage/controller.h" | 26 | #include "interface/storage/controller.h" |
| @@ -62,6 +63,7 @@ | |||
| 62 | #include "interface/storage/volume/settings.h" | 63 | #include "interface/storage/volume/settings.h" |
| 63 | #include "interface/storage/volume/status.h" | 64 | #include "interface/storage/volume/status.h" |
| 64 | #include "mc/app/task/task_mgmt.h" | 65 | #include "mc/app/task/task_mgmt.h" |
| 66 | +#include "mc/app/task/task_object.h" | ||
| 65 | #include "smlib/sml_library.h" | 67 | #include "smlib/sml_library.h" |
| 66 | #include "smlib/sml_public.h" | 68 | #include "smlib/sml_public.h" |
| 67 | #include "smlib/sml_public_ctrl.h" | 69 | #include "smlib/sml_public_ctrl.h" |
| @@ -1523,6 +1525,8 @@ gint32 MockLdOperation(guint32 ctrlId, guint16 targetId, guint8 operation, gpoin | |||
| 1523 | } | 1525 | } |
| 1524 | 1526 | ||
| 1525 | // 失败路径 mock:返回非零错误码 | 1527 | // 失败路径 mock:返回非零错误码 |
| 1528 | +constexpr gint32 kMockLdOperationFailure = -1; | ||
| 1529 | + | ||
| 1526 | gint32 MockLdOperation_Fail(guint32 ctrlId, guint16 targetId, guint8 operation, gpointer param, guint32 paramLength) | 1530 | gint32 MockLdOperation_Fail(guint32 ctrlId, guint16 targetId, guint8 operation, gpointer param, guint32 paramLength) |
| 1527 | { | 1531 | { |
| 1528 | (void)ctrlId; | 1532 | (void)ctrlId; |
| @@ -1530,7 +1534,7 @@ gint32 MockLdOperation_Fail(guint32 ctrlId, guint16 targetId, guint8 operation, | |||
| 1530 | (void)operation; | 1534 | (void)operation; |
| 1531 | (void)param; | 1535 | (void)param; |
| 1532 | (void)paramLength; | 1536 | (void)paramLength; |
| 1533 | - return -1; // 失败 | 1537 | + return kMockLdOperationFailure; |
| 1534 | } | 1538 | } |
| 1535 | 1539 | ||
| 1536 | // 需重启生效路径 mock:返回SML_ERR_REBOOT_REQUIRED(命令执行成功但需OS重启生效) | 1540 | // 需重启生效路径 mock:返回SML_ERR_REBOOT_REQUIRED(命令执行成功但需OS重启生效) |
| @@ -2876,8 +2880,16 @@ TEST_F(StorageDriveSettingsTest, SetHotspare_Global) | |||
| 2876 | return 0; | 2880 | return 0; |
| 2877 | }); | 2881 | }); |
| 2878 | m_drive_settings->SetSmlLibrary(&mockLib, 0, 1, 100); | 2882 | m_drive_settings->SetSmlLibrary(&mockLib, 0, 1, 100); |
| 2883 | + bool refreshed = false; | ||
| 2884 | + m_drive_settings->SetOnPropertyChanged([&] { | ||
| 2885 | + SML_PD_INFO_S info{}; | ||
| 2886 | + info.pdinfo.hot_spare = PD_HOT_SPARE_GLOBAL; | ||
| 2887 | + m_drive_settings->UpdateSettings(info); | ||
| 2888 | + refreshed = true; | ||
| 2889 | + }); | ||
| 2879 | uint32_t ret = m_drive_settings->SetHotspare(PD_HOT_SPARE_GLOBAL, 5); | 2890 | uint32_t ret = m_drive_settings->SetHotspare(PD_HOT_SPARE_GLOBAL, 5); |
| 2880 | EXPECT_EQ(ret, 0u); | 2891 | EXPECT_EQ(ret, 0u); |
| 2892 | + EXPECT_TRUE(refreshed); | ||
| 2881 | EXPECT_EQ(m_drive_settings->Hotspare.get_value(), PD_HOT_SPARE_GLOBAL); | 2893 | EXPECT_EQ(m_drive_settings->Hotspare.get_value(), PD_HOT_SPARE_GLOBAL); |
| 2882 | } | 2894 | } |
| 2883 | 2895 | ||
| @@ -2892,8 +2904,16 @@ TEST_F(StorageDriveSettingsTest, SetHotspare_Dedicated) | |||
| 2892 | return 0; | 2904 | return 0; |
| 2893 | }); | 2905 | }); |
| 2894 | m_drive_settings->SetSmlLibrary(&mockLib, 0, 1, 100); | 2906 | m_drive_settings->SetSmlLibrary(&mockLib, 0, 1, 100); |
| 2907 | + bool refreshed = false; | ||
| 2908 | + m_drive_settings->SetOnPropertyChanged([&] { | ||
| 2909 | + SML_PD_INFO_S info{}; | ||
| 2910 | + info.pdinfo.hot_spare = PD_HOT_SPARE_DEDICATED; | ||
| 2911 | + m_drive_settings->UpdateSettings(info); | ||
| 2912 | + refreshed = true; | ||
| 2913 | + }); | ||
| 2895 | uint32_t ret = m_drive_settings->SetHotspare(PD_HOT_SPARE_DEDICATED, 5); | 2914 | uint32_t ret = m_drive_settings->SetHotspare(PD_HOT_SPARE_DEDICATED, 5); |
| 2896 | EXPECT_EQ(ret, 0u); | 2915 | EXPECT_EQ(ret, 0u); |
| 2916 | + EXPECT_TRUE(refreshed); | ||
| 2897 | EXPECT_EQ(m_drive_settings->Hotspare.get_value(), PD_HOT_SPARE_DEDICATED); | 2917 | EXPECT_EQ(m_drive_settings->Hotspare.get_value(), PD_HOT_SPARE_DEDICATED); |
| 2898 | } | 2918 | } |
| 2899 | 2919 | ||
| @@ -2923,9 +2943,17 @@ TEST_F(StorageDriveSettingsTest, SetHotspare_Default) | |||
| 2923 | return 0; | 2943 | return 0; |
| 2924 | }); | 2944 | }); |
| 2925 | m_drive_settings->SetSmlLibrary(&mockLib, 0, 1, 100); | 2945 | m_drive_settings->SetSmlLibrary(&mockLib, 0, 1, 100); |
| 2946 | + bool refreshed = false; | ||
| 2947 | + m_drive_settings->SetOnPropertyChanged([&] { | ||
| 2948 | + SML_PD_INFO_S info{}; | ||
| 2949 | + info.pdinfo.hot_spare = PD_HOT_SPARE_NONE; | ||
| 2950 | + m_drive_settings->UpdateSettings(info); | ||
| 2951 | + refreshed = true; | ||
| 2952 | + }); | ||
| 2926 | uint32_t ret = m_drive_settings->SetHotspare(99, 5); | 2953 | uint32_t ret = m_drive_settings->SetHotspare(99, 5); |
| 2927 | EXPECT_EQ(ret, 0u); | 2954 | EXPECT_EQ(ret, 0u); |
| 2928 | - EXPECT_EQ(m_drive_settings->Hotspare.get_value(), 99u); | 2955 | + EXPECT_TRUE(refreshed); |
| 2956 | + EXPECT_EQ(m_drive_settings->Hotspare.get_value(), PD_HOT_SPARE_NONE); | ||
| 2929 | } | 2957 | } |
| 2930 | 2958 | ||
| 2931 | TEST_F(StorageDriveSettingsTest, SetHotspare_Failure) | 2959 | TEST_F(StorageDriveSettingsTest, SetHotspare_Failure) |
| @@ -3326,11 +3354,33 @@ TEST_F(StorageVolumeSettingsTest, SetSSDCachingEnable_WithMock) | |||
| 3326 | // 测试设置 SSD 缓存启用(成功路径)- 使用独立 mock 对象 | 3354 | // 测试设置 SSD 缓存启用(成功路径)- 使用独立 mock 对象 |
| 3327 | mc::shared_ptr<Storage_Volume_Settings> settings = mc::make_shared<Storage_Volume_Settings>(); | 3355 | mc::shared_ptr<Storage_Volume_Settings> settings = mc::make_shared<Storage_Volume_Settings>(); |
| 3328 | SmlLibrary mockLib{}; | 3356 | SmlLibrary mockLib{}; |
| 3329 | - mock_inject_ldOperation(mockLib, MockLdOperation); | 3357 | + mock_inject_ldOperation( |
| 3358 | + mockLib, [](guint32 ctrlId, guint16 targetId, guint8 operation, gpointer data, guint32 size) -> gint32 { | ||
| 3359 | + EXPECT_EQ(ctrlId, 1u); | ||
| 3360 | + EXPECT_EQ(targetId, 100u); | ||
| 3361 | + EXPECT_EQ(operation, LD_OPERATION_SET_SSCD_CACHING_ENABLE); | ||
| 3362 | + EXPECT_EQ(size, sizeof(SML_LD_SET_SSCD_CACHING_ENABLE_S)); | ||
| 3363 | + const auto& request = *static_cast<SML_LD_SET_SSCD_CACHING_ENABLE_S*>(data); | ||
| 3364 | + EXPECT_EQ(request.enable, 1); | ||
| 3365 | + EXPECT_EQ(request.sscd_count, 1u); | ||
| 3366 | + EXPECT_EQ(request.sscd_target_ids[0], 7u); | ||
| 3367 | + return 0; | ||
| 3368 | + }); | ||
| 3330 | settings->SetSmlLibrary(&mockLib, 0, 1, 100); | 3369 | settings->SetSmlLibrary(&mockLib, 0, 1, 100); |
| 3370 | + settings->SetSscdCachingContextProvider([] { | ||
| 3371 | + return Storage_Volume_Settings::SscdCachingContext{true, false, {7}}; | ||
| 3372 | + }); | ||
| 3373 | + | ||
| 3374 | + settings->updateSsdCachingEnable(false); | ||
| 3375 | + bool refreshed = false; | ||
| 3376 | + settings->SetOnLdChangedCallback([&](const LdChangeInfo&) { | ||
| 3377 | + settings->updateSsdCachingEnable(true); | ||
| 3378 | + refreshed = true; | ||
| 3379 | + }); | ||
| 3331 | 3380 | ||
| 3332 | uint32_t ret = settings->SetSSDCachingEnable(true); | 3381 | uint32_t ret = settings->SetSSDCachingEnable(true); |
| 3333 | EXPECT_EQ(ret, 0u); | 3382 | EXPECT_EQ(ret, 0u); |
| 3383 | + EXPECT_TRUE(refreshed); | ||
| 3334 | EXPECT_EQ(settings->SSDCachingEnable.get_value(), true); | 3384 | EXPECT_EQ(settings->SSDCachingEnable.get_value(), true); |
| 3335 | } | 3385 | } |
| 3336 | 3386 | ||
| @@ -3339,11 +3389,33 @@ TEST_F(StorageVolumeSettingsTest, SetSSDCachingEnable_Disable_WithMock) | |||
| 3339 | // 测试设置 SSD 缓存禁用(成功路径) | 3389 | // 测试设置 SSD 缓存禁用(成功路径) |
| 3340 | mc::shared_ptr<Storage_Volume_Settings> settings = mc::make_shared<Storage_Volume_Settings>(); | 3390 | mc::shared_ptr<Storage_Volume_Settings> settings = mc::make_shared<Storage_Volume_Settings>(); |
| 3341 | SmlLibrary mockLib{}; | 3391 | SmlLibrary mockLib{}; |
| 3342 | - mock_inject_ldOperation(mockLib, MockLdOperation); | 3392 | + mock_inject_ldOperation( |
| 3393 | + mockLib, [](guint32 ctrlId, guint16 targetId, guint8 operation, gpointer data, guint32 size) -> gint32 { | ||
| 3394 | + EXPECT_EQ(ctrlId, 1u); | ||
| 3395 | + EXPECT_EQ(targetId, 100u); | ||
| 3396 | + EXPECT_EQ(operation, LD_OPERATION_SET_SSCD_CACHING_ENABLE); | ||
| 3397 | + EXPECT_EQ(size, sizeof(SML_LD_SET_SSCD_CACHING_ENABLE_S)); | ||
| 3398 | + const auto& request = *static_cast<SML_LD_SET_SSCD_CACHING_ENABLE_S*>(data); | ||
| 3399 | + EXPECT_EQ(request.enable, 0); | ||
| 3400 | + EXPECT_EQ(request.sscd_count, 1u); | ||
| 3401 | + EXPECT_EQ(request.sscd_target_ids[0], 7u); | ||
| 3402 | + return 0; | ||
| 3403 | + }); | ||
| 3343 | settings->SetSmlLibrary(&mockLib, 0, 1, 100); | 3404 | settings->SetSmlLibrary(&mockLib, 0, 1, 100); |
| 3405 | + settings->SetSscdCachingContextProvider([] { | ||
| 3406 | + return Storage_Volume_Settings::SscdCachingContext{true, false, {7}}; | ||
| 3407 | + }); | ||
| 3408 | + | ||
| 3409 | + settings->updateSsdCachingEnable(true); | ||
| 3410 | + bool refreshed = false; | ||
| 3411 | + settings->SetOnLdChangedCallback([&](const LdChangeInfo&) { | ||
| 3412 | + settings->updateSsdCachingEnable(false); | ||
| 3413 | + refreshed = true; | ||
| 3414 | + }); | ||
| 3344 | 3415 | ||
| 3345 | uint32_t ret = settings->SetSSDCachingEnable(false); | 3416 | uint32_t ret = settings->SetSSDCachingEnable(false); |
| 3346 | EXPECT_EQ(ret, 0u); | 3417 | EXPECT_EQ(ret, 0u); |
| 3418 | + EXPECT_TRUE(refreshed); | ||
| 3347 | EXPECT_EQ(settings->SSDCachingEnable.get_value(), false); | 3419 | EXPECT_EQ(settings->SSDCachingEnable.get_value(), false); |
| 3348 | } | 3420 | } |
| 3349 | 3421 | ||
| @@ -3439,9 +3511,12 @@ TEST_F(StorageVolumeSettingsTest, SetSSDCachingEnable_Failure) | |||
| 3439 | SmlLibrary mockLib{}; | 3511 | SmlLibrary mockLib{}; |
| 3440 | mock_inject_ldOperation(mockLib, MockLdOperation_Fail); | 3512 | mock_inject_ldOperation(mockLib, MockLdOperation_Fail); |
| 3441 | settings->SetSmlLibrary(&mockLib, 0, 1, 100); | 3513 | settings->SetSmlLibrary(&mockLib, 0, 1, 100); |
| 3514 | + settings->SetSscdCachingContextProvider([] { | ||
| 3515 | + return Storage_Volume_Settings::SscdCachingContext{true, false, {7}}; | ||
| 3516 | + }); | ||
| 3442 | 3517 | ||
| 3443 | - uint32_t ret = settings->SetSSDCachingEnable(true); | 3518 | + const int32_t ret = settings->SetSSDCachingEnable(true); |
| 3444 | - EXPECT_NE(ret, 0u); | 3519 | + EXPECT_EQ(ret, kMockLdOperationFailure); |
| 3445 | } | 3520 | } |
| 3446 | 3521 | ||
| 3447 | TEST_F(StorageVolumeSettingsTest, SetBootPriority_Failure) | 3522 | TEST_F(StorageVolumeSettingsTest, SetBootPriority_Failure) |
| @@ -3528,6 +3603,9 @@ TEST_F(StorageVolumeSettingsTest, SetSSDCachingEnable_WithCallback) | |||
| 3528 | SmlLibrary mockLib{}; | 3603 | SmlLibrary mockLib{}; |
| 3529 | mock_inject_ldOperation(mockLib, MockLdOperation); | 3604 | mock_inject_ldOperation(mockLib, MockLdOperation); |
| 3530 | settings->SetSmlLibrary(&mockLib, 0, 1, 100); | 3605 | settings->SetSmlLibrary(&mockLib, 0, 1, 100); |
| 3606 | + settings->SetSscdCachingContextProvider([] { | ||
| 3607 | + return Storage_Volume_Settings::SscdCachingContext{true, false, {7}}; | ||
| 3608 | + }); | ||
| 3531 | 3609 | ||
| 3532 | bool callbackCalled = false; | 3610 | bool callbackCalled = false; |
| 3533 | settings->SetOnLdChangedCallback([&callbackCalled](const LdChangeInfo&) { | 3611 | settings->SetOnLdChangedCallback([&callbackCalled](const LdChangeInfo&) { |
| @@ -4371,7 +4449,7 @@ public: | |||
| 4371 | 4449 | ||
| 4372 | void SetUp() override | 4450 | void SetUp() override |
| 4373 | { | 4451 | { |
| 4374 | - m_settings = mc::make_shared<Storage_Controller_Settings>(); | 4452 | + m_settings = make_iface<Storage_Controller_Settings>(); |
| 4375 | m_settings->SetSmlLibrary(&m_mockSmlLib, 0, 1); | 4453 | m_settings->SetSmlLibrary(&m_mockSmlLib, 0, 1); |
| 4376 | m_settings->SetObjectPath("/bmc/dev/Storage/Controller/0"); | 4454 | m_settings->SetObjectPath("/bmc/dev/Storage/Controller/0"); |
| 4377 | // 关键链路:mc::app::service 不是 abstract_object,无法直接 set_owner。 | 4455 | // 关键链路:mc::app::service 不是 abstract_object,无法直接 set_owner。 |
| @@ -4387,6 +4465,7 @@ public: | |||
| 4387 | mock_inject_ldOperation(m_mockSmlLib, MockLdOperation_Capture); | 4465 | mock_inject_ldOperation(m_mockSmlLib, MockLdOperation_Capture); |
| 4388 | mock_inject_getLdInfo(m_mockSmlLib, MockGetLdInfo); | 4466 | mock_inject_getLdInfo(m_mockSmlLib, MockGetLdInfo); |
| 4389 | mock_inject_getLdPdList(m_mockSmlLib, MockGetLdPdList); | 4467 | mock_inject_getLdPdList(m_mockSmlLib, MockGetLdPdList); |
| 4468 | + mock_inject_getPdInfo(m_mockSmlLib, MockGetPdInfo); | ||
| 4390 | g_mock_ctrlOperation_ret = 0; | 4469 | g_mock_ctrlOperation_ret = 0; |
| 4391 | g_mock_ctrlOperation_last_op = 0xFF; | 4470 | g_mock_ctrlOperation_last_op = 0xFF; |
| 4392 | g_mock_ctrlOperation_last_data = nullptr; | 4471 | g_mock_ctrlOperation_last_data = nullptr; |
| @@ -4407,8 +4486,7 @@ public: | |||
| 4407 | 4486 | ||
| 4408 | void TearDown() override | 4487 | void TearDown() override |
| 4409 | { | 4488 | { |
| 4410 | - // 等待 post 回调完成 | 4489 | + drain_executor(*m_settings); |
| 4411 | - std::this_thread::sleep_for(std::chrono::milliseconds(100)); | ||
| 4412 | m_settings.reset(); | 4490 | m_settings.reset(); |
| 4413 | m_owner.reset(); | 4491 | m_owner.reset(); |
| 4414 | } | 4492 | } |
| @@ -4694,11 +4772,22 @@ TEST_F(StorageControllerSettingsEngineTest, CreateCommonLD_CapacityOverflow_Full | |||
| 4694 | 4772 | ||
| 4695 | TEST_F(StorageControllerSettingsEngineTest, CreateCommonLD_DriveIdNotFound) | 4773 | TEST_F(StorageControllerSettingsEngineTest, CreateCommonLD_DriveIdNotFound) |
| 4696 | { | 4774 | { |
| 4697 | - g_mock_configOperation_ret = 0; | 4775 | + g_mock_getPdInfo_ret = SML_ERR_NULL_DATA; |
| 4776 | + m_settings->SetOnLdChangedCallback([this](const LdChangeInfo&) { | ||
| 4777 | + m_callback_called = true; | ||
| 4778 | + }); | ||
| 4698 | 4779 | ||
| 4699 | uint32_t ret = m_settings->CreateCommonLD({1, 2}, 1, 1, "TestLD", 100, 0, 64, 0, 0, 0, 0, 0, 0, 0); | 4780 | uint32_t ret = m_settings->CreateCommonLD({1, 2}, 1, 1, "TestLD", 100, 0, 64, 0, 0, 0, 0, 0, 0, 0); |
| 4700 | 4781 | ||
| 4701 | - EXPECT_EQ(ret, (uint32_t)SML_ERR_NULL_DATA); | 4782 | + // Drive lookup now happens in the posted operation; the method returns a task ID. |
| 4783 | + ASSERT_TRUE(dev::test::wait_executor_idle(*m_settings)); | ||
| 4784 | + auto* task = m_test_service->task_mgmt().get_task_obj(ret); | ||
| 4785 | + ASSERT_NE(task, nullptr); | ||
| 4786 | + EXPECT_EQ(task->task_interface.m_state.get_value(), "Exception"); | ||
| 4787 | + EXPECT_EQ(task->task_interface.m_status.get_value(), "Error"); | ||
| 4788 | + EXPECT_EQ(task->task_interface.m_message_args.get_value(), std::vector<mc::string>{"0x10c0"}); | ||
| 4789 | + EXPECT_EQ(g_mock_configOperation_last_op, 0xFF); | ||
| 4790 | + EXPECT_FALSE(m_callback_called); | ||
| 4702 | } | 4791 | } |
| 4703 | 4792 | ||
| 4704 | TEST_F(StorageControllerSettingsEngineTest, CreateCommonLD_LongName) | 4793 | TEST_F(StorageControllerSettingsEngineTest, CreateCommonLD_LongName) |
| @@ -4954,11 +5043,22 @@ TEST_F(StorageControllerSettingsEngineTest, CreateCachecadeLD_CapacityOverflow_F | |||
| 4954 | 5043 | ||
| 4955 | TEST_F(StorageControllerSettingsEngineTest, CreateCachecadeLD_DriveIdNotFound) | 5044 | TEST_F(StorageControllerSettingsEngineTest, CreateCachecadeLD_DriveIdNotFound) |
| 4956 | { | 5045 | { |
| 4957 | - g_mock_configOperation_ret = 0; | 5046 | + g_mock_getPdInfo_ret = SML_ERR_NULL_DATA; |
| 5047 | + m_settings->SetOnLdChangedCallback([this](const LdChangeInfo&) { | ||
| 5048 | + m_callback_called = true; | ||
| 5049 | + }); | ||
| 4958 | 5050 | ||
| 4959 | uint32_t ret = m_settings->CreateCachecadeLD({1, 2}, 1, "TestCachecade", 0, 0, 100, 0, 0, 64); | 5051 | uint32_t ret = m_settings->CreateCachecadeLD({1, 2}, 1, "TestCachecade", 0, 0, 100, 0, 0, 64); |
| 4960 | 5052 | ||
| 4961 | - EXPECT_EQ(ret, (uint32_t)SML_ERR_NULL_DATA); | 5053 | + // Drive lookup now happens in the posted operation; the method returns a task ID. |
| 5054 | + ASSERT_TRUE(dev::test::wait_executor_idle(*m_settings)); | ||
| 5055 | + auto* task = m_test_service->task_mgmt().get_task_obj(ret); | ||
| 5056 | + ASSERT_NE(task, nullptr); | ||
| 5057 | + EXPECT_EQ(task->task_interface.m_state.get_value(), "Exception"); | ||
| 5058 | + EXPECT_EQ(task->task_interface.m_status.get_value(), "Error"); | ||
| 5059 | + EXPECT_EQ(task->task_interface.m_message_args.get_value(), std::vector<mc::string>{"0x10c0"}); | ||
| 5060 | + EXPECT_EQ(g_mock_configOperation_last_op, 0xFF); | ||
| 5061 | + EXPECT_FALSE(m_callback_called); | ||
| 4962 | } | 5062 | } |
| 4963 | 5063 | ||
| 4964 | TEST_F(StorageControllerSettingsEngineTest, CreateCachecadeLD_LongName) | 5064 | TEST_F(StorageControllerSettingsEngineTest, CreateCachecadeLD_LongName) |
| @@ -5116,10 +5216,7 @@ TEST_F(StorageControllerSettingsEngineTest, CreateCommonLD_OperationInProgress) | |||
| 5116 | { | 5216 | { |
| 5117 | g_mock_configOperation_ret = 0; | 5217 | g_mock_configOperation_ret = 0; |
| 5118 | 5218 | ||
| 5119 | - m_settings->SetGetDriveInfoCallback([](uint16_t) -> std::optional<DrivePdInfo> { | 5219 | + mock_inject_getPdInfo(m_mockSmlLib, MockGetPdInfo); |
| 5120 | - DrivePdInfo info; | ||
| 5121 | - return info; | ||
| 5122 | - }); | ||
| 5123 | 5220 | ||
| 5124 | // 把第一次投递的异步"钉"在 mock 内,保证第二次调用时操作仍在进行中(确定性命中冲突) | 5221 | // 把第一次投递的异步"钉"在 mock 内,保证第二次调用时操作仍在进行中(确定性命中冲突) |
| 5125 | { | 5222 | { |
| @@ -5162,10 +5259,7 @@ TEST_F(StorageControllerSettingsEngineTest, CreateCachecadeLD_OperationInProgres | |||
| 5162 | { | 5259 | { |
| 5163 | g_mock_configOperation_ret = 0; | 5260 | g_mock_configOperation_ret = 0; |
| 5164 | 5261 | ||
| 5165 | - m_settings->SetGetDriveInfoCallback([](uint16_t) -> std::optional<DrivePdInfo> { | 5262 | + mock_inject_getPdInfo(m_mockSmlLib, MockGetPdInfo); |
| 5166 | - DrivePdInfo info; | ||
| 5167 | - return info; | ||
| 5168 | - }); | ||
| 5169 | 5263 | ||
| 5170 | // 把第一次投递的异步"钉"在 mock 内,保证第二次调用时操作仍在进行中(确定性命中冲突) | 5264 | // 把第一次投递的异步"钉"在 mock 内,保证第二次调用时操作仍在进行中(确定性命中冲突) |
| 5171 | { | 5265 | { |
| @@ -13,7 +13,9 @@ | |||
| 13 | #include <atomic> | 13 | #include <atomic> |
| 14 | #include <chrono> | 14 | #include <chrono> |
| 15 | #include <future> | 15 | #include <future> |
| 16 | +#include <mc/engine/errors/std_errors.h> | ||
| 16 | #include <mc/engine/property/ref_object.h> | 17 | #include <mc/engine/property/ref_object.h> |
| 18 | +#include <mc/error_engine.h> | ||
| 17 | #include <mc/log.h> | 19 | #include <mc/log.h> |
| 18 | #include <mc/runtime/runtime_strand.h> | 20 | #include <mc/runtime/runtime_strand.h> |
| 19 | #include <memory> | 21 | #include <memory> |
| @@ -23,7 +25,13 @@ | |||
| 23 | #include <vector> | 25 | #include <vector> |
| 24 | #include <gtest/gtest.h> | 26 | #include <gtest/gtest.h> |
| 25 | 27 | ||
| 28 | +#include <mc/app/object.h> | ||
| 29 | +#include <mc/debounce/base.h> | ||
| 30 | +#include <mc/timer.h> | ||
| 31 | + | ||
| 32 | +#define private public | ||
| 26 | #include "../../../drivers/scanner/scanner/scanner.h" | 33 | #include "../../../drivers/scanner/scanner/scanner.h" |
| 34 | +#undef private | ||
| 27 | #include "../test_utils/root_object.h" | 35 | #include "../test_utils/root_object.h" |
| 28 | 36 | ||
| 29 | using namespace dev; | 37 | using namespace dev; |
| @@ -571,6 +579,33 @@ TEST_F(ScannerTest, test_init_missing_fields) | |||
| 571 | EXPECT_FALSE(result); | 579 | EXPECT_FALSE(result); |
| 572 | } | 580 | } |
| 573 | 581 | ||
| 582 | +TEST_F(ScannerTest, stopped_timer_can_outlive_embedded_interface) | ||
| 583 | +{ | ||
| 584 | + m_scanner->i_scanner.Period.set_value(uint32_t{60000}); | ||
| 585 | + m_scanner->i_scanner.start_scanner_task(); | ||
| 586 | + auto timer = m_scanner->i_scanner.m_scanner_timer; | ||
| 587 | + ASSERT_NE(timer, nullptr); | ||
| 588 | + ASSERT_TRUE(m_scanner->stop()); | ||
| 589 | + EXPECT_FALSE(timer->is_active()); | ||
| 590 | + EXPECT_EQ(timer->get_parent(), nullptr); | ||
| 591 | + m_test_service->unregister_object(std::string(m_scanner->get_object_path())); | ||
| 592 | + m_scanner.reset(); | ||
| 593 | + timer.reset(); | ||
| 594 | +} | ||
| 595 | + | ||
| 596 | +TEST_F(ScannerTest, replaced_timer_is_stopped_and_detached) | ||
| 597 | +{ | ||
| 598 | + auto& scanner = m_scanner->i_scanner; | ||
| 599 | + scanner.Period.set_value(uint32_t{60000}); | ||
| 600 | + scanner.start_scanner_task(); | ||
| 601 | + auto timer = scanner.m_scanner_timer; | ||
| 602 | + ASSERT_NE(timer, nullptr); | ||
| 603 | + scanner.start_scanner_task(); | ||
| 604 | + EXPECT_NE(timer, scanner.m_scanner_timer); | ||
| 605 | + EXPECT_FALSE(timer->is_active()); | ||
| 606 | + EXPECT_EQ(timer->get_parent(), nullptr); | ||
| 607 | +} | ||
| 608 | + | ||
| 574 | TEST_F(ScannerTest, test_start_stop_scanner) | 609 | TEST_F(ScannerTest, test_start_stop_scanner) |
| 575 | { | 610 | { |
| 576 | auto csr = create_test_csr(); | 611 | auto csr = create_test_csr(); |
| @@ -1404,72 +1439,30 @@ TEST_F(ScannerTest, test_set_value_zero) | |||
| 1404 | 1439 | ||
| 1405 | TEST_F(ScannerTest, test_success_debounce_count) | 1440 | TEST_F(ScannerTest, test_success_debounce_count) |
| 1406 | { | 1441 | { |
| 1407 | - auto csr = create_test_csr(); | 1442 | + auto csr = create_test_csr(); |
| 1408 | - auto connector = create_test_connector(); | 1443 | + ASSERT_TRUE(m_scanner->init(csr, create_test_connector())); |
| 1444 | + auto& scanner = m_scanner->i_scanner; | ||
| 1445 | + scanner.FailureDebounceCount.set_value(2); | ||
| 1446 | + scanner.SuccessDebounceCount.set_value(3); | ||
| 1447 | + scanner.Value.set_value(uint64_t{42}); | ||
| 1409 | 1448 | ||
| 1410 | - bool init_result = m_scanner->init(csr, connector); | 1449 | + // Drive completed read results directly so each debounce boundary is observable. |
| 1411 | - ASSERT_TRUE(init_result); | 1450 | + scanner.handle_result_failure("read failed"); |
| 1451 | + EXPECT_EQ(scanner.Status.get_value().as<uint8_t>(), SCANNER_PRE_FAILURE); | ||
| 1452 | + scanner.handle_result_failure("read failed"); | ||
| 1453 | + ASSERT_EQ(scanner.Status.get_value().as<uint8_t>(), SCANNER_FAILURE); | ||
| 1412 | 1454 | ||
| 1413 | - // 设置失败防抖计数和成功防抖计数 | 1455 | + for (uint64_t value : {100u, 101u}) { |
| 1414 | - m_scanner->i_scanner.FailureDebounceCount.set_value(2); | 1456 | + scanner.handle_result_success(value); |
| 1415 | - m_scanner->i_scanner.SuccessDebounceCount.set_value(3); | 1457 | + EXPECT_EQ(scanner.Status.get_value().as<uint8_t>(), SCANNER_FAILURE); |
| 1416 | - | 1458 | + EXPECT_EQ(scanner.Value.get_value().as<uint64_t>(), 42u); |
| 1417 | - set_chip_reference(); | 1459 | + EXPECT_TRUE(scanner.dump().error.has_value()); |
| 1418 | - | ||
| 1419 | - // 先触发失败状态 | ||
| 1420 | - mc::mutable_dict failure_csr; | ||
| 1421 | - failure_csr["Chip"] = "#/" + std::string(m_exception_chip->get_object_name()) | ||
| 1422 | - .substr(0, std::string(m_exception_chip->get_object_name()).find("_")); | ||
| 1423 | - failure_csr["Offset"] = static_cast<uint32_t>(0xDEAD); | ||
| 1424 | - failure_csr["Size"] = static_cast<uint8_t>(4); | ||
| 1425 | - failure_csr["Mask"] = static_cast<uint8_t>(0xFF); | ||
| 1426 | - failure_csr["Type"] = static_cast<uint8_t>(1); | ||
| 1427 | - failure_csr["Period"] = static_cast<uint32_t>(10); | ||
| 1428 | - | ||
| 1429 | - // 先停止当前的扫描任务(init时启动的),并等待停止完成 | ||
| 1430 | - m_scanner->stop(); | ||
| 1431 | - std::this_thread::sleep_for(std::chrono::milliseconds(20)); | ||
| 1432 | - | ||
| 1433 | - // 设置新的Period和FailureDebounceCount | ||
| 1434 | - m_scanner->i_scanner.Period.set_value(20); | ||
| 1435 | - | ||
| 1436 | - // 临时切换到异常chip来触发失败 | ||
| 1437 | - std::string exception_chip_ref = "#/" + std::string(m_exception_chip->get_object_name()) | ||
| 1438 | - .substr(0, std::string(m_exception_chip->get_object_name()).find("_")); | ||
| 1439 | - mc::variant exception_ref_obj{exception_chip_ref}; | ||
| 1440 | - from_variant(exception_ref_obj, m_scanner->i_scanner.Chip); | ||
| 1441 | - | ||
| 1442 | - // 启动扫描任务(这会启动定时器) | ||
| 1443 | - bool start_result = m_scanner->start(); | ||
| 1444 | - ASSERT_TRUE(start_result); | ||
| 1445 | - | ||
| 1446 | - // 等待失败发生 - 等待足够的时间让扫描任务执行并触发失败 | ||
| 1447 | - // FailureDebounceCount为2,Period为20ms,需要至少2次失败 = 40ms + 余量 | ||
| 1448 | - // 但由于异常chip会立即抛出异常,每次扫描都会失败 | ||
| 1449 | - // 第一次失败:failure_count=1, status=SCANNER_PRE_FAILURE(2) | ||
| 1450 | - // 第二次失败:failure_count=2, status=SCANNER_FAILURE(1) | ||
| 1451 | - std::this_thread::sleep_for(std::chrono::milliseconds(50)); | ||
| 1452 | - uint8_t failure_status = m_scanner->i_scanner.Status.get_value().as<uint8_t>(); | ||
| 1453 | - // 验证状态变为失败或预失败(取决于FailureDebounceCount) | ||
| 1454 | - // 如果状态不是预期的失败状态,跳过测试 | ||
| 1455 | - if (failure_status != SCANNER_FAILURE && failure_status != SCANNER_PRE_FAILURE) { | ||
| 1456 | - GTEST_SKIP() << "Scanner status is not in failure state, got " << static_cast<int>(failure_status); | ||
| 1457 | } | 1460 | } |
| 1458 | - EXPECT_TRUE(failure_status == SCANNER_FAILURE || failure_status == SCANNER_PRE_FAILURE); | 1461 | + scanner.handle_result_success(102); |
| 1459 | - | 1462 | + EXPECT_EQ(scanner.Status.get_value().as<uint8_t>(), SCANNER_OK); |
| 1460 | - // 切换回正常chip | 1463 | + EXPECT_EQ(scanner.Value.get_value().as<uint64_t>(), 102u); |
| 1461 | - set_chip_reference(); | 1464 | + EXPECT_EQ(scanner.dump().success_count, 3u); |
| 1462 | - | 1465 | + EXPECT_FALSE(scanner.dump().error.has_value()); |
| 1463 | - // 等待成功计数累积 - 需要足够的时间让成功计数达到SuccessDebounceCount(3) | ||
| 1464 | - // Period为20ms,成功计数需要3次,所以需要至少 20ms * 3 = 60ms,加上余量 | ||
| 1465 | - std::this_thread::sleep_for(std::chrono::milliseconds(80)); | ||
| 1466 | - | ||
| 1467 | - // 验证状态,成功计数应该足够后状态变为OK | ||
| 1468 | - uint8_t final_status = m_scanner->i_scanner.Status.get_value().as<uint8_t>(); | ||
| 1469 | - // 成功计数达到后,状态应该恢复为OK | ||
| 1470 | - EXPECT_EQ(final_status, SCANNER_OK); | ||
| 1471 | - | ||
| 1472 | - m_scanner->stop(); | ||
| 1473 | } | 1466 | } |
| 1474 | 1467 | ||
| 1475 | // ========================= Scanner异常处理分支测试 ========================= | 1468 | // ========================= Scanner异常处理分支测试 ========================= |
| @@ -1586,30 +1579,18 @@ TEST_F(ScannerTest, test_set_value_exception_handling) | |||
| 1586 | EXPECT_EQ(actual_value, 0x12345678ULL); | 1579 | EXPECT_EQ(actual_value, 0x12345678ULL); |
| 1587 | } | 1580 | } |
| 1588 | 1581 | ||
| 1589 | -// 测试 Scanner::init() 异常处理 - Offset字段为null | 1582 | +// 缺省 Offset 保留数值属性的零默认值 |
| 1590 | -TEST_F(ScannerTest, test_init_exception_offset_null) | 1583 | +TEST_F(ScannerTest, test_init_missing_offset_uses_zero_default) |
| 1591 | { | 1584 | { |
| 1592 | mc::mutable_dict csr; | 1585 | mc::mutable_dict csr; |
| 1593 | - csr["Chip"] = "#/" + m_chip_name.substr(0, m_chip_name.find("_")); | 1586 | + csr["Chip"] = "#/" + m_chip_name.substr(0, m_chip_name.find("_")); |
| 1594 | - // Offset字段缺失,from_variant后Offset应该是null | ||
| 1595 | - // 注意:如果from_variant没有设置Offset,get_value()可能返回默认值而不是null | ||
| 1596 | - // 因此这个测试可能不会失败,取决于from_variant的实现 | ||
| 1597 | csr["Size"] = static_cast<uint8_t>(4); | 1587 | csr["Size"] = static_cast<uint8_t>(4); |
| 1598 | csr["Period"] = static_cast<uint32_t>(10); | 1588 | csr["Period"] = static_cast<uint32_t>(10); |
| 1599 | 1589 | ||
| 1600 | - auto connector = create_test_connector(); | 1590 | + // Offset is a typed uint32_t property: omission preserves its valid zero default. |
| 1601 | - bool result = m_scanner->init(csr, connector); | 1591 | + ASSERT_EQ(m_scanner->i_scanner.Offset.get_value().as<uint32_t>(), 0u); |
| 1602 | - // 如果Offset字段缺失,init应该失败,但如果from_variant设置了默认值,则可能成功 | 1592 | + EXPECT_TRUE(m_scanner->init(csr, create_test_connector())); |
| 1603 | - // 根据实际行为调整断言 | 1593 | + EXPECT_EQ(m_scanner->i_scanner.Offset.get_value().as<uint32_t>(), 0u); |
| 1604 | - // 如果Offset缺失导致get_value()返回null,则应该失败 | ||
| 1605 | - // 否则,这个测试用例可能需要移除或修改 | ||
| 1606 | - if (result) { | ||
| 1607 | - // 如果init成功了,说明Offset有默认值,这不是我们要测试的场景 | ||
| 1608 | - // 这个测试用例在这种情况下无法测试null检查 | ||
| 1609 | - GTEST_SKIP() << "Offset field has default value, cannot test null check"; | ||
| 1610 | - } else { | ||
| 1611 | - EXPECT_FALSE(result); | ||
| 1612 | - } | ||
| 1613 | } | 1594 | } |
| 1614 | 1595 | ||
| 1615 | // 测试 IScanner::update_scanner_task() 异常处理 - BitIORead调用失败 | 1596 | // 测试 IScanner::update_scanner_task() 异常处理 - BitIORead调用失败 |
| @@ -1711,40 +1692,20 @@ TEST_F(ScannerTest, test_stop_scanner_task_exception_handling) | |||
| 1711 | // 测试 IScanner::init_debounce_config() 异常处理 - Debounce引用为null | 1692 | // 测试 IScanner::init_debounce_config() 异常处理 - Debounce引用为null |
| 1712 | TEST_F(ScannerTest, test_init_debounce_config_exception_null_ref) | 1693 | TEST_F(ScannerTest, test_init_debounce_config_exception_null_ref) |
| 1713 | { | 1694 | { |
| 1714 | - auto csr = create_test_csr(); | 1695 | + auto csr = create_test_csr(); |
| 1715 | - auto connector = create_test_connector(); | 1696 | + ASSERT_TRUE(m_scanner->init(csr, create_test_connector())); |
| 1697 | + auto& scanner = m_scanner->i_scanner; | ||
| 1698 | + scanner.FailureDebounceCount.set_value(1); | ||
| 1699 | + scanner.Debounce.set_property_type(mc::engine::p_type::ref_object); | ||
| 1700 | + scanner.Debounce.set_ref_object_cache(std::make_unique<mc::variant>()); | ||
| 1701 | + ASSERT_EQ(scanner.Debounce.get_value().as<mc::engine::ref_object*>(), nullptr); | ||
| 1716 | 1702 | ||
| 1717 | - bool init_result = m_scanner->init(csr, connector); | 1703 | + EXPECT_FALSE(scanner.init_debounce_config()); |
| 1718 | - ASSERT_TRUE(init_result); | 1704 | + EXPECT_EQ(scanner.Status.get_value().as<uint8_t>(), SCANNER_FAILURE); |
| 1719 | - | 1705 | + const auto info = scanner.dump(); |
| 1720 | - set_chip_reference(); | 1706 | + EXPECT_EQ(info.failure_count, 1u); |
| 1721 | - | 1707 | + ASSERT_TRUE(info.error.has_value()); |
| 1722 | - // 设置Debounce为无效引用(空字符串) | 1708 | + EXPECT_EQ(*info.error, "init_debounce_config failed: Debounce object reference is invalid"); |
| 1723 | - // 注意:如果Debounce属性类型不是ref_object,可能不会触发debounce初始化 | ||
| 1724 | - // 需要确保Debounce属性类型是ref_object才能测试这个场景 | ||
| 1725 | - m_scanner->i_scanner.Debounce.set_value(std::string("")); | ||
| 1726 | - m_scanner->i_scanner.FailureDebounceCount.set_value(1); | ||
| 1727 | - m_scanner->i_scanner.Period.set_value(10); | ||
| 1728 | - m_scanner->i_scanner.ScanEnabled.set_value(1); | ||
| 1729 | - | ||
| 1730 | - // 检查Debounce属性类型,如果不是ref_object,则跳过此测试 | ||
| 1731 | - uint32_t debounce_type = m_scanner->i_scanner.Debounce.get_property_type_value(); | ||
| 1732 | - if (debounce_type != static_cast<uint32_t>(mc::engine::p_type::ref_object)) { | ||
| 1733 | - GTEST_SKIP() << "Debounce property is not ref_object type, cannot test " | ||
| 1734 | - "null ref check"; | ||
| 1735 | - } | ||
| 1736 | - | ||
| 1737 | - // 等待扫描任务执行(需要足够的时间让任务执行至少2次以触发失败) | ||
| 1738 | - // Period为10ms,FailureDebounceCount为1,需要至少1次失败 = 10ms + 余量 | ||
| 1739 | - // debounce初始化失败会导致每次扫描都失败,所以failure_count会快速增加 | ||
| 1740 | - std::this_thread::sleep_for(std::chrono::milliseconds(30)); | ||
| 1741 | - | ||
| 1742 | - // 验证状态变为失败或预失败(由于debounce初始化失败) | ||
| 1743 | - uint8_t status = m_scanner->i_scanner.Status.get_value().as<uint8_t>(); | ||
| 1744 | - EXPECT_TRUE(status == SCANNER_FAILURE || status == SCANNER_PRE_FAILURE) | ||
| 1745 | - << "Expected failure status (1 or 2), but got " << static_cast<int>(status); | ||
| 1746 | - | ||
| 1747 | - m_scanner->stop(); | ||
| 1748 | } | 1709 | } |
| 1749 | 1710 | ||
| 1750 | // 测试 update_scanner_task 整体异常捕获 | 1711 | // 测试 update_scanner_task 整体异常捕获 |
| @@ -1845,43 +1806,25 @@ TEST_F(ScannerTest, test_stop_scanner_task_nominal_value_null) | |||
| 1845 | // 测试 init_debounce_config - 异常处理路径(获取 Debounce 对象引用失败) | 1806 | // 测试 init_debounce_config - 异常处理路径(获取 Debounce 对象引用失败) |
| 1846 | TEST_F(ScannerTest, test_init_debounce_config_exception_get_ref) | 1807 | TEST_F(ScannerTest, test_init_debounce_config_exception_get_ref) |
| 1847 | { | 1808 | { |
| 1848 | - auto csr = create_test_csr(); | 1809 | + const auto error_name = mc::engine::errors::internal_error.name; |
| 1849 | - auto connector = create_test_connector(); | 1810 | + ASSERT_TRUE(mc::error_engine::get_instance().is_registered(error_name)); |
| 1850 | 1811 | ||
| 1851 | - bool init_result = m_scanner->init(csr, connector); | 1812 | + auto csr = create_test_csr(); |
| 1852 | - ASSERT_TRUE(init_result); | 1813 | + ASSERT_TRUE(m_scanner->init(csr, create_test_connector())); |
| 1814 | + auto& scanner = m_scanner->i_scanner; | ||
| 1815 | + scanner.FailureDebounceCount.set_value(1); | ||
| 1816 | + install_ref_object(scanner.Debounce, m_mock_chip); | ||
| 1817 | + ASSERT_NE(scanner.Debounce.get_value().as<mc::engine::ref_object*>(), nullptr); | ||
| 1818 | + scanner.Debounce.set_error(error_name); | ||
| 1819 | + EXPECT_THROW(scanner.Debounce.get_value(), mc::error_exception); | ||
| 1853 | 1820 | ||
| 1854 | - set_chip_reference(); | 1821 | + EXPECT_FALSE(scanner.init_debounce_config()); |
| 1855 | - | 1822 | + EXPECT_EQ(scanner.Status.get_value().as<uint8_t>(), SCANNER_FAILURE); |
| 1856 | - // 检查 Debounce 属性类型 | 1823 | + const auto info = scanner.dump(); |
| 1857 | - uint32_t debounce_type = m_scanner->i_scanner.Debounce.get_property_type_value(); | 1824 | + EXPECT_EQ(info.failure_count, 1u); |
| 1858 | - if (debounce_type != static_cast<uint32_t>(mc::engine::p_type::ref_object)) { | 1825 | + ASSERT_TRUE(info.error.has_value()); |
| 1859 | - GTEST_SKIP() << "Debounce property is not ref_object type, cannot test " | 1826 | + EXPECT_NE(info.error->find("Unable to get Debounce object reference"), std::string::npos); |
| 1860 | - "exception path"; | 1827 | + scanner.Debounce.clear_error(); |
| 1861 | - } | ||
| 1862 | - | ||
| 1863 | - // 设置一个无效的 debounce 引用(可能导致异常) | ||
| 1864 | - // 尝试设置一个不存在的对象引用 | ||
| 1865 | - m_scanner->i_scanner.Debounce.set_value(std::string("#/NonExistentDebounceObject")); | ||
| 1866 | - m_scanner->i_scanner.FailureDebounceCount.set_value(1); | ||
| 1867 | - m_scanner->i_scanner.Period.set_value(10); | ||
| 1868 | - m_scanner->i_scanner.ScanEnabled.set_value(1); | ||
| 1869 | - | ||
| 1870 | - // 启动 scanner | ||
| 1871 | - bool start_result = m_scanner->start(); | ||
| 1872 | - ASSERT_TRUE(start_result); | ||
| 1873 | - | ||
| 1874 | - // 等待扫描任务执行(init_debounce_config 会在 update_scanner_task 中被调用) | ||
| 1875 | - std::this_thread::sleep_for(std::chrono::milliseconds(50)); | ||
| 1876 | - | ||
| 1877 | - // 验证状态变为失败或预失败(由于 debounce 初始化失败) | ||
| 1878 | - uint8_t status = m_scanner->i_scanner.Status.get_value().as<uint8_t>(); | ||
| 1879 | - EXPECT_TRUE(status == SCANNER_FAILURE || status == SCANNER_PRE_FAILURE || status == SCANNER_OK || | ||
| 1880 | - status == SCANNER_STOPPED) | ||
| 1881 | - << "Status: " << static_cast<int>(status); | ||
| 1882 | - | ||
| 1883 | - m_scanner->stop(); | ||
| 1884 | - std::this_thread::sleep_for(std::chrono::milliseconds(20)); | ||
| 1885 | } | 1828 | } |
| 1886 | 1829 | ||
| 1887 | // 测试 handle_result_success - 成功计数不足时跳过的情况 | 1830 | // 测试 handle_result_success - 成功计数不足时跳过的情况 |
| @@ -271,48 +271,96 @@ TEST_F(smbus_test, send_and_receive_request_in_frames_EmptyData) | |||
| 271 | EXPECT_FALSE(result.first); | 271 | EXPECT_FALSE(result.first); |
| 272 | } | 272 | } |
| 273 | 273 | ||
| 274 | -TEST_F(smbus_test, DISABLED_send_and_receive_request_in_frames_WithData) | 274 | +TEST_F(smbus_test, send_and_receive_request_in_frames_WithData) |
| 275 | { | 275 | { |
| 276 | // 目标:验证 send_and_receive_request_in_frames 方法可以处理有数据的情况 | 276 | // 目标:验证 send_and_receive_request_in_frames 方法可以处理有数据的情况 |
| 277 | + constexpr uint32_t receive_buffer_len = 64; | ||
| 278 | + constexpr uint16_t opcode = 0x0404; | ||
| 279 | + constexpr uint32_t data_written = 2; | ||
| 280 | + constexpr uint32_t frame_len = 10; | ||
| 281 | + constexpr int32_t expect_data_len = 0; // Pure write: reserved bytes stay zero. | ||
| 282 | + | ||
| 283 | + const std::string payload = "test_data_longer_than_max_frame"; | ||
| 284 | + | ||
| 277 | std::vector<uint8_t> response_data = {0x01, 0x02}; | 285 | std::vector<uint8_t> response_data = {0x01, 0x02}; |
| 286 | + std::vector<uint8_t> sent; | ||
| 278 | std::function<std::vector<uint8_t>(const std::vector<uint8_t>&, uint32_t)> func = | 287 | std::function<std::vector<uint8_t>(const std::vector<uint8_t>&, uint32_t)> func = |
| 279 | - [response_data](const std::vector<uint8_t>&, uint32_t) -> std::vector<uint8_t> { | 288 | + [&sent, response_data, receive_buffer_len](const std::vector<uint8_t>& request, |
| 289 | + uint32_t length) -> std::vector<uint8_t> { | ||
| 290 | + sent = request; | ||
| 291 | + EXPECT_EQ(length, receive_buffer_len); | ||
| 280 | return response_data; | 292 | return response_data; |
| 281 | }; | 293 | }; |
| 282 | 294 | ||
| 283 | m_smbus->register_WriteRead_method(func); | 295 | m_smbus->register_WriteRead_method(func); |
| 284 | 296 | ||
| 285 | // 初始化 smbus | 297 | // 初始化 smbus |
| 286 | - mc::mutable_dict params = {{"BufferLen", 64}, {"McuAddress", 0xD4}}; | 298 | + mc::mutable_dict params = {{"BufferLen", receive_buffer_len}, {"McuAddress", 0xD4}}; |
| 287 | m_smbus->init(params); | 299 | m_smbus->init(params); |
| 288 | 300 | ||
| 289 | mc::mutable_dict track_request = { | 301 | mc::mutable_dict track_request = { |
| 290 | - {"opcode", 0x0404}, {"data_written", 2}, {"data", "test_data_longer_than_max_frame"}, {"max_frame_len", 10}}; | 302 | + {"opcode", opcode}, {"data_written", data_written}, {"data", payload}, |
| 303 | + {"len", frame_len}, {"expect_data_len", expect_data_len}, {"max_frame_len", frame_len}}; | ||
| 291 | auto result = m_smbus->send_and_receive_request_in_frames(track_request); | 304 | auto result = m_smbus->send_and_receive_request_in_frames(track_request); |
| 292 | - // 注册了 WriteRead 函数,expect success | 305 | + // The transport response must be preserved, including a short reply. |
| 293 | EXPECT_TRUE(result.first); | 306 | EXPECT_TRUE(result.first); |
| 307 | + EXPECT_EQ(result.second, response_data); | ||
| 308 | + // Independent little-endian wire bytes; do not use the production packer. | ||
| 309 | + const std::vector<uint8_t> header{0, 0, // reserved: pure write | ||
| 310 | + 0x04, 0x04, // opcode 0x0404 | ||
| 311 | + 2, 0, 0, 0, // data_written / offset | ||
| 312 | + 10, 0, 0, 0}; // frame_len | ||
| 313 | + ASSERT_EQ(header.size(), header_len); | ||
| 314 | + // construct_request_data appends CRC32 after the payload, without padding. | ||
| 315 | + ASSERT_EQ(sent.size(), header.size() + payload.size() + crc32_len); | ||
| 316 | + EXPECT_EQ(std::vector<uint8_t>(sent.begin(), sent.begin() + header.size()), header); | ||
| 317 | + EXPECT_EQ(std::string(sent.begin() + header.size(), sent.end() - crc32_len), payload); | ||
| 294 | } | 318 | } |
| 295 | 319 | ||
| 296 | -TEST_F(smbus_test, DISABLED_send_and_receive_request_in_frames_PartialData) | 320 | +TEST_F(smbus_test, send_and_receive_request_in_frames_PartialData) |
| 297 | { | 321 | { |
| 298 | // 目标:验证 send_and_receive_request_in_frames 方法可以处理部分数据 | 322 | // 目标:验证 send_and_receive_request_in_frames 方法可以处理部分数据 |
| 323 | + constexpr uint32_t receive_buffer_len = 64; | ||
| 324 | + constexpr uint16_t opcode = 0x0404; | ||
| 325 | + constexpr uint32_t data_written = 1; | ||
| 326 | + constexpr uint32_t frame_len = 10; | ||
| 327 | + constexpr int32_t expect_data_len = 0; // Pure write: reserved bytes stay zero. | ||
| 328 | + | ||
| 329 | + const std::string payload = "test_data_longer_than_max_frame"; | ||
| 330 | + | ||
| 299 | std::vector<uint8_t> response_data = {0x01, 0x02}; | 331 | std::vector<uint8_t> response_data = {0x01, 0x02}; |
| 332 | + std::vector<uint8_t> sent; | ||
| 300 | std::function<std::vector<uint8_t>(const std::vector<uint8_t>&, uint32_t)> func = | 333 | std::function<std::vector<uint8_t>(const std::vector<uint8_t>&, uint32_t)> func = |
| 301 | - [response_data](const std::vector<uint8_t>&, uint32_t) -> std::vector<uint8_t> { | 334 | + [&sent, response_data, receive_buffer_len](const std::vector<uint8_t>& request, |
| 335 | + uint32_t length) -> std::vector<uint8_t> { | ||
| 336 | + sent = request; | ||
| 337 | + EXPECT_EQ(length, receive_buffer_len); | ||
| 302 | return response_data; | 338 | return response_data; |
| 303 | }; | 339 | }; |
| 304 | 340 | ||
| 305 | m_smbus->register_WriteRead_method(func); | 341 | m_smbus->register_WriteRead_method(func); |
| 306 | 342 | ||
| 307 | // 初始化 smbus | 343 | // 初始化 smbus |
| 308 | - mc::mutable_dict params = {{"BufferLen", 64}, {"McuAddress", 0xD4}}; | 344 | + mc::mutable_dict params = {{"BufferLen", receive_buffer_len}, {"McuAddress", 0xD4}}; |
| 309 | m_smbus->init(params); | 345 | m_smbus->init(params); |
| 310 | 346 | ||
| 311 | mc::mutable_dict track_request = { | 347 | mc::mutable_dict track_request = { |
| 312 | - {"opcode", 0x0404}, {"data_written", 1}, {"data", "test_data_longer_than_max_frame"}, {"max_frame_len", 10}}; | 348 | + {"opcode", opcode}, {"data_written", data_written}, {"data", payload}, |
| 349 | + {"len", frame_len}, {"expect_data_len", expect_data_len}, {"max_frame_len", frame_len}}; | ||
| 313 | auto result = m_smbus->send_and_receive_request_in_frames(track_request); | 350 | auto result = m_smbus->send_and_receive_request_in_frames(track_request); |
| 314 | - // 注册了 WriteRead 函数,expect success | 351 | + // The transport response must be preserved, including a short reply. |
| 315 | EXPECT_TRUE(result.first); | 352 | EXPECT_TRUE(result.first); |
| 353 | + EXPECT_EQ(result.second, response_data); | ||
| 354 | + // Independent little-endian wire bytes; do not use the production packer. | ||
| 355 | + const std::vector<uint8_t> header{0, 0, // reserved: pure write | ||
| 356 | + 0x04, 0x04, // opcode 0x0404 | ||
| 357 | + 1, 0, 0, 0, // data_written / offset | ||
| 358 | + 10, 0, 0, 0}; // frame_len | ||
| 359 | + ASSERT_EQ(header.size(), header_len); | ||
| 360 | + // construct_request_data appends CRC32 after the payload, without padding. | ||
| 361 | + ASSERT_EQ(sent.size(), header.size() + payload.size() + crc32_len); | ||
| 362 | + EXPECT_EQ(std::vector<uint8_t>(sent.begin(), sent.begin() + header.size()), header); | ||
| 363 | + EXPECT_EQ(std::string(sent.begin() + header.size(), sent.end() - crc32_len), payload); | ||
| 316 | } | 364 | } |
| 317 | 365 | ||
| 318 | TEST_F(smbus_test, unpack_response_data_ExpectDataLenZero) | 366 | TEST_F(smbus_test, unpack_response_data_ExpectDataLenZero) |
⚠️ 警告:读取 LinkStatus 可能与回调竞争
ready信号在send_request被调用时触发,但LinkStatus.set_value()是在同一回调内收到响应后才执行(update_link_status_by_smbus)。也就是说“等待 ready”不代表属性已更新。在 ready 触发后主线程调用stop_smbus_update_task()与EXPECT_EQ(LinkStatus, 1)之间存在一个竞争窗口,可能读到仍未更新的旧值(0)而触发偶发失败。修复建议:改为在“属性已更新”的信号上等待(例如对 LinkStatus 的变化回调托平 promise),而不是仅等
send_request被调用。