The analyze Issues pane and the dashboard Health rows each recomputed the same verify passes inline (pin-role mismatches, Power/Gnd net-mix, NC orphan rollup, model-driven checks) — the third and second copies of what the verify command also did. Route both screens through app::verify(System*) instead, so the passes live in exactly one place. Enrich VerifyReport with a per-pin OrphanPin detail list (module/part/pin + dropped flag) so the dashboard can still nest its dropped-singleton breakdown under the NC health line without re-walking modules/parts/pins. Output is unchanged in both screens (same label formats, same numbers). Prune the now-dead includes (nets/bsdl_check/connect/parts/pins as applicable, <unordered_set>) from both screens. Extend tests/test_verify.cpp to cover the new orphans detail. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
96 lines
3.4 KiB
C++
96 lines
3.4 KiB
C++
#include <doctest/doctest.h>
|
|
|
|
#include "core/app/verify.hpp"
|
|
#include "core/domain/connect.hpp"
|
|
#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"
|
|
|
|
// app::verify is pure core: it takes a System* and returns a VerifyReport of
|
|
// structured findings, with no Print/dialog/FTXUI. These tests build small
|
|
// systems by hand and assert the report — no UI involved.
|
|
|
|
TEST_CASE("verify on a null or empty system reports nothing") {
|
|
app::VerifyReport none = app::verify(nullptr);
|
|
CHECK(none.typed_pins == 0);
|
|
CHECK(none.total_nets == 0);
|
|
CHECK(none.role_mismatches.empty());
|
|
|
|
System sys;
|
|
app::VerifyReport r = app::verify(&sys);
|
|
CHECK(r.typed_pins == 0);
|
|
CHECK(r.total_nets == 0);
|
|
CHECK(r.bridged_nets == 0);
|
|
CHECK(r.net_inconsistencies.empty());
|
|
CHECK(r.orphan_total() == 0);
|
|
CHECK(r.model_total() == 0);
|
|
}
|
|
|
|
TEST_CASE("verify flags a bridged net that mixes Power and GndShield") {
|
|
// Two cards, one wired pin pair: A.NETA (Power) <-> B.NETB (GndShield).
|
|
System sys;
|
|
Module *a = sys.modules()->merge("A");
|
|
Module *b = sys.modules()->merge("B");
|
|
Part *ja = new Part("J1"); a->add(ja);
|
|
Part *jb = new Part("P1"); b->add(jb);
|
|
Pin *pa = new Pin("1"); ja->add(pa);
|
|
Pin *pb = new Pin("1"); jb->add(pb);
|
|
Signal *sa = a->signals->merge("NETA"); sa->type = SignalType::Power;
|
|
Signal *sb = b->signals->merge("NETB"); sb->type = SignalType::GndShield;
|
|
sa->add(pa); pa->connect(sa);
|
|
sb->add(pb); pb->connect(sb);
|
|
|
|
Connection *c = new Connection("A.J1<->B.P1", a, ja, b, jb);
|
|
c->transform_name = "identity";
|
|
c->pin_map.emplace_back(pa, pb);
|
|
sys.connections()->add(c);
|
|
|
|
app::VerifyReport r = app::verify(&sys);
|
|
|
|
CHECK(r.total_nets == 1);
|
|
CHECK(r.bridged_nets == 1);
|
|
REQUIRE(r.net_inconsistencies.size() == 1);
|
|
CHECK(r.net_inconsistencies[0].members.size() == 2);
|
|
// Both endpoints are present with their declared types.
|
|
bool seen_power = false, seen_gnd = false;
|
|
for (const auto &m : r.net_inconsistencies[0].members) {
|
|
if (m.type == SignalType::Power) seen_power = true;
|
|
if (m.type == SignalType::GndShield) seen_gnd = true;
|
|
}
|
|
CHECK(seen_power);
|
|
CHECK(seen_gnd);
|
|
}
|
|
|
|
TEST_CASE("verify counts orphan pins by their import origin") {
|
|
System sys;
|
|
Module *m = sys.modules()->merge("M");
|
|
Part *p = new Part("J1"); m->add(p);
|
|
Pin *imp = new Pin("1"); imp->nc_origin = NcOrigin::ImportedUnconnected; p->add(imp);
|
|
Pin *drp = new Pin("2"); drp->nc_origin = NcOrigin::DroppedSingleton; p->add(drp);
|
|
Pin *wired = new Pin("3"); p->add(wired);
|
|
Signal *s = m->signals->merge("NET"); s->add(wired); wired->connect(s);
|
|
|
|
app::VerifyReport r = app::verify(&sys);
|
|
|
|
CHECK(r.orphan_imported == 1);
|
|
CHECK(r.orphan_dropped == 1);
|
|
CHECK(r.orphan_total() == 2);
|
|
|
|
// Per-pin detail carries the path and origin (the dashboard lists the
|
|
// dropped ones under the NC health row).
|
|
REQUIRE(r.orphans.size() == 2);
|
|
int n_dropped = 0;
|
|
bool dropped_path_ok = false;
|
|
for (const auto &o : r.orphans) {
|
|
if (o.dropped) {
|
|
++n_dropped;
|
|
if (o.module == "M" && o.part == "J1" && o.pin == "2")
|
|
dropped_path_ok = true;
|
|
}
|
|
}
|
|
CHECK(n_dropped == 1);
|
|
CHECK(dropped_path_ok);
|
|
}
|