28#include <wx/filename.h>
54wxString stageProject(
const wxString& aStem )
56 wxString sep = wxFileName::GetPathSeparator();
57 wxString dir = wxFileName::GetTempDir() + sep + aStem + wxT(
"-symreconcile-qa" );
59 if( wxDirExists( dir ) )
60 wxFileName::Rmdir( dir, wxPATH_RMDIR_RECURSIVE );
62 wxFileName::Mkdir( dir, wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL );
64 wxString projectPath = dir + sep + aStem + wxT(
".kicad_pro" );
68 Pgm().GetSettingsManager().
Prj().GetProjectDirectory() );
74wxString easyEdaProV3Archive()
77 +
"pcbnew/plugins/easyedapro/ProProject_LS2K0300Core_2025-11-14.epro2" );
82bool hasSymbolLib(
const wxString& aPath )
92 return dir.GetFirst( &
name, spec, wxDIR_FILES );
100 std::unique_ptr<SCHEMATIC> m_schematic;
101 std::vector<std::unique_ptr<LIB_SYMBOL>> m_definitions;
102 wxString m_sourceDir;
106IMPORTED_SAMPLE importSample(
PROJECT& aProject )
108 IMPORTED_SAMPLE sample;
110 wxString sep = wxFileName::GetPathSeparator();
111 sample.m_sourceDir = aProject.
GetProjectPath() + wxT(
"import-src" );
112 BOOST_REQUIRE( wxFileName::Mkdir( sample.m_sourceDir, wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL ) );
114 wxFileName archiveFile( sample.m_sourceDir + sep, wxS(
"sample" ), wxS(
"epro2" ) );
115 BOOST_REQUIRE( wxCopyFile( easyEdaProV3Archive(), archiveFile.GetFullPath() ) );
117 sample.m_schematic = std::make_unique<SCHEMATIC>(
nullptr );
120 sample.m_plugin.reset( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_EASYEDAPRO_V3 ) );
124 BOOST_REQUIRE_NO_THROW( rootSheet = sample.m_plugin->LoadSchematicFile(
125 archiveFile.GetFullPath(), sample.m_schematic.get() ) );
130 for(
LIB_SYMBOL* symbol : sample.m_plugin->GetImportedCachedLibrarySymbols() )
131 sample.m_definitions.emplace_back( symbol );
133 BOOST_REQUIRE_GT( sample.m_definitions.size(), 0 );
139std::vector<SCH_SYMBOL*> placedSymbols(
SCHEMATIC& aSchematic )
141 std::vector<SCH_SYMBOL*> symbols;
145 if(
SCH_SCREEN* screen = sheetPath.LastScreen() )
148 symbols.push_back(
static_cast<SCH_SYMBOL*
>( item ) );
158 const wxString& aFileStem,
const LIB_SYMBOL& aSymbol )
165 pi->CreateLibrary( libFn.GetFullPath() );
167 auto copy = std::make_unique<LIB_SYMBOL>( aSymbol );
169 pi->SaveSymbol( libFn.GetFullPath(),
copy.release() );
176 row.
SetURI( wxS(
"${KIPRJMOD}/" ) + libFn.GetFullName() );
186struct COLLISION_CANDIDATE
188 SCH_SYMBOL* m_symbol =
nullptr;
189 LIB_SYMBOL* m_definition =
nullptr;
195COLLISION_CANDIDATE findCandidate( IMPORTED_SAMPLE& aSample )
197 COLLISION_CANDIDATE candidate;
199 for(
SCH_SYMBOL* symbol : placedSymbols( *aSample.m_schematic ) )
201 wxString nick = symbol->GetLibId().GetUniStringLibNickname();
202 wxString
name = symbol->GetLibId().GetUniStringLibItemName();
204 if( nick.IsEmpty() ||
name.IsEmpty() )
207 for(
const std::unique_ptr<LIB_SYMBOL>& def : aSample.m_definitions )
209 if( def->GetLibId().GetUniStringLibItemName() !=
name || def->GetPins().empty() )
212 candidate.m_symbol = symbol;
213 candidate.m_definition = def.get();
214 candidate.m_nickname = nick;
215 candidate.m_name =
name;
233 wxString projectPath = stageProject( wxS(
"easyedapro_v3" ) );
236 IMPORTED_SAMPLE sample = importSample(
project );
237 SCHEMATIC& schematic = *sample.m_schematic;
240 BOOST_CHECK_MESSAGE( !hasSymbolLib( sample.m_sourceDir ),
241 "LoadSchematicFile wrote a library into the source directory" );
246 const wxString cacheNick = wxS(
"ls2k0300-import-syms" );
250 reconciler.
Reconcile( &schematic, std::move( sample.m_definitions ), cacheNick, {} );
254 BOOST_CHECK_GT(
result.m_savedToCache, 0 );
256 wxFileName cacheFile(
project.GetProjectPath(), cacheNick,
258 BOOST_CHECK_MESSAGE( wxFileExists( cacheFile.GetFullPath() ),
259 "Generated .kicad_sym was not published into the project" );
263 BOOST_CHECK( projectTable->
HasRow( cacheNick ) );
285 BOOST_CHECK_MESSAGE( !nick.IsEmpty(),
286 wxString::Format(
"Symbol '%s' left with empty nickname",
name ) );
287 BOOST_CHECK_MESSAGE( adapter->
LoadSymbol( nick,
name ) !=
nullptr,
288 wxString::Format(
"LIB_ID '%s:%s' does not resolve after "
289 "reconciliation", nick,
name ) );
294 BOOST_CHECK_GT( resolved, 0 );
297 wxFileName::Rmdir( projectPath, wxPATH_RMDIR_RECURSIVE );
304 wxString projectPath = stageProject( wxS(
"easyedapro_v2" ) );
307 wxString sep = wxFileName::GetPathSeparator();
308 wxString srcDir = projectPath + wxT(
"import-src" );
309 BOOST_REQUIRE( wxFileName::Mkdir( srcDir, wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL ) );
311 wxString source = wxString::FromUTF8(
313 +
"pcbnew/plugins/easyedapro/ProProject_Yuzuki Chameleon_2023-09-02.epro" );
316 wxFileName archiveFile( srcDir + sep, wxS(
"sample" ), wxS(
"epro" ) );
317 BOOST_REQUIRE( wxCopyFile( source, archiveFile.GetFullPath() ) );
320 schematic.SetProject( &
project );
326 BOOST_REQUIRE_NO_THROW(
327 rootSheet = plugin->LoadSchematicFile( archiveFile.GetFullPath(), &schematic ) );
330 BOOST_CHECK_MESSAGE( !hasSymbolLib( srcDir ),
331 "LoadSchematicFile wrote a library into the source directory" );
334 std::vector<LIB_SYMBOL*> raw = plugin->GetImportedCachedLibrarySymbols();
335 BOOST_CHECK_GT( raw.size(), 0 );
340 wxFileName::Rmdir( projectPath, wxPATH_RMDIR_RECURSIVE );
348 wxString projectPath = stageProject( wxS(
"symreconcile_collide" ) );
351 IMPORTED_SAMPLE sample = importSample(
project );
352 COLLISION_CANDIDATE candidate = findCandidate( sample );
361 publishLibrary(
project, *adapter, candidate.m_nickname, wxS(
"impostor" ), impostor );
363 const wxString cacheNick = wxS(
"collide-import-syms" );
367 sample.m_schematic.get(), std::move( sample.m_definitions ), cacheNick, {} );
376 BOOST_CHECK_GT( linked->
GetPins().size(), 0 );
378 wxFileName::Rmdir( projectPath, wxPATH_RMDIR_RECURSIVE );
386 wxString projectPath = stageProject( wxS(
"symreconcile_equivalent" ) );
389 IMPORTED_SAMPLE sample = importSample(
project );
390 COLLISION_CANDIDATE candidate = findCandidate( sample );
396 publishLibrary(
project, *adapter, candidate.m_nickname, wxS(
"genuine" ),
397 *candidate.m_definition );
399 const wxString cacheNick = wxS(
"equivalent-import-syms" );
400 wxString sourceNick = candidate.m_nickname;
404 sample.m_schematic.get(), std::move( sample.m_definitions ), cacheNick, {} );
407 BOOST_CHECK_GT(
result.m_linkedToSource, 0 );
409 wxFileName::Rmdir( projectPath, wxPATH_RMDIR_RECURSIVE );
417 wxString projectPath = stageProject( wxS(
"symreconcile_row" ) );
420 IMPORTED_SAMPLE sample = importSample(
project );
425 const wxString cacheNick = wxS(
"row-import-syms" );
429 publishLibrary(
project, *adapter, cacheNick, wxS(
"user-owned" ), owned );
437 const wxString uriBefore = row->
URI();
442 sample.m_schematic.get(), std::move( sample.m_definitions ), cacheNick, {} );
444 BOOST_CHECK(
result.m_cacheNickname.IsEmpty() );
447 wxFileName cacheFile(
project.GetProjectPath(), cacheNick,
449 BOOST_CHECK_MESSAGE( !wxFileExists( cacheFile.GetFullPath() ),
450 "Published a cache over a nickname the user already owns" );
452 wxFileName::Rmdir( projectPath, wxPATH_RMDIR_RECURSIVE );
459 wxString projectPath = stageProject( wxS(
"symreconcile_rotable" ) );
469 wxFileName tableFn(
table->Path() );
470 tableFn.SetPermissions( wxS_IRUSR | wxS_IRGRP | wxS_IROTH );
473 if( tableFn.IsFileWritable() )
475 BOOST_TEST_MESSAGE(
"Skipping read-only table check; file remains writable (running as "
477 tableFn.SetPermissions( wxS_IRUSR | wxS_IWUSR );
478 wxFileName::Rmdir( projectPath, wxPATH_RMDIR_RECURSIVE );
482 IMPORTED_SAMPLE sample = importSample(
project );
484 const wxString cacheNick = wxS(
"rotable-import-syms" );
488 sample.m_schematic.get(), std::move( sample.m_definitions ), cacheNick, {} );
490 BOOST_CHECK(
result.m_cacheNickname.IsEmpty() );
492 BOOST_CHECK_GT(
result.m_unresolved, 0 );
494 tableFn.SetPermissions( wxS_IRUSR | wxS_IWUSR );
495 wxFileName::Rmdir( projectPath, wxPATH_RMDIR_RECURSIVE );
502 wxString projectPath = stageProject( wxS(
"symreconcile_clobber" ) );
505 IMPORTED_SAMPLE sample = importSample(
project );
510 const wxString cacheNick = wxS(
"victim-import-syms" );
512 wxFileName victim(
project.GetProjectPath(), cacheNick,
515 const wxString sentinel = wxS(
"(kicad_symbol_lib (version 20231120) (generator \"qa\"))" );
516 BOOST_REQUIRE( wxFile( victim.GetFullPath(), wxFile::write ).Write( sentinel ) );
521 sample.m_schematic.get(), std::move( sample.m_definitions ), cacheNick, {} );
524 BOOST_CHECK(
result.m_cacheNickname.IsEmpty() );
525 BOOST_CHECK_GT(
result.m_unresolved, 0 );
528 wxFile readBack( victim.GetFullPath(), wxFile::read );
532 wxFileName::Rmdir( projectPath, wxPATH_RMDIR_RECURSIVE );
EE_TYPE OfType(KICAD_T aType) const
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.
const wxString GetUniStringLibItemName() const
Get strings for display messages in dialogs.
const wxString GetUniStringLibNickname() const
Define a library symbol object.
std::vector< SCH_PIN * > GetPins() const override
virtual SETTINGS_MANAGER & GetSettingsManager() const
virtual LIBRARY_MANAGER & GetLibraryManager() const
static SYMBOL_LIBRARY_ADAPTER * SymbolLibAdapter(PROJECT *aProject)
Accessor for project symbol library manager adapter.
Container for project specific data.
virtual const wxString GetProjectPath() const
Return the full path of the project.
Holds all the data relating to one schematic.
SCH_SHEET_LIST Hierarchy() const
Return the full schematic flattened hierarchical sheet list.
void SetProject(PROJECT *aPrj)
Base class for any item which can be embedded within the SCHEMATIC container class,...
EE_RTREE & Items()
Get the full RTree, usually for iterating.
Handle access to a stack of flattened SCH_SHEET objects by way of a path for creating a flattened sch...
Sheet symbol placed in a schematic, and is the entry point for a sub schematic.
const LIB_ID & GetLibId() const override
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.
Frame-independent, non-interactive service that reconciles the symbol-library references of a freshly...
SYMBOL_IMPORT_RECONCILE_RESULT Reconcile(SCHEMATIC *aSchematic, std::vector< std::unique_ptr< LIB_SYMBOL > > aDefinitions, const wxString &aCacheNickname, const std::vector< wxString > &aSourceLibNicknames)
Reconcile aSchematic against the importer definitions and the provenance source libraries.
An interface to the global shared library manager that is schematic-specific and linked to one projec...
std::optional< LIB_STATUS > LoadOne(LIB_DATA *aLib) override
Loads or reloads the given library, if it exists.
LIB_SYMBOL * LoadSymbol(const wxString &aNickname, const wxString &aName)
Load a LIB_SYMBOL having aName from the library given by aNickname.
static const std::string KiCadSymbolLibFileExtension
std::unique_ptr< T > IO_RELEASER
Helper to hold and release an IO_BASE object when exceptions are thrown.
std::string GetTestDataRootDir()
PGM_BASE & Pgm()
The global program "get" accessor.
Definition of the SCH_SHEET_PATH and SCH_SHEET_LIST classes for Eeschema.
Outcome of a post-import symbol-library reconciliation pass.
BOOST_AUTO_TEST_CASE(HorizontalAlignment)
BOOST_AUTO_TEST_SUITE(CadstarPartParser)
BOOST_REQUIRE(intersection.has_value()==c.ExpectedIntersection.has_value())
BOOST_AUTO_TEST_SUITE_END()
BOOST_TEST_MESSAGE("Polyline has "<< chain.PointCount()<< " points")
BOOST_AUTO_TEST_CASE(EasyEdaProV3SchematicResolvesToGeneratedCache)
wxString result
Test unit parsing edge cases and error handling.
BOOST_CHECK_EQUAL(result, "25.4")
Definition of file extensions used in Kicad.