29#include <wx/filename.h>
30#include <wx/stdpaths.h>
57wxString stageProject(
const wxString& aStem )
59 wxString sep = wxFileName::GetPathSeparator();
60 wxString dir = wxStandardPaths::Get().GetTempDir() + sep + aStem + wxT(
"-fpreconcile-qa" );
62 if( wxDirExists( dir ) )
63 wxFileName::Rmdir( dir, wxPATH_RMDIR_RECURSIVE );
65 wxFileName::Mkdir( dir, wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL );
67 wxString projectPath = dir + sep + aStem + wxT(
".kicad_pro" );
71 Pgm().GetSettingsManager().
Prj().GetProjectDirectory() );
80 std::unique_ptr<PCB_IO_EASYEDAPRO_V3> m_plugin;
81 std::unique_ptr<BOARD> m_board;
82 std::vector<std::unique_ptr<FOOTPRINT>> m_definitions;
83 std::unique_ptr<KI_TEST::TEMPORARY_DIRECTORY> m_sourceDir;
87IMPORTED_BOARD importSampleBoard(
PROJECT& aProject,
const std::string& aTag )
89 IMPORTED_BOARD sample;
91 const wxString archiveName = wxS(
"ProProject_LS2K0300Core_2025-11-14.epro2" );
94 srcFn.AppendDir( wxS(
"plugins" ) );
95 srcFn.AppendDir( wxS(
"easyedapro" ) );
96 srcFn.SetFullName( archiveName );
97 BOOST_REQUIRE_MESSAGE( srcFn.FileExists(),
"Missing EasyEDA Pro v3 board fixture" );
99 sample.m_sourceDir = std::make_unique<KI_TEST::TEMPORARY_DIRECTORY>( aTag,
"" );
101 wxFileName importFn( wxString::FromUTF8( sample.m_sourceDir->GetPath().string() ),
103 BOOST_REQUIRE( wxCopyFile( srcFn.GetFullPath(), importFn.GetFullPath() ) );
105 std::map<std::string, UTF8> properties;
106 properties[
"pcb_id"] =
"eb9fbfba682940f7a002816e66fbb3d7";
108 sample.m_plugin = std::make_unique<PCB_IO_EASYEDAPRO_V3>();
109 sample.m_board = std::make_unique<BOARD>();
111 sample.m_plugin->LoadBoard( importFn.GetFullPath(), sample.m_board.get(), &properties,
114 BOOST_REQUIRE_GT( sample.m_board->
Footprints().size(), 0 );
116 for(
FOOTPRINT* fp : sample.m_plugin->GetImportedCachedLibraryFootprints() )
117 sample.m_definitions.emplace_back( fp );
119 BOOST_REQUIRE_GT( sample.m_definitions.size(), 0 );
127 const wxString& aNickname,
const wxString& aDirStem,
132 wxString libPath = libDir.GetFullPath();
137 pi->CreateLibrary( libPath );
141 copy->SetReference( wxS(
"REF**" ) );
142 pi->FootprintSave( libPath,
copy.get() );
149 row.
SetURI( wxS(
"${KIPRJMOD}/" ) + libDir.GetFullName() );
159struct COLLISION_CANDIDATE
167COLLISION_CANDIDATE findCandidate( IMPORTED_BOARD& aSample )
169 COLLISION_CANDIDATE candidate;
173 wxString nick = fp->GetFPID().GetUniStringLibNickname();
174 wxString
name = fp->GetFPID().GetUniStringLibItemName();
176 if( nick.IsEmpty() ||
name.IsEmpty() )
179 for(
const std::unique_ptr<FOOTPRINT>& def : aSample.m_definitions )
181 if( def->GetFPID().GetUniStringLibItemName() !=
name || def->Pads().empty() )
184 candidate.m_footprint = fp;
185 candidate.m_nickname = nick;
186 candidate.m_name =
name;
204 wxString projectPath = stageProject( wxS(
"eagle_fpreconcile" ) );
208 brdFn.AppendDir( wxS(
"io" ) );
209 brdFn.AppendDir( wxS(
"eagle" ) );
210 brdFn.SetFullName( wxS(
"eagle-import-testfile.brd" ) );
211 BOOST_REQUIRE_MESSAGE( brdFn.FileExists(),
"Missing Eagle board fixture" );
214 std::unique_ptr<BOARD> board = std::make_unique<BOARD>();
218 BOOST_REQUIRE_GT( board->Footprints().size(), 0 );
221 std::vector<std::unique_ptr<FOOTPRINT>> defs;
224 defs.emplace_back( fp );
229 const wxString cacheNick = wxS(
"eagle_test-import-fps" );
233 reconciler.
Reconcile( board.get(), std::move( defs ), cacheNick, {} );
237 BOOST_CHECK_GT(
result.m_savedToCache, 0 );
239 wxFileName prettyDir(
project.GetProjectPath(), cacheNick,
241 BOOST_CHECK_MESSAGE( wxDir::Exists( prettyDir.GetFullPath() ),
242 "Generated .pretty was not published" );
247 BOOST_CHECK( projectTable->
HasRow( cacheNick ) );
252 for(
FOOTPRINT* fp : board->Footprints() )
254 wxString
name = fp->GetFPID().GetUniStringLibItemName();
259 wxString nick = fp->GetFPID().GetUniStringLibNickname();
261 BOOST_CHECK_MESSAGE( !nick.IsEmpty(),
262 wxString::Format(
"Board footprint '%s' left with empty nickname",
265 wxString::Format(
"FPID '%s:%s' does not resolve after reconciliation",
270 BOOST_CHECK_GT( resolved, 0 );
279 stageProject( wxS(
"altium_fpreconcile" ) );
282 std::string dataPath =
286 std::unique_ptr<BOARD> board = std::make_unique<BOARD>();
290 BOOST_REQUIRE_GT( board->Footprints().size(), 0 );
293 std::vector<std::unique_ptr<FOOTPRINT>> defs;
296 defs.emplace_back( fp );
301 const wxString cacheNick = wxS(
"hifive-import-fps" );
305 reconciler.
Reconcile( board.get(), std::move( defs ), cacheNick, {} );
312 for(
FOOTPRINT* fp : board->Footprints() )
314 wxString
name = fp->GetFPID().GetUniStringLibItemName();
319 wxString nick = fp->GetFPID().GetUniStringLibNickname();
322 wxString::Format(
"FPID '%s:%s' does not resolve after reconciliation",
327 BOOST_CHECK_GT( resolved, 0 );
335 stageProject( wxS(
"easyedapro_v3_fpreconcile" ) );
338 IMPORTED_BOARD sample = importSampleBoard(
project,
"easyedapro_v3_fpreconcile_src" );
339 BOARD* board = sample.m_board.get();
342 wxFileName srcDir( wxString::FromUTF8( sample.m_sourceDir->GetPath().string() ),
344 wxFileName strayLib( srcDir.GetPath(),
347 BOOST_CHECK_MESSAGE( !wxDir::Exists( strayLib.GetFullPath() ),
348 "LoadBoard wrote a library into the source directory" );
353 const wxString cacheNick = wxS(
"ls2k0300-import-fps" );
357 reconciler.
Reconcile( board, std::move( sample.m_definitions ), cacheNick, {} );
361 BOOST_CHECK_GT(
result.m_savedToCache, 0 );
363 wxFileName prettyDir(
project.GetProjectPath(), cacheNick,
365 BOOST_CHECK_MESSAGE( wxDir::Exists( prettyDir.GetFullPath() ),
366 "Generated .pretty was not published into the project" );
370 BOOST_CHECK( projectTable->
HasRow( cacheNick ) );
376 wxString
name = fp->GetFPID().GetUniStringLibItemName();
381 wxString nick = fp->GetFPID().GetUniStringLibNickname();
384 wxString::Format(
"FPID '%s:%s' does not resolve after reconciliation",
389 BOOST_CHECK_GT( resolved, 0 );
398 stageProject( wxS(
"fpreconcile_collide" ) );
401 IMPORTED_BOARD sample = importSampleBoard(
project,
"fpreconcile_collide_src" );
402 COLLISION_CANDIDATE candidate = findCandidate( sample );
410 impostor.
SetFPID(
LIB_ID( candidate.m_nickname, candidate.m_name ) );
412 publishLibrary(
project, *adapter, candidate.m_nickname, wxS(
"impostor" ), impostor );
414 const wxString cacheNick = wxS(
"collide-import-fps" );
418 sample.m_board.get(), std::move( sample.m_definitions ), cacheNick, {} );
423 BOOST_CHECK_EQUAL( candidate.m_footprint->GetFPID().GetUniStringLibNickname(), cacheNick );
425 std::unique_ptr<FOOTPRINT> linked( adapter->
LoadFootprint( cacheNick, candidate.m_name,
428 BOOST_CHECK_GT( linked->Pads().size(), 0 );
436 stageProject( wxS(
"fpreconcile_row" ) );
439 IMPORTED_BOARD sample = importSampleBoard(
project,
"fpreconcile_row_src" );
444 const wxString cacheNick = wxS(
"row-import-fps" );
449 publishLibrary(
project, *adapter, cacheNick, wxS(
"user-owned" ), owned );
457 const wxString uriBefore = row->
URI();
462 sample.m_board.get(), std::move( sample.m_definitions ), cacheNick, {} );
464 BOOST_CHECK(
result.m_cacheNickname.IsEmpty() );
467 wxFileName prettyDir(
project.GetProjectPath(), cacheNick,
469 BOOST_CHECK_MESSAGE( !wxDir::Exists( prettyDir.GetFullPath() ),
470 "Published a cache over a nickname the user already owns" );
478 stageProject( wxS(
"eagle_netlist_roundtrip" ) );
482 brdFn.AppendDir( wxS(
"io" ) );
483 brdFn.AppendDir( wxS(
"eagle" ) );
484 brdFn.SetFullName( wxS(
"eagle-import-testfile.brd" ) );
488 std::unique_ptr<BOARD> imported = std::make_unique<BOARD>();
489 imported->SetProject( &
project );
493 std::vector<std::unique_ptr<FOOTPRINT>> defs;
496 defs.emplace_back( fp );
501 const wxString cacheNick = wxS(
"eagle_test-import-fps" );
503 reconciler.
Reconcile( imported.get(), std::move( defs ), cacheNick, {} );
507 std::set<wxString> seen;
508 int expectedComponents = 0;
510 for(
FOOTPRINT* fp : imported->Footprints() )
512 LIB_ID fpid = fp->GetFPID();
517 wxString ref = wxString::Format( wxS(
"U%d" ), ++expectedComponents );
522 BOOST_REQUIRE_GT( expectedComponents, 0 );
525 std::unique_ptr<BOARD> target = std::make_unique<BOARD>();
526 target->SetProject( &
project );
529 toolMgr.
SetEnvironment( target.get(),
nullptr,
nullptr,
nullptr,
nullptr );
541 BOOST_CHECK_MESSAGE( !
reporter.GetMessages().Lower().Contains( wxS(
"not found" ) ),
542 "Netlist updater reported a footprint not found after reconciliation" );
543 BOOST_CHECK_EQUAL(
static_cast<int>( target->Footprints().size() ), expectedComponents );
Update the BOARD with a new netlist.
void SetReporter(REPORTER *aReporter)
Enable dry run mode (just report, no changes to PCB).
bool UpdateNetlist(NETLIST &aNetlist)
Update the board's components according to the new netlist.
void SetDeleteUnusedFootprints(bool aEnabled)
int GetErrorCount() const
void SetReplaceFootprints(bool aEnabled)
Information pertinent to a Pcbnew printed circuit board.
const FOOTPRINTS & Footprints() const
void SetProject(PROJECT *aProject, bool aReferenceOnly=false)
Link a board to a given project.
std::optional< LIBRARY_TABLE * > ProjectTable() const
Retrieves the project library table for this adapter type, or nullopt if one doesn't exist.
void LoadProjectTables(std::initializer_list< LIBRARY_TABLE_TYPE > aTablesToLoad={})
(Re)loads the project library tables in the given list, or all tables if no list is given
void SetNickname(const wxString &aNickname)
void SetType(const wxString &aType)
void SetURI(const wxString &aUri)
void SetScope(LIBRARY_TABLE_SCOPE aScope)
const wxString & URI() const
bool HasRow(const wxString &aNickname) const
A logical library item identifier and consists of various portions much like a URI.
wxString GetUniStringLibId() const
const wxString GetUniStringLibItemName() const
Get strings for display messages in dialogs.
Store information read from a netlist along with the flags used to update the NETLIST in the BOARD.
std::vector< FOOTPRINT * > GetImportedCachedLibraryFootprints() override
Return a container with the cached library footprints generated in the last call to Load.
BOARD * LoadBoard(const wxString &aFileName, BOARD *aAppendToMe, const std::map< std::string, UTF8 > *aProperties, PROJECT *aProject=nullptr) override
Load information from some input file format that this PCB_IO implementation knows about into either ...
Works with Eagle 6.x XML board files and footprints to implement the Pcbnew #PLUGIN API or a portion ...
std::vector< FOOTPRINT * > GetImportedCachedLibraryFootprints() override
Return a container with the cached library footprints generated in the last call to Load.
BOARD * LoadBoard(const wxString &aFileName, BOARD *aAppendToMe, const std::map< std::string, UTF8 > *aProperties=nullptr, PROJECT *aProject=nullptr) override
Load information from some input file format that this PCB_IO implementation knows about into either ...
@ KICAD_SEXP
S-expression Pcbnew file format.
static PCB_IO * FindPlugin(PCB_FILE_T aFileType)
Return a #PLUGIN which the caller can use to import, export, save, or load design documents.
virtual SETTINGS_MANAGER & GetSettingsManager() const
virtual LIBRARY_MANAGER & GetLibraryManager() const
static FOOTPRINT_LIBRARY_ADAPTER * FootprintLibAdapter(PROJECT *aProject)
Container for project specific data.
virtual const wxString GetProjectPath() const
Return the full path of the project.
bool LoadProject(const wxString &aFullPath, bool aSetActive=true)
Load a project or sets up a new project with a specified path.
PROJECT & Prj() const
A helper while we are not MDI-capable – return the one and only project.
A wrapper for reporting to a wxString object.
static const std::string KiCadFootprintLibPathExtension
std::unique_ptr< T > IO_RELEASER
Helper to hold and release an IO_BASE object when exceptions are thrown.
wxString ShortenLibName(wxString aProjectName)
std::string GetPcbnewTestDataDir()
Utility which returns a path to the data directory where the test board files are stored.
std::string GetEeschemaTestDataDir()
Get the configured location of Eeschema test data.
PGM_BASE & Pgm()
The global program "get" accessor.
BOOST_AUTO_TEST_CASE(HorizontalAlignment)
BOOST_AUTO_TEST_SUITE(CadstarPartParser)
BOOST_REQUIRE(intersection.has_value()==c.ExpectedIntersection.has_value())
BOOST_AUTO_TEST_SUITE_END()
IbisParser parser & reporter
wxString result
Test unit parsing edge cases and error handling.
BOOST_CHECK_EQUAL(result, "25.4")