#define PK_IMPL_MEM #define PK_IMPL_EV #include "../pkmacros.h" #include "../pkev.h" #include #include #include static bool expected_exit = false; static bool caught = false; static jmp_buf jmp_env; // https://stackoverflow.com/questions/64190847/how-to-catch-a-call-to-exit-for-unit-testing // stub function void exit(int code) { if (expected_exit) { caught = true; longjmp(jmp_env, 1); } else { _exit(code); } } struct ev { pk_membucket *bkt; pk_ev_mgr_id_T evmgr; pk_ev_id_T evid; atomic_int count; atomic_bool handled; }; struct ev ev_one = {}; struct ev ev_two = {}; pk_ev_mgr_id_T test_setup() { ev_one.evmgr = {}; ev_one.evid = {}; ev_one.count = {}; ev_one.handled = {}; ev_two.evmgr = {}; ev_two.evid = {}; ev_two.count = {}; ev_two.handled = {}; ev_one.bkt = pk_mem_bucket_create("pkev_test_cpp", PK_MEM_DEFAULT_BUCKET_SIZE, PK_MEMBUCKET_FLAG_NONE); ev_two.bkt = ev_one.bkt; pk_ev_init(ev_one.bkt); const pk_ev_mgr_id_T evmgr = pk_ev_create_mgr(); if (evmgr >= 64) { PK_LOGV_ERR("%s: failed to create pk_ev_mgr\n", __FILE__); exit(1); } ev_one.evmgr = evmgr; ev_two.evmgr = evmgr; return evmgr; } void test_teardown() { pk_ev_teardown(); pk_mem_bucket_destroy(ev_one.bkt); ev_one.bkt = NULL; ev_two.bkt = NULL; } typedef struct cb_data { int i; struct ev *ev; } cb_data; void stress_cb(void *, void *, void *user_data) { cb_data *data = reinterpret_cast(user_data); data->ev[data->i].handled = true; data->ev[data->i].count++; } void invoke_packged_task(void *, void *, void* ptr) { (*static_cast*>(ptr))(); } int main(int argc, char *argv[]) { (void)argc; (void)argv; (void)stdout; size_t i, ii; // register, emit, catch { std::packaged_task handle_ev_one([](){ ev_one.handled = true; }); std::packaged_task handle_ev_two([](){ ev_two.handled = true; }); pk_ev_mgr_id_T evmgr = test_setup(); ev_one.evid = pk_ev_register_ev(evmgr, NULL); ev_two.evid = pk_ev_register_ev(evmgr, NULL); pk_ev_register_cb(evmgr, ev_one.evid, &invoke_packged_task, NULL); pk_ev_register_cb(evmgr, ev_two.evid, &invoke_packged_task, NULL); pk_ev_emit(evmgr, ev_one.evid, &handle_ev_one); pk_ev_emit(evmgr, ev_two.evid, &handle_ev_two); PK_LOGV_INF("%s: ev_one: %s, ev_two: %s\n", __FILE__, ev_one.handled ? "true" : "false", ev_two.handled ? "true" : "false"); pk_ev_teardown(); fflush(stdout); fflush(stderr); if (ev_one.handled == false || ev_two.handled == false) exit(1); } // threaded register, emit, catch { std::packaged_task handle_ev_one([](){ ev_one.handled = true; }); std::packaged_task handle_ev_two([](){ ev_two.handled = true; }); const pk_ev_mgr_id_T evmgr = test_setup(); ev_one.evid = pk_ev_register_ev(evmgr, NULL); ev_two.evid = pk_ev_register_ev(evmgr, NULL); pk_ev_register_cb(evmgr, ev_one.evid, &invoke_packged_task, NULL); pk_ev_register_cb(evmgr, ev_two.evid, &invoke_packged_task, NULL); std::thread t1 = std::thread([&]() { pk_ev_emit(ev_one.evmgr, ev_one.evid, &handle_ev_one); }); std::thread t2 = std::thread([&]() { pk_ev_emit(ev_two.evmgr, ev_two.evid, &handle_ev_two); }); t1.join(); t2.join(); PK_LOGV_INF("%s: ev_one: %s, ev_two: %s\n", __FILE__, ev_one.handled ? "true" : "false", ev_two.handled ? "true" : "false"); pk_ev_teardown(); fflush(stdout); fflush(stderr); if (ev_one.handled == false || ev_two.handled == false) exit(1); } // overload cbs and evs (grow once) { const uint64_t cb_count = PK_EV_INIT_CB_COUNT + 2; const uint64_t ev_count = PK_EV_INIT_EV_COUNT + 2; struct ev evs[ev_count] = {}; const pk_ev_mgr_id_T evmgr = test_setup(); for (i = 0; i < ev_count; ++i) { evs[i].evmgr = evmgr; evs[i].evid = pk_ev_register_ev(evmgr, NULL); for (ii = 0; ii < cb_count; ++ii) { pk_ev_register_cb(evmgr, evs[i].evid, &stress_cb, NULL); } } for (i = 0; i < ev_count; ++i) { cb_data *d = new cb_data{}; d->ev = evs; d->i = i; pk_ev_emit(evs[i].evmgr, evs[i].evid, d); delete d; } bool any_false = false; for (i = 0; i < ev_count; ++i) { PK_LOGV_INF("%s: ev# %.2zu: %s, called count: %i\n", __FILE__, i, evs[i].handled ? "true" : "false", atomic_load(&evs[i].count)); any_false = any_false || !evs[i].handled; } pk_ev_teardown(); fflush(stdout); fflush(stderr); if (any_false == true) exit(1); } // overload cbs and evs (test limits) { const uint64_t cb_count = 255; const uint64_t ev_count = 255; struct ev evs[ev_count] = {}; const pk_ev_mgr_id_T evmgr = test_setup(); for (i = 0; i < ev_count; ++i) { evs[i].evmgr = evmgr; evs[i].evid = pk_ev_register_ev(evmgr, NULL); for (ii = 0; ii < cb_count; ++ii) { pk_ev_register_cb(evmgr, evs[i].evid, &stress_cb, NULL); } } for (i = 0; i < ev_count; ++i) { cb_data *d = new cb_data; d->i = i; d->ev = &evs[0]; pk_ev_emit(evs[i].evmgr, evs[i].evid, d); delete d; } bool any_false = false; uint64_t valid_count = 0; uint64_t partial_valid_count = 0; for (i = 0; i < ev_count; ++i) { if (evs[i].handled == true && evs[i].count == 0xFF) valid_count += 1; if (evs[i].handled == true && evs[i].count != 0xFF) { partial_valid_count += 1; } any_false = any_false || !evs[i].handled; } PK_LOGV_INF("%s: #valid: %lu, #partial_valid: %lu, called count: %i\n", __FILE__, valid_count, partial_valid_count, atomic_load(&evs[0].count)); pk_ev_teardown(); fflush(stdout); fflush(stderr); if (any_false == true) exit(1); } // overload cbs and evs (enforce uint8_t limits) do { int r; const uint64_t cb_count = 256; const uint64_t ev_count = 1; struct ev evs[ev_count] = {}; const pk_ev_mgr_id_T evmgr = test_setup(); r = setjmp(jmp_env); if (r == 1) { if (expected_exit == true && caught == true) { expected_exit = false; caught = false; pk_ev_teardown(); PK_LOGV_INF("%s: successfully caught err.\n", __FILE__); fflush(stdout); fflush(stderr); break; } else { goto uncaught_err; } } for (i = 0; i < ev_count; ++i) { evs[i].evmgr = evmgr; evs[i].evid = pk_ev_register_ev(evmgr, NULL); for (ii = 0; ii < cb_count; ++ii) { caught = false; expected_exit = true; pk_ev_register_cb(evmgr, evs[i].evid, &stress_cb, NULL); expected_exit = false; caught = false; } } goto uncaught_err; } while(false); // overload cbs and evs (enforce uint8_t limits) do { int r; const uint64_t cb_count = 1; const uint64_t ev_count = 256; struct ev evs[ev_count] = {}; const pk_ev_mgr_id_T evmgr = test_setup(); r = setjmp(jmp_env); if (r == 1) { if (expected_exit == true && caught == true) { expected_exit = false; caught = false; pk_ev_teardown(); PK_LOGV_INF("%s: successfully caught err.\n", __FILE__); fflush(stdout); fflush(stderr); break; } else { goto uncaught_err; } } for (i = 0; i < ev_count; ++i) { evs[i].evmgr = evmgr; caught = false; expected_exit = true; evs[i].evid = pk_ev_register_ev(evmgr, NULL); expected_exit = false; caught = false; for (ii = 0; ii < cb_count; ++ii) { pk_ev_register_cb(evmgr, evs[i].evid, &stress_cb, NULL); } } goto uncaught_err; } while(false); // create multiple do { uint64_t mask = 0b1; const pk_ev_mgr_id_T evmgr = test_setup(); (void)evmgr; for (i = 1; i < PK_EV_INIT_MGR_COUNT; ++i) { auto ev_mgr_id = pk_ev_create_mgr(); (void)ev_mgr_id; mask |= (1ull << i); } assert(pk_ev_mstr.flg_mgrs == mask); for (i = PK_EV_INIT_MGR_COUNT-1; i > 0; --i) { pk_ev_destroy_mgr(pk_ev_mgr_id_T{i}); } pk_ev_teardown(); } while (false); return 0; uncaught_err: PK_LOGV_ERR("%s: failed to catch err.\n", __FILE__); fflush(stdout); fflush(stderr); return 1; }