KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_symbol_import_reconciler.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 */
19
20#include <memory>
21#include <vector>
22
24
25#include <wx/dir.h>
26#include <wx/file.h>
27#include <wx/filefn.h>
28#include <wx/filename.h>
29
30#include <lib_id.h>
31#include <lib_symbol.h>
32#include <pgm_base.h>
33#include <project.h>
34#include <project_sch.h>
35#include <schematic.h>
36#include <sch_io/sch_io.h>
37#include <sch_io/sch_io_mgr.h>
38#include <sch_screen.h>
39#include <sch_sheet.h>
40#include <sch_sheet_path.h>
41#include <sch_symbol.h>
47
49
50
51namespace
52{
54wxString stageProject( const wxString& aStem )
55{
56 wxString sep = wxFileName::GetPathSeparator();
57 wxString dir = wxFileName::GetTempDir() + sep + aStem + wxT( "-symreconcile-qa" );
58
59 if( wxDirExists( dir ) )
60 wxFileName::Rmdir( dir, wxPATH_RMDIR_RECURSIVE );
61
62 wxFileName::Mkdir( dir, wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL );
63
64 wxString projectPath = dir + sep + aStem + wxT( ".kicad_pro" );
65
66 Pgm().GetSettingsManager().LoadProject( projectPath );
68 Pgm().GetSettingsManager().Prj().GetProjectDirectory() );
69
71}
72
73
74wxString easyEdaProV3Archive()
75{
76 return wxString::FromUTF8( KI_TEST::GetTestDataRootDir()
77 + "pcbnew/plugins/easyedapro/ProProject_LS2K0300Core_2025-11-14.epro2" );
78}
79
80
82bool hasSymbolLib( const wxString& aPath )
83{
84 wxDir dir( aPath );
85 wxString name;
86
87 if( !dir.IsOpened() )
88 return false;
89
90 wxString spec = wxS( "*." ) + wxString( FILEEXT::KiCadSymbolLibFileExtension );
91
92 return dir.GetFirst( &name, spec, wxDIR_FILES );
93}
94
95
97struct IMPORTED_SAMPLE
98{
99 IO_RELEASER<SCH_IO> m_plugin;
100 std::unique_ptr<SCHEMATIC> m_schematic;
101 std::vector<std::unique_ptr<LIB_SYMBOL>> m_definitions;
102 wxString m_sourceDir;
103};
104
105
106IMPORTED_SAMPLE importSample( PROJECT& aProject )
107{
108 IMPORTED_SAMPLE sample;
109
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 ) );
113
114 wxFileName archiveFile( sample.m_sourceDir + sep, wxS( "sample" ), wxS( "epro2" ) );
115 BOOST_REQUIRE( wxCopyFile( easyEdaProV3Archive(), archiveFile.GetFullPath() ) );
116
117 sample.m_schematic = std::make_unique<SCHEMATIC>( nullptr );
118 sample.m_schematic->SetProject( &aProject );
119
120 sample.m_plugin.reset( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_EASYEDAPRO_V3 ) );
121 BOOST_REQUIRE( sample.m_plugin );
122
123 SCH_SHEET* rootSheet = nullptr;
124 BOOST_REQUIRE_NO_THROW( rootSheet = sample.m_plugin->LoadSchematicFile(
125 archiveFile.GetFullPath(), sample.m_schematic.get() ) );
126 BOOST_REQUIRE( rootSheet );
127
128 sample.m_schematic->RefreshHierarchy();
129
130 for( LIB_SYMBOL* symbol : sample.m_plugin->GetImportedCachedLibrarySymbols() )
131 sample.m_definitions.emplace_back( symbol );
132
133 BOOST_REQUIRE_GT( sample.m_definitions.size(), 0 );
134
135 return sample;
136}
137
138
139std::vector<SCH_SYMBOL*> placedSymbols( SCHEMATIC& aSchematic )
140{
141 std::vector<SCH_SYMBOL*> symbols;
142
143 for( const SCH_SHEET_PATH& sheetPath : aSchematic.Hierarchy() )
144 {
145 if( SCH_SCREEN* screen = sheetPath.LastScreen() )
146 {
147 for( SCH_ITEM* item : screen->Items().OfType( SCH_SYMBOL_T ) )
148 symbols.push_back( static_cast<SCH_SYMBOL*>( item ) );
149 }
150 }
151
152 return symbols;
153}
154
155
157void publishLibrary( PROJECT& aProject, SYMBOL_LIBRARY_ADAPTER& aAdapter, const wxString& aNickname,
158 const wxString& aFileStem, const LIB_SYMBOL& aSymbol )
159{
160 wxFileName libFn( aProject.GetProjectPath(), aFileStem, FILEEXT::KiCadSymbolLibFileExtension );
161
162 IO_RELEASER<SCH_IO> pi( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_KICAD ) );
163 BOOST_REQUIRE( pi );
164
165 pi->CreateLibrary( libFn.GetFullPath() );
166
167 auto copy = std::make_unique<LIB_SYMBOL>( aSymbol );
168 copy->SetLibId( LIB_ID( aNickname, copy->GetName() ) );
169 pi->SaveSymbol( libFn.GetFullPath(), copy.release() );
170
171 LIBRARY_TABLE* table = aAdapter.ProjectTable().value_or( nullptr );
173
174 LIBRARY_TABLE_ROW& row = table->InsertRow();
175 row.SetNickname( aNickname );
176 row.SetURI( wxS( "${KIPRJMOD}/" ) + libFn.GetFullName() );
177 row.SetType( wxS( "KiCad" ) );
179
180 BOOST_REQUIRE( table->Save().has_value() );
181 aAdapter.LoadOne( aNickname );
182}
183
184
186struct COLLISION_CANDIDATE
187{
188 SCH_SYMBOL* m_symbol = nullptr;
189 LIB_SYMBOL* m_definition = nullptr;
190 wxString m_nickname;
191 wxString m_name;
192};
193
194
195COLLISION_CANDIDATE findCandidate( IMPORTED_SAMPLE& aSample )
196{
197 COLLISION_CANDIDATE candidate;
198
199 for( SCH_SYMBOL* symbol : placedSymbols( *aSample.m_schematic ) )
200 {
201 wxString nick = symbol->GetLibId().GetUniStringLibNickname();
202 wxString name = symbol->GetLibId().GetUniStringLibItemName();
203
204 if( nick.IsEmpty() || name.IsEmpty() )
205 continue;
206
207 for( const std::unique_ptr<LIB_SYMBOL>& def : aSample.m_definitions )
208 {
209 if( def->GetLibId().GetUniStringLibItemName() != name || def->GetPins().empty() )
210 continue;
211
212 candidate.m_symbol = symbol;
213 candidate.m_definition = def.get();
214 candidate.m_nickname = nick;
215 candidate.m_name = name;
216
217 return candidate;
218 }
219 }
220
221 return candidate;
222}
223} // namespace
224
225
226BOOST_AUTO_TEST_SUITE( SymbolImportReconciler )
227
228
229// EasyEDA Pro v3 hands definitions over the standard hook, leaving the source directory untouched
230// fails on revert, since LoadSchematicFile then publishes its own .kicad_sym beside the archive
231BOOST_AUTO_TEST_CASE( EasyEdaProV3SchematicResolvesToGeneratedCache )
232{
233 wxString projectPath = stageProject( wxS( "easyedapro_v3" ) );
235
236 IMPORTED_SAMPLE sample = importSample( project );
237 SCHEMATIC& schematic = *sample.m_schematic;
238
239 // the importer must not have published anything of its own beside the archive
240 BOOST_CHECK_MESSAGE( !hasSymbolLib( sample.m_sourceDir ),
241 "LoadSchematicFile wrote a library into the source directory" );
242
244 BOOST_REQUIRE( adapter );
245
246 const wxString cacheNick = wxS( "ls2k0300-import-syms" );
247 SYMBOL_IMPORT_RECONCILER reconciler( *adapter, project.GetProjectPath() );
248
250 reconciler.Reconcile( &schematic, std::move( sample.m_definitions ), cacheNick, {} );
251
252 // the cache lands in the project directory, not next to the source archive
253 BOOST_CHECK_EQUAL( result.m_cacheNickname, cacheNick );
254 BOOST_CHECK_GT( result.m_savedToCache, 0 );
255
256 wxFileName cacheFile( project.GetProjectPath(), cacheNick,
258 BOOST_CHECK_MESSAGE( wxFileExists( cacheFile.GetFullPath() ),
259 "Generated .kicad_sym was not published into the project" );
260
261 LIBRARY_TABLE* projectTable = adapter->ProjectTable().value_or( nullptr );
262 BOOST_REQUIRE( projectTable );
263 BOOST_CHECK( projectTable->HasRow( cacheNick ) );
264
265 // every placed symbol resolves through the adapter after reconciliation
266 int resolved = 0;
267
268 for( const SCH_SHEET_PATH& sheetPath : schematic.Hierarchy() )
269 {
270 SCH_SCREEN* screen = sheetPath.LastScreen();
271
272 if( !screen )
273 continue;
274
275 for( SCH_ITEM* item : screen->Items().OfType( SCH_SYMBOL_T ) )
276 {
277 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
278 wxString name = symbol->GetLibId().GetUniStringLibItemName();
279
280 if( name.IsEmpty() )
281 continue;
282
283 wxString nick = symbol->GetLibId().GetUniStringLibNickname();
284
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 ) );
290 resolved++;
291 }
292 }
293
294 BOOST_CHECK_GT( resolved, 0 );
295 BOOST_CHECK_EQUAL( result.m_unresolved, 0 );
296
297 wxFileName::Rmdir( projectPath, wxPATH_RMDIR_RECURSIVE );
298}
299
300
301// EasyEDA Pro v2 wrote its .kicad_sym beside the source archive, exactly as v3 did
302BOOST_AUTO_TEST_CASE( EasyEdaProV2LeavesSourceDirectoryClean )
303{
304 wxString projectPath = stageProject( wxS( "easyedapro_v2" ) );
306
307 wxString sep = wxFileName::GetPathSeparator();
308 wxString srcDir = projectPath + wxT( "import-src" );
309 BOOST_REQUIRE( wxFileName::Mkdir( srcDir, wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL ) );
310
311 wxString source = wxString::FromUTF8(
313 + "pcbnew/plugins/easyedapro/ProProject_Yuzuki Chameleon_2023-09-02.epro" );
314 BOOST_REQUIRE( wxFileExists( source ) );
315
316 wxFileName archiveFile( srcDir + sep, wxS( "sample" ), wxS( "epro" ) );
317 BOOST_REQUIRE( wxCopyFile( source, archiveFile.GetFullPath() ) );
318
319 SCHEMATIC schematic( nullptr );
320 schematic.SetProject( &project );
321
322 IO_RELEASER<SCH_IO> plugin( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_EASYEDAPRO ) );
323 BOOST_REQUIRE( plugin );
324
325 SCH_SHEET* rootSheet = nullptr;
326 BOOST_REQUIRE_NO_THROW(
327 rootSheet = plugin->LoadSchematicFile( archiveFile.GetFullPath(), &schematic ) );
328 BOOST_REQUIRE( rootSheet );
329
330 BOOST_CHECK_MESSAGE( !hasSymbolLib( srcDir ),
331 "LoadSchematicFile wrote a library into the source directory" );
332
333 // the definitions still reach the reconciler through the standard hook
334 std::vector<LIB_SYMBOL*> raw = plugin->GetImportedCachedLibrarySymbols();
335 BOOST_CHECK_GT( raw.size(), 0 );
336
337 for( LIB_SYMBOL* symbol : raw )
338 delete symbol;
339
340 wxFileName::Rmdir( projectPath, wxPATH_RMDIR_RECURSIVE );
341}
342
343
344// An unrelated library that happens to carry the nickname the importer emitted must not swallow
345// the imported definition; without the provenance check the symbol relinks to the wrong part
346BOOST_AUTO_TEST_CASE( CollidingNicknameDoesNotStealTheLink )
347{
348 wxString projectPath = stageProject( wxS( "symreconcile_collide" ) );
350
351 IMPORTED_SAMPLE sample = importSample( project );
352 COLLISION_CANDIDATE candidate = findCandidate( sample );
353 BOOST_REQUIRE( candidate.m_symbol );
354
356 BOOST_REQUIRE( adapter );
357
358 // a pinless namesake registered under the importer's nickname: same name, different part
359 LIB_SYMBOL impostor( candidate.m_name );
360 BOOST_REQUIRE( impostor.GetPins().empty() );
361 publishLibrary( project, *adapter, candidate.m_nickname, wxS( "impostor" ), impostor );
362
363 const wxString cacheNick = wxS( "collide-import-syms" );
364 SYMBOL_IMPORT_RECONCILER reconciler( *adapter, project.GetProjectPath() );
365
367 sample.m_schematic.get(), std::move( sample.m_definitions ), cacheNick, {} );
368
369 BOOST_CHECK_EQUAL( result.m_cacheNickname, cacheNick );
370
371 // the imported definition is kept in the cache rather than dropped for the namesake
372 BOOST_CHECK_EQUAL( candidate.m_symbol->GetLibId().GetUniStringLibNickname(), cacheNick );
373
374 LIB_SYMBOL* linked = adapter->LoadSymbol( cacheNick, candidate.m_name );
375 BOOST_REQUIRE( linked );
376 BOOST_CHECK_GT( linked->GetPins().size(), 0 );
377
378 wxFileName::Rmdir( projectPath, wxPATH_RMDIR_RECURSIVE );
379}
380
381
382// The same nickname collision resolves the other way when the library really does hold the part,
383// so the equivalence escape hatch keeps a genuine match out of the generated cache
384BOOST_AUTO_TEST_CASE( EquivalentNamesakeTakesTheLink )
385{
386 wxString projectPath = stageProject( wxS( "symreconcile_equivalent" ) );
388
389 IMPORTED_SAMPLE sample = importSample( project );
390 COLLISION_CANDIDATE candidate = findCandidate( sample );
391 BOOST_REQUIRE( candidate.m_symbol );
392
394 BOOST_REQUIRE( adapter );
395
396 publishLibrary( project, *adapter, candidate.m_nickname, wxS( "genuine" ),
397 *candidate.m_definition );
398
399 const wxString cacheNick = wxS( "equivalent-import-syms" );
400 wxString sourceNick = candidate.m_nickname;
401 SYMBOL_IMPORT_RECONCILER reconciler( *adapter, project.GetProjectPath() );
402
404 sample.m_schematic.get(), std::move( sample.m_definitions ), cacheNick, {} );
405
406 BOOST_CHECK_EQUAL( candidate.m_symbol->GetLibId().GetUniStringLibNickname(), sourceNick );
407 BOOST_CHECK_GT( result.m_linkedToSource, 0 );
408
409 wxFileName::Rmdir( projectPath, wxPATH_RMDIR_RECURSIVE );
410}
411
412
413// A project row already owning the cache nickname is a user library even when no file sits at the
414// generated path, so publishing must not rewrite its URI
415BOOST_AUTO_TEST_CASE( ExistingUserRowIsNotRepurposed )
416{
417 wxString projectPath = stageProject( wxS( "symreconcile_row" ) );
419
420 IMPORTED_SAMPLE sample = importSample( project );
421
423 BOOST_REQUIRE( adapter );
424
425 const wxString cacheNick = wxS( "row-import-syms" );
426
427 // the user's row owns the nickname but points at a file of its own
428 LIB_SYMBOL owned( wxS( "UserPart" ) );
429 publishLibrary( project, *adapter, cacheNick, wxS( "user-owned" ), owned );
430
431 LIBRARY_TABLE* table = adapter->ProjectTable().value_or( nullptr );
433
434 LIBRARY_TABLE_ROW* row = table->Row( cacheNick ).value_or( nullptr );
435 BOOST_REQUIRE( row );
436
437 const wxString uriBefore = row->URI();
438
439 SYMBOL_IMPORT_RECONCILER reconciler( *adapter, project.GetProjectPath() );
440
442 sample.m_schematic.get(), std::move( sample.m_definitions ), cacheNick, {} );
443
444 BOOST_CHECK( result.m_cacheNickname.IsEmpty() );
445 BOOST_CHECK_EQUAL( row->URI(), uriBefore );
446
447 wxFileName cacheFile( project.GetProjectPath(), cacheNick,
449 BOOST_CHECK_MESSAGE( !wxFileExists( cacheFile.GetFullPath() ),
450 "Published a cache over a nickname the user already owns" );
451
452 wxFileName::Rmdir( projectPath, wxPATH_RMDIR_RECURSIVE );
453}
454
455
456// A cache whose table row cannot be persisted is gone on restart, so it must not be claimed
457BOOST_AUTO_TEST_CASE( UnsavableTableLeavesCacheUnclaimed )
458{
459 wxString projectPath = stageProject( wxS( "symreconcile_rotable" ) );
461
463 BOOST_REQUIRE( adapter );
464
465 LIBRARY_TABLE* table = adapter->ProjectTable().value_or( nullptr );
467 BOOST_REQUIRE( table->Save().has_value() );
468
469 wxFileName tableFn( table->Path() );
470 tableFn.SetPermissions( wxS_IRUSR | wxS_IRGRP | wxS_IROTH );
471
472 // CI containers commonly run as root, where the permission bits do not deny access
473 if( tableFn.IsFileWritable() )
474 {
475 BOOST_TEST_MESSAGE( "Skipping read-only table check; file remains writable (running as "
476 "root?)" );
477 tableFn.SetPermissions( wxS_IRUSR | wxS_IWUSR );
478 wxFileName::Rmdir( projectPath, wxPATH_RMDIR_RECURSIVE );
479 return;
480 }
481
482 IMPORTED_SAMPLE sample = importSample( project );
483
484 const wxString cacheNick = wxS( "rotable-import-syms" );
485 SYMBOL_IMPORT_RECONCILER reconciler( *adapter, project.GetProjectPath() );
486
488 sample.m_schematic.get(), std::move( sample.m_definitions ), cacheNick, {} );
489
490 BOOST_CHECK( result.m_cacheNickname.IsEmpty() );
491 BOOST_CHECK_EQUAL( result.m_linkedToCache, 0 );
492 BOOST_CHECK_GT( result.m_unresolved, 0 );
493
494 tableFn.SetPermissions( wxS_IRUSR | wxS_IWUSR );
495 wxFileName::Rmdir( projectPath, wxPATH_RMDIR_RECURSIVE );
496}
497
498
499// An unregistered library already sitting at the cache path must never be overwritten
500BOOST_AUTO_TEST_CASE( ExistingUserLibraryIsNotClobbered )
501{
502 wxString projectPath = stageProject( wxS( "symreconcile_clobber" ) );
504
505 IMPORTED_SAMPLE sample = importSample( project );
506
508 BOOST_REQUIRE( adapter );
509
510 const wxString cacheNick = wxS( "victim-import-syms" );
511
512 wxFileName victim( project.GetProjectPath(), cacheNick,
514
515 const wxString sentinel = wxS( "(kicad_symbol_lib (version 20231120) (generator \"qa\"))" );
516 BOOST_REQUIRE( wxFile( victim.GetFullPath(), wxFile::write ).Write( sentinel ) );
517
518 SYMBOL_IMPORT_RECONCILER reconciler( *adapter, project.GetProjectPath() );
519
521 sample.m_schematic.get(), std::move( sample.m_definitions ), cacheNick, {} );
522
523 // the symbols stay unresolved rather than the file being taken over
524 BOOST_CHECK( result.m_cacheNickname.IsEmpty() );
525 BOOST_CHECK_GT( result.m_unresolved, 0 );
526
527 wxString contents;
528 wxFile readBack( victim.GetFullPath(), wxFile::read );
529 BOOST_REQUIRE( readBack.ReadAll( &contents ) );
530 BOOST_CHECK_EQUAL( contents, sentinel );
531
532 wxFileName::Rmdir( projectPath, wxPATH_RMDIR_RECURSIVE );
533}
534
535
const char * name
EE_TYPE OfType(KICAD_T aType) const
Definition sch_rtree.h:221
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.
Definition lib_id.h:45
const wxString GetUniStringLibItemName() const
Get strings for display messages in dialogs.
Definition lib_id.h:108
const wxString GetUniStringLibNickname() const
Definition lib_id.h:84
Define a library symbol object.
Definition lib_symbol.h:114
std::vector< SCH_PIN * > GetPins() const override
virtual SETTINGS_MANAGER & GetSettingsManager() const
Definition pgm_base.h:124
virtual LIBRARY_MANAGER & GetLibraryManager() const
Definition pgm_base.h:126
static SYMBOL_LIBRARY_ADAPTER * SymbolLibAdapter(PROJECT *aProject)
Accessor for project symbol library manager adapter.
Container for project specific data.
Definition project.h:63
virtual const wxString GetProjectPath() const
Return the full path of the project.
Definition project.cpp:183
Holds all the data relating to one schematic.
Definition schematic.h:90
SCH_SHEET_LIST Hierarchy() const
Return the full schematic flattened hierarchical sheet list.
void SetProject(PROJECT *aPrj)
void RefreshHierarchy()
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition sch_item.h:162
EE_RTREE & Items()
Get the full RTree, usually for iterating.
Definition sch_screen.h:115
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.
Definition sch_sheet.h:44
Schematic symbol object.
Definition sch_symbol.h:69
const LIB_ID & GetLibId() const override
Definition sch_symbol.h:158
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.
Definition io_mgr.h:33
PROJECT & Prj()
Definition kicad.cpp:728
std::string GetTestDataRootDir()
PGM_BASE & Pgm()
The global program "get" accessor.
see class PGM_BASE
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")
@ SCH_SYMBOL_T
Definition typeinfo.h:169
Definition of file extensions used in Kicad.