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
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
209 candidate.m_symbol = symbol;
210 candidate.m_definition = def.get();
211 candidate.m_nickname = nick;
212 candidate.m_name =
name;
223BOOST_FIXTURE_TEST_SUITE( SymbolImportReconciler, STAGED_PROJECT_FIXTURE )
232 IMPORTED_SAMPLE sample = importSample(
project );
233 SCHEMATIC& schematic = *sample.m_schematic;
236 BOOST_CHECK_MESSAGE( !hasSymbolLib( sample.m_sourceDir ),
237 "LoadSchematicFile wrote a library into the source directory" );
242 const wxString cacheNick = wxS(
"ls2k0300-import-syms" );
246 reconciler.
Reconcile( &schematic, std::move( sample.m_definitions ), cacheNick, {} );
250 BOOST_CHECK_GT(
result.m_savedToCache, 0 );
252 wxFileName cacheFile(
project.GetProjectPath(), cacheNick,
254 BOOST_CHECK_MESSAGE( wxFileExists( cacheFile.GetFullPath() ),
255 "Generated .kicad_sym was not published into the project" );
259 BOOST_CHECK( projectTable->
HasRow( cacheNick ) );
281 BOOST_CHECK_MESSAGE( !nick.IsEmpty(),
282 wxString::Format(
"Symbol '%s' left with empty nickname",
name ) );
283 BOOST_CHECK_MESSAGE( adapter->
LoadSymbol( nick,
name ) !=
nullptr,
284 wxString::Format(
"LIB_ID '%s:%s' does not resolve after "
285 "reconciliation", nick,
name ) );
290 wxString::Format(
"No embedded copy under '%s'",
299 BOOST_CHECK_GT( resolved, 0 );
309 wxString sep = wxFileName::GetPathSeparator();
310 wxString srcDir =
project.GetProjectPath() + wxT(
"import-src" );
311 BOOST_REQUIRE( wxFileName::Mkdir( srcDir, wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL ) );
313 wxString
source = wxString::FromUTF8(
315 +
"pcbnew/plugins/easyedapro/ProProject_Yuzuki Chameleon_2023-09-02.epro" );
318 wxFileName archiveFile( srcDir + sep, wxS(
"sample" ), wxS(
"epro" ) );
328 BOOST_REQUIRE_NO_THROW(
329 rootSheet = plugin->LoadSchematicFile( archiveFile.GetFullPath(), &schematic ) );
332 BOOST_CHECK_MESSAGE( !hasSymbolLib( srcDir ),
333 "LoadSchematicFile wrote a library into the source directory" );
336 std::vector<LIB_SYMBOL*> raw = plugin->GetImportedCachedLibrarySymbols();
337 BOOST_CHECK_GT( raw.size(), 0 );
350 IMPORTED_SAMPLE sample = importSample(
project );
351 COLLISION_CANDIDATE candidate = findCandidate( sample );
360 publishLibrary(
project, *adapter, candidate.m_nickname, wxS(
"impostor" ), impostor );
362 const wxString cacheNick = wxS(
"collide-import-syms" );
366 sample.m_schematic.get(), std::move( sample.m_definitions ), cacheNick, {} );
385 IMPORTED_SAMPLE sample = importSample(
project );
386 COLLISION_CANDIDATE candidate = findCandidate( sample );
392 publishLibrary(
project, *adapter, candidate.m_nickname, wxS(
"genuine" ),
393 *candidate.m_definition );
395 const wxString cacheNick = wxS(
"equivalent-import-syms" );
396 wxString sourceNick = candidate.m_nickname;
400 sample.m_schematic.get(), std::move( sample.m_definitions ), cacheNick, {} );
403 BOOST_CHECK_GT(
result.m_linkedToSource, 0 );
413 IMPORTED_SAMPLE sample = importSample(
project );
419 for(
SCH_SYMBOL* symbol : placedSymbols( *sample.m_schematic ) )
421 const std::unique_ptr<LIB_SYMBOL>& part = symbol->GetLibSymbolRef();
428 firstPlaced = symbol;
433 secondPlaced = symbol;
443 const wxString sharedName = wxS(
"SHARED_SYM" );
447 std::vector<std::unique_ptr<LIB_SYMBOL>> defs;
450 auto relabel = [&](
SCH_SYMBOL* aSymbol,
const wxString& aNickname )
452 auto def = std::make_unique<LIB_SYMBOL>( *aSymbol->
GetLibSymbolRef() );
453 def->SetName( sharedName );
454 def->SetLibId(
LIB_ID( aNickname, sharedName ) );
455 defs.push_back( std::move( def ) );
460 relabel( firstPlaced, wxS(
"libAlpha" ) );
461 relabel( secondPlaced, wxS(
"libBeta" ) );
466 const wxString cacheNick = wxS(
"namecollide-import-syms" );
471 reconciler.
Reconcile( sample.m_schematic.get(), std::move( defs ), cacheNick, {} );
481 "Symbols from two source libraries share one cache item name" );
496 BOOST_CHECK_MESSAGE(
reporter.GetMessages().Contains( wxString::Format( wxS(
"renamed to '%s'" ), renamed ) ),
497 "Cache rename was not reported" );
507 IMPORTED_SAMPLE sample = importSample(
project );
512 const wxString cacheNick = wxS(
"row-import-syms" );
516 publishLibrary(
project, *adapter, cacheNick, wxS(
"user-owned" ), owned );
524 const wxString uriBefore = row->
URI();
529 sample.m_schematic.get(), std::move( sample.m_definitions ), cacheNick, {} );
531 BOOST_CHECK(
result.m_cacheNickname.IsEmpty() );
535 BOOST_CHECK_MESSAGE( !wxFileExists( cacheFile.GetFullPath() ),
536 "Published a cache over a nickname the user already owns" );
552 wxFileName tableFn(
table->Path() );
553 tableFn.SetPermissions( wxS_IRUSR | wxS_IRGRP | wxS_IROTH );
556 if( tableFn.IsFileWritable() )
558 BOOST_TEST_MESSAGE(
"Skipping read-only table check; file remains writable (running as root?)" );
559 tableFn.SetPermissions( wxS_IRUSR | wxS_IWUSR );
563 IMPORTED_SAMPLE sample = importSample(
project );
565 const wxString cacheNick = wxS(
"rotable-import-syms" );
569 std::move( sample.m_definitions ),
572 BOOST_CHECK(
result.m_cacheNickname.IsEmpty() );
574 BOOST_CHECK_GT(
result.m_unresolved, 0 );
576 tableFn.SetPermissions( wxS_IRUSR | wxS_IWUSR );
585 IMPORTED_SAMPLE sample = importSample(
project );
590 const wxString cacheNick = wxS(
"victim-import-syms" );
592 wxFileName victim(
project.GetProjectPath(), cacheNick,
595 const wxString sentinel = wxS(
"(kicad_symbol_lib (version 20231120) (generator \"qa\"))" );
596 BOOST_REQUIRE( wxFile( victim.GetFullPath(), wxFile::write ).Write( sentinel ) );
601 sample.m_schematic.get(), std::move( sample.m_definitions ), cacheNick, {} );
604 BOOST_CHECK(
result.m_cacheNickname.IsEmpty() );
605 BOOST_CHECK_GT(
result.m_unresolved, 0 );
608 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 * > GetGraphicalPins(int aUnit=0, int aBodyStyle=0) const override
Graphical pins: Return schematic pin objects as drawn (unexpanded), filtered by unit/body.
LIB_ID GetLIB_ID() 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,...
const std::map< wxString, LIB_SYMBOL * > & GetLibSymbols() const
Fetch a list of unique LIB_SYMBOL object pointers required to properly render each SCH_SYMBOL in this...
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)
wxString GetSchSymbolLibraryName() const
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.
@ SCH_SYMBOL
one item stream of a Schematic Symbol Library
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.