#include #include "core/domain/parts.hpp" #include "core/domain/pin_name.hpp" #include "core/domain/pins.hpp" #include "core/domain/transform.hpp" #include TEST_CASE("canonical_pin_name: zero-pads pure digit suffix to 3") { CHECK(canonical_pin_name("A1") == "A001"); CHECK(canonical_pin_name("A001") == "A001"); CHECK(canonical_pin_name("AB12") == "AB012"); CHECK(canonical_pin_name("12") == "012"); CHECK(canonical_pin_name("J1") == "J001"); CHECK(canonical_pin_name("J999") == "J999"); CHECK(canonical_pin_name("J1000") == "J1000"); } TEST_CASE("canonical_pin_name: leaves names without digit suffix unchanged") { CHECK(canonical_pin_name("") == ""); CHECK(canonical_pin_name("VCC") == "VCC"); CHECK(canonical_pin_name("GND") == "GND"); } TEST_CASE("canonical_pin_name: leaves mixed suffixes unchanged") { CHECK(canonical_pin_name("A1B") == "A1B"); CHECK(canonical_pin_name("P3.3V") == "P3.3V"); CHECK(canonical_pin_name("D+") == "D+"); } TEST_CASE("IdentityTransform pairs A1 ↔ A001 and reports compatible") { auto a = std::make_unique("U_a"); auto b = std::make_unique("U_b"); Pin *a1 = new Pin("A1"); Pin *a2 = new Pin("A2"); Pin *b1 = new Pin("A001"); Pin *b2 = new Pin("A002"); a->add(a1); a->add(a2); b->add(b1); b->add(b2); CHECK(CheckIdentityCompatible(a.get(), b.get()) == ""); IdentityTransform t; auto wires = t.apply(a.get(), b.get()); CHECK(wires.size() == 2); // Build a quick set for order-independent verification. bool saw_a1 = false, saw_a2 = false; for (auto &w : wires) { if (w.first == a1) { CHECK(w.second == b1); saw_a1 = true; } if (w.first == a2) { CHECK(w.second == b2); saw_a2 = true; } } CHECK(saw_a1); CHECK(saw_a2); } TEST_CASE("CheckIdentityCompatible accepts subset, surfaces orphans as info") { auto a = std::make_unique("U_a"); auto b = std::make_unique("U_b"); a->add(new Pin("A1")); a->add(new Pin("A2")); b->add(new Pin("A001")); // canonical match for A1 // b is a strict subset of a — accept, info mentions A2 (orphan on a). std::string info; std::string err = CheckIdentityCompatible(a.get(), b.get(), &info); CHECK(err.empty()); CHECK(!info.empty()); CHECK(info.find("A2") != std::string::npos); CHECK(info.find("U_a") != std::string::npos); } TEST_CASE("FillIdentityNCs materialises the missing side") { auto a = std::make_unique("U_a"); auto b = std::make_unique("U_b"); a->add(new Pin("A1")); a->add(new Pin("A2")); a->add(new Pin("A3")); b->add(new Pin("A001")); // b is a strict subset; A2/A3 missing → 2 created on b. int added = FillIdentityNCs(a.get(), b.get()); CHECK(added == 2); CHECK(b->size() == 3); CHECK(b->exists("A2")); CHECK(b->exists("A3")); // Idempotent. CHECK(FillIdentityNCs(a.get(), b.get()) == 0); // Materialised pins are NC. CHECK(b->get("A2")->signal() == nullptr); } TEST_CASE("CheckIdentityCompatible refuses bidirectional mismatch") { auto a = std::make_unique("U_a"); auto b = std::make_unique("U_b"); a->add(new Pin("A1")); a->add(new Pin("X9")); // only on a b->add(new Pin("A001")); // canonical match b->add(new Pin("Y7")); // only on b std::string err = CheckIdentityCompatible(a.get(), b.get()); CHECK(!err.empty()); CHECK(err.find("X9") != std::string::npos); CHECK(err.find("Y7") != std::string::npos); }