// Regression tests for C++-vs-Python semantic traps found by the audit (G2): const-reference // aliasing of members, killed objects reachable from the argument, buffer self-reference. // Links all of src/mplex and src/mplex/mplex_plexer_flow.cpp. Plain main + asserts. #include #include #include #include #include #include "mplex_server.h" using namespace detz::mplex; static int failures = 0; static int checks = 0; static void check(bool ok, const char* what) { ++checks; if (!ok) { ++failures; std::cerr << "FAIL: " << what << "\n"; } } int main() { char tmpl[] = "/tmp/mplex-regress-XXXXXX"; std::string home = mkdtemp(tmpl); { MplexServer server(home); MplexPane::EnvList env = {{"PATH", "/usr/bin:/bin"}, {"HOME", home}}; auto session = server.create_session("s", "/", env, {"/bin/sh"}, "w", {80, 24}); auto window = session->window(); auto p0 = window->active; // set_active / select_pane called with window->last_pane (member aliasing) auto p1 = server.split_pane(window, p0, "lr", false, {"/bin/sh"}, "/", nullptr, -1, true); auto p2 = server.split_pane(window, p1, "tb", false, {"/bin/sh"}, "/", nullptr, -1, true); check(window->active == p2 && window->last_pane == p1, "split leaves last_pane = previous active"); server.select_pane(window, window->last_pane); check(window->active == p1, "select_pane(window, window->last_pane) activates the last pane"); check(window->last_pane == p2, "... and records the old active as last_pane"); window->set_active(window->last_pane); check(window->active == p2 && window->last_pane == p1, "set_active(window->last_pane) swaps"); // add_buffer with text aliasing a buffer that gets replaced server.add_buffer("alpha", std::string("b")); server.add_buffer(server.buffers[0].data, std::string("b")); check(server.buffers.size() == 1 && server.buffers[0].data == "alpha", "add_buffer(buffers[0].data, same name)"); // kill_pane with the argument aliasing server.marked (cleared inside) server.marked = p1; server.kill_pane(server.marked); check(!server.marked && !server.panes.contains(p1->id), "kill_pane(server.marked)"); check(window->panes().size() == 2, "window keeps the other panes"); // kill_pane with the argument aliasing window->active server.kill_pane(window->active); check(window->panes().size() == 1 && window->active, "kill_pane(window->active) leaves an active pane"); // kill_session with the argument aliasing an element of server.sessions auto other = server.create_session("t", "/", env, {"/bin/sh"}, "w", {80, 24}); int other_id = other->id; other.reset(); server.kill_session(server.sessions.items()[1].second); check(!server.sessions.contains(other_id) && server.sessions.size() == 1, "kill_session(sessions[...])"); // kill_window with the argument aliasing an element of server.windows auto w2 = server.create_window(session, "x", "/", {"/bin/sh"}, nullptr, 5, false); int w2id = w2->id; w2.reset(); server.kill_window(server.windows.items().back().second); check(!server.windows.contains(w2id) && session->windows.size() == 1, "kill_window(windows[...])"); server.kill_session(session); check(server.sessions.empty() && server.panes.empty(), "everything gone"); } std::filesystem::remove_all(home); std::printf("mplex_regress_test: %d checks, %d failures\n", checks, failures); return failures == 0 ? 0 : 1; }