Files
essim/tests/test_component_kind.cpp
François c3bb00cb4d Altium import, nets, canonical pins, component kinds, set/$var, scrollback, source modal.
Major additions, all wired end-to-end with doctest coverage:

- Altium netlist importer (`imports/import_altium.{hpp,cpp}`): two-pass
  parser for `[ ]` parts and `( )` signals; `System::Load` no longer has
  the IMPORT_ALTIUM hole.
- `duplicate <src> <dst>` deep-copies a module (signals, parts, pins,
  rewired signals); connections excluded by design.
- Nets (`system/nets.{hpp,cpp}`): BFS over `Connection::pin_map` to
  return the transitive (Module, Signal) closure. `verify` extended with
  a second pass flagging Power↔GndShield inconsistencies in bridged
  nets; new `net <module> <signal>` command for inspection.
- Canonical pin names (`system/pin_name.{hpp,cpp}`): zero-padded digit
  suffix lets A1 ↔ A001 pair via `IdentityTransform` and
  `CheckIdentityCompatible` without losing the imported notation.
- Component classification (`system/component_kind.{hpp,cpp}`):
  `Part::kind` inferred at construction from the reference-designator
  prefix (longest-match: LED/TP/SW/FB/MK/MP/MH/HS/RA/RN/RP/RV first,
  then R/C/L/F/D/Q/U/J/P/Y/X/S).
- Identity wiring tolerance: `CheckIdentityCompatible` accepts the
  subset case (typical when one importer drops NC pins, e.g. Altium)
  and surfaces orphans as an info string. `FillIdentityNCs`
  materialises orphan canonical positions as NC pins on the missing
  side at connect time.
- Connector layout preparation: `pin_layout(kind)` and
  `FillPartFromLayout(part, kind)` stubs in `pin_role`, called from
  `set-type`. Empty today; populate alongside `vpx_3u_role`.
- TUI scrollback: PageUp/PageDown step 10 lines, Home/End jump to
  ends; `Print()` snaps back to the tail.
- `set <name> <value>` declares session variables; `$name` / `${name}`
  expanded inside `Finalize` between canonical-form recording and the
  action call — history and script-save preserve `$var` references.
- Long `source` scripts now show a centred "Computing…" modal with a
  N/M progress counter. Driven by a ticker thread that posts one
  paced `Event::Special` per processed line, ack'd by the main thread,
  so heavy lines don't backlog ticks and freeze the counter.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-09 20:28:21 +02:00

71 lines
3.0 KiB
C++

#include <doctest/doctest.h>
#include "system/component_kind.hpp"
#include "system/parts.hpp"
#include <memory>
TEST_CASE("infer_component_kind: passives") {
CHECK(infer_component_kind("R12") == ComponentKind::Passive);
CHECK(infer_component_kind("C1") == ComponentKind::Passive);
CHECK(infer_component_kind("L4") == ComponentKind::Passive);
CHECK(infer_component_kind("FB1") == ComponentKind::Passive);
CHECK(infer_component_kind("RN2") == ComponentKind::Passive);
CHECK(infer_component_kind("F3") == ComponentKind::Passive); // fuse
}
TEST_CASE("infer_component_kind: semiconductors") {
CHECK(infer_component_kind("D1") == ComponentKind::Semiconductor);
CHECK(infer_component_kind("Q4") == ComponentKind::Semiconductor);
CHECK(infer_component_kind("LED2") == ComponentKind::Semiconductor);
}
TEST_CASE("infer_component_kind: ICs") {
CHECK(infer_component_kind("U1") == ComponentKind::IntegratedCircuit);
CHECK(infer_component_kind("U100") == ComponentKind::IntegratedCircuit);
}
TEST_CASE("infer_component_kind: connectors") {
CHECK(infer_component_kind("J1") == ComponentKind::Connector);
CHECK(infer_component_kind("P0") == ComponentKind::Connector);
CHECK(infer_component_kind("J100") == ComponentKind::Connector);
}
TEST_CASE("infer_component_kind: misc") {
CHECK(infer_component_kind("TP1") == ComponentKind::TestPoint);
CHECK(infer_component_kind("SW1") == ComponentKind::Switch);
CHECK(infer_component_kind("Y1") == ComponentKind::Crystal);
CHECK(infer_component_kind("X1") == ComponentKind::Crystal);
CHECK(infer_component_kind("MK1") == ComponentKind::Mechanical);
CHECK(infer_component_kind("HS2") == ComponentKind::Mechanical);
}
TEST_CASE("infer_component_kind: fallback") {
CHECK(infer_component_kind("") == ComponentKind::Other);
CHECK(infer_component_kind("123") == ComponentKind::Other); // no letter prefix
CHECK(infer_component_kind("ZZZ1") == ComponentKind::Other); // unknown prefix
}
TEST_CASE("infer_component_kind: case insensitive") {
CHECK(infer_component_kind("r1") == ComponentKind::Passive);
CHECK(infer_component_kind("u1") == ComponentKind::IntegratedCircuit);
}
TEST_CASE("Part ctor populates kind from name") {
auto r = std::make_unique<Part>("R12");
auto u = std::make_unique<Part>("U7");
auto j = std::make_unique<Part>("J1");
CHECK(r->kind == ComponentKind::Passive);
CHECK(u->kind == ComponentKind::IntegratedCircuit);
CHECK(j->kind == ComponentKind::Connector);
}
TEST_CASE("component_kind_from_name accepts canonical and aliases") {
ComponentKind k;
CHECK(component_kind_from_name("passive", k)); CHECK(k == ComponentKind::Passive);
CHECK(component_kind_from_name("ic", k)); CHECK(k == ComponentKind::IntegratedCircuit);
CHECK(component_kind_from_name("conn", k)); CHECK(k == ComponentKind::Connector);
CHECK(component_kind_from_name("Connector", k)); CHECK(k == ComponentKind::Connector);
CHECK(!component_kind_from_name("nonsense", k));
}