KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_symbol_import_manager.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 modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation, either version 3 of the License, or (at your
9 * option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
24
26
28#include <sch_io/sch_io_mgr.h>
29#include <sch_io/sch_io.h>
30#include <ki_exception.h>
31#include <lib_symbol.h>
32#include <wx/filename.h>
33
34
36{
37public:
39 {
40 // Get the path to our test libraries
41 wxFileName kicadSymLib( KI_TEST::GetTestDataRootDir() );
42 kicadSymLib.AppendDir( "eeschema" );
43 kicadSymLib.AppendDir( "libs" );
44 kicadSymLib.SetFullName( "4xxx.kicad_sym" );
45 m_kicadSymLibPath = kicadSymLib.GetFullPath();
46
47 wxFileName legacyLib( KI_TEST::GetTestDataRootDir() );
48 legacyLib.AppendDir( "eeschema" );
49 legacyLib.AppendDir( "libs" );
50 legacyLib.SetFullName( "4xxx.lib" );
51 m_legacyLibPath = legacyLib.GetFullPath();
52 }
53
58 {
59 IO_RELEASER<SCH_IO> pi( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_KICAD ) );
60
61 if( !pi )
62 return false;
63
64 wxArrayString symbolNames;
65
66 try
67 {
68 pi->EnumerateSymbolLib( symbolNames, m_kicadSymLibPath );
69 }
70 catch( const IO_ERROR& )
71 {
72 return false;
73 }
74
75 for( const wxString& name : symbolNames )
76 {
77 try
78 {
79 LIB_SYMBOL* sym = pi->LoadSymbol( m_kicadSymLibPath, name );
80
81 if( sym )
82 {
83 wxString parentName = sym->GetParentName();
84 bool isPower = sym->IsPower();
85
86 // Don't pass symbol pointer to manager - LoadSymbol returns
87 // a cached pointer, not a copy. Don't delete - cache owns it.
88 aManager.AddSymbol( name, parentName, isPower, nullptr );
89 }
90 }
91 catch( const IO_ERROR& )
92 {
93 // Add without full symbol data
94 aManager.AddSymbol( name, wxEmptyString, false, nullptr );
95 }
96 }
97
98 aManager.BuildDependencyMaps();
99 return true;
100 }
101
106 {
107 IO_RELEASER<SCH_IO> pi( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_LEGACY ) );
108
109 if( !pi )
110 return false;
111
112 wxArrayString symbolNames;
113
114 try
115 {
116 pi->EnumerateSymbolLib( symbolNames, m_legacyLibPath );
117 }
118 catch( const IO_ERROR& )
119 {
120 return false;
121 }
122
123 for( const wxString& name : symbolNames )
124 {
125 try
126 {
127 LIB_SYMBOL* sym = pi->LoadSymbol( m_legacyLibPath, name );
128
129 if( sym )
130 {
131 wxString parentName = sym->GetParentName();
132 bool isPower = sym->IsPower();
133
134 // Don't pass symbol pointer to manager - LoadSymbol returns
135 // a cached pointer, not a copy. Don't delete - cache owns it.
136 aManager.AddSymbol( name, parentName, isPower, nullptr );
137 }
138 }
139 catch( const IO_ERROR& )
140 {
141 aManager.AddSymbol( name, wxEmptyString, false, nullptr );
142 }
143 }
144
145 aManager.BuildDependencyMaps();
146 return true;
147 }
148
149protected:
152};
153
154
155BOOST_FIXTURE_TEST_SUITE( SymbolImportManager, TEST_SYMBOL_IMPORT_MANAGER_FIXTURE )
156
157
158
161BOOST_AUTO_TEST_CASE( LoadKiCadSymbolLibrary )
162{
163 SYMBOL_IMPORT_MANAGER manager;
164
165 BOOST_REQUIRE( LoadKiCadLibraryHelper( manager ) );
166 BOOST_CHECK_GT( manager.GetSymbolCount(), 0 );
167
168 // Should have 51 symbols based on our test data
169 BOOST_CHECK_EQUAL( manager.GetSymbolCount(), 51 );
170}
171
172
176BOOST_AUTO_TEST_CASE( LoadLegacyLibrary )
177{
178 SYMBOL_IMPORT_MANAGER manager;
179
180 BOOST_REQUIRE( LoadLegacyLibraryHelper( manager ) );
181 BOOST_CHECK_GT( manager.GetSymbolCount(), 0 );
182
183 // Should have 48 symbols based on our test data (44 DEF + 4 ALIAS entries)
184 BOOST_CHECK_EQUAL( manager.GetSymbolCount(), 48 );
185}
186
187
191BOOST_AUTO_TEST_CASE( DependencyMapKiCadSym )
192{
193 SYMBOL_IMPORT_MANAGER manager;
194 BOOST_REQUIRE( LoadKiCadLibraryHelper( manager ) );
195
196 // Test known derived symbols from 4xxx.kicad_sym:
197 // 14528, 14538, 4528 extend 4538
198 // 4066 extends 4016
199
200 // Check IsDerived
201 BOOST_CHECK( manager.IsDerived( "14528" ) );
202 BOOST_CHECK( manager.IsDerived( "14538" ) );
203 BOOST_CHECK( manager.IsDerived( "4528" ) );
204 BOOST_CHECK( manager.IsDerived( "4066" ) );
205
206 // Check that base symbols are not derived
207 BOOST_CHECK( !manager.IsDerived( "4538" ) );
208 BOOST_CHECK( !manager.IsDerived( "4016" ) );
209 BOOST_CHECK( !manager.IsDerived( "4001" ) );
210
211 // Check GetParent
212 BOOST_CHECK_EQUAL( manager.GetParent( "14528" ), "4538" );
213 BOOST_CHECK_EQUAL( manager.GetParent( "14538" ), "4538" );
214 BOOST_CHECK_EQUAL( manager.GetParent( "4528" ), "4538" );
215 BOOST_CHECK_EQUAL( manager.GetParent( "4066" ), "4016" );
216 BOOST_CHECK_EQUAL( manager.GetParent( "4538" ), "" );
217
218 // Check GetDirectDerivatives
219 std::vector<wxString> derivatives = manager.GetDirectDerivatives( "4538" );
220 BOOST_CHECK_EQUAL( derivatives.size(), 3 );
221
222 // Check 4016 has one derivative
223 derivatives = manager.GetDirectDerivatives( "4016" );
224 BOOST_CHECK_EQUAL( derivatives.size(), 1 );
225 BOOST_CHECK_EQUAL( derivatives[0], "4066" );
226}
227
228
232BOOST_AUTO_TEST_CASE( GetAncestorsSingleLevel )
233{
234 SYMBOL_IMPORT_MANAGER manager;
235 BOOST_REQUIRE( LoadKiCadLibraryHelper( manager ) );
236
237 // 4066 extends 4016 (single level)
238 std::set<wxString> ancestors = manager.GetAncestors( "4066" );
239 BOOST_CHECK_EQUAL( ancestors.size(), 1 );
240 BOOST_CHECK( ancestors.count( "4016" ) == 1 );
241
242 // 4016 has no ancestors (is root)
243 ancestors = manager.GetAncestors( "4016" );
244 BOOST_CHECK_EQUAL( ancestors.size(), 0 );
245}
246
247
251BOOST_AUTO_TEST_CASE( GetDescendants )
252{
253 SYMBOL_IMPORT_MANAGER manager;
254 BOOST_REQUIRE( LoadKiCadLibraryHelper( manager ) );
255
256 // 4538 has 3 direct derivatives: 14528, 14538, 4528
257 std::set<wxString> descendants = manager.GetDescendants( "4538" );
258 BOOST_CHECK_EQUAL( descendants.size(), 3 );
259 BOOST_CHECK( descendants.count( "14528" ) == 1 );
260 BOOST_CHECK( descendants.count( "14538" ) == 1 );
261 BOOST_CHECK( descendants.count( "4528" ) == 1 );
262
263 // 4016 has 1 descendant: 4066
264 descendants = manager.GetDescendants( "4016" );
265 BOOST_CHECK_EQUAL( descendants.size(), 1 );
266 BOOST_CHECK( descendants.count( "4066" ) == 1 );
267
268 // A leaf symbol has no descendants
269 descendants = manager.GetDescendants( "4066" );
270 BOOST_CHECK_EQUAL( descendants.size(), 0 );
271}
272
273
277BOOST_AUTO_TEST_CASE( SelectAutoSelectsAncestors )
278{
279 SYMBOL_IMPORT_MANAGER manager;
280 BOOST_REQUIRE( LoadKiCadLibraryHelper( manager ) );
281
282 // Select a derived symbol
283 manager.SetSymbolSelected( "4066", true );
284
285 // Check the derived symbol is checked
286 const SYMBOL_IMPORT_INFO* info4066 = manager.GetSymbolInfo( "4066" );
287 BOOST_REQUIRE( info4066 != nullptr );
288 BOOST_CHECK( info4066->m_checked );
289 BOOST_CHECK( !info4066->m_autoSelected );
290
291 // Check the parent is auto-selected
292 const SYMBOL_IMPORT_INFO* info4016 = manager.GetSymbolInfo( "4016" );
293 BOOST_REQUIRE( info4016 != nullptr );
294 BOOST_CHECK( !info4016->m_checked );
295 BOOST_CHECK( info4016->m_autoSelected );
296
297 // Both should be in the import list
298 std::vector<wxString> toImport = manager.GetSymbolsToImport();
299 BOOST_CHECK_EQUAL( toImport.size(), 2 );
300}
301
302
306BOOST_AUTO_TEST_CASE( SelectParentDoesNotSelectDescendants )
307{
308 SYMBOL_IMPORT_MANAGER manager;
309 BOOST_REQUIRE( LoadKiCadLibraryHelper( manager ) );
310
311 // Select a parent symbol
312 manager.SetSymbolSelected( "4538", true );
313
314 // Check the parent is checked
315 const SYMBOL_IMPORT_INFO* info4538 = manager.GetSymbolInfo( "4538" );
316 BOOST_REQUIRE( info4538 != nullptr );
317 BOOST_CHECK( info4538->m_checked );
318
319 // Check descendants are NOT selected
320 const SYMBOL_IMPORT_INFO* info14528 = manager.GetSymbolInfo( "14528" );
321 BOOST_REQUIRE( info14528 != nullptr );
322 BOOST_CHECK( !info14528->m_checked );
323 BOOST_CHECK( !info14528->m_autoSelected );
324
325 // Only the parent should be in the import list
326 std::vector<wxString> toImport = manager.GetSymbolsToImport();
327 BOOST_CHECK_EQUAL( toImport.size(), 1 );
328}
329
330
334BOOST_AUTO_TEST_CASE( GetSelectedDescendants )
335{
336 SYMBOL_IMPORT_MANAGER manager;
337 BOOST_REQUIRE( LoadKiCadLibraryHelper( manager ) );
338
339 // Select multiple derivatives of 4538
340 manager.SetSymbolSelected( "14528", true );
341 manager.SetSymbolSelected( "4528", true );
342
343 // 4538 should be auto-selected
344 const SYMBOL_IMPORT_INFO* info4538 = manager.GetSymbolInfo( "4538" );
345 BOOST_REQUIRE( info4538 != nullptr );
346 BOOST_CHECK( info4538->m_autoSelected );
347
348 // GetSelectedDescendants should return the two selected derivatives
349 std::vector<wxString> selectedDesc = manager.GetSelectedDescendants( "4538" );
350 BOOST_CHECK_EQUAL( selectedDesc.size(), 2 );
351}
352
353
357BOOST_AUTO_TEST_CASE( DeselectWithDescendants )
358{
359 SYMBOL_IMPORT_MANAGER manager;
360 BOOST_REQUIRE( LoadKiCadLibraryHelper( manager ) );
361
362 // Select parent and all derivatives
363 manager.SetSymbolSelected( "4538", true );
364 manager.SetSymbolSelected( "14528", true );
365 manager.SetSymbolSelected( "14538", true );
366 manager.SetSymbolSelected( "4528", true );
367
368 // Verify all are selected
369 BOOST_CHECK_EQUAL( manager.GetSymbolsToImport().size(), 4 );
370
371 // Deselect parent with descendants
372 manager.DeselectWithDescendants( "4538" );
373
374 // All should be deselected
375 BOOST_CHECK_EQUAL( manager.GetSymbolsToImport().size(), 0 );
376}
377
378
383{
384 SYMBOL_IMPORT_MANAGER manager;
385 BOOST_REQUIRE( LoadKiCadLibraryHelper( manager ) );
386
387 manager.SelectAll();
388
389 // All symbols should be in import list
390 BOOST_CHECK_EQUAL( manager.GetSymbolsToImport().size(), manager.GetSymbolCount() );
391
392 // Manual count should match total (no auto-selections needed when all selected)
393 BOOST_CHECK_EQUAL( manager.GetManualSelectionCount(), (int) manager.GetSymbolCount() );
394}
395
396
400BOOST_AUTO_TEST_CASE( SelectAllWithFilter )
401{
402 SYMBOL_IMPORT_MANAGER manager;
403 BOOST_REQUIRE( LoadKiCadLibraryHelper( manager ) );
404
405 // Select only symbols starting with "40"
406 manager.SelectAll( []( const wxString& name ) { return name.StartsWith( "40" ); } );
407
408 // Should have some but not all symbols selected
409 int selectedCount = manager.GetManualSelectionCount() + manager.GetAutoSelectionCount();
410 BOOST_CHECK_GT( selectedCount, 0 );
411 BOOST_CHECK_LT( selectedCount, (int) manager.GetSymbolCount() );
412}
413
414
419{
420 SYMBOL_IMPORT_MANAGER manager;
421 BOOST_REQUIRE( LoadKiCadLibraryHelper( manager ) );
422
423 manager.SelectAll();
424 BOOST_CHECK_GT( manager.GetSymbolsToImport().size(), 0 );
425
426 manager.DeselectAll();
427 BOOST_CHECK_EQUAL( manager.GetSymbolsToImport().size(), 0 );
428}
429
430
434BOOST_AUTO_TEST_CASE( ConflictDetection )
435{
436 SYMBOL_IMPORT_MANAGER manager;
437 BOOST_REQUIRE( LoadKiCadLibraryHelper( manager ) );
438
439 // Simulate that some symbols exist in destination
440 std::set<wxString> existingSymbols = { "4001", "4011", "4538" };
441 manager.CheckExistingSymbols(
442 [&existingSymbols]( const wxString& name ) { return existingSymbols.count( name ) > 0; } );
443
444 // Check the flags are set correctly
445 const SYMBOL_IMPORT_INFO* info4001 = manager.GetSymbolInfo( "4001" );
446 BOOST_REQUIRE( info4001 != nullptr );
447 BOOST_CHECK( info4001->m_existsInDest );
448
449 const SYMBOL_IMPORT_INFO* info4002 = manager.GetSymbolInfo( "4002" );
450 BOOST_REQUIRE( info4002 != nullptr );
451 BOOST_CHECK( !info4002->m_existsInDest );
452
453 // Select some symbols including conflicting ones
454 manager.SetSymbolSelected( "4001", true );
455 manager.SetSymbolSelected( "4002", true );
456
457 // Check GetConflicts
458 std::vector<wxString> conflicts = manager.GetConflicts();
459 BOOST_CHECK_EQUAL( conflicts.size(), 1 );
460 BOOST_CHECK_EQUAL( conflicts[0], "4001" );
461}
462
463
467BOOST_AUTO_TEST_CASE( FilterMatching )
468{
469 // Case-insensitive contains matching
470 BOOST_CHECK( SYMBOL_IMPORT_MANAGER::MatchesFilter( "4001", "40" ) );
471 BOOST_CHECK( SYMBOL_IMPORT_MANAGER::MatchesFilter( "4001", "01" ) );
472 BOOST_CHECK( SYMBOL_IMPORT_MANAGER::MatchesFilter( "4001", "4001" ) );
473 BOOST_CHECK( SYMBOL_IMPORT_MANAGER::MatchesFilter( "CD4001", "cd" ) );
474 BOOST_CHECK( SYMBOL_IMPORT_MANAGER::MatchesFilter( "CD4001", "CD" ) );
475
476 // Non-matching
477 BOOST_CHECK( !SYMBOL_IMPORT_MANAGER::MatchesFilter( "4001", "xyz" ) );
478 BOOST_CHECK( !SYMBOL_IMPORT_MANAGER::MatchesFilter( "4001", "4002" ) );
479
480 // Empty filter matches everything
481 BOOST_CHECK( SYMBOL_IMPORT_MANAGER::MatchesFilter( "4001", "" ) );
482 BOOST_CHECK( SYMBOL_IMPORT_MANAGER::MatchesFilter( "anything", "" ) );
483}
484
485
489BOOST_AUTO_TEST_CASE( SelectionCounts )
490{
491 SYMBOL_IMPORT_MANAGER manager;
492 BOOST_REQUIRE( LoadKiCadLibraryHelper( manager ) );
493
494 // Initially no selections
497
498 // Select a derived symbol (should auto-select 1 ancestor)
499 manager.SetSymbolSelected( "4066", true );
502
503 // Select another derived symbol with same parent (no new auto-selections)
504 // (4066's parent is 4016, not related to 4538)
505 // Select 14528 which extends 4538
506 manager.SetSymbolSelected( "14528", true );
508 BOOST_CHECK_EQUAL( manager.GetAutoSelectionCount(), 2 ); // 4016 and 4538
509
510 // Select the auto-selected parent manually (should decrease auto count)
511 manager.SetSymbolSelected( "4538", true );
513 BOOST_CHECK_EQUAL( manager.GetAutoSelectionCount(), 1 ); // Only 4016 now
514}
515
516
520BOOST_AUTO_TEST_CASE( ClearResetsState )
521{
522 SYMBOL_IMPORT_MANAGER manager;
523 BOOST_REQUIRE( LoadKiCadLibraryHelper( manager ) );
524
525 manager.SelectAll();
526 BOOST_CHECK_GT( manager.GetSymbolCount(), 0 );
527
528 manager.Clear();
529
530 BOOST_CHECK_EQUAL( manager.GetSymbolCount(), 0 );
531 BOOST_CHECK_EQUAL( manager.GetSymbolsToImport().size(), 0 );
532}
533
534
const char * name
Hold an error message and may be used when throwing exceptions containing meaningful error messages.
Define a library symbol object.
Definition lib_symbol.h:83
bool IsPower() const override
const wxString & GetParentName() const
Definition lib_symbol.h:841
Manages the logic for selecting symbols to import from a library file.
void DeselectWithDescendants(const wxString &aSymbolName)
Force deselection of a symbol and all its descendants.
std::vector< wxString > GetConflicts() const
Get list of symbols that will conflict (selected and exist in destination).
void CheckExistingSymbols(SYMBOL_EXISTS_FUNC aExistsFunc)
Mark symbols that exist in the destination library.
void SelectAll(std::function< bool(const wxString &)> aFilter=nullptr)
Select all symbols (optionally filtered).
std::vector< wxString > GetDirectDerivatives(const wxString &aSymbolName) const
Get direct children (derivatives) of a symbol.
wxString GetParent(const wxString &aSymbolName) const
Get the direct parent of a symbol.
int GetManualSelectionCount() const
Get count of manually selected symbols.
void DeselectAll(std::function< bool(const wxString &)> aFilter=nullptr)
Deselect all symbols (optionally filtered).
size_t GetSymbolCount() const
Get total number of symbols.
void AddSymbol(const wxString &aName, const wxString &aParentName, bool aIsPower, LIB_SYMBOL *aSymbol=nullptr)
Add a symbol to the import list.
std::vector< wxString > GetSelectedDescendants(const wxString &aSymbolName) const
Get selected descendants that would be orphaned if a symbol is deselected.
std::vector< wxString > SetSymbolSelected(const wxString &aSymbolName, bool aSelected)
Set the selection state of a symbol.
static bool MatchesFilter(const wxString &aSymbolName, const wxString &aFilter)
Check if a symbol name matches a filter string (case-insensitive contains).
int GetAutoSelectionCount() const
Get count of auto-selected symbols (dependencies).
void Clear()
Clear all loaded symbols and reset state.
SYMBOL_IMPORT_INFO * GetSymbolInfo(const wxString &aName)
Get symbol info by name.
bool IsDerived(const wxString &aSymbolName) const
Check if a symbol is derived from another.
std::set< wxString > GetAncestors(const wxString &aSymbolName) const
Get all ancestors of a symbol (full inheritance chain).
std::set< wxString > GetDescendants(const wxString &aSymbolName) const
Get all descendants of a symbol (all derived symbols recursively).
std::vector< wxString > GetSymbolsToImport() const
Get list of all symbols that will be imported (checked + auto-selected).
void BuildDependencyMaps()
Build the dependency maps from parent relationships.
bool LoadLegacyLibraryHelper(SYMBOL_IMPORT_MANAGER &aManager)
Load symbols from a legacy symbol library into the manager.
bool LoadKiCadLibraryHelper(SYMBOL_IMPORT_MANAGER &aManager)
Load symbols from a KiCad symbol library into the manager.
std::unique_ptr< T > IO_RELEASER
Helper to hold and release an IO_BASE object when exceptions are thrown.
Definition io_mgr.h:33
std::string GetTestDataRootDir()
Information about a symbol available for import.
bool m_existsInDest
True if symbol exists in destination library.
bool m_checked
User's manual selection state.
bool m_autoSelected
True if auto-selected as dependency.
BOOST_AUTO_TEST_CASE(HorizontalAlignment)
BOOST_REQUIRE(intersection.has_value()==c.ExpectedIntersection.has_value())
BOOST_AUTO_TEST_SUITE_END()
BOOST_AUTO_TEST_CASE(LoadKiCadSymbolLibrary)
Test loading symbols from a KiCad symbol library.
BOOST_CHECK_EQUAL(result, "25.4")