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 BOOST_CHECK_GT( resolved, 0 );
300 wxString sep = wxFileName::GetPathSeparator();
301 wxString srcDir =
project.GetProjectPath() + wxT(
"import-src" );
302 BOOST_REQUIRE( wxFileName::Mkdir( srcDir, wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL ) );
304 wxString
source = wxString::FromUTF8(
306 +
"pcbnew/plugins/easyedapro/ProProject_Yuzuki Chameleon_2023-09-02.epro" );
309 wxFileName archiveFile( srcDir + sep, wxS(
"sample" ), wxS(
"epro" ) );
319 BOOST_REQUIRE_NO_THROW(
320 rootSheet = plugin->LoadSchematicFile( archiveFile.GetFullPath(), &schematic ) );
323 BOOST_CHECK_MESSAGE( !hasSymbolLib( srcDir ),
324 "LoadSchematicFile wrote a library into the source directory" );
327 std::vector<LIB_SYMBOL*> raw = plugin->GetImportedCachedLibrarySymbols();
328 BOOST_CHECK_GT( raw.size(), 0 );
341 IMPORTED_SAMPLE sample = importSample(
project );
342 COLLISION_CANDIDATE candidate = findCandidate( sample );
351 publishLibrary(
project, *adapter, candidate.m_nickname, wxS(
"impostor" ), impostor );
353 const wxString cacheNick = wxS(
"collide-import-syms" );
357 sample.m_schematic.get(), std::move( sample.m_definitions ), cacheNick, {} );
376 IMPORTED_SAMPLE sample = importSample(
project );
377 COLLISION_CANDIDATE candidate = findCandidate( sample );
383 publishLibrary(
project, *adapter, candidate.m_nickname, wxS(
"genuine" ),
384 *candidate.m_definition );
386 const wxString cacheNick = wxS(
"equivalent-import-syms" );
387 wxString sourceNick = candidate.m_nickname;
391 sample.m_schematic.get(), std::move( sample.m_definitions ), cacheNick, {} );
394 BOOST_CHECK_GT(
result.m_linkedToSource, 0 );
404 IMPORTED_SAMPLE sample = importSample(
project );
410 for(
SCH_SYMBOL* symbol : placedSymbols( *sample.m_schematic ) )
412 const std::unique_ptr<LIB_SYMBOL>& part = symbol->GetLibSymbolRef();
419 firstPlaced = symbol;
424 secondPlaced = symbol;
434 const wxString sharedName = wxS(
"SHARED_SYM" );
438 std::vector<std::unique_ptr<LIB_SYMBOL>> defs;
441 auto relabel = [&](
SCH_SYMBOL* aSymbol,
const wxString& aNickname )
443 auto def = std::make_unique<LIB_SYMBOL>( *aSymbol->
GetLibSymbolRef() );
444 def->SetName( sharedName );
445 def->SetLibId(
LIB_ID( aNickname, sharedName ) );
446 defs.push_back( std::move( def ) );
451 relabel( firstPlaced, wxS(
"libAlpha" ) );
452 relabel( secondPlaced, wxS(
"libBeta" ) );
457 const wxString cacheNick = wxS(
"namecollide-import-syms" );
462 reconciler.
Reconcile( sample.m_schematic.get(), std::move( defs ), cacheNick, {} );
472 "Symbols from two source libraries share one cache item name" );
487 BOOST_CHECK_MESSAGE(
reporter.GetMessages().Contains( wxString::Format( wxS(
"renamed to '%s'" ), renamed ) ),
488 "Cache rename was not reported" );
498 IMPORTED_SAMPLE sample = importSample(
project );
503 const wxString cacheNick = wxS(
"row-import-syms" );
507 publishLibrary(
project, *adapter, cacheNick, wxS(
"user-owned" ), owned );
515 const wxString uriBefore = row->
URI();
520 sample.m_schematic.get(), std::move( sample.m_definitions ), cacheNick, {} );
522 BOOST_CHECK(
result.m_cacheNickname.IsEmpty() );
526 BOOST_CHECK_MESSAGE( !wxFileExists( cacheFile.GetFullPath() ),
527 "Published a cache over a nickname the user already owns" );
543 wxFileName tableFn(
table->Path() );
544 tableFn.SetPermissions( wxS_IRUSR | wxS_IRGRP | wxS_IROTH );
547 if( tableFn.IsFileWritable() )
549 BOOST_TEST_MESSAGE(
"Skipping read-only table check; file remains writable (running as root?)" );
550 tableFn.SetPermissions( wxS_IRUSR | wxS_IWUSR );
554 IMPORTED_SAMPLE sample = importSample(
project );
556 const wxString cacheNick = wxS(
"rotable-import-syms" );
560 std::move( sample.m_definitions ),
563 BOOST_CHECK(
result.m_cacheNickname.IsEmpty() );
565 BOOST_CHECK_GT(
result.m_unresolved, 0 );
567 tableFn.SetPermissions( wxS_IRUSR | wxS_IWUSR );
576 IMPORTED_SAMPLE sample = importSample(
project );
581 const wxString cacheNick = wxS(
"victim-import-syms" );
583 wxFileName victim(
project.GetProjectPath(), cacheNick,
586 const wxString sentinel = wxS(
"(kicad_symbol_lib (version 20231120) (generator \"qa\"))" );
587 BOOST_REQUIRE( wxFile( victim.GetFullPath(), wxFile::write ).Write( sentinel ) );
592 sample.m_schematic.get(), std::move( sample.m_definitions ), cacheNick, {} );
595 BOOST_CHECK(
result.m_cacheNickname.IsEmpty() );
596 BOOST_CHECK_GT(
result.m_unresolved, 0 );
599 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.
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.
@ 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.