KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_symbol_library_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 (C) 2023 Wayne Stambaugh <[email protected]>
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * This program is free software: you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation, either version 3 of the License, or (at your
10 * option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <https://www.gnu.org/licenses/>.
19 */
20
25
27
28// Code under test
31#include <sch_field.h>
34#include <sch_io/sch_io_mgr.h>
35#include <richio.h>
36
37#include <wx/filename.h>
38
46
47
48BOOST_FIXTURE_TEST_SUITE( SymbolLibraryManager, SYMBOL_LIBRARY_MANAGER_TEST_FIXTURE )
49
50
51
54BOOST_AUTO_TEST_CASE( SymbolBuffer )
55{
56 std::unique_ptr<LIB_SYMBOL> symbol = std::make_unique<LIB_SYMBOL>( wxS( "Symbol" ) );
57 std::unique_ptr<SCH_SCREEN> screen = std::make_unique<SCH_SCREEN>();
58
59 LIB_SYMBOL& symbolRef = *symbol;
60
61 SYMBOL_BUFFER buffer( std::move( symbol ), std::move( screen ) );
62
63 BOOST_CHECK( !buffer.IsModified() );
64 BOOST_CHECK( &buffer.GetSymbol() == &symbolRef );
65 BOOST_CHECK( buffer.GetOriginal() == symbolRef );
66
67 buffer.GetScreen()->SetContentModified();
68 BOOST_CHECK( buffer.IsModified() );
69
70 std::unique_ptr<LIB_SYMBOL> originalSymbol =
71 std::make_unique<LIB_SYMBOL>( wxS( "OriginalSymbol" ) );
72 LIB_SYMBOL& originalSymbolRef = *originalSymbol;
73
74 buffer.SetOriginal( std::move( originalSymbol ) );
75
76 BOOST_CHECK( &buffer.GetOriginal() == &originalSymbolRef );
77 BOOST_CHECK( buffer.GetOriginal() == originalSymbolRef );
78 BOOST_CHECK( &buffer.GetSymbol() != &buffer.GetOriginal() );
79}
80
81
86{
87 wxArrayString symbolNames;
88 LIB_BUFFER libBuffer( wxS( "TestLibrary" ) );
89
90 BOOST_CHECK( !libBuffer.IsModified() );
91 BOOST_CHECK_EQUAL( libBuffer.GetHash(), 1 );
92
93 auto parentSymbol1 = std::make_unique<LIB_SYMBOL>( wxS( "Parent1" ) );
94 // A but clunky, but real code would get symbol from the LIB_BUFFER
95 // via GetSymbol etc, rather than retaining a reference after construction.
96 LIB_SYMBOL& parentSymbol1Ref = *parentSymbol1;
97
98 BOOST_CHECK_EQUAL( libBuffer.GetSymbol( parentSymbol1->GetName() ), nullptr );
99
100 parentSymbol1->GetValueField().SetText( parentSymbol1->GetName() );
101 libBuffer.CreateBuffer( std::move( parentSymbol1 ), std::make_unique<SCH_SCREEN>() );
102 BOOST_CHECK( libBuffer.GetSymbol( parentSymbol1Ref.GetName() ) == &parentSymbol1Ref );
103 BOOST_CHECK( !libBuffer.HasDerivedSymbols( parentSymbol1Ref.GetName() ) );
104 BOOST_CHECK_EQUAL( libBuffer.GetHash(), 2 );
105
106 libBuffer.GetSymbolNames( symbolNames );
107 BOOST_CHECK_EQUAL( symbolNames.GetCount(), 1 );
108 BOOST_CHECK_EQUAL( symbolNames[0], parentSymbol1Ref.GetName() );
109
110 symbolNames.Clear();
111 libBuffer.GetSymbolNames( symbolNames, SYMBOL_NAME_FILTER::ROOT_ONLY );
112 BOOST_CHECK_EQUAL( symbolNames.GetCount(), 1 );
113 BOOST_CHECK_EQUAL( symbolNames[0], parentSymbol1Ref.GetName() );
114
115 symbolNames.Clear();
116 libBuffer.GetSymbolNames( symbolNames, SYMBOL_NAME_FILTER::DERIVED_ONLY );
117 BOOST_CHECK_EQUAL( symbolNames.GetCount(), 0 );
118
119 auto childSymbol1 = std::make_unique<LIB_SYMBOL>( wxS( "Child1" ) );
120 LIB_SYMBOL& childSymbol1Ref = *childSymbol1;
121
122 childSymbol1->SetParent( &parentSymbol1Ref );
123 childSymbol1->GetValueField().SetText( childSymbol1->GetName() );
124 libBuffer.CreateBuffer( std::move( childSymbol1 ), std::make_unique<SCH_SCREEN>() );
125 BOOST_CHECK( libBuffer.GetSymbol( childSymbol1Ref.GetName() ) == &childSymbol1Ref );
126 BOOST_CHECK( libBuffer.HasDerivedSymbols( parentSymbol1Ref.GetName() ) );
127 BOOST_CHECK_EQUAL( libBuffer.GetHash(), 3 );
128
129 symbolNames.Clear();
130 libBuffer.GetSymbolNames( symbolNames );
131 BOOST_CHECK_EQUAL( symbolNames.GetCount(), 2 );
132 BOOST_CHECK_EQUAL( symbolNames[0], parentSymbol1Ref.GetName() );
133 BOOST_CHECK_EQUAL( symbolNames[1], childSymbol1Ref.GetName() );
134
135 symbolNames.Clear();
136 libBuffer.GetSymbolNames( symbolNames, SYMBOL_NAME_FILTER::DERIVED_ONLY );
137 BOOST_CHECK_EQUAL( symbolNames.GetCount(), 1 );
138 BOOST_CHECK_EQUAL( symbolNames[0], childSymbol1Ref.GetName() );
139
140 symbolNames.Clear();
141
142 BOOST_CHECK_EQUAL( libBuffer.GetDerivedSymbolNames( parentSymbol1Ref.GetName(), symbolNames ),
143 1 );
144 BOOST_CHECK_EQUAL( symbolNames[0], childSymbol1Ref.GetName() );
145
146 std::shared_ptr<SYMBOL_BUFFER> buf = libBuffer.GetBuffer( parentSymbol1Ref.GetName() );
147
148 LIB_SYMBOL tmp( parentSymbol1Ref );
149 tmp.GetDescriptionField().SetText( wxS( "Description" ) );
150
151 libBuffer.UpdateBuffer( *buf, tmp );
152 BOOST_CHECK_EQUAL( libBuffer.GetHash(), 4 );
153 BOOST_CHECK( *libBuffer.GetSymbol( parentSymbol1Ref.GetName() ) == tmp );
154
155 const bool deletedOk = libBuffer.DeleteBuffer( *buf );
156 BOOST_CHECK( deletedOk );
157 BOOST_CHECK( libBuffer.GetBuffers().empty() );
158}
159
160
164BOOST_AUTO_TEST_CASE( NewSymbolCreation )
165{
167
168 props.name = wxS( "Standalone" );
169 props.reference = wxS( "U" );
170 props.unitCount = 2;
171 props.pinNameInside = true;
172 props.pinTextPosition = 2;
173 props.powerSymbol = false;
174 props.showPinNumber = true;
175 props.showPinName = true;
176 props.unitsInterchangeable = false;
177 props.includeInBom = true;
178 props.includeOnBoard = true;
179 props.alternateBodyStyle = false;
180
181 std::unique_ptr<LIB_SYMBOL> standalone =
183
184 BOOST_CHECK_EQUAL( standalone->GetReferenceField().GetText(), props.reference );
185 BOOST_CHECK_EQUAL( standalone->GetUnitCount(), props.unitCount );
186 BOOST_CHECK( standalone->GetPinNameOffset() > 0 );
187
188 auto parent = std::make_unique<LIB_SYMBOL>( wxS( "Parent" ) );
189 parent->GetValueField().SetText( parent->GetName() );
190 SCH_FIELD* user = new SCH_FIELD( parent.get(), FIELD_T::USER, wxS( "UF" ) );
191 user->SetText( wxS( "V" ) );
192 parent->AddField( user );
193
194 props.name = wxS( "Child" );
195 props.parentSymbolName = parent->GetName();
196 props.keepFootprint = false;
197 props.keepDatasheet = false;
198 props.transferUserFields = true;
199 props.keepContentUserFields = false;
200
201 std::unique_ptr<LIB_SYMBOL> child =
202 LIB_SYMBOL_LIBRARY_MANAGER::CreateSymbol( props, parent.get() );
203
204 BOOST_CHECK( child->GetParent().lock().get() == parent.get() );
205 BOOST_CHECK( child->GetFootprintField().GetText().IsEmpty() );
206 BOOST_CHECK( child->GetDatasheetField().GetText().IsEmpty() );
207
208 std::vector<SCH_FIELD*> childFields;
209 child->GetFields( childFields );
210
211 bool found = false;
212
213 for( SCH_FIELD* field : childFields )
214 {
215 if( field->GetId() == FIELD_T::USER )
216 {
217 found = true;
218 BOOST_CHECK( field->GetText().IsEmpty() );
219 }
220 }
221
222 BOOST_CHECK( found );
223}
224
225
231BOOST_AUTO_TEST_CASE( NewPowerSymbolSeedsValueFromName )
232{
234
235 props.name = wxS( "+5V" );
236 props.reference = wxS( "#PWR" );
237 props.unitCount = 1;
238 props.pinNameInside = false;
239 props.pinTextPosition = 0;
240 props.powerSymbol = true;
241 props.showPinNumber = false;
242 props.showPinName = false;
243 props.unitsInterchangeable = true;
244 props.includeInBom = false;
245 props.includeOnBoard = false;
246 props.alternateBodyStyle = false;
247
248 std::unique_ptr<LIB_SYMBOL> symbol = LIB_SYMBOL_LIBRARY_MANAGER::CreateSymbol( props, nullptr );
249
250 BOOST_REQUIRE( symbol->IsGlobalPower() );
251 BOOST_CHECK_EQUAL( symbol->GetValueField().GetText(), props.name );
252}
253
254
263BOOST_AUTO_TEST_CASE( DeletedSymbolsAreRemovedFromFile )
264{
265 // Create a temporary directory and library file
266 wxString tempDir = wxFileName::CreateTempFileName( wxS( "kicad_test_" ) );
267 wxRemoveFile( tempDir );
268 wxFileName::Mkdir( tempDir );
269 wxString libPath = wxFileName( tempDir, wxS( "test_lib.kicad_sym" ) ).GetFullPath();
270
271 // Step 1: Create a library with a parent and derived symbol using the plugin directly
272 {
273 IO_RELEASER<SCH_IO> plugin( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_KICAD ) );
274 plugin->CreateLibrary( libPath );
275
276 // Create parent symbol
277 std::unique_ptr<LIB_SYMBOL> parentSymbol = std::make_unique<LIB_SYMBOL>( wxS( "Parent" ) );
278 parentSymbol->GetValueField().SetText( wxS( "Parent" ) );
279 parentSymbol->GetReferenceField().SetText( wxS( "U" ) );
280
281 LIB_SYMBOL* parentPtr = parentSymbol.get();
282 plugin->SaveSymbol( libPath, std::make_unique<LIB_SYMBOL>( *parentSymbol ) );
283
284 // Create derived symbol
285 std::unique_ptr<LIB_SYMBOL> derivedSymbol = std::make_unique<LIB_SYMBOL>( wxS( "Derived" ) );
286 derivedSymbol->GetValueField().SetText( wxS( "Derived" ) );
287 derivedSymbol->SetParent( parentPtr );
288
289 plugin->SaveSymbol( libPath, std::make_unique<LIB_SYMBOL>( *derivedSymbol ) );
290 plugin->SaveLibrary( libPath );
291 }
292
293 // Step 2: Verify both symbols exist in the library
294 {
295 IO_RELEASER<SCH_IO> plugin( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_KICAD ) );
296 LIB_SYMBOL* parent = plugin->LoadSymbol( libPath, wxS( "Parent" ) );
297 LIB_SYMBOL* derived = plugin->LoadSymbol( libPath, wxS( "Derived" ) );
298
299 BOOST_REQUIRE( parent != nullptr );
300 BOOST_REQUIRE( derived != nullptr );
301 BOOST_CHECK( derived->IsDerived() );
302 }
303
304 // Step 3: Load symbols into LIB_BUFFER and delete the derived symbol
305 LIB_BUFFER libBuffer( wxS( "TestLibrary" ) );
306
307 {
308 IO_RELEASER<SCH_IO> plugin( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_KICAD ) );
309 LIB_SYMBOL* loadedParent = plugin->LoadSymbol( libPath, wxS( "Parent" ) );
310 LIB_SYMBOL* loadedDerived = plugin->LoadSymbol( libPath, wxS( "Derived" ) );
311
312 BOOST_REQUIRE( loadedParent != nullptr );
313 BOOST_REQUIRE( loadedDerived != nullptr );
314
315 // Set up parent relationship
316 loadedDerived->SetParent( loadedParent );
317
318 // Create buffers (parent first, then derived)
319 libBuffer.CreateBuffer( std::make_unique<LIB_SYMBOL>( *loadedParent ),
320 std::make_unique<SCH_SCREEN>() );
321
322 std::unique_ptr<LIB_SYMBOL> derivedCopy = std::make_unique<LIB_SYMBOL>( *loadedDerived );
323 derivedCopy->SetParent( libBuffer.GetSymbol( wxS( "Parent" ) ) );
324 libBuffer.CreateBuffer( std::move( derivedCopy ), std::make_unique<SCH_SCREEN>() );
325 }
326
327 // Verify buffer state before deletion
328 BOOST_CHECK_EQUAL( libBuffer.GetBuffers().size(), 2 );
329 BOOST_CHECK( libBuffer.GetSymbol( wxS( "Parent" ) ) != nullptr );
330 BOOST_CHECK( libBuffer.GetSymbol( wxS( "Derived" ) ) != nullptr );
331
332 // Delete the derived symbol from the buffer
333 std::shared_ptr<SYMBOL_BUFFER> derivedBuf = libBuffer.GetBuffer( wxS( "Derived" ) );
334 BOOST_REQUIRE( derivedBuf != nullptr );
335
336 bool deleteResult = libBuffer.DeleteBuffer( *derivedBuf );
337 BOOST_CHECK( deleteResult );
338
339 // Verify buffer state after deletion
340 BOOST_CHECK_EQUAL( libBuffer.GetBuffers().size(), 1 );
341 BOOST_CHECK( libBuffer.GetSymbol( wxS( "Parent" ) ) != nullptr );
342 BOOST_CHECK( libBuffer.GetSymbol( wxS( "Derived" ) ) == nullptr );
343
344 // Step 4: Save the library using the same pattern as SYMBOL_LIBRARY_MANAGER::SaveLibrary
345 {
346 IO_RELEASER<SCH_IO> plugin( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_KICAD ) );
347 std::map<std::string, UTF8> properties;
348 properties.emplace( SCH_IO_KICAD_SEXPR::PropBuffering, "" );
349
350 // Save remaining buffers (just Parent)
351 for( const std::shared_ptr<SYMBOL_BUFFER>& symbolBuf : libBuffer.GetBuffers() )
352 {
353 libBuffer.SaveBuffer( *symbolBuf, libPath, &*plugin, true );
354 }
355
356 // Delete symbols that were removed from the buffer (this is the fix for the bug)
357 for( const std::shared_ptr<SYMBOL_BUFFER>& deletedBuf : libBuffer.GetDeletedBuffers() )
358 {
359 const wxString& originalName = deletedBuf->GetOriginal().GetName();
360
361 if( plugin->LoadSymbol( libPath, originalName ) )
362 plugin->DeleteSymbol( libPath, originalName, &properties );
363 }
364
365 plugin->SaveLibrary( libPath );
366 libBuffer.ClearDeletedBuffer();
367 }
368
369 // Step 5: Reload the library and verify the derived symbol is gone
370 {
371 IO_RELEASER<SCH_IO> plugin( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_KICAD ) );
372
373 LIB_SYMBOL* parent = plugin->LoadSymbol( libPath, wxS( "Parent" ) );
374 LIB_SYMBOL* derived = plugin->LoadSymbol( libPath, wxS( "Derived" ) );
375
376 BOOST_CHECK( parent != nullptr );
377 // This is the actual check for the bug - the derived symbol should be deleted
378 BOOST_CHECK_MESSAGE( derived == nullptr,
379 "Derived symbol should have been deleted from the library file" );
380 }
381
382 // Cleanup
383 if( wxFileName::DirExists( tempDir ) )
384 {
385 wxFileName::Rmdir( tempDir, wxPATH_RMDIR_RECURSIVE );
386 }
387}
388
389
401BOOST_AUTO_TEST_CASE( SaveLibraryAsToNewFile )
402{
403 wxString tempDir = wxFileName::CreateTempFileName( wxS( "kicad_test_" ) );
404 wxRemoveFile( tempDir );
405 wxFileName::Mkdir( tempDir );
406
407 wxString srcPath = wxFileName( tempDir, wxS( "source.kicad_sym" ) ).GetFullPath();
408 wxString dstPath = wxFileName( tempDir, wxS( "destination.kicad_sym" ) ).GetFullPath();
409
410 // Create a source library with two symbols (one parent, one derived)
411 {
412 IO_RELEASER<SCH_IO> plugin( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_KICAD ) );
413 plugin->CreateLibrary( srcPath );
414
415 auto parent = std::make_unique<LIB_SYMBOL>( wxS( "Parent" ) );
416 parent->GetValueField().SetText( wxS( "Parent" ) );
417 parent->GetReferenceField().SetText( wxS( "U" ) );
418 LIB_SYMBOL* parentPtr = parent.get();
419
420 plugin->SaveSymbol( srcPath, std::make_unique<LIB_SYMBOL>( *parent ) );
421
422 auto derived = std::make_unique<LIB_SYMBOL>( wxS( "Derived" ) );
423 derived->GetValueField().SetText( wxS( "Derived" ) );
424 derived->SetParent( parentPtr );
425
426 plugin->SaveSymbol( srcPath, std::make_unique<LIB_SYMBOL>( *derived ) );
427 plugin->SaveLibrary( srcPath );
428 }
429
430 // Simulate "Save Library As" to a new file that does not exist yet.
431 // This mirrors the else-branch in SYMBOL_LIBRARY_MANAGER::SaveLibrary.
432 {
433 IO_RELEASER<SCH_IO> srcPlugin( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_KICAD ) );
434 IO_RELEASER<SCH_IO> dstPlugin( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_KICAD ) );
435 std::map<std::string, UTF8> properties;
436 properties.emplace( SCH_IO_KICAD_SEXPR::PropBuffering, "" );
437
438 LIB_SYMBOL* loadedParent = srcPlugin->LoadSymbol( srcPath, wxS( "Parent" ) );
439 LIB_SYMBOL* loadedDerived = srcPlugin->LoadSymbol( srcPath, wxS( "Derived" ) );
440 BOOST_REQUIRE( loadedParent != nullptr );
441 BOOST_REQUIRE( loadedDerived != nullptr );
442
443 // Buffer symbols into the destination plugin using the target path (which
444 // does not exist yet). This must not throw.
445 BOOST_CHECK_NO_THROW(
446 dstPlugin->SaveSymbol( dstPath, std::make_unique<LIB_SYMBOL>( *loadedParent ),
447 &properties ) );
448
449 auto newDerived = std::make_unique<LIB_SYMBOL>( *loadedDerived );
450 LIB_SYMBOL* dstParent = dstPlugin->LoadSymbol( dstPath, wxS( "Parent" ), &properties );
451 BOOST_REQUIRE( dstParent != nullptr );
452 newDerived->SetParent( dstParent );
453
454 BOOST_CHECK_NO_THROW(
455 dstPlugin->SaveSymbol( dstPath, std::move( newDerived ), &properties ) );
456
457 BOOST_CHECK_NO_THROW( dstPlugin->SaveLibrary( dstPath ) );
458 }
459
460 // Reload the destination library and verify both symbols are present
461 {
462 IO_RELEASER<SCH_IO> plugin( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_KICAD ) );
463
464 LIB_SYMBOL* parent = plugin->LoadSymbol( dstPath, wxS( "Parent" ) );
465 LIB_SYMBOL* derived = plugin->LoadSymbol( dstPath, wxS( "Derived" ) );
466
467 BOOST_CHECK_MESSAGE( parent != nullptr,
468 "Parent symbol should exist in the saved-as library" );
469 BOOST_CHECK_MESSAGE( derived != nullptr,
470 "Derived symbol should exist in the saved-as library" );
471
472 if( derived )
473 BOOST_CHECK( derived->IsDerived() );
474 }
475
476 if( wxFileName::DirExists( tempDir ) )
477 wxFileName::Rmdir( tempDir, wxPATH_RMDIR_RECURSIVE );
478}
479
480
490BOOST_AUTO_TEST_CASE( SetParentKeepsParentNameInSync )
491{
492 auto parent = std::make_unique<LIB_SYMBOL>( wxS( "BaseSymbol" ) );
493
494 LIB_SYMBOL derived( wxS( "DerivedSymbol" ) );
495 BOOST_CHECK( derived.GetParentName().IsEmpty() );
496
497 derived.SetParent( parent.get() );
498 BOOST_CHECK( derived.IsDerived() );
499 BOOST_CHECK_EQUAL( derived.GetParentName(), wxS( "BaseSymbol" ) );
500
501 // Detaching the live pointer must not erase the recorded name, so a derived symbol whose live
502 // parent has been lost can still be serialized by name.
503 derived.SetParent( static_cast<LIB_SYMBOL*>( nullptr ) );
504 BOOST_CHECK_EQUAL( derived.GetParent().lock(), nullptr );
505 BOOST_CHECK_EQUAL( derived.GetParentName(), wxS( "BaseSymbol" ) );
506}
507
508
521BOOST_AUTO_TEST_CASE( SerializeDerivedSymbolUsesParentName )
522{
523 auto parent = std::make_unique<LIB_SYMBOL>( wxS( "BaseSymbol" ) );
524 parent->GetValueField().SetText( wxS( "BaseSymbol" ) );
525 parent->GetReferenceField().SetText( wxS( "U" ) );
526
527 LIB_SYMBOL derived( wxS( "DerivedSymbol" ) );
528 derived.SetParent( parent.get() );
529 derived.GetValueField().SetText( wxS( "DerivedSymbol" ) );
530
531 BOOST_REQUIRE( derived.IsDerived() );
532
533 // Force the recorded name to differ from the live parent's name. The writer must emit the
534 // recorded name, proving it does not read through the live (potentially dangling) pointer.
535 derived.SetParentName( wxS( "RecordedParent" ) );
536
537 STRING_FORMATTER formatter;
538
539 BOOST_REQUIRE_NO_THROW(
540 SCH_IO_KICAD_SEXPR_LIB_CACHE::SaveSymbol( &derived, formatter, wxEmptyString, true ) );
541
542 const std::string out = formatter.GetString();
543
544 BOOST_CHECK_MESSAGE( out.find( "(extends \"RecordedParent\")" ) != std::string::npos,
545 "Derived symbol should serialize (extends ...) using the recorded parent name" );
546}
547
548
555BOOST_AUTO_TEST_CASE( SaveCopyAsDerivedSymbolToNewLibrary )
556{
557 wxString tempDir = wxFileName::CreateTempFileName( wxS( "kicad_test_" ) );
558 wxRemoveFile( tempDir );
559 wxFileName::Mkdir( tempDir );
560
561 wxString dstPath = wxFileName( tempDir, wxS( "copy_target.kicad_sym" ) ).GetFullPath();
562
563 // Build an in-memory parent/derived pair like the symbol editor holds while editing.
564 auto parent = std::make_unique<LIB_SYMBOL>( wxS( "BaseSymbol" ) );
565 parent->GetValueField().SetText( wxS( "BaseSymbol" ) );
566 parent->GetReferenceField().SetText( wxS( "U" ) );
567
568 auto derived = std::make_unique<LIB_SYMBOL>( wxS( "DerivedSymbol" ) );
569 derived->SetParent( parent.get() );
570 derived->SetParentName( parent->GetName() );
571 derived->GetValueField().SetText( wxS( "DerivedSymbol" ) );
572
573 // Save the parent then the child to a brand new library, mirroring SYMBOL_SAVE_AS_HANDLER.
574 {
575 IO_RELEASER<SCH_IO> plugin( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_KICAD ) );
576 std::map<std::string, UTF8> properties;
577 properties.emplace( SCH_IO_KICAD_SEXPR::PropBuffering, "" );
578
579 BOOST_CHECK_NO_THROW(
580 plugin->SaveSymbol( dstPath, std::make_unique<LIB_SYMBOL>( *parent ), &properties ) );
581
582 LIB_SYMBOL* dstParent = plugin->LoadSymbol( dstPath, wxS( "BaseSymbol" ), &properties );
583 BOOST_REQUIRE( dstParent != nullptr );
584
585 auto newChild = std::make_unique<LIB_SYMBOL>( *derived );
586 newChild->SetParent( dstParent );
587 newChild->SetParentName( dstParent->GetName() );
588
589 BOOST_CHECK_NO_THROW( plugin->SaveSymbol( dstPath, std::move( newChild ), &properties ) );
590 BOOST_CHECK_NO_THROW( plugin->SaveLibrary( dstPath ) );
591 }
592
593 // Reload and verify the derived symbol survived with a valid parent.
594 {
595 IO_RELEASER<SCH_IO> plugin( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_KICAD ) );
596
597 LIB_SYMBOL* reloadedParent = plugin->LoadSymbol( dstPath, wxS( "BaseSymbol" ) );
598 LIB_SYMBOL* reloadedChild = plugin->LoadSymbol( dstPath, wxS( "DerivedSymbol" ) );
599
600 BOOST_CHECK( reloadedParent != nullptr );
601 BOOST_REQUIRE( reloadedChild != nullptr );
602 BOOST_CHECK( reloadedChild->IsDerived() );
603 BOOST_CHECK_EQUAL( reloadedChild->GetParent().lock().get(), reloadedParent );
604 }
605
606 if( wxFileName::DirExists( tempDir ) )
607 wxFileName::Rmdir( tempDir, wxPATH_RMDIR_RECURSIVE );
608}
609
610
619BOOST_AUTO_TEST_CASE( DerivedSymbolNamesForUnbufferedSymbol )
620{
621 LIB_BUFFER libBuffer( wxS( "TestLibrary" ) );
622
623 auto parent = std::make_unique<LIB_SYMBOL>( wxS( "Parent" ) );
624 LIB_SYMBOL& parentRef = *parent;
625
626 libBuffer.CreateBuffer( std::move( parent ), std::make_unique<SCH_SCREEN>() );
627
628 auto child = std::make_unique<LIB_SYMBOL>( wxS( "Child" ) );
629 child->SetParent( &parentRef );
630 libBuffer.CreateBuffer( std::move( child ), std::make_unique<SCH_SCREEN>() );
631
632 wxArrayString names;
633
634 BOOST_CHECK_EQUAL( libBuffer.GetDerivedSymbolNames( wxS( "NeverBuffered" ), names ), 0 );
635 BOOST_CHECK( names.IsEmpty() );
636
637 // Deleting a symbol drops it from the buffer while the tree selection and the open symbol can
638 // still name it.
639 std::shared_ptr<SYMBOL_BUFFER> parentBuf = libBuffer.GetBuffer( wxS( "Parent" ) );
640 BOOST_REQUIRE( parentBuf != nullptr );
641 BOOST_CHECK( libBuffer.DeleteBuffer( *parentBuf ) );
642 BOOST_CHECK( libBuffer.GetSymbol( wxS( "Parent" ) ) == nullptr );
643
644 names.Clear();
645 BOOST_CHECK_EQUAL( libBuffer.GetDerivedSymbolNames( wxS( "Parent" ), names ), 0 );
646 BOOST_CHECK( names.IsEmpty() );
647}
648
649
658BOOST_AUTO_TEST_CASE( DerivedSymbolNamesAcrossRebufferedParent )
659{
660 // The originals, as the library adapter owns them. The buffer only ever holds copies.
661 auto originalRoot = std::make_unique<LIB_SYMBOL>( wxS( "Root" ) );
662 auto originalMid = std::make_unique<LIB_SYMBOL>( wxS( "Mid" ) );
663 auto originalLeaf = std::make_unique<LIB_SYMBOL>( wxS( "Leaf" ) );
664
665 originalMid->SetParent( originalRoot.get() );
666 originalLeaf->SetParent( originalMid.get() );
667
668 LIB_BUFFER libBuffer( wxS( "TestLibrary" ) );
669
670 // Reaching Leaf first buffers a copy of Mid whose parent link still points at the unbuffered
671 // original Root.
672 auto midCopy = std::make_unique<LIB_SYMBOL>( *originalMid );
673 LIB_SYMBOL* midCopyPtr = midCopy.get();
674
675 libBuffer.CreateBuffer( std::move( midCopy ), std::make_unique<SCH_SCREEN>() );
676
677 auto leafCopy = std::make_unique<LIB_SYMBOL>( *originalLeaf );
678 leafCopy->SetParent( midCopyPtr );
679 libBuffer.CreateBuffer( std::move( leafCopy ), std::make_unique<SCH_SCREEN>() );
680
681 auto rootCopy = std::make_unique<LIB_SYMBOL>( *originalRoot );
682 LIB_SYMBOL* rootCopyPtr = rootCopy.get();
683
684 libBuffer.CreateBuffer( std::move( rootCopy ), std::make_unique<SCH_SCREEN>() );
685
686 // Reaching Mid itself then buffers a second copy, this time parented to the buffered Root.
687 // The duplicate entry is a separate getLibraryBuffer() defect; the walk has to tolerate it.
688 auto midDuplicate = std::make_unique<LIB_SYMBOL>( *originalMid );
689 midDuplicate->SetParent( rootCopyPtr );
690 libBuffer.CreateBuffer( std::move( midDuplicate ), std::make_unique<SCH_SCREEN>() );
691
692 wxArrayString names;
693
694 BOOST_CHECK_EQUAL( libBuffer.GetDerivedSymbolNames( wxS( "Root" ), names ), 2 );
695 BOOST_CHECK( names.Index( wxS( "Mid" ) ) != wxNOT_FOUND );
696 BOOST_CHECK( names.Index( wxS( "Leaf" ) ) != wxNOT_FOUND );
697
698 // HasDerivedSymbols() has always matched on the name. The two have to agree or DeleteBuffer()
699 // reports failure and orphans the children it did not remove.
700 BOOST_CHECK( libBuffer.HasDerivedSymbols( wxS( "Root" ) ) );
701 BOOST_CHECK( libBuffer.HasDerivedSymbols( wxS( "Mid" ) ) );
702}
703
704
713BOOST_AUTO_TEST_CASE( DerivedSymbolNamesExcludeRootFromNameCycle )
714{
715 // Unbuffered originals that close the cycle by name
716 auto originalA = std::make_unique<LIB_SYMBOL>( wxS( "A" ) );
717 auto originalB = std::make_unique<LIB_SYMBOL>( wxS( "B" ) );
718
719 LIB_BUFFER libBuffer( wxS( "TestLibrary" ) );
720
721 auto symbolA = std::make_unique<LIB_SYMBOL>( wxS( "A" ) );
722 symbolA->SetParent( originalB.get() );
723 libBuffer.CreateBuffer( std::move( symbolA ), std::make_unique<SCH_SCREEN>() );
724
725 auto symbolB = std::make_unique<LIB_SYMBOL>( wxS( "B" ) );
726 symbolB->SetParent( originalA.get() );
727 libBuffer.CreateBuffer( std::move( symbolB ), std::make_unique<SCH_SCREEN>() );
728
729 wxArrayString names;
730
731 BOOST_CHECK_EQUAL( libBuffer.GetDerivedSymbolNames( wxS( "A" ), names ), 1 );
732 BOOST_REQUIRE_EQUAL( names.GetCount(), 1 );
733 BOOST_CHECK_EQUAL( names[0], wxS( "B" ) );
734
735 std::shared_ptr<SYMBOL_BUFFER> bufferA = libBuffer.GetBuffer( wxS( "A" ) );
736 BOOST_REQUIRE( bufferA != nullptr );
737 BOOST_CHECK( libBuffer.DeleteBuffer( *bufferA ) );
738 BOOST_CHECK( libBuffer.GetBuffers().empty() );
739}
740
741
void SetContentModified(bool aModified=true)
Definition base_screen.h:55
Store a working copy of a library.
bool CreateBuffer(std::unique_ptr< LIB_SYMBOL > aCopy, std::unique_ptr< SCH_SCREEN > aScreen, bool aNew=false)
Create a new buffer to store a symbol. LIB_BUFFER takes ownership of aCopy.
bool IsModified() const
bool DeleteBuffer(const SYMBOL_BUFFER &aSymbolBuf)
Delete the given symbol buffer from the library buffer.
void GetSymbolNames(wxArrayString &aSymbolNames, SYMBOL_NAME_FILTER aFilter=SYMBOL_NAME_FILTER::ALL)
Fetch a list of root symbols names from the library buffer.
const std::deque< std::shared_ptr< SYMBOL_BUFFER > > & GetDeletedBuffers() const
Return the deleted symbol buffers that need to be removed from the library file.
size_t GetDerivedSymbolNames(const wxString &aSymbolName, wxArrayString &aList) const
Fetch all of the symbols derived from a aSymbolName into aList.
bool SaveBuffer(SYMBOL_BUFFER &aSymbolBuf, const wxString &aFileName, SCH_IO *aPlugin, bool aBuffer)
Save stored modifications using a plugin.
const std::deque< std::shared_ptr< SYMBOL_BUFFER > > & GetBuffers() const
Return all buffered symbols.
std::shared_ptr< SYMBOL_BUFFER > GetBuffer(const wxString &aAlias) const
Return a symbol buffer with LIB_SYMBOL holding a symbolic alias.
LIB_SYMBOL * GetSymbol(const wxString &aAlias) const
Return the working copy of a LIB_SYMBOL root object with specified alias.
bool HasDerivedSymbols(const wxString &aParentName) const
Check to see any symbols in the buffer are derived from a parent named aParentName.
bool UpdateBuffer(SYMBOL_BUFFER &aSymbolBuf, const LIB_SYMBOL &aCopy)
Update the buffered symbol with the contents of aCopy.
static std::unique_ptr< LIB_SYMBOL > CreateSymbol(const NEW_SYMBOL_PROPERTIES &aProps, LIB_SYMBOL *aParent)
Define a library symbol object.
Definition lib_symbol.h:114
SCH_FIELD & GetDescriptionField()
Definition lib_symbol.h:463
std::weak_ptr< LIB_SYMBOL > & GetParent()
Definition lib_symbol.h:152
bool IsDerived() const
Definition lib_symbol.h:241
void SetParent(LIB_SYMBOL *aParent=nullptr)
wxString GetName() const override
Definition lib_symbol.h:183
SCH_FIELD & GetValueField()
Definition lib_symbol.h:447
const wxString & GetParentName() const
Definition lib_symbol.h:995
void SetParentName(const wxString &aParentName)
Definition lib_symbol.h:994
void SetText(const wxString &aText) override
static void SaveSymbol(LIB_SYMBOL *aSymbol, OUTPUTFORMATTER &aFormatter, const wxString &aLibName=wxEmptyString, bool aIncludeData=true)
static const char * PropBuffering
The property used internally by the plugin to enable cache buffering which prevents the library file ...
Implement an OUTPUTFORMATTER to a memory buffer.
Definition richio.h:430
const std::string & GetString()
Definition richio.h:453
LIB_SYMBOL & GetSymbol() const
SCH_SCREEN * GetScreen() const
LIB_SYMBOL & GetOriginal() const
void SetOriginal(std::unique_ptr< LIB_SYMBOL > aSymbol)
std::unique_ptr< T > IO_RELEASER
Helper to hold and release an IO_BASE object when exceptions are thrown.
Definition io_mgr.h:33
@ USER
The field ID hasn't been set yet; field is invalid.
BOOST_AUTO_TEST_CASE(HorizontalAlignment)
BOOST_REQUIRE(intersection.has_value()==c.ExpectedIntersection.has_value())
BOOST_AUTO_TEST_SUITE_END()
BOOST_AUTO_TEST_CASE(SymbolBuffer)
Test the SYMBOL_BUFFER object.
BOOST_CHECK_EQUAL(result, "25.4")