// Tests parciales para viz::render (cpp/functions/viz/viz_render.cpp). // // render() requiere un contexto ImGui + ImPlot vivo — no se puede ejercitar // en tests headless. Este archivo cubre SOLO las funciones helper publicas // (first_numeric_col, first_category_col, extract_numeric, extract_category) // que son logica pura sin dependencia de ImGui/ImPlot. // // Smoke real del dispatcher: primitives_gallery --capture (golden images, issue 0048). // Issue 0081-G. #define CATCH_CONFIG_MAIN #include "catch_amalgamated.hpp" // Incluir solo el header de tipos (no ImGui, no ImPlot) para construir // StageOutput de prueba. #include "core/data_table_types.h" // Declaraciones adelantadas de los helpers publicos (evita incluir viz_render.h // que arrastra imgui.h). Replicar aqui es legal para tests headless. #include #include #include namespace data_table { struct StageOutput; } // Incluir la implementacion completa via el .h para obtener las firmas // publicas. viz_render.h incluye imgui.h, que en tests sin contexto GLFW // es solo tipos — safe para compilar (no linkamos fn_framework aqui). // // Para evitar el pull-in de imgui.h (que necesita GLFW o al menos el stub), // re-declaramos solo las funciones helper que necesitamos testear. // Las implementaciones estan en viz_render.cpp que linkamos directamente. namespace viz { int first_numeric_col(const data_table::StageOutput& out); int first_category_col(const data_table::StageOutput& out); std::vector extract_numeric(const data_table::StageOutput& out, int col); std::vector extract_category(const data_table::StageOutput& out, int col); } // namespace viz using namespace data_table; // --------------------------------------------------------------------------- // Helper: construir un StageOutput simple para tests // --------------------------------------------------------------------------- namespace { struct TestTable { std::vector backing; std::vector cells; std::vector headers; std::vector types; int rows = 0, cols = 0; void add_row(std::initializer_list row) { for (const char* s : row) backing.emplace_back(s ? s : ""); ++rows; } StageOutput build() { StageOutput out; cols = (int)headers.size(); cells.clear(); for (const auto& s : backing) cells.push_back(s.c_str()); out.headers = headers; out.types = types; out.rows = rows; out.cols = cols; out.cells = cells; return out; } }; } // anon // --------------------------------------------------------------------------- // first_numeric_col // --------------------------------------------------------------------------- TEST_CASE("first_numeric_col returns -1 on empty output", "[viz_render]") { StageOutput out; REQUIRE(viz::first_numeric_col(out) == -1); } TEST_CASE("first_numeric_col returns 0 for all-numeric output", "[viz_render]") { TestTable t; t.headers = {"a", "b"}; t.types = {ColumnType::Float, ColumnType::Int}; t.add_row({"1.0", "2"}); auto out = t.build(); REQUIRE(viz::first_numeric_col(out) == 0); } TEST_CASE("first_numeric_col skips string columns", "[viz_render]") { TestTable t; t.headers = {"cat", "val"}; t.types = {ColumnType::String, ColumnType::Float}; t.add_row({"alfa", "3.14"}); auto out = t.build(); // Primera col es String -> primera numerica es col 1 REQUIRE(viz::first_numeric_col(out) == 1); } // --------------------------------------------------------------------------- // first_category_col // --------------------------------------------------------------------------- TEST_CASE("first_category_col returns -1 on all-numeric output", "[viz_render]") { TestTable t; t.headers = {"x", "y"}; t.types = {ColumnType::Int, ColumnType::Float}; t.add_row({"1", "2.0"}); auto out = t.build(); REQUIRE(viz::first_category_col(out) == -1); } TEST_CASE("first_category_col returns first string column", "[viz_render]") { TestTable t; t.headers = {"num", "cat"}; t.types = {ColumnType::Int, ColumnType::String}; t.add_row({"42", "hello"}); auto out = t.build(); REQUIRE(viz::first_category_col(out) == 1); } // --------------------------------------------------------------------------- // extract_numeric // --------------------------------------------------------------------------- TEST_CASE("extract_numeric returns empty for out-of-range col", "[viz_render]") { TestTable t; t.headers = {"x"}; t.types = {ColumnType::Float}; t.add_row({"1.5"}); auto out = t.build(); // col -1 REQUIRE(viz::extract_numeric(out, -1).empty()); // col >= cols REQUIRE(viz::extract_numeric(out, 5).empty()); } TEST_CASE("extract_numeric returns NaN for unparseable cells", "[viz_render]") { TestTable t; t.headers = {"val"}; t.types = {ColumnType::String}; t.add_row({"abc"}); t.add_row({"3.14"}); t.add_row({""}); auto out = t.build(); auto v = viz::extract_numeric(out, 0); REQUIRE(v.size() == 3); REQUIRE(std::isnan(v[0])); REQUIRE(!std::isnan(v[1])); REQUIRE(std::abs(v[1] - 3.14) < 1e-9); REQUIRE(std::isnan(v[2])); } // --------------------------------------------------------------------------- // extract_category // --------------------------------------------------------------------------- TEST_CASE("extract_category returns empty for out-of-range col", "[viz_render]") { TestTable t; t.headers = {"s"}; t.types = {ColumnType::String}; t.add_row({"hello"}); auto out = t.build(); REQUIRE(viz::extract_category(out, -1).empty()); REQUIRE(viz::extract_category(out, 10).empty()); } TEST_CASE("extract_category returns empty strings for null cells", "[viz_render]") { // Construimos un StageOutput con un cell ptr nulo manualmente StageOutput out; out.headers = {"s"}; out.types = {ColumnType::String}; out.rows = 2; out.cols = 1; // cell[0] = nullptr, cell[1] = "hello" static const char* hello = "hello"; out.cells = {nullptr, hello}; auto v = viz::extract_category(out, 0); REQUIRE(v.size() == 2); REQUIRE(v[0].empty()); REQUIRE(v[1] == "hello"); }