Move the import orchestration — System::Load + drop_singleton_signals +
infer_signal_types + the post-import counts — out of the `load` command into
core/app/load.{hpp,cpp}: app::load_module(System*, name, path, ImportType)
returns a LoadResult (ok/error, parts, signals, dropped, power/gnd/kept_other)
with no Print/dialog/FTXUI. The "mentor|altium|ods" string→enum mapping moves
to app::import_type_from_name (mirrors export_format_from_path). The command
only parses the type and renders the counts; output is byte-identical.
Add tests/test_load.cpp (core, no UI): the name mapping; a minimal Mentor
netlist that imports two parts and drops one singleton signal; and a pin test
of the pre-existing missing-file behaviour (ImportBase doesn't check is_open(),
so a missing file yields an empty module rather than an error — preserved by
the extraction and pinned so any future hardening is a deliberate change).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
65 lines
2.3 KiB
C++
65 lines
2.3 KiB
C++
#include <doctest/doctest.h>
|
|
|
|
#include "core/app/load.hpp"
|
|
#include "core/domain/system.hpp"
|
|
|
|
#include <cstdio>
|
|
#include <fstream>
|
|
#include <string>
|
|
|
|
// app::load_module is pure core: import a module, drop singleton signals, infer
|
|
// signal types, return counts or an error — no Print/dialog/FTXUI. The parse
|
|
// helper import_type_from_name is likewise UI-free.
|
|
|
|
TEST_CASE("import_type_from_name maps names case-insensitively") {
|
|
ImportType t;
|
|
CHECK(app::import_type_from_name("mentor", t));
|
|
CHECK(t == ImportType::IMPORT_MENTOR);
|
|
CHECK(app::import_type_from_name("ALTIUM", t));
|
|
CHECK(t == ImportType::IMPORT_ALTIUM);
|
|
CHECK(app::import_type_from_name("Ods", t));
|
|
CHECK(t == ImportType::IMPORT_ODS);
|
|
CHECK_FALSE(app::import_type_from_name("kicad", t));
|
|
CHECK_FALSE(app::import_type_from_name("", t));
|
|
}
|
|
|
|
TEST_CASE("load_module imports, drops singletons and reports counts") {
|
|
// Minimal Mentor netlist: two parts; NETA/NETB span both parts (2 pins
|
|
// each, kept), LONELY sits on one pin only (dropped as a singleton).
|
|
const char *path = "test_load_in.net";
|
|
{
|
|
std::ofstream f(path);
|
|
f << "COMP: 'C1' 'J1'\n"
|
|
" Explicit Pin: '1' 'x' 'NETA'\n"
|
|
" Explicit Pin: '2' 'x' 'NETB'\n"
|
|
" Explicit Pin: '3' 'x' 'LONELY'\n"
|
|
"COMP: 'C2' 'J2'\n"
|
|
" Explicit Pin: '1' 'x' 'NETA'\n"
|
|
" Explicit Pin: '2' 'x' 'NETB'\n";
|
|
}
|
|
|
|
System sys;
|
|
app::LoadResult r = app::load_module(&sys, "M", path, ImportType::IMPORT_MENTOR);
|
|
|
|
CHECK(r.ok);
|
|
CHECK(r.error.empty());
|
|
CHECK(r.parts == 2);
|
|
CHECK(r.signals == 2); // NETA, NETB — LONELY dropped
|
|
CHECK(r.dropped == 1); // LONELY
|
|
|
|
std::remove(path);
|
|
}
|
|
|
|
TEST_CASE("load_module on a missing file currently succeeds empty (no throw)") {
|
|
// Pre-existing behaviour, preserved by the extraction: ImportBase opens the
|
|
// stream without checking is_open(), so a missing file yields an empty
|
|
// module rather than an error. Pinned here so any future hardening of this
|
|
// path is a deliberate, visible change.
|
|
System sys;
|
|
app::LoadResult r = app::load_module(
|
|
&sys, "M", "/nonexistent-dir-xyz/nope.net", ImportType::IMPORT_MENTOR);
|
|
CHECK(r.ok);
|
|
CHECK(r.parts == 0);
|
|
CHECK(r.signals == 0);
|
|
}
|