a03675113a
- .claude/agents/fn-orquestador/SKILL.md - .claude/commands/fn_claude.md - .claude/rules/INDEX.md - .claude/rules/cpp_apps.md - .claude/rules/ids_naming.md - CHANGELOG.md - apps/dag_engine/README.md - apps/dag_engine/api.go - apps/dag_engine/dags_migrated/example.yaml - apps/dag_engine/dags_migrated/example_lineage_tracking.yaml - ... Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
187 lines
6.2 KiB
C++
187 lines
6.2 KiB
C++
// 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 <cmath>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
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<double> extract_numeric(const data_table::StageOutput& out, int col);
|
|
std::vector<std::string> 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<std::string> backing;
|
|
std::vector<const char*> cells;
|
|
std::vector<std::string> headers;
|
|
std::vector<ColumnType> types;
|
|
int rows = 0, cols = 0;
|
|
|
|
void add_row(std::initializer_list<const char*> 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");
|
|
}
|