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>
86 lines
2.8 KiB
C++
86 lines
2.8 KiB
C++
#include <doctest/doctest.h>
|
|
|
|
#include "core/domain/parts.hpp"
|
|
#include "core/domain/pin_model.hpp"
|
|
#include "core/domain/pin_spec.hpp"
|
|
#include "core/domain/pins.hpp"
|
|
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
namespace {
|
|
|
|
// A stand-in PinModel: knows VCC (power) and CLK (clock input), has a 3-pin
|
|
// canonical layout, and says nothing about anything else.
|
|
struct FakeModel : PinModel {
|
|
PinSpec spec_for(const std::string &name) const override {
|
|
PinSpec s;
|
|
if (name == "VCC") {
|
|
s.function = PinFunction::Power;
|
|
s.direction = PinDirection::Power;
|
|
s.source = SpecSource::Bsdl;
|
|
} else if (name == "CLK") {
|
|
s.function = PinFunction::Clock;
|
|
s.direction = PinDirection::In;
|
|
s.source = SpecSource::Bsdl;
|
|
}
|
|
return s; // else default: source == None
|
|
}
|
|
std::vector<std::string> layout() const override {
|
|
return {"VCC", "CLK", "GND"};
|
|
}
|
|
SpecSource source() const override { return SpecSource::Bsdl; }
|
|
};
|
|
|
|
} // namespace
|
|
|
|
TEST_CASE("apply_model materialises layout pins and sets specs where the model speaks") {
|
|
Part part("U1");
|
|
part.add(new Pin("VCC")); // already present; CLK and GND are not
|
|
|
|
FakeModel m;
|
|
ApplyReport r = apply_model(&part, m);
|
|
|
|
// CLK and GND materialised from layout(); VCC was already there.
|
|
CHECK(r.materialised == 2);
|
|
CHECK(part.exists("CLK"));
|
|
CHECK(part.exists("GND"));
|
|
CHECK(r.pins_total == 3);
|
|
|
|
// Specs set only where the model speaks (VCC, CLK), not GND.
|
|
CHECK(part.get("VCC")->spec.function == PinFunction::Power);
|
|
CHECK(part.get("VCC")->spec.source == SpecSource::Bsdl);
|
|
CHECK(part.get("CLK")->spec.function == PinFunction::Clock);
|
|
CHECK(part.get("GND")->spec.source == SpecSource::None);
|
|
CHECK(r.set == 2);
|
|
}
|
|
|
|
TEST_CASE("apply_model does not overwrite a spec the model is silent about") {
|
|
Part part("U2");
|
|
Pin *p = new Pin("DATA");
|
|
p->spec.function = PinFunction::Signal;
|
|
p->spec.source = SpecSource::Bsdl; // a prior model wrote this
|
|
part.add(p);
|
|
|
|
FakeModel m; // says nothing about DATA
|
|
apply_model(&part, m);
|
|
|
|
CHECK(part.get("DATA")->spec.function == PinFunction::Signal);
|
|
CHECK(part.get("DATA")->spec.source == SpecSource::Bsdl);
|
|
}
|
|
|
|
TEST_CASE("apply_model never overwrites a higher-precedence source") {
|
|
Part part("U3");
|
|
Pin *p = new Pin("VCC");
|
|
p->spec.function = PinFunction::Ground; // user-set, deliberately != the model
|
|
p->spec.source = SpecSource::UserOverride;
|
|
part.add(p);
|
|
|
|
FakeModel m; // would set VCC = Power / Bsdl
|
|
apply_model(&part, m);
|
|
|
|
// UserOverride (rank 5) outranks Bsdl (rank 4): kept untouched.
|
|
CHECK(part.get("VCC")->spec.source == SpecSource::UserOverride);
|
|
CHECK(part.get("VCC")->spec.function == PinFunction::Ground);
|
|
}
|