29#include <wx/filename.h>
56struct STAGED_PROJECT_FIXTURE
58 STAGED_PROJECT_FIXTURE() :
59 m_staged(
Pgm().GetSettingsManager(), wxS(
"symreconcile-qa" ), wxS(
"symreconcile" ) )
64 KI_TEST::SCOPED_TEMP_PROJECT m_staged;
68wxString easyEdaProV3Archive()
71 +
"pcbnew/plugins/easyedapro/ProProject_LS2K0300Core_2025-11-14.epro2" );
76bool hasSymbolLib(
const wxString& aPath )
86 return dir.GetFirst( &
name, spec, wxDIR_FILES );
94 std::unique_ptr<SCHEMATIC> m_schematic;
95 std::vector<std::unique_ptr<LIB_SYMBOL>> m_definitions;
100IMPORTED_SAMPLE importSample(
PROJECT& aProject )
102 IMPORTED_SAMPLE sample;
104 wxString sep = wxFileName::GetPathSeparator();
105 sample.m_sourceDir = aProject.
GetProjectPath() + wxT(
"import-src" );
106 BOOST_REQUIRE( wxFileName::Mkdir( sample.m_sourceDir, wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL ) );
108 wxFileName archiveFile( sample.m_sourceDir + sep, wxS(
"sample" ), wxS(
"epro2" ) );
109 BOOST_REQUIRE( wxCopyFile( easyEdaProV3Archive(), archiveFile.GetFullPath() ) );
111 sample.m_schematic = std::make_unique<SCHEMATIC>(
nullptr );
114 sample.m_plugin.reset( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_EASYEDAPRO_V3 ) );
118 BOOST_REQUIRE_NO_THROW( rootSheet = sample.m_plugin->LoadSchematicFile(
119 archiveFile.GetFullPath(), sample.m_schematic.get() ) );
124 for(
LIB_SYMBOL* symbol : sample.m_plugin->GetImportedCachedLibrarySymbols() )
125 sample.m_definitions.emplace_back( symbol );
127 BOOST_REQUIRE_GT( sample.m_definitions.size(), 0 );
133std::vector<SCH_SYMBOL*> placedSymbols(
SCHEMATIC& aSchematic )
135 std::vector<SCH_SYMBOL*> symbols;
139 if(
SCH_SCREEN* screen = sheetPath.LastScreen() )
142 symbols.push_back(
static_cast<SCH_SYMBOL*
>( item ) );
152 const wxString& aFileStem,
const LIB_SYMBOL& aSymbol )
159 pi->CreateLibrary( libFn.GetFullPath() );
161 auto copy = std::make_unique<LIB_SYMBOL>( aSymbol );
163 pi->SaveSymbol( libFn.GetFullPath(), std::move(
copy ) );
170 row.
SetURI( wxS(
"${KIPRJMOD}/" ) + libFn.GetFullName() );
180struct COLLISION_CANDIDATE
182 SCH_SYMBOL* m_symbol =
nullptr;
183 LIB_SYMBOL* m_definition =
nullptr;
189COLLISION_CANDIDATE findCandidate( IMPORTED_SAMPLE& aSample )
191 COLLISION_CANDIDATE candidate;
193 for(
SCH_SYMBOL* symbol : placedSymbols( *aSample.m_schematic ) )
195 wxString nick = symbol->GetLibId().GetUniStringLibNickname();
196 wxString
name = symbol->GetLibId().GetUniStringLibItemName();
198 if( nick.IsEmpty() ||
name.IsEmpty() )
201 for(
const std::unique_ptr<LIB_SYMBOL>& def : aSample.m_definitions )
203 if( def->GetLibId().GetUniStringLibItemName() !=
name || def->GetPins().empty() )
206 candidate.m_symbol = symbol;
207 candidate.m_definition = def.get();
208 candidate.m_nickname = nick;
209 candidate.m_name =
name;
220BOOST_FIXTURE_TEST_SUITE( SymbolImportReconciler, STAGED_PROJECT_FIXTURE )
229 IMPORTED_SAMPLE sample = importSample(
project );
230 SCHEMATIC& schematic = *sample.m_schematic;
233 BOOST_CHECK_MESSAGE( !hasSymbolLib( sample.m_sourceDir ),
234 "LoadSchematicFile wrote a library into the source directory" );
239 const wxString cacheNick = wxS(
"ls2k0300-import-syms" );
243 reconciler.
Reconcile( &schematic, std::move( sample.m_definitions ), cacheNick, {} );
247 BOOST_CHECK_GT(
result.m_savedToCache, 0 );
249 wxFileName cacheFile(
project.GetProjectPath(), cacheNick,
251 BOOST_CHECK_MESSAGE( wxFileExists( cacheFile.GetFullPath() ),
252 "Generated .kicad_sym was not published into the project" );
256 BOOST_CHECK( projectTable->
HasRow( cacheNick ) );
278 BOOST_CHECK_MESSAGE( !nick.IsEmpty(),
279 wxString::Format(
"Symbol '%s' left with empty nickname",
name ) );
280 BOOST_CHECK_MESSAGE( adapter->
LoadSymbol( nick,
name ) !=
nullptr,
281 wxString::Format(
"LIB_ID '%s:%s' does not resolve after "
282 "reconciliation", nick,
name ) );
287 BOOST_CHECK_GT( resolved, 0 );
297 wxString sep = wxFileName::GetPathSeparator();
298 wxString srcDir =
project.GetProjectPath() + wxT(
"import-src" );
299 BOOST_REQUIRE( wxFileName::Mkdir( srcDir, wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL ) );
301 wxString source = wxString::FromUTF8(
303 +
"pcbnew/plugins/easyedapro/ProProject_Yuzuki Chameleon_2023-09-02.epro" );
306 wxFileName archiveFile( srcDir + sep, wxS(
"sample" ), wxS(
"epro" ) );
307 BOOST_REQUIRE( wxCopyFile( source, archiveFile.GetFullPath() ) );
316 BOOST_REQUIRE_NO_THROW(
317 rootSheet = plugin->LoadSchematicFile( archiveFile.GetFullPath(), &schematic ) );
320 BOOST_CHECK_MESSAGE( !hasSymbolLib( srcDir ),
321 "LoadSchematicFile wrote a library into the source directory" );
324 std::vector<LIB_SYMBOL*> raw = plugin->GetImportedCachedLibrarySymbols();
325 BOOST_CHECK_GT( raw.size(), 0 );
338 IMPORTED_SAMPLE sample = importSample(
project );
339 COLLISION_CANDIDATE candidate = findCandidate( sample );
348 publishLibrary(
project, *adapter, candidate.m_nickname, wxS(
"impostor" ), impostor );
350 const wxString cacheNick = wxS(
"collide-import-syms" );
354 sample.m_schematic.get(), std::move( sample.m_definitions ), cacheNick, {} );
363 BOOST_CHECK_GT( linked->
GetPins().size(), 0 );
373 IMPORTED_SAMPLE sample = importSample(
project );
374 COLLISION_CANDIDATE candidate = findCandidate( sample );
380 publishLibrary(
project, *adapter, candidate.m_nickname, wxS(
"genuine" ),
381 *candidate.m_definition );
383 const wxString cacheNick = wxS(
"equivalent-import-syms" );
384 wxString sourceNick = candidate.m_nickname;
388 sample.m_schematic.get(), std::move( sample.m_definitions ), cacheNick, {} );
391 BOOST_CHECK_GT(
result.m_linkedToSource, 0 );
401 IMPORTED_SAMPLE sample = importSample(
project );
407 for(
SCH_SYMBOL* symbol : placedSymbols( *sample.m_schematic ) )
409 const std::unique_ptr<LIB_SYMBOL>& part = symbol->GetLibSymbolRef();
411 if( !part || part->GetPins().empty() )
415 firstPlaced = symbol;
417 secondPlaced = symbol;
426 const wxString sharedName = wxS(
"SHARED_SYM" );
430 std::vector<std::unique_ptr<LIB_SYMBOL>> defs;
433 auto relabel = [&](
SCH_SYMBOL* aSymbol,
const wxString& aNickname )
435 auto def = std::make_unique<LIB_SYMBOL>( *aSymbol->
GetLibSymbolRef() );
436 def->SetName( sharedName );
437 def->SetLibId(
LIB_ID( aNickname, sharedName ) );
438 defs.push_back( std::move( def ) );
443 relabel( firstPlaced, wxS(
"libAlpha" ) );
444 relabel( secondPlaced, wxS(
"libBeta" ) );
449 const wxString cacheNick = wxS(
"namecollide-import-syms" );
454 reconciler.
Reconcile( sample.m_schematic.get(), std::move( defs ), cacheNick, {} );
464 "Symbols from two source libraries share one cache item name" );
480 BOOST_CHECK_MESSAGE(
reporter.GetMessages().Contains(
481 wxString::Format( wxS(
"renamed to '%s'" ), renamed ) ),
482 "Cache rename was not reported" );
492 IMPORTED_SAMPLE sample = importSample(
project );
497 const wxString cacheNick = wxS(
"row-import-syms" );
501 publishLibrary(
project, *adapter, cacheNick, wxS(
"user-owned" ), owned );
509 const wxString uriBefore = row->
URI();
514 sample.m_schematic.get(), std::move( sample.m_definitions ), cacheNick, {} );
516 BOOST_CHECK(
result.m_cacheNickname.IsEmpty() );
519 wxFileName cacheFile(
project.GetProjectPath(), cacheNick,
521 BOOST_CHECK_MESSAGE( !wxFileExists( cacheFile.GetFullPath() ),
522 "Published a cache over a nickname the user already owns" );
538 wxFileName tableFn(
table->Path() );
539 tableFn.SetPermissions( wxS_IRUSR | wxS_IRGRP | wxS_IROTH );
542 if( tableFn.IsFileWritable() )
544 BOOST_TEST_MESSAGE(
"Skipping read-only table check; file remains writable (running as "
546 tableFn.SetPermissions( wxS_IRUSR | wxS_IWUSR );
550 IMPORTED_SAMPLE sample = importSample(
project );
552 const wxString cacheNick = wxS(
"rotable-import-syms" );
556 sample.m_schematic.get(), std::move( sample.m_definitions ), cacheNick, {} );
558 BOOST_CHECK(
result.m_cacheNickname.IsEmpty() );
560 BOOST_CHECK_GT(
result.m_unresolved, 0 );
562 tableFn.SetPermissions( wxS_IRUSR | wxS_IWUSR );
571 IMPORTED_SAMPLE sample = importSample(
project );
576 const wxString cacheNick = wxS(
"victim-import-syms" );
578 wxFileName victim(
project.GetProjectPath(), cacheNick,
581 const wxString sentinel = wxS(
"(kicad_symbol_lib (version 20231120) (generator \"qa\"))" );
582 BOOST_REQUIRE( wxFile( victim.GetFullPath(), wxFile::write ).Write( sentinel ) );
587 sample.m_schematic.get(), std::move( sample.m_definitions ), cacheNick, {} );
590 BOOST_CHECK(
result.m_cacheNickname.IsEmpty() );
591 BOOST_CHECK_GT(
result.m_unresolved, 0 );
594 wxFile readBack( victim.GetFullPath(), wxFile::read );
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 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.
void SetLibId(const LIB_ID &aName)
const LIB_ID & GetLibId() const override
std::unique_ptr< LIB_SYMBOL > & GetLibSymbolRef()
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.
A wrapper for reporting to a wxString object.
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_REQUIRE(intersection.has_value()==c.ExpectedIntersection.has_value())
BOOST_AUTO_TEST_SUITE_END()
IbisParser parser & reporter
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.