// test_fn_table_viz_smoke.cpp — Linker smoke test for fn_table_viz static lib. // Issue 0081-I. Verifies that all 11 .cpp files in fn_table_viz resolve symbols // at link time. Does NOT call data_table::render (requires ImGui context). // // Build: cmake --build cpp/build/linux --target test_fn_table_viz_smoke // Run: ./cpp/build/linux/tests/test_fn_table_viz_smoke #include "core/compute_stage.h" #include "core/compute_pipeline.h" #include "core/tql_emit.h" #include "core/tql_apply.h" #include "core/lua_engine.h" #include "core/join_tables.h" #include "core/auto_detect_type.h" #include "core/compute_column_stats.h" #include "viz/viz_render.h" #include "viz/data_table.h" #include #include #include #include using namespace data_table; // --------------------------------------------------------------------------- // Minimal input: 3 rows x 2 cols (string + numeric). // --------------------------------------------------------------------------- static const char* g_cells[] = { "Alice", "10", "Bob", "20", "Carol", "30", }; // --------------------------------------------------------------------------- // Test 1: compute_stage with trivial Stage (no filters, no agg, no sort). // --------------------------------------------------------------------------- static void test_compute_stage_passthrough() { Stage s; // empty stage = passthrough std::vector hdrs = {"Name", "Value"}; std::vector types = {ColumnType::String, ColumnType::Float}; StageOutput out = compute_stage(g_cells, 3, 2, hdrs, types, s); assert(out.rows == 3 && "compute_stage: rows must be 3"); assert(out.cols == 2 && "compute_stage: cols must be 2"); std::printf("PASS: compute_stage passthrough (rows=%d cols=%d)\n", out.rows, out.cols); } // --------------------------------------------------------------------------- // Test 2: auto_detect_type on the Value column (all numeric). // --------------------------------------------------------------------------- static void test_auto_detect_type() { std::vector hdrs = {"Name", "Value"}; std::vector types = {ColumnType::String, ColumnType::Float}; // Detect type for column 1 (Value: "10","20","30" -> Float or Int) ColumnType t = auto_detect_type(g_cells, 3, 2, /*col=*/1); assert((t == ColumnType::Float || t == ColumnType::Int) && "auto_detect_type: Value col should be Float or Int"); std::printf("PASS: auto_detect_type numeric (%s)\n", t == ColumnType::Float ? "Float" : "Int"); } // --------------------------------------------------------------------------- // Test 3: compute_column_stats on the Value column. // --------------------------------------------------------------------------- static void test_compute_column_stats() { ColStats s = compute_column_stats(g_cells, 3, 2, /*col=*/1); assert(s.numeric && "compute_column_stats: Value col should be numeric"); assert(s.numeric_count == 3 && "compute_column_stats: 3 numeric values"); assert(s.min < s.max && "compute_column_stats: min < max"); std::printf("PASS: compute_column_stats (min=%.1f max=%.1f mean=%.1f)\n", s.min, s.max, s.mean); } // --------------------------------------------------------------------------- // Test 4: tql_emit -> tql_apply round-trip on a trivial State. // --------------------------------------------------------------------------- static void test_tql_roundtrip() { State st; st.stages.push_back(Stage{}); st.active_stage = 0; st.display = ViewMode::Table; std::vector hdrs = {"Name", "Value"}; std::vector types = {ColumnType::String, ColumnType::Float}; std::string lua_text = tql::emit(st, hdrs, types); assert(!lua_text.empty() && "tql_emit: must produce non-empty Lua text"); auto res = tql::apply(lua_text, hdrs); assert(res.ok && "tql_apply: round-trip must succeed"); assert(!res.state.stages.empty() && "tql_apply: state must have stages"); std::printf("PASS: tql_emit+tql_apply round-trip (ok=%s warnings=%zu)\n", res.ok ? "true" : "false", res.warnings.size()); } // --------------------------------------------------------------------------- // Test 5: tql_apply extended overload (playground-compat bool signature). // --------------------------------------------------------------------------- static void test_tql_apply_extended() { State st; st.stages.push_back(Stage{}); std::vector hdrs = {"Name", "Value"}; std::vector types = {ColumnType::String, ColumnType::Float}; std::string lua_text = tql::emit(st, hdrs, types); std::string err; State out_st; bool ok = tql::apply(lua_text, out_st, hdrs, types, g_cells, 3, 2, &err); assert(ok && "tql_apply extended: must succeed"); assert(!out_st.stages.empty() && "tql_apply extended: state must have stages"); std::printf("PASS: tql_apply extended overload (ok=%s)\n", ok ? "true" : "false"); } // --------------------------------------------------------------------------- // Test 6: lua_engine get + compile + release (verifies Lua 5.4 links). // --------------------------------------------------------------------------- static void test_lua_engine_compile() { lua_engine::Engine* e = lua_engine::get(); assert(e && "lua_engine::get must return non-null"); std::string err; int id = lua_engine::compile(e, "return row['Value'] * 2", &err); assert(id >= 0 && "lua_engine::compile must succeed"); lua_engine::release(e, id); lua_engine::shutdown(); std::printf("PASS: lua_engine compile + release (id=%d)\n", id); } // --------------------------------------------------------------------------- // Test 7: join_tables trivial self-join. // --------------------------------------------------------------------------- static void test_join_tables_trivial() { std::vector hdrs = {"Name", "Value"}; std::vector types = {ColumnType::String, ColumnType::Float}; TableInput right; right.name = "right"; right.headers = hdrs; right.types = types; right.cells = g_cells; right.rows = 3; right.cols = 2; Join j; j.alias = "r"; j.source = "right"; j.on = {{"Name", "Name"}}; j.strategy = JoinStrategy::Left; StageOutput out = join_tables(g_cells, 3, 2, hdrs, types, right, j); assert(out.rows == 3 && "join_tables: self-join must produce 3 rows"); std::printf("PASS: join_tables trivial self-join (rows=%d cols=%d)\n", out.rows, out.cols); } // --------------------------------------------------------------------------- // Test 8: data_table::render symbol is linkable (Wave 3.5 — issue 0081-I). // Does NOT call render (requires ImGui context); just takes its address. // --------------------------------------------------------------------------- static void test_data_table_render_links() { // Taking the address of render verifies the linker resolved the symbol // from data_table.cpp inside fn_table_viz. No ImGui context needed. // Use the events_out overload (Phase 2) as the canonical full-signature check. auto* render_fn = static_cast&, data_table::State&, std::vector*, bool)>(&data_table::render); (void)render_fn; std::printf("PASS: data_table::render symbol links (address=%p)\n", reinterpret_cast(render_fn)); } // --------------------------------------------------------------------------- // main // --------------------------------------------------------------------------- int main() { std::printf("=== test_fn_table_viz_smoke ===\n"); test_compute_stage_passthrough(); test_auto_detect_type(); test_compute_column_stats(); test_tql_roundtrip(); test_tql_apply_extended(); test_lua_engine_compile(); test_join_tables_trivial(); test_data_table_render_links(); std::printf("=== ALL TESTS PASSED ===\n"); return 0; }