Files
fn_registry/cpp/tests/test_column_specs.cpp
T
egutierrez 380a7a8f35 data_table: declarative cell renderers Phase 1 (Badge/Progress/Duration/Icon)
Adds TableInput.column_specs sidecar field enabling apps to declare Badge,
Progress, Duration and Icon renderers per column without writing ImGui inline.
Back-compat: apps without column_specs compile and behave identically.

- data_table_types.h: CellRenderer enum, BadgeRule, IconMapEntry, ColumnSpec types
- data_table.cpp: hex_to_imcolor helper, icon_name_to_glyph static map (~30 Tabler icons),
  draw_cell_custom dispatcher, integration in Stage-0 and Stage-N cell loops and draw_extra_panel
- Bump version 1.0.0 -> 1.1.0 with capability growth log
- cpp/tests/test_column_specs.cpp: 5 smoke/linker tests (back-compat + 4 renderer types)
- cpp/tests/CMakeLists.txt: register test_column_specs target linked against fn_table_viz
- types/core/{cell_renderer,badge_rule,icon_map_entry,column_spec}.md: registry type mds
- docs/capabilities/data_table_renderers.md: canonical doc with end-to-end examples
- docs/capabilities/INDEX.md: added data-table-renderers group

All tests green: test_column_specs 5/5, test_fn_table_viz_smoke 8/8,
tql_emit 41/41, tql_apply 88/88, Wave-1 tests 8/8.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-15 16:38:24 +02:00

197 lines
6.6 KiB
C++

// test_column_specs.cpp — Smoke / back-compat tests for declarative cell renderers.
// Issue 0081-N, v1.1.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.
//
// 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 <cassert>
#include <cstdio>
#include <string>
#include <vector>
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<std::string> g_headers = {"status", "progress", "duration_ms", "kind"};
static const std::vector<ColumnType> 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).
auto* render_fn = &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.
auto* render_fn = &data_table::render;
(void)render_fn;
std::printf("PASS: test_icon_column_spec (2 entries, render symbol links)\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();
std::printf("=== ALL TESTS PASSED (5/5) ===\n");
return 0;
}