Test WinRT BLE exception isolation and pending shutdown
This commit is contained in:
1 parent
dc70174af9
commit
4a7f3253f6
2 files changed
+105
No files matched your search
@@ -24,6 +24,13 @@ if(WIN32)
|
||||
find_path(BLE_WINRT_INCLUDE winrt/Windows.Devices.Bluetooth.h REQUIRED)
|
||||
target_include_directories(vibechat_ble PRIVATE "${BLE_WINRT_INCLUDE}")
|
||||
target_link_libraries(vibechat_ble PRIVATE windowsapp)
|
||||
add_executable(test_ble_winrt tests/test_ble_winrt.cpp)
|
||||
target_compile_features(test_ble_winrt PRIVATE cxx_std_20)
|
||||
target_include_directories(test_ble_winrt PRIVATE "${BLE_WINRT_INCLUDE}")
|
||||
target_link_libraries(test_ble_winrt PRIVATE uasync windowsapp)
|
||||
set_target_properties(test_ble_winrt PROPERTIES AUTOMOC OFF)
|
||||
add_test(NAME test_ble_winrt COMMAND test_ble_winrt)
|
||||
set_tests_properties(test_ble_winrt PROPERTIES TIMEOUT 30)
|
||||
elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(BLE_SYSTEMD REQUIRED IMPORTED_TARGET libsystemd)
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
/* Настоящий WinRT mailbox без радио: исключения, изоляция link и отмена поздних callbacks.
|
||||
* Включаем backend для доступа к внутренним событиям; общий transport заменён наблюдателем. */
|
||||
#include "../ble_winrt.cpp"
|
||||
#include <cstdlib>
|
||||
|
||||
#define CHECK(value) do { if (!(value)) { DEBUG_ERROR(CAT, "CHECK failed line=%d: %s", __LINE__, #value); std::abort(); } } while (0)
|
||||
|
||||
struct ble_transport {
|
||||
WinState* state = nullptr;
|
||||
unsigned radio_failures = 0, link_failures = 0;
|
||||
ble_link_id closed = 0;
|
||||
std::string reason;
|
||||
};
|
||||
extern "C" void ble_backend_closed(ble_transport* t, ble_link_id id, const char* reason) {
|
||||
++t->link_failures; t->closed = id; t->reason = reason; t->state->close(id);
|
||||
}
|
||||
extern "C" void ble_backend_status(ble_transport* t, int ready, const char* reason) {
|
||||
CHECK(!ready); ++t->radio_failures; t->reason = reason;
|
||||
while (!t->state->links.empty()) t->state->close(t->state->links.begin()->first);
|
||||
}
|
||||
/* Эти события не должны возникать в проверке mailbox. */
|
||||
extern "C" ble_link_id ble_backend_incoming(ble_transport*, const ble_endpoint*) { CHECK(false); return 0; }
|
||||
extern "C" void ble_backend_advertising(ble_transport*, ble_state, const char*) { CHECK(false); }
|
||||
extern "C" void ble_backend_broadcast(ble_transport*, const ble_endpoint*, const uint8_t*, size_t, int) { CHECK(false); }
|
||||
extern "C" void ble_backend_opened(ble_transport*, ble_link_id, unsigned) { CHECK(false); }
|
||||
extern "C" void ble_backend_received(ble_transport*, ble_link_id, ble_wire_channel, const uint8_t*, size_t) { CHECK(false); }
|
||||
extern "C" void ble_backend_sent(ble_transport*, ble_link_id, int) { CHECK(false); }
|
||||
|
||||
static std::shared_ptr<WinState> make_state(UASYNC* ua, ble_transport& owner) {
|
||||
auto s = std::make_shared<WinState>(); s->ua = ua; s->owner = &owner; owner.state = s.get();
|
||||
for (ble_link_id id : {7, 9}) {
|
||||
auto l = std::make_unique<WinLink>(); l->id = id; s->links.emplace(id, std::move(l));
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
/* Ошибка GATT одного пира не переводит radio в failed и не теряет событие другого пира. */
|
||||
static void link_exception(UASYNC* ua) {
|
||||
ble_transport owner;
|
||||
auto s = make_state(ua, owner);
|
||||
bool other_processed = false;
|
||||
s->post("test.ValueChanged", 7, [](WinState& state) {
|
||||
state.step("test.CharacteristicValue", 7);
|
||||
throw hresult_error(E_NOINTERFACE, L"test interface absent");
|
||||
});
|
||||
s->post("test.other_peer", 9, [&other_processed](WinState&) { other_processed = true; });
|
||||
uasync_poll(ua, 0);
|
||||
CHECK(owner.link_failures == 1 && owner.closed == 7 && owner.radio_failures == 0);
|
||||
CHECK(!s->failed && !s->link(7) && s->link(9) && other_processed);
|
||||
CHECK(owner.reason.find("phase=test.CharacteristicValue link=7 HRESULT=0x80004002") != std::string::npos);
|
||||
s->shutdown();
|
||||
DEBUG_INFO(CAT, "WinRT test PASS: link exception preserves other peer and exact HRESULT context");
|
||||
}
|
||||
|
||||
/* Общая ошибка сохраняет точную операцию даже при выключенном TRACE и останавливает batch. */
|
||||
static void radio_exception(UASYNC* ua) {
|
||||
ble_transport owner;
|
||||
auto s = make_state(ua, owner);
|
||||
bool processed = false;
|
||||
s->post("test.Received", 0, [](WinState& state) {
|
||||
state.step("test.BluetoothAddressType");
|
||||
throw hresult_error(E_NOINTERFACE, L"test interface absent");
|
||||
});
|
||||
s->post("test.after_failure", 9, [&processed](WinState&) { processed = true; });
|
||||
uasync_poll(ua, 0);
|
||||
CHECK(owner.radio_failures == 1 && owner.link_failures == 0 && s->failed);
|
||||
CHECK(s->links.empty() && !processed);
|
||||
CHECK(owner.reason.find("phase=test.BluetoothAddressType link=0 HRESULT=0x80004002") != std::string::npos);
|
||||
s->shutdown();
|
||||
DEBUG_INFO(CAT, "WinRT test PASS: radio exception stops queued delivery with TRACE disabled");
|
||||
}
|
||||
|
||||
/* shutdown отменяет posted_task, освобождает captures и отвергает позднее событие Windows. */
|
||||
static void pending_shutdown(UASYNC* ua) {
|
||||
ble_transport owner;
|
||||
auto s = make_state(ua, owner);
|
||||
auto payload = std::make_shared<int>(0);
|
||||
std::weak_ptr<int> weak_payload = payload;
|
||||
bool processed = false;
|
||||
s->post("test.pending", 9, [payload, &processed](WinState&) { processed = true; });
|
||||
payload.reset(); CHECK(!weak_payload.expired());
|
||||
s->shutdown(); CHECK(weak_payload.expired());
|
||||
s->post("test.late", 9, [&processed](WinState&) { processed = true; });
|
||||
CHECK(s->events.empty() && !s->posted);
|
||||
std::weak_ptr<WinState> weak = s; s.reset();
|
||||
uasync_poll(ua, 0);
|
||||
CHECK(weak.expired() && !processed && owner.radio_failures == 0 && owner.link_failures == 0);
|
||||
DEBUG_INFO(CAT, "WinRT test PASS: shutdown cancels queued work and rejects late callbacks");
|
||||
}
|
||||
|
||||
int main() {
|
||||
debug_config_init(); debug_set_level(DEBUG_LEVEL_INFO);
|
||||
init_apartment(apartment_type::multi_threaded);
|
||||
UASYNC* ua = uasync_create(); CHECK(ua);
|
||||
link_exception(ua); radio_exception(ua); pending_shutdown(ua);
|
||||
uasync_destroy(ua, 0); uninit_apartment();
|
||||
return 0;
|
||||
}
|
||||
Reference in new issue
Block a user