Files
essim/tests/test_vpx_transform.cpp
François 63ca17d048 build: split core/ from frontends/; prepare for multiple GUI/TUI targets
Reorganise the tree into business vs frontend as separate directories:
  src/core/{domain,imports,app}   (was system/, imports/, app/)
  src/frontends/tui/              (was tui/ + main.cpp)
  tests/tui/                      (the FTXUI-coupled helper test)
All cross-dir #include paths rewritten; same-dir includes untouched.

CMake: essim_core is the frontend-agnostic business library — links libzip,
pugixml and bsdl, NO GUI toolkit. Each frontend is a self-contained
src/frontends/<name>/ (own CMakeLists, toolkit, main.cpp) that links
essim_core, selected with -DESSIM_FRONTEND=<name> (default tui; 'none' = core +
tests only, no toolkit fetched). FTXUI moved into the tui frontend. Tests are
split: essim_tests links essim_core (no FTXUI), essim_tui_tests links essim_tui.

Verified: default tui build green (ctest 2/2); ESSIM_FRONTEND=none builds the
core + tests with FTXUI never fetched and no `essim` binary.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 19:33:06 +02:00

125 lines
4.5 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include <doctest/doctest.h>
#include "core/domain/modules.hpp"
#include "core/domain/parts.hpp"
#include "core/domain/pins.hpp"
#include "core/domain/signals.hpp"
#include "core/domain/system.hpp"
#include "core/domain/transform.hpp"
#include "core/domain/transform_vpx.hpp"
#include <map>
#include <memory>
#include <set>
#include <string>
namespace {
// Build a Part with `pin_names` pins, attached to a fresh module so prnt
// chains exist (set-connector-type's validation depends on pins; transforms don't).
Part *make_part(Module *mod, const std::string &part_name,
const std::vector<std::string> &pin_names) {
Part *p = new Part(part_name);
mod->add(p); // sets p->prnt = mod
for (const auto &n : pin_names) {
Pin *pin = new Pin(n);
p->add(pin);
}
return p;
}
// VPX 3U bkp pinout for connector P0: 9 cols A-I × 1 row of "1".
std::vector<std::string> bkp_p0_pins_row1() {
std::vector<std::string> out;
for (char c = 'A'; c <= 'I'; ++c) out.push_back(std::string(1, c) + "1");
return out;
}
std::vector<std::string> payload_p0_pins_row1() {
std::vector<std::string> out;
for (char c = 'A'; c <= 'G'; ++c) out.push_back(std::string(1, c) + "1");
return out;
}
} // namespace
TEST_CASE("VPX transform registered and looked up by name") {
auto &reg = TransformRegistry::get();
Transform *t = reg.lookup("vpx-3u-bkp-p0", "vpx-3u-payload-p0");
REQUIRE(t != reg.identity());
CHECK(t->name == "vpx-3u-p0");
}
TEST_CASE("VPX transform lookup is symmetric (both pair orders work)") {
auto &reg = TransformRegistry::get();
Transform *forward = reg.lookup("vpx-3u-bkp-p1", "vpx-3u-payload-p1");
Transform *backward = reg.lookup("vpx-3u-payload-p1", "vpx-3u-bkp-p1");
REQUIRE(forward != reg.identity());
CHECK(forward == backward);
}
TEST_CASE("VPX transform refuses non-matching pairs (returns identity)") {
auto &reg = TransformRegistry::get();
CHECK(reg.lookup("vpx-3u-bkp-p0", "vpx-3u-payload-p1") == reg.identity());
CHECK(reg.lookup("vpx-3u-bkp-p0", "vpx-3u-bkp-p0") == reg.identity());
CHECK(reg.lookup("foo", "bar") == reg.identity());
}
TEST_CASE("VPX P0 row 1 mapping matches the reference table") {
// bkp_to_payload PCORR[0] row 1: A→A, B→C, C→C, D→C, E→D, F→E, G→E, H→F, I→G.
Module mod("M");
Part *bkp = make_part(&mod, "BKP", bkp_p0_pins_row1());
Part *pl = make_part(&mod, "PL", payload_p0_pins_row1());
bkp->connector_type = "vpx-3u-bkp-p0";
pl->connector_type = "vpx-3u-payload-p0";
auto &reg = TransformRegistry::get();
Transform *t = reg.lookup(bkp->connector_type, pl->connector_type);
REQUIRE(t != reg.identity());
auto pin_map = t->apply(bkp, pl);
// Build (bkp_pin_name → payload_pin_name) map for easy assertion.
std::map<std::string, std::string> got;
for (auto &wp : pin_map) got[wp.first->name] = wp.second->name;
std::map<std::string, std::string> expected = {
{"A1","A1"}, {"B1","C1"}, {"C1","C1"}, {"D1","C1"},
{"E1","D1"}, {"F1","E1"}, {"G1","E1"}, {"H1","F1"},
{"I1","G1"},
};
CHECK(got == expected);
}
TEST_CASE("VPX transform skips pins missing on the target side") {
// bkp has all 9 cols; payload has only A1, B1 → most bkp pins should drop.
Module mod("M");
Part *bkp = make_part(&mod, "BKP", bkp_p0_pins_row1());
Part *pl = make_part(&mod, "PL", {"A1", "B1"});
bkp->connector_type = "vpx-3u-bkp-p0";
pl->connector_type = "vpx-3u-payload-p0";
auto &reg = TransformRegistry::get();
Transform *t = reg.lookup(bkp->connector_type, pl->connector_type);
auto pin_map = t->apply(bkp, pl);
std::set<std::string> bkp_used;
for (auto &wp : pin_map) bkp_used.insert(wp.first->name);
// A1 maps to A1 (present), B1→C1 (absent), C1→C1 (absent), D1→C1 (absent),
// E1→D1 (absent), …, the only target-pin that exists for the row 1 mapping
// besides A1 is none — so only A1 keeps its wire (B1 in payload isn't
// a target of any bkp col on row 1).
CHECK(bkp_used == std::set<std::string>{"A1"});
}
TEST_CASE("Identity fallback wires same-named pins") {
Module mod("M");
Part *a = make_part(&mod, "A", {"X1", "X2", "Y1"});
Part *b = make_part(&mod, "B", {"X1", "X2", "Z1"});
auto &reg = TransformRegistry::get();
auto pin_map = reg.identity()->apply(a, b);
std::set<std::string> wired;
for (auto &wp : pin_map) wired.insert(wp.first->name);
CHECK(wired == std::set<std::string>{"X1", "X2"});
}