// test_column_specs.cpp — Smoke / back-compat tests for declarative cell renderers. // Issue 0081-N, v1.1.0. Phase 2 (issue 0081-O, v1.2.0). // // These tests verify: // 1. TableInput without column_specs compiles and links (back-compat). // 2-5. TableInput with Badge/Progress/Duration/Icon column_specs compiles and links. // 6. Button renderer: TableEvent struct is constructible; events_out pointer accepted. // 7. Tooltip field: ColumnSpec with tooltip_on_hover=true compiles and links. // 8. render() overload with events_out=nullptr back-compat (symbol resolution only). // // None of these tests call data_table::render() (requires ImGui context). // They only verify that the new types are usable and that the symbols from // fn_table_viz link correctly. // // Build: cmake --build cpp/build/linux --target test_column_specs // Run: ./cpp/build/linux/tests/test_column_specs #include "core/data_table_types.h" #include "viz/data_table.h" #include #include #include #include using namespace data_table; // Shared trivial dataset (3 rows x 4 cols). static const char* g_cells[] = { "ok", "0.75", "250", "fn", "error", "0.20", "3500", "type", "warn", "1.00", "12000", "fn", }; static const std::vector g_headers = {"status", "progress", "duration_ms", "kind"}; static const std::vector g_types = { ColumnType::String, ColumnType::Float, ColumnType::Float, ColumnType::String }; // --------------------------------------------------------------------------- // Test 1: back-compat — TableInput without column_specs. // --------------------------------------------------------------------------- static void test_no_column_specs() { TableInput t; t.name = "t1"; t.rows = 3; t.cols = 4; t.cells = g_cells; t.headers = g_headers; t.types = g_types; // column_specs intentionally left empty (back-compat: default behavior). assert(t.column_specs.empty() && "column_specs must be empty by default"); // Verify that render symbol is still linkable (no ImGui context needed // to take the address; the linker verifies the symbol resolves). // Use the classic overload (without events_out) for the back-compat check. auto* render_fn = static_cast&, State&, bool)>(&data_table::render); (void)render_fn; std::printf("PASS: test_no_column_specs (back-compat, column_specs empty)\n"); } // --------------------------------------------------------------------------- // Test 2: Badge renderer — construct TableInput with Badge column_spec. // --------------------------------------------------------------------------- static void test_badge_column_spec() { TableInput t; t.name = "t2"; t.rows = 3; t.cols = 4; t.cells = g_cells; t.headers = g_headers; t.types = g_types; // Column 0: Badge renderer mapping ok/error/warn to colors. ColumnSpec cs_status; cs_status.id = "status"; cs_status.renderer = CellRenderer::Badge; cs_status.badges = { BadgeRule{"ok", "#22c55e", "OK"}, BadgeRule{"error", "#ef4444", ""}, // label empty -> use value BadgeRule{"warn", "#f59e0b", "WARN"}, }; // Remaining columns: default Text. t.column_specs.resize(4); // default-initialized = CellRenderer::Text t.column_specs[0] = cs_status; assert(t.column_specs.size() == 4); assert(t.column_specs[0].renderer == CellRenderer::Badge); assert(t.column_specs[0].badges.size() == 3); assert(t.column_specs[1].renderer == CellRenderer::Text); std::printf("PASS: test_badge_column_spec (3 badge rules, remaining cols Text)\n"); } // --------------------------------------------------------------------------- // Test 3: Progress renderer. // --------------------------------------------------------------------------- static void test_progress_column_spec() { TableInput t; t.name = "t3"; t.rows = 3; t.cols = 4; t.cells = g_cells; t.headers = g_headers; t.types = g_types; ColumnSpec cs_progress; cs_progress.id = "progress"; cs_progress.renderer = CellRenderer::Progress; cs_progress.progress_scale_100 = false; cs_progress.progress_color_hex = "#3b82f6"; t.column_specs.resize(4); t.column_specs[1] = cs_progress; assert(t.column_specs[1].renderer == CellRenderer::Progress); assert(!t.column_specs[1].progress_scale_100); assert(t.column_specs[1].progress_color_hex == "#3b82f6"); std::printf("PASS: test_progress_column_spec\n"); } // --------------------------------------------------------------------------- // Test 4: Duration renderer. // --------------------------------------------------------------------------- static void test_duration_column_spec() { TableInput t; t.name = "t4"; t.rows = 3; t.cols = 4; t.cells = g_cells; t.headers = g_headers; t.types = g_types; ColumnSpec cs_dur; cs_dur.id = "duration_ms"; cs_dur.renderer = CellRenderer::Duration; cs_dur.duration_warn_ms = 500.0f; cs_dur.duration_error_ms = 2000.0f; t.column_specs.resize(4); t.column_specs[2] = cs_dur; assert(t.column_specs[2].renderer == CellRenderer::Duration); assert(t.column_specs[2].duration_warn_ms == 500.0f); assert(t.column_specs[2].duration_error_ms == 2000.0f); std::printf("PASS: test_duration_column_spec (warn=500ms error=2000ms)\n"); } // --------------------------------------------------------------------------- // Test 5: Icon renderer. // --------------------------------------------------------------------------- static void test_icon_column_spec() { TableInput t; t.name = "t5"; t.rows = 3; t.cols = 4; t.cells = g_cells; t.headers = g_headers; t.types = g_types; ColumnSpec cs_icon; cs_icon.id = "kind"; cs_icon.renderer = CellRenderer::Icon; cs_icon.icon_map = { IconMapEntry{"fn", "TI_BOLT", "#3b82f6"}, IconMapEntry{"type", "TI_DATABASE", ""}, }; t.column_specs.resize(4); t.column_specs[3] = cs_icon; assert(t.column_specs[3].renderer == CellRenderer::Icon); assert(t.column_specs[3].icon_map.size() == 2); assert(t.column_specs[3].icon_map[0].value == "fn"); assert(t.column_specs[3].icon_map[0].icon_name == "TI_BOLT"); // Verify render symbol still links with column_specs populated (classic overload). auto* render_fn = static_cast&, State&, bool)>(&data_table::render); (void)render_fn; std::printf("PASS: test_icon_column_spec (2 entries, render symbol links)\n"); } // --------------------------------------------------------------------------- // Test 6: Button renderer — TableEvent struct is constructible; events_out ptr // is accepted by the new render() overload (symbol resolution only). // --------------------------------------------------------------------------- static void test_button_column_spec_and_event_struct() { TableInput t; t.name = "t6"; t.rows = 3; t.cols = 4; t.cells = g_cells; t.headers = g_headers; t.types = g_types; ColumnSpec cs_btn; cs_btn.id = "actions"; cs_btn.renderer = CellRenderer::Button; cs_btn.button_action = "cancel"; cs_btn.button_label = "Cancel"; cs_btn.button_color_hex = "#ef4444"; t.column_specs.resize(4); t.column_specs[0] = cs_btn; assert(t.column_specs[0].renderer == CellRenderer::Button); assert(t.column_specs[0].button_action == "cancel"); assert(t.column_specs[0].button_label == "Cancel"); assert(t.column_specs[0].button_color_hex == "#ef4444"); // Verify TableEvent struct can be constructed and holds expected fields. TableEvent ev; ev.kind = TableEventKind::ButtonClick; ev.row = 1; ev.col = 0; ev.column_id = "actions"; ev.action_id = "cancel"; ev.value = "ok"; assert(ev.kind == TableEventKind::ButtonClick); assert(ev.row == 1); assert(ev.action_id == "cancel"); // Verify the render() overload with events_out is linkable. std::vector events; auto* render_with_events = static_cast&, State&, std::vector*, bool)>(&data_table::render); (void)render_with_events; std::printf("PASS: test_button_column_spec_and_event_struct " "(Button spec + TableEvent + render overload link)\n"); } // --------------------------------------------------------------------------- // Test 7: Tooltip field — ColumnSpec with tooltip_on_hover=true. // --------------------------------------------------------------------------- static void test_tooltip_column_spec() { TableInput t; t.name = "t7"; t.rows = 3; t.cols = 4; t.cells = g_cells; t.headers = g_headers; t.types = g_types; ColumnSpec cs_tip; cs_tip.id = "status"; cs_tip.renderer = CellRenderer::Text; // tooltip works on any renderer cs_tip.tooltip = "auto"; // "auto" -> show cell value cs_tip.tooltip_on_hover = true; t.column_specs.resize(4); t.column_specs[0] = cs_tip; assert(t.column_specs[0].tooltip == "auto"); assert(t.column_specs[0].tooltip_on_hover == true); // Also test explicit tooltip text. ColumnSpec cs_tip2; cs_tip2.id = "progress"; cs_tip2.renderer = CellRenderer::Progress; cs_tip2.tooltip = "Progress percentage (0..1)"; cs_tip2.tooltip_on_hover = true; t.column_specs[1] = cs_tip2; assert(t.column_specs[1].tooltip == "Progress percentage (0..1)"); assert(t.column_specs[1].tooltip_on_hover == true); std::printf("PASS: test_tooltip_column_spec (auto + explicit, tooltip_on_hover=true)\n"); } // --------------------------------------------------------------------------- // Test 8: render() back-compat overload without events_out — symbol links. // --------------------------------------------------------------------------- static void test_render_backcompat_overload() { // Verify both render() signatures are resolvable at link time. // Classic (no events_out): auto* render_classic = static_cast&, State&, bool)>(&data_table::render); (void)render_classic; // New (with events_out): auto* render_events = static_cast&, State&, std::vector*, bool)>(&data_table::render); (void)render_events; std::printf("PASS: test_render_backcompat_overload (both render() signatures link)\n"); } // --------------------------------------------------------------------------- // main // --------------------------------------------------------------------------- int main() { std::printf("=== test_column_specs ===\n"); test_no_column_specs(); test_badge_column_spec(); test_progress_column_spec(); test_duration_column_spec(); test_icon_column_spec(); test_button_column_spec_and_event_struct(); test_tooltip_column_spec(); test_render_backcompat_overload(); std::printf("=== ALL TESTS PASSED (8/8) ===\n"); return 0; }