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 <reporter.h>
36#include <schematic.h>
37#include <sch_io/sch_io.h>
38#include <sch_io/sch_io_mgr.h>
39#include <sch_screen.h>
40#include <sch_sheet.h>
41#include <sch_sheet_path.h>
42#include <sch_symbol.h>
48
50
51
52namespace
53{
55wxString stageProject( const wxString& aStem )
56{
57 wxString sep = wxFileName::GetPathSeparator();
58 wxString dir = wxFileName::GetTempDir() + sep + aStem + wxT( "-symreconcile-qa" );
59
60 if( wxDirExists( dir ) )
61 wxFileName::Rmdir( dir, wxPATH_RMDIR_RECURSIVE );
62
63 wxFileName::Mkdir( dir, wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL );
64
65 wxString projectPath = dir + sep + aStem + wxT( ".kicad_pro" );
66
67 Pgm().GetSettingsManager().LoadProject( projectPath );
69 Pgm().GetSettingsManager().Prj().GetProjectDirectory() );
70
72}
73
74
75wxString easyEdaProV3Archive()
76{
77 return wxString::FromUTF8( KI_TEST::GetTestDataRootDir()
78 + "pcbnew/plugins/easyedapro/ProProject_LS2K0300Core_2025-11-14.epro2" );
79}
80
81
83bool hasSymbolLib( const wxString& aPath )
84{
85 wxDir dir( aPath );
86 wxString name;
87
88 if( !dir.IsOpened() )
89 return false;
90
91 wxString spec = wxS( "*." ) + wxString( FILEEXT::KiCadSymbolLibFileExtension );
92
93 return dir.GetFirst( &name, spec, wxDIR_FILES );
94}
95
96
98struct IMPORTED_SAMPLE
99{
100 IO_RELEASER<SCH_IO> m_plugin;
101 std::unique_ptr<SCHEMATIC> m_schematic;
102 std::vector<std::unique_ptr<LIB_SYMBOL>> m_definitions;
103 wxString m_sourceDir;
104};
105
106
107IMPORTED_SAMPLE importSample( PROJECT& aProject )
108{
109 IMPORTED_SAMPLE sample;
110
111 wxString sep = wxFileName::GetPathSeparator();
112 sample.m_sourceDir = aProject.GetProjectPath() + wxT( "import-src" );
113 BOOST_REQUIRE( wxFileName::Mkdir( sample.m_sourceDir, wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL ) );
114
115 wxFileName archiveFile( sample.m_sourceDir + sep, wxS( "sample" ), wxS( "epro2" ) );
116 BOOST_REQUIRE( wxCopyFile( easyEdaProV3Archive(), archiveFile.GetFullPath() ) );
117
118 sample.m_schematic = std::make_unique<SCHEMATIC>( nullptr );
119 sample.m_schematic->SetProject( &aProject );
120
121 sample.m_plugin.reset( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_EASYEDAPRO_V3 ) );
122 BOOST_REQUIRE( sample.m_plugin );
123
124 SCH_SHEET* rootSheet = nullptr;
125 BOOST_REQUIRE_NO_THROW( rootSheet = sample.m_plugin->LoadSchematicFile(
126 archiveFile.GetFullPath(), sample.m_schematic.get() ) );
127 BOOST_REQUIRE( rootSheet );
128
129 sample.m_schematic->RefreshHierarchy();
130
131 for( LIB_SYMBOL* symbol : sample.m_plugin->GetImportedCachedLibrarySymbols() )
132 sample.m_definitions.emplace_back( symbol );
133
134 BOOST_REQUIRE_GT( sample.m_definitions.size(), 0 );
135
136 return sample;
137}
138
139
140std::vector<SCH_SYMBOL*> placedSymbols( SCHEMATIC& aSchematic )
141{
142 std::vector<SCH_SYMBOL*> symbols;
143
144 for( const SCH_SHEET_PATH& sheetPath : aSchematic.Hierarchy() )
145 {
146 if( SCH_SCREEN* screen = sheetPath.LastScreen() )
147 {
148 for( SCH_ITEM* item : screen->Items().OfType( SCH_SYMBOL_T ) )
149 symbols.push_back( static_cast<SCH_SYMBOL*>( item ) );
150 }
151 }
152
153 return symbols;
154}
155
156
158void publishLibrary( PROJECT& aProject, SYMBOL_LIBRARY_ADAPTER& aAdapter, const wxString& aNickname,
159 const wxString& aFileStem, const LIB_SYMBOL& aSymbol )
160{
161 wxFileName libFn( aProject.GetProjectPath(), aFileStem, FILEEXT::KiCadSymbolLibFileExtension );
162
163 IO_RELEASER<SCH_IO> pi( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_KICAD ) );
164 BOOST_REQUIRE( pi );
165
166 pi->CreateLibrary( libFn.GetFullPath() );
167
168 auto copy = std::make_unique<LIB_SYMBOL>( aSymbol );
169 copy->SetLibId( LIB_ID( aNickname, copy->GetName() ) );
170 pi->SaveSymbol( libFn.GetFullPath(), copy.release() );
171
172 LIBRARY_TABLE* table = aAdapter.ProjectTable().value_or( nullptr );
174
175 LIBRARY_TABLE_ROW& row = table->InsertRow();
176 row.SetNickname( aNickname );
177 row.SetURI( wxS( "${KIPRJMOD}/" ) + libFn.GetFullName() );
178 row.SetType( wxS( "KiCad" ) );
180
181 BOOST_REQUIRE( table->Save().has_value() );
182 aAdapter.LoadOne( aNickname );
183}
184
185
187struct COLLISION_CANDIDATE
188{
189 SCH_SYMBOL* m_symbol = nullptr;
190 LIB_SYMBOL* m_definition = nullptr;
191 wxString m_nickname;
192 wxString m_name;
193};
194
195
196COLLISION_CANDIDATE findCandidate( IMPORTED_SAMPLE& aSample )
197{
198 COLLISION_CANDIDATE candidate;
199
200 for( SCH_SYMBOL* symbol : placedSymbols( *aSample.m_schematic ) )
201 {
202 wxString nick = symbol->GetLibId().GetUniStringLibNickname();
203 wxString name = symbol->GetLibId().GetUniStringLibItemName();
204
205 if( nick.IsEmpty() || name.IsEmpty() )
206 continue;
207
208 for( const std::unique_ptr<LIB_SYMBOL>& def : aSample.m_definitions )
209 {
210 if( def->GetLibId().GetUniStringLibItemName() != name || def->GetPins().empty() )
211 continue;
212
213 candidate.m_symbol = symbol;
214 candidate.m_definition = def.get();
215 candidate.m_nickname = nick;
216 candidate.m_name = name;
217
218 return candidate;
219 }
220 }
221
222 return candidate;
223}
224} // namespace
225
226
227BOOST_AUTO_TEST_SUITE( SymbolImportReconciler )
228
229
230// EasyEDA Pro v3 hands definitions over the standard hook, leaving the source directory untouched
231// fails on revert, since LoadSchematicFile then publishes its own .kicad_sym beside the archive
232BOOST_AUTO_TEST_CASE( EasyEdaProV3SchematicResolvesToGeneratedCache )
233{
234 wxString projectPath = stageProject( wxS( "easyedapro_v3" ) );
236
237 IMPORTED_SAMPLE sample = importSample( project );
238 SCHEMATIC& schematic = *sample.m_schematic;
239
240 // the importer must not have published anything of its own beside the archive
241 BOOST_CHECK_MESSAGE( !hasSymbolLib( sample.m_sourceDir ),
242 "LoadSchematicFile wrote a library into the source directory" );
243
245 BOOST_REQUIRE( adapter );
246
247 const wxString cacheNick = wxS( "ls2k0300-import-syms" );
248 SYMBOL_IMPORT_RECONCILER reconciler( *adapter, project.GetProjectPath() );
249
251 reconciler.Reconcile( &schematic, std::move( sample.m_definitions ), cacheNick, {} );
252
253 // the cache lands in the project directory, not next to the source archive
254 BOOST_CHECK_EQUAL( result.m_cacheNickname, cacheNick );
255 BOOST_CHECK_GT( result.m_savedToCache, 0 );
256
257 wxFileName cacheFile( project.GetProjectPath(), cacheNick,
259 BOOST_CHECK_MESSAGE( wxFileExists( cacheFile.GetFullPath() ),
260 "Generated .kicad_sym was not published into the project" );
261
262 LIBRARY_TABLE* projectTable = adapter->ProjectTable().value_or( nullptr );
263 BOOST_REQUIRE( projectTable );
264 BOOST_CHECK( projectTable->HasRow( cacheNick ) );
265
266 // every placed symbol resolves through the adapter after reconciliation
267 int resolved = 0;
268
269 for( const SCH_SHEET_PATH& sheetPath : schematic.Hierarchy() )
270 {
271 SCH_SCREEN* screen = sheetPath.LastScreen();
272
273 if( !screen )
274 continue;
275
276 for( SCH_ITEM* item : screen->Items().OfType( SCH_SYMBOL_T ) )
277 {
278 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
279 wxString name = symbol->GetLibId().GetUniStringLibItemName();
280
281 if( name.IsEmpty() )
282 continue;
283
284 wxString nick = symbol->GetLibId().GetUniStringLibNickname();
285
286 BOOST_CHECK_MESSAGE( !nick.IsEmpty(),
287 wxString::Format( "Symbol '%s' left with empty nickname", name ) );
288 BOOST_CHECK_MESSAGE( adapter->LoadSymbol( nick, name ) != nullptr,
289 wxString::Format( "LIB_ID '%s:%s' does not resolve after "
290 "reconciliation", nick, name ) );
291 resolved++;
292 }
293 }
294
295 BOOST_CHECK_GT( resolved, 0 );
296 BOOST_CHECK_EQUAL( result.m_unresolved, 0 );
297
298 wxFileName::Rmdir( projectPath, wxPATH_RMDIR_RECURSIVE );
299}
300
301
302// EasyEDA Pro v2 wrote its .kicad_sym beside the source archive, exactly as v3 did
303BOOST_AUTO_TEST_CASE( EasyEdaProV2LeavesSourceDirectoryClean )
304{
305 wxString projectPath = stageProject( wxS( "easyedapro_v2" ) );
307
308 wxString sep = wxFileName::GetPathSeparator();
309 wxString srcDir = projectPath + wxT( "import-src" );
310 BOOST_REQUIRE( wxFileName::Mkdir( srcDir, wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL ) );
311
312 wxString source = wxString::FromUTF8(
314 + "pcbnew/plugins/easyedapro/ProProject_Yuzuki Chameleon_2023-09-02.epro" );
315 BOOST_REQUIRE( wxFileExists( source ) );
316
317 wxFileName archiveFile( srcDir + sep, wxS( "sample" ), wxS( "epro" ) );
318 BOOST_REQUIRE( wxCopyFile( source, archiveFile.GetFullPath() ) );
319
320 SCHEMATIC schematic( nullptr );
321 schematic.SetProject( &project );
322
323 IO_RELEASER<SCH_IO> plugin( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_EASYEDAPRO ) );
324 BOOST_REQUIRE( plugin );
325
326 SCH_SHEET* rootSheet = nullptr;
327 BOOST_REQUIRE_NO_THROW(
328 rootSheet = plugin->LoadSchematicFile( archiveFile.GetFullPath(), &schematic ) );
329 BOOST_REQUIRE( rootSheet );
330
331 BOOST_CHECK_MESSAGE( !hasSymbolLib( srcDir ),
332 "LoadSchematicFile wrote a library into the source directory" );
333
334 // the definitions still reach the reconciler through the standard hook
335 std::vector<LIB_SYMBOL*> raw = plugin->GetImportedCachedLibrarySymbols();
336 BOOST_CHECK_GT( raw.size(), 0 );
337
338 for( LIB_SYMBOL* symbol : raw )
339 delete symbol;
340
341 wxFileName::Rmdir( projectPath, wxPATH_RMDIR_RECURSIVE );
342}
343
344
345// An unrelated library that happens to carry the nickname the importer emitted must not swallow
346// the imported definition; without the provenance check the symbol relinks to the wrong part
347BOOST_AUTO_TEST_CASE( CollidingNicknameDoesNotStealTheLink )
348{
349 wxString projectPath = stageProject( wxS( "symreconcile_collide" ) );
351
352 IMPORTED_SAMPLE sample = importSample( project );
353 COLLISION_CANDIDATE candidate = findCandidate( sample );
354 BOOST_REQUIRE( candidate.m_symbol );
355
357 BOOST_REQUIRE( adapter );
358
359 // a pinless namesake registered under the importer's nickname: same name, different part
360 LIB_SYMBOL impostor( candidate.m_name );
361 BOOST_REQUIRE( impostor.GetPins().empty() );
362 publishLibrary( project, *adapter, candidate.m_nickname, wxS( "impostor" ), impostor );
363
364 const wxString cacheNick = wxS( "collide-import-syms" );
365 SYMBOL_IMPORT_RECONCILER reconciler( *adapter, project.GetProjectPath() );
366
368 sample.m_schematic.get(), std::move( sample.m_definitions ), cacheNick, {} );
369
370 BOOST_CHECK_EQUAL( result.m_cacheNickname, cacheNick );
371
372 // the imported definition is kept in the cache rather than dropped for the namesake
373 BOOST_CHECK_EQUAL( candidate.m_symbol->GetLibId().GetUniStringLibNickname(), cacheNick );
374
375 LIB_SYMBOL* linked = adapter->LoadSymbol( cacheNick, candidate.m_name );
376 BOOST_REQUIRE( linked );
377 BOOST_CHECK_GT( linked->GetPins().size(), 0 );
378
379 wxFileName::Rmdir( projectPath, wxPATH_RMDIR_RECURSIVE );
380}
381
382
383// The same nickname collision resolves the other way when the library really does hold the part,
384// so the equivalence escape hatch keeps a genuine match out of the generated cache
385BOOST_AUTO_TEST_CASE( EquivalentNamesakeTakesTheLink )
386{
387 wxString projectPath = stageProject( wxS( "symreconcile_equivalent" ) );
389
390 IMPORTED_SAMPLE sample = importSample( project );
391 COLLISION_CANDIDATE candidate = findCandidate( sample );
392 BOOST_REQUIRE( candidate.m_symbol );
393
395 BOOST_REQUIRE( adapter );
396
397 publishLibrary( project, *adapter, candidate.m_nickname, wxS( "genuine" ),
398 *candidate.m_definition );
399
400 const wxString cacheNick = wxS( "equivalent-import-syms" );
401 wxString sourceNick = candidate.m_nickname;
402 SYMBOL_IMPORT_RECONCILER reconciler( *adapter, project.GetProjectPath() );
403
405 sample.m_schematic.get(), std::move( sample.m_definitions ), cacheNick, {} );
406
407 BOOST_CHECK_EQUAL( candidate.m_symbol->GetLibId().GetUniStringLibNickname(), sourceNick );
408 BOOST_CHECK_GT( result.m_linkedToSource, 0 );
409
410 wxFileName::Rmdir( projectPath, wxPATH_RMDIR_RECURSIVE );
411}
412
413
414// Two source libraries supplying different symbols under one bare name must both survive the
415// cache; keying the cache by the bare name alone dropped the second and relinked its instance
416BOOST_AUTO_TEST_CASE( SameNameFromDifferentLibrariesKeepsBothDefinitions )
417{
418 wxString projectPath = stageProject( wxS( "symreconcile_namecollide" ) );
420
421 IMPORTED_SAMPLE sample = importSample( project );
422
423 // two real imported symbols that a pin count tells apart
424 SCH_SYMBOL* firstPlaced = nullptr;
425 SCH_SYMBOL* secondPlaced = nullptr;
426
427 for( SCH_SYMBOL* symbol : placedSymbols( *sample.m_schematic ) )
428 {
429 const std::unique_ptr<LIB_SYMBOL>& part = symbol->GetLibSymbolRef();
430
431 if( !part || part->GetPins().empty() )
432 continue;
433
434 if( !firstPlaced )
435 firstPlaced = symbol;
436 else if( part->GetPins().size() != firstPlaced->GetLibSymbolRef()->GetPins().size() )
437 secondPlaced = symbol;
438
439 if( secondPlaced )
440 break;
441 }
442
443 BOOST_REQUIRE( firstPlaced );
444 BOOST_REQUIRE( secondPlaced );
445
446 const wxString sharedName = wxS( "SHARED_SYM" );
447 const size_t firstPins = firstPlaced->GetLibSymbolRef()->GetPins().size();
448 const size_t secondPins = secondPlaced->GetLibSymbolRef()->GetPins().size();
449
450 std::vector<std::unique_ptr<LIB_SYMBOL>> defs;
451
452 // the same bare name under two source libraries, both placed and defined
453 auto relabel = [&]( SCH_SYMBOL* aSymbol, const wxString& aNickname )
454 {
455 auto def = std::make_unique<LIB_SYMBOL>( *aSymbol->GetLibSymbolRef() );
456 def->SetName( sharedName );
457 def->SetLibId( LIB_ID( aNickname, sharedName ) );
458 defs.push_back( std::move( def ) );
459
460 aSymbol->SetLibId( LIB_ID( aNickname, sharedName ) );
461 };
462
463 relabel( firstPlaced, wxS( "libAlpha" ) );
464 relabel( secondPlaced, wxS( "libBeta" ) );
465
467 BOOST_REQUIRE( adapter );
468
469 const wxString cacheNick = wxS( "namecollide-import-syms" );
471 SYMBOL_IMPORT_RECONCILER reconciler( *adapter, project.GetProjectPath(), reporter );
472
474 reconciler.Reconcile( sample.m_schematic.get(), std::move( defs ), cacheNick, {} );
475
476 BOOST_CHECK_EQUAL( result.m_cacheNickname, cacheNick );
477
478 LIB_ID firstId = firstPlaced->GetLibId();
479 LIB_ID secondId = secondPlaced->GetLibId();
480
481 BOOST_CHECK_EQUAL( firstId.GetUniStringLibNickname(), cacheNick );
482 BOOST_CHECK_EQUAL( secondId.GetUniStringLibNickname(), cacheNick );
483 BOOST_CHECK_MESSAGE( firstId.GetUniStringLibItemName() != secondId.GetUniStringLibItemName(),
484 "Symbols from two source libraries share one cache item name" );
485
486 // each instance still resolves to the symbol it was imported as
487 LIB_SYMBOL* firstLinked = adapter->LoadSymbol( cacheNick, firstId.GetUniStringLibItemName() );
488 LIB_SYMBOL* secondLinked = adapter->LoadSymbol( cacheNick, secondId.GetUniStringLibItemName() );
489
490 BOOST_REQUIRE( firstLinked );
491 BOOST_REQUIRE( secondLinked );
492 BOOST_CHECK_EQUAL( firstLinked->GetPins().size(), firstPins );
493 BOOST_CHECK_EQUAL( secondLinked->GetPins().size(), secondPins );
494
495 // the user is told which symbol the cache renamed
496 const wxString renamed = firstId.GetUniStringLibItemName() == sharedName
497 ? secondId.GetUniStringLibItemName()
498 : firstId.GetUniStringLibItemName();
499
500 BOOST_CHECK_MESSAGE( reporter.GetMessages().Contains(
501 wxString::Format( wxS( "renamed to '%s'" ), renamed ) ),
502 "Cache rename was not reported" );
503
504 wxFileName::Rmdir( projectPath, wxPATH_RMDIR_RECURSIVE );
505}
506
507
508// A project row already owning the cache nickname is a user library even when no file sits at the
509// generated path, so publishing must not rewrite its URI
510BOOST_AUTO_TEST_CASE( ExistingUserRowIsNotRepurposed )
511{
512 wxString projectPath = stageProject( wxS( "symreconcile_row" ) );
514
515 IMPORTED_SAMPLE sample = importSample( project );
516
518 BOOST_REQUIRE( adapter );
519
520 const wxString cacheNick = wxS( "row-import-syms" );
521
522 // the user's row owns the nickname but points at a file of its own
523 LIB_SYMBOL owned( wxS( "UserPart" ) );
524 publishLibrary( project, *adapter, cacheNick, wxS( "user-owned" ), owned );
525
526 LIBRARY_TABLE* table = adapter->ProjectTable().value_or( nullptr );
528
529 LIBRARY_TABLE_ROW* row = table->Row( cacheNick ).value_or( nullptr );
530 BOOST_REQUIRE( row );
531
532 const wxString uriBefore = row->URI();
533
534 SYMBOL_IMPORT_RECONCILER reconciler( *adapter, project.GetProjectPath() );
535
537 sample.m_schematic.get(), std::move( sample.m_definitions ), cacheNick, {} );
538
539 BOOST_CHECK( result.m_cacheNickname.IsEmpty() );
540 BOOST_CHECK_EQUAL( row->URI(), uriBefore );
541
542 wxFileName cacheFile( project.GetProjectPath(), cacheNick,
544 BOOST_CHECK_MESSAGE( !wxFileExists( cacheFile.GetFullPath() ),
545 "Published a cache over a nickname the user already owns" );
546
547 wxFileName::Rmdir( projectPath, wxPATH_RMDIR_RECURSIVE );
548}
549
550
551// A cache whose table row cannot be persisted is gone on restart, so it must not be claimed
552BOOST_AUTO_TEST_CASE( UnsavableTableLeavesCacheUnclaimed )
553{
554 wxString projectPath = stageProject( wxS( "symreconcile_rotable" ) );
556
558 BOOST_REQUIRE( adapter );
559
560 LIBRARY_TABLE* table = adapter->ProjectTable().value_or( nullptr );
562 BOOST_REQUIRE( table->Save().has_value() );
563
564 wxFileName tableFn( table->Path() );
565 tableFn.SetPermissions( wxS_IRUSR | wxS_IRGRP | wxS_IROTH );
566
567 // CI containers commonly run as root, where the permission bits do not deny access
568 if( tableFn.IsFileWritable() )
569 {
570 BOOST_TEST_MESSAGE( "Skipping read-only table check; file remains writable (running as "
571 "root?)" );
572 tableFn.SetPermissions( wxS_IRUSR | wxS_IWUSR );
573 wxFileName::Rmdir( projectPath, wxPATH_RMDIR_RECURSIVE );
574 return;
575 }
576
577 IMPORTED_SAMPLE sample = importSample( project );
578
579 const wxString cacheNick = wxS( "rotable-import-syms" );
580 SYMBOL_IMPORT_RECONCILER reconciler( *adapter, project.GetProjectPath() );
581
583 sample.m_schematic.get(), std::move( sample.m_definitions ), cacheNick, {} );
584
585 BOOST_CHECK( result.m_cacheNickname.IsEmpty() );
586 BOOST_CHECK_EQUAL( result.m_linkedToCache, 0 );
587 BOOST_CHECK_GT( result.m_unresolved, 0 );
588
589 tableFn.SetPermissions( wxS_IRUSR | wxS_IWUSR );
590 wxFileName::Rmdir( projectPath, wxPATH_RMDIR_RECURSIVE );
591}
592
593
594// An unregistered library already sitting at the cache path must never be overwritten
595BOOST_AUTO_TEST_CASE( ExistingUserLibraryIsNotClobbered )
596{
597 wxString projectPath = stageProject( wxS( "symreconcile_clobber" ) );
599
600 IMPORTED_SAMPLE sample = importSample( project );
601
603 BOOST_REQUIRE( adapter );
604
605 const wxString cacheNick = wxS( "victim-import-syms" );
606
607 wxFileName victim( project.GetProjectPath(), cacheNick,
609
610 const wxString sentinel = wxS( "(kicad_symbol_lib (version 20231120) (generator \"qa\"))" );
611 BOOST_REQUIRE( wxFile( victim.GetFullPath(), wxFile::write ).Write( sentinel ) );
612
613 SYMBOL_IMPORT_RECONCILER reconciler( *adapter, project.GetProjectPath() );
614
616 sample.m_schematic.get(), std::move( sample.m_definitions ), cacheNick, {} );
617
618 // the symbols stay unresolved rather than the file being taken over
619 BOOST_CHECK( result.m_cacheNickname.IsEmpty() );
620 BOOST_CHECK_GT( result.m_unresolved, 0 );
621
622 wxString contents;
623 wxFile readBack( victim.GetFullPath(), wxFile::read );
624 BOOST_REQUIRE( readBack.ReadAll( &contents ) );
625 BOOST_CHECK_EQUAL( contents, sentinel );
626
627 wxFileName::Rmdir( projectPath, wxPATH_RMDIR_RECURSIVE );
628}
629
630
const char * name
EE_TYPE OfType(KICAD_T aType) const
Definition sch_rtree.h:242
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:123
virtual LIBRARY_MANAGER & GetLibraryManager() const
Definition pgm_base.h:125
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:147
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:118
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:48
Schematic symbol object.
Definition sch_symbol.h:75
void SetLibId(const LIB_ID &aName)
const LIB_ID & GetLibId() const override
Definition sch_symbol.h:164
std::unique_ptr< LIB_SYMBOL > & GetLibSymbolRef()
Definition sch_symbol.h:183
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.
A wrapper for reporting to a wxString object.
Definition reporter.h:242
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:724
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()
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")
@ SCH_SYMBOL_T
Definition typeinfo.h:168
Definition of file extensions used in Kicad.