Wave 8: tech-debt audits, core unit tests, CLI pipe input (W11.1-W11.7)
- W11.1 context_plugin audit (architect-huang): 3 findings on ABI exception safety, strdup null checks, dead g_max_tokens variable. Rating: B. - W11.2 config audit (engineer-chen): identified 74-line TOML parser duplication between config_plugin and config_store, dual-store data isolation, dangling c_str() risk. Rating: C. - W11.3 event_bus + service_registry unit tests (qa-liu): 12 cases total, ctest coverage 2 -> 4 targets, 100% pass. - W11.4 CLI stdin pipe mode (engineer-zhao): isatty detection, single-shot inference path with exit codes 0/1/2/3. - W11.6 scripts/refresh_status.py (engineer-li): 431-line generator that scans 16 profile.md + 5 group.md to regenerate STATUS.md. - W11.7 destructive testing (qa-xu): 10 input scenarios PASS, found bin copy mismatch (BUG-1) plus 3 minor UX bugs for follow-up. Verified: cmake build 0 error, ctest 4/4 pass. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -34,3 +34,41 @@ target_link_libraries(dstalk-host-api-test
|
||||
)
|
||||
|
||||
add_test(NAME dstalk-host-api-test COMMAND dstalk-host-api-test)
|
||||
|
||||
# ============================================================
|
||||
# dstalk-event-bus-test — EventBus 单元测试
|
||||
# ============================================================
|
||||
|
||||
add_executable(dstalk-event-bus-test
|
||||
event_bus_test.cpp
|
||||
${CMAKE_SOURCE_DIR}/dstalk-core/src/event_bus.cpp
|
||||
)
|
||||
|
||||
target_include_directories(dstalk-event-bus-test
|
||||
PRIVATE ${CMAKE_SOURCE_DIR}/dstalk-core/src
|
||||
)
|
||||
|
||||
target_compile_features(dstalk-event-bus-test
|
||||
PRIVATE cxx_std_17
|
||||
)
|
||||
|
||||
add_test(NAME dstalk-event-bus-test COMMAND dstalk-event-bus-test)
|
||||
|
||||
# ============================================================
|
||||
# dstalk-service-registry-test — ServiceRegistry 补充单元测试
|
||||
# ============================================================
|
||||
|
||||
add_executable(dstalk-service-registry-test
|
||||
service_registry_test.cpp
|
||||
${CMAKE_SOURCE_DIR}/dstalk-core/src/service_registry.cpp
|
||||
)
|
||||
|
||||
target_include_directories(dstalk-service-registry-test
|
||||
PRIVATE ${CMAKE_SOURCE_DIR}/dstalk-core/src
|
||||
)
|
||||
|
||||
target_compile_features(dstalk-service-registry-test
|
||||
PRIVATE cxx_std_17
|
||||
)
|
||||
|
||||
add_test(NAME dstalk-service-registry-test COMMAND dstalk-service-registry-test)
|
||||
|
||||
133
tests/event_bus_test.cpp
Normal file
133
tests/event_bus_test.cpp
Normal file
@@ -0,0 +1,133 @@
|
||||
// ============================================================================
|
||||
// event_bus_test.cpp — EventBus 单元测试
|
||||
// ============================================================================
|
||||
// 测试: subscribe / unsubscribe / emit / 多订阅者 / 空总线
|
||||
// ============================================================================
|
||||
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "event_bus.hpp"
|
||||
|
||||
// ---- 轻量断言 ----
|
||||
static int g_failures = 0;
|
||||
#define TCHECK(cond, msg) do { \
|
||||
if (cond) { \
|
||||
std::cout << "[OK] " << (msg) << "\n"; \
|
||||
} else { \
|
||||
std::cerr << "[FAIL] " << (msg) << "\n"; \
|
||||
g_failures++; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
// ============================================================
|
||||
int main()
|
||||
{
|
||||
std::cout << "=== dstalk event_bus unit tests ===\n\n";
|
||||
|
||||
// ====================================================================
|
||||
// Test 1: subscribe + emit — 基本发布订阅流程
|
||||
// ====================================================================
|
||||
{
|
||||
dstalk::EventBus bus;
|
||||
int call_count = 0;
|
||||
int received_type = 0;
|
||||
|
||||
int id = bus.subscribe(42, [&](int event_type, const void* data) {
|
||||
call_count++;
|
||||
received_type = event_type;
|
||||
});
|
||||
TCHECK(id >= 1, "subscribe returns valid subscription ID");
|
||||
|
||||
int emitted = bus.emit(42, nullptr);
|
||||
TCHECK(emitted == 1, "emit returns 1 handler called");
|
||||
TCHECK(call_count == 1, "handler was invoked exactly once");
|
||||
TCHECK(received_type == 42, "handler received correct event_type");
|
||||
}
|
||||
|
||||
// ====================================================================
|
||||
// Test 2: unsubscribe — 取消订阅后 handler 不再被调用
|
||||
// ====================================================================
|
||||
{
|
||||
dstalk::EventBus bus;
|
||||
int call_count = 0;
|
||||
|
||||
int id = bus.subscribe(10, [&](int, const void*) { call_count++; });
|
||||
bus.unsubscribe(id);
|
||||
|
||||
int emitted = bus.emit(10, nullptr);
|
||||
TCHECK(emitted == 0, "emit after unsubscribe returns 0");
|
||||
TCHECK(call_count == 0, "unsubscribed handler was NOT called");
|
||||
}
|
||||
|
||||
// ====================================================================
|
||||
// Test 3: 多订阅者 — 同一事件多个 handler 按订阅顺序全部调用
|
||||
// ====================================================================
|
||||
{
|
||||
dstalk::EventBus bus;
|
||||
std::vector<int> order;
|
||||
|
||||
bus.subscribe(1, [&](int, const void*) { order.push_back(1); });
|
||||
bus.subscribe(1, [&](int, const void*) { order.push_back(2); });
|
||||
bus.subscribe(1, [&](int, const void*) { order.push_back(3); });
|
||||
|
||||
int emitted = bus.emit(1, nullptr);
|
||||
TCHECK(emitted == 3, "emit returns 3 handlers called");
|
||||
TCHECK(order.size() == 3, "all 3 handlers invoked");
|
||||
|
||||
// 验证订阅顺序 (FIFO: 按 subscribe 顺序触发)
|
||||
bool ordered = (order[0] == 1 && order[1] == 2 && order[2] == 3);
|
||||
TCHECK(ordered, "handlers invoked in subscription order (1,2,3)");
|
||||
}
|
||||
|
||||
// ====================================================================
|
||||
// Test 4: 空总线 emit 不崩溃,返回 0
|
||||
// ====================================================================
|
||||
{
|
||||
dstalk::EventBus bus;
|
||||
int emitted = bus.emit(99, nullptr);
|
||||
TCHECK(emitted == 0, "emit on empty bus returns 0 (no crash)");
|
||||
}
|
||||
|
||||
// ====================================================================
|
||||
// Test 5: 不同 event_type 独立分发 — 只触发匹配的 handler
|
||||
// ====================================================================
|
||||
{
|
||||
dstalk::EventBus bus;
|
||||
int count_a = 0, count_b = 0;
|
||||
|
||||
bus.subscribe(100, [&](int, const void*) { count_a++; });
|
||||
bus.subscribe(200, [&](int, const void*) { count_b++; });
|
||||
|
||||
bus.emit(100, nullptr);
|
||||
TCHECK(count_a == 1 && count_b == 0,
|
||||
"emit type=100 only triggers type-100 handler");
|
||||
|
||||
bus.emit(200, nullptr);
|
||||
TCHECK(count_a == 1 && count_b == 1,
|
||||
"emit type=200 only triggers type-200 handler");
|
||||
}
|
||||
|
||||
// ====================================================================
|
||||
// Test 6: 退订不存在的 ID 不崩溃
|
||||
// ====================================================================
|
||||
{
|
||||
dstalk::EventBus bus;
|
||||
bus.unsubscribe(99999); // 不存在的 ID
|
||||
std::cout << "[OK] unsubscribe non-existent ID (99999) did not crash\n";
|
||||
}
|
||||
|
||||
// ====================================================================
|
||||
// 结果
|
||||
// ====================================================================
|
||||
std::cout << "\n";
|
||||
if (g_failures == 0) {
|
||||
std::cout << "=== All event_bus tests passed ===\n";
|
||||
return 0;
|
||||
} else {
|
||||
std::cerr << "=== " << g_failures << " event_bus test(s) FAILED ===\n";
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
114
tests/service_registry_test.cpp
Normal file
114
tests/service_registry_test.cpp
Normal file
@@ -0,0 +1,114 @@
|
||||
// ============================================================================
|
||||
// service_registry_test.cpp — ServiceRegistry 单元测试(补充覆盖,不与 host_api_test 重叠)
|
||||
// ============================================================================
|
||||
// host_api_test 已覆盖: 重复注册(同名同版/同名异版)、查询不存在服务、版本不满足、
|
||||
// shutdown 后查询。本测试补充边界与生命周期路径。
|
||||
// ============================================================================
|
||||
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
|
||||
#include "service_registry.hpp"
|
||||
|
||||
// ---- 轻量断言 ----
|
||||
static int g_failures = 0;
|
||||
#define TCHECK(cond, msg) do { \
|
||||
if (cond) { \
|
||||
std::cout << "[OK] " << (msg) << "\n"; \
|
||||
} else { \
|
||||
std::cerr << "[FAIL] " << (msg) << "\n"; \
|
||||
g_failures++; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
// ============================================================
|
||||
int main()
|
||||
{
|
||||
std::cout << "=== dstalk service_registry unit tests (supplement) ===\n\n";
|
||||
|
||||
// ====================================================================
|
||||
// Test 1: register_service(nullptr name) → -1
|
||||
// ====================================================================
|
||||
{
|
||||
dstalk::ServiceRegistry reg;
|
||||
void* vt = reinterpret_cast<void*>(0x10);
|
||||
int r = reg.register_service(nullptr, 1, vt);
|
||||
TCHECK(r == -1, "register_service(nullptr name) returns -1");
|
||||
}
|
||||
|
||||
// ====================================================================
|
||||
// Test 2: register_service(nullptr vtable) → -1
|
||||
// ====================================================================
|
||||
{
|
||||
dstalk::ServiceRegistry reg;
|
||||
int r = reg.register_service("valid_name", 1, nullptr);
|
||||
TCHECK(r == -1, "register_service(nullptr vtable) returns -1");
|
||||
}
|
||||
|
||||
// ====================================================================
|
||||
// Test 3: 完整生命周期 — register → query → unregister → query(nullptr)
|
||||
// ====================================================================
|
||||
{
|
||||
dstalk::ServiceRegistry reg;
|
||||
void* vt = reinterpret_cast<void*>(0xDEAD);
|
||||
|
||||
int r = reg.register_service("life", 3, vt);
|
||||
TCHECK(r == 0, "register_service(\"life\",3) returns 0");
|
||||
|
||||
void* q1 = reg.query_service("life", 2);
|
||||
TCHECK(q1 == vt, "query_service(\"life\",2) returns vtable after register");
|
||||
|
||||
reg.unregister_service("life");
|
||||
|
||||
void* q2 = reg.query_service("life", 1);
|
||||
TCHECK(q2 == nullptr, "query_service(\"life\",1) returns nullptr after unregister");
|
||||
}
|
||||
|
||||
// ====================================================================
|
||||
// Test 4: unregister_service(nullptr name) 不崩溃(安全空操作)
|
||||
// ====================================================================
|
||||
{
|
||||
dstalk::ServiceRegistry reg;
|
||||
reg.unregister_service(nullptr);
|
||||
std::cout << "[OK] unregister_service(nullptr) did not crash\n";
|
||||
}
|
||||
|
||||
// ====================================================================
|
||||
// Test 5: 注册后重新注册同名 → 先 unregister 再 register 成功
|
||||
// ====================================================================
|
||||
{
|
||||
dstalk::ServiceRegistry reg;
|
||||
void* vt1 = reinterpret_cast<void*>(0xA);
|
||||
void* vt2 = reinterpret_cast<void*>(0xB);
|
||||
|
||||
reg.register_service("reborn", 1, vt1);
|
||||
reg.unregister_service("reborn");
|
||||
|
||||
int r = reg.register_service("reborn", 2, vt2);
|
||||
TCHECK(r == 0, "register_service after unregister same name returns 0");
|
||||
|
||||
void* q = reg.query_service("reborn", 2);
|
||||
TCHECK(q == vt2, "query_service after re-register returns new vtable");
|
||||
}
|
||||
|
||||
// ====================================================================
|
||||
// Test 6: query_service(nullptr name) → nullptr
|
||||
// ====================================================================
|
||||
{
|
||||
dstalk::ServiceRegistry reg;
|
||||
void* q = reg.query_service(nullptr, 1);
|
||||
TCHECK(q == nullptr, "query_service(nullptr name) returns nullptr");
|
||||
}
|
||||
|
||||
// ====================================================================
|
||||
// 结果
|
||||
// ====================================================================
|
||||
std::cout << "\n";
|
||||
if (g_failures == 0) {
|
||||
std::cout << "=== All service_registry tests passed ===\n";
|
||||
return 0;
|
||||
} else {
|
||||
std::cerr << "=== " << g_failures << " service_registry test(s) FAILED ===\n";
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user