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
234BOOST_AUTO_TEST_CASE( DeletedSymbolsAreRemovedFromFile )
235{
236 // Create a temporary directory and library file
237 wxString tempDir = wxFileName::CreateTempFileName( wxS( "kicad_test_" ) );
238 wxRemoveFile( tempDir );
239 wxFileName::Mkdir( tempDir );
240 wxString libPath = wxFileName( tempDir, wxS( "test_lib.kicad_sym" ) ).GetFullPath();
241
242 // Step 1: Create a library with a parent and derived symbol using the plugin directly
243 {
244 IO_RELEASER<SCH_IO> plugin( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_KICAD ) );
245 plugin->CreateLibrary( libPath );
246
247 // Create parent symbol
248 std::unique_ptr<LIB_SYMBOL> parentSymbol = std::make_unique<LIB_SYMBOL>( wxS( "Parent" ) );
249 parentSymbol->GetValueField().SetText( wxS( "Parent" ) );
250 parentSymbol->GetReferenceField().SetText( wxS( "U" ) );
251
252 LIB_SYMBOL* parentPtr = parentSymbol.get();
253 plugin->SaveSymbol( libPath, std::make_unique<LIB_SYMBOL>( *parentSymbol ) );
254
255 // Create derived symbol
256 std::unique_ptr<LIB_SYMBOL> derivedSymbol = std::make_unique<LIB_SYMBOL>( wxS( "Derived" ) );
257 derivedSymbol->GetValueField().SetText( wxS( "Derived" ) );
258 derivedSymbol->SetParent( parentPtr );
259
260 plugin->SaveSymbol( libPath, std::make_unique<LIB_SYMBOL>( *derivedSymbol ) );
261 plugin->SaveLibrary( libPath );
262 }
263
264 // Step 2: Verify both symbols exist in the library
265 {
266 IO_RELEASER<SCH_IO> plugin( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_KICAD ) );
267 LIB_SYMBOL* parent = plugin->LoadSymbol( libPath, wxS( "Parent" ) );
268 LIB_SYMBOL* derived = plugin->LoadSymbol( libPath, wxS( "Derived" ) );
269
270 BOOST_REQUIRE( parent != nullptr );
271 BOOST_REQUIRE( derived != nullptr );
272 BOOST_CHECK( derived->IsDerived() );
273 }
274
275 // Step 3: Load symbols into LIB_BUFFER and delete the derived symbol
276 LIB_BUFFER libBuffer( wxS( "TestLibrary" ) );
277
278 {
279 IO_RELEASER<SCH_IO> plugin( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_KICAD ) );
280 LIB_SYMBOL* loadedParent = plugin->LoadSymbol( libPath, wxS( "Parent" ) );
281 LIB_SYMBOL* loadedDerived = plugin->LoadSymbol( libPath, wxS( "Derived" ) );
282
283 BOOST_REQUIRE( loadedParent != nullptr );
284 BOOST_REQUIRE( loadedDerived != nullptr );
285
286 // Set up parent relationship
287 loadedDerived->SetParent( loadedParent );
288
289 // Create buffers (parent first, then derived)
290 libBuffer.CreateBuffer( std::make_unique<LIB_SYMBOL>( *loadedParent ),
291 std::make_unique<SCH_SCREEN>() );
292
293 std::unique_ptr<LIB_SYMBOL> derivedCopy = std::make_unique<LIB_SYMBOL>( *loadedDerived );
294 derivedCopy->SetParent( libBuffer.GetSymbol( wxS( "Parent" ) ) );
295 libBuffer.CreateBuffer( std::move( derivedCopy ), std::make_unique<SCH_SCREEN>() );
296 }
297
298 // Verify buffer state before deletion
299 BOOST_CHECK_EQUAL( libBuffer.GetBuffers().size(), 2 );
300 BOOST_CHECK( libBuffer.GetSymbol( wxS( "Parent" ) ) != nullptr );
301 BOOST_CHECK( libBuffer.GetSymbol( wxS( "Derived" ) ) != nullptr );
302
303 // Delete the derived symbol from the buffer
304 std::shared_ptr<SYMBOL_BUFFER> derivedBuf = libBuffer.GetBuffer( wxS( "Derived" ) );
305 BOOST_REQUIRE( derivedBuf != nullptr );
306
307 bool deleteResult = libBuffer.DeleteBuffer( *derivedBuf );
308 BOOST_CHECK( deleteResult );
309
310 // Verify buffer state after deletion
311 BOOST_CHECK_EQUAL( libBuffer.GetBuffers().size(), 1 );
312 BOOST_CHECK( libBuffer.GetSymbol( wxS( "Parent" ) ) != nullptr );
313 BOOST_CHECK( libBuffer.GetSymbol( wxS( "Derived" ) ) == nullptr );
314
315 // Step 4: Save the library using the same pattern as SYMBOL_LIBRARY_MANAGER::SaveLibrary
316 {
317 IO_RELEASER<SCH_IO> plugin( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_KICAD ) );
318 std::map<std::string, UTF8> properties;
319 properties.emplace( SCH_IO_KICAD_SEXPR::PropBuffering, "" );
320
321 // Save remaining buffers (just Parent)
322 for( const std::shared_ptr<SYMBOL_BUFFER>& symbolBuf : libBuffer.GetBuffers() )
323 {
324 libBuffer.SaveBuffer( *symbolBuf, libPath, &*plugin, true );
325 }
326
327 // Delete symbols that were removed from the buffer (this is the fix for the bug)
328 for( const std::shared_ptr<SYMBOL_BUFFER>& deletedBuf : libBuffer.GetDeletedBuffers() )
329 {
330 const wxString& originalName = deletedBuf->GetOriginal().GetName();
331
332 if( plugin->LoadSymbol( libPath, originalName ) )
333 plugin->DeleteSymbol( libPath, originalName, &properties );
334 }
335
336 plugin->SaveLibrary( libPath );
337 libBuffer.ClearDeletedBuffer();
338 }
339
340 // Step 5: Reload the library and verify the derived symbol is gone
341 {
342 IO_RELEASER<SCH_IO> plugin( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_KICAD ) );
343
344 LIB_SYMBOL* parent = plugin->LoadSymbol( libPath, wxS( "Parent" ) );
345 LIB_SYMBOL* derived = plugin->LoadSymbol( libPath, wxS( "Derived" ) );
346
347 BOOST_CHECK( parent != nullptr );
348 // This is the actual check for the bug - the derived symbol should be deleted
349 BOOST_CHECK_MESSAGE( derived == nullptr,
350 "Derived symbol should have been deleted from the library file" );
351 }
352
353 // Cleanup
354 if( wxFileName::DirExists( tempDir ) )
355 {
356 wxFileName::Rmdir( tempDir, wxPATH_RMDIR_RECURSIVE );
357 }
358}
359
360
372BOOST_AUTO_TEST_CASE( SaveLibraryAsToNewFile )
373{
374 wxString tempDir = wxFileName::CreateTempFileName( wxS( "kicad_test_" ) );
375 wxRemoveFile( tempDir );
376 wxFileName::Mkdir( tempDir );
377
378 wxString srcPath = wxFileName( tempDir, wxS( "source.kicad_sym" ) ).GetFullPath();
379 wxString dstPath = wxFileName( tempDir, wxS( "destination.kicad_sym" ) ).GetFullPath();
380
381 // Create a source library with two symbols (one parent, one derived)
382 {
383 IO_RELEASER<SCH_IO> plugin( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_KICAD ) );
384 plugin->CreateLibrary( srcPath );
385
386 auto parent = std::make_unique<LIB_SYMBOL>( wxS( "Parent" ) );
387 parent->GetValueField().SetText( wxS( "Parent" ) );
388 parent->GetReferenceField().SetText( wxS( "U" ) );
389 LIB_SYMBOL* parentPtr = parent.get();
390
391 plugin->SaveSymbol( srcPath, std::make_unique<LIB_SYMBOL>( *parent ) );
392
393 auto derived = std::make_unique<LIB_SYMBOL>( wxS( "Derived" ) );
394 derived->GetValueField().SetText( wxS( "Derived" ) );
395 derived->SetParent( parentPtr );
396
397 plugin->SaveSymbol( srcPath, std::make_unique<LIB_SYMBOL>( *derived ) );
398 plugin->SaveLibrary( srcPath );
399 }
400
401 // Simulate "Save Library As" to a new file that does not exist yet.
402 // This mirrors the else-branch in SYMBOL_LIBRARY_MANAGER::SaveLibrary.
403 {
404 IO_RELEASER<SCH_IO> srcPlugin( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_KICAD ) );
405 IO_RELEASER<SCH_IO> dstPlugin( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_KICAD ) );
406 std::map<std::string, UTF8> properties;
407 properties.emplace( SCH_IO_KICAD_SEXPR::PropBuffering, "" );
408
409 LIB_SYMBOL* loadedParent = srcPlugin->LoadSymbol( srcPath, wxS( "Parent" ) );
410 LIB_SYMBOL* loadedDerived = srcPlugin->LoadSymbol( srcPath, wxS( "Derived" ) );
411 BOOST_REQUIRE( loadedParent != nullptr );
412 BOOST_REQUIRE( loadedDerived != nullptr );
413
414 // Buffer symbols into the destination plugin using the target path (which
415 // does not exist yet). This must not throw.
416 BOOST_CHECK_NO_THROW(
417 dstPlugin->SaveSymbol( dstPath, std::make_unique<LIB_SYMBOL>( *loadedParent ),
418 &properties ) );
419
420 auto newDerived = std::make_unique<LIB_SYMBOL>( *loadedDerived );
421 LIB_SYMBOL* dstParent = dstPlugin->LoadSymbol( dstPath, wxS( "Parent" ), &properties );
422 BOOST_REQUIRE( dstParent != nullptr );
423 newDerived->SetParent( dstParent );
424
425 BOOST_CHECK_NO_THROW(
426 dstPlugin->SaveSymbol( dstPath, std::move( newDerived ), &properties ) );
427
428 BOOST_CHECK_NO_THROW( dstPlugin->SaveLibrary( dstPath ) );
429 }
430
431 // Reload the destination library and verify both symbols are present
432 {
433 IO_RELEASER<SCH_IO> plugin( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_KICAD ) );
434
435 LIB_SYMBOL* parent = plugin->LoadSymbol( dstPath, wxS( "Parent" ) );
436 LIB_SYMBOL* derived = plugin->LoadSymbol( dstPath, wxS( "Derived" ) );
437
438 BOOST_CHECK_MESSAGE( parent != nullptr,
439 "Parent symbol should exist in the saved-as library" );
440 BOOST_CHECK_MESSAGE( derived != nullptr,
441 "Derived symbol should exist in the saved-as library" );
442
443 if( derived )
444 BOOST_CHECK( derived->IsDerived() );
445 }
446
447 if( wxFileName::DirExists( tempDir ) )
448 wxFileName::Rmdir( tempDir, wxPATH_RMDIR_RECURSIVE );
449}
450
451
461BOOST_AUTO_TEST_CASE( SetParentKeepsParentNameInSync )
462{
463 auto parent = std::make_unique<LIB_SYMBOL>( wxS( "BaseSymbol" ) );
464
465 LIB_SYMBOL derived( wxS( "DerivedSymbol" ) );
466 BOOST_CHECK( derived.GetParentName().IsEmpty() );
467
468 derived.SetParent( parent.get() );
469 BOOST_CHECK( derived.IsDerived() );
470 BOOST_CHECK_EQUAL( derived.GetParentName(), wxS( "BaseSymbol" ) );
471
472 // Detaching the live pointer must not erase the recorded name, so a derived symbol whose live
473 // parent has been lost can still be serialized by name.
474 derived.SetParent( static_cast<LIB_SYMBOL*>( nullptr ) );
475 BOOST_CHECK_EQUAL( derived.GetParent().lock(), nullptr );
476 BOOST_CHECK_EQUAL( derived.GetParentName(), wxS( "BaseSymbol" ) );
477}
478
479
492BOOST_AUTO_TEST_CASE( SerializeDerivedSymbolUsesParentName )
493{
494 auto parent = std::make_unique<LIB_SYMBOL>( wxS( "BaseSymbol" ) );
495 parent->GetValueField().SetText( wxS( "BaseSymbol" ) );
496 parent->GetReferenceField().SetText( wxS( "U" ) );
497
498 LIB_SYMBOL derived( wxS( "DerivedSymbol" ) );
499 derived.SetParent( parent.get() );
500 derived.GetValueField().SetText( wxS( "DerivedSymbol" ) );
501
502 BOOST_REQUIRE( derived.IsDerived() );
503
504 // Force the recorded name to differ from the live parent's name. The writer must emit the
505 // recorded name, proving it does not read through the live (potentially dangling) pointer.
506 derived.SetParentName( wxS( "RecordedParent" ) );
507
508 STRING_FORMATTER formatter;
509
510 BOOST_REQUIRE_NO_THROW(
511 SCH_IO_KICAD_SEXPR_LIB_CACHE::SaveSymbol( &derived, formatter, wxEmptyString, true ) );
512
513 const std::string out = formatter.GetString();
514
515 BOOST_CHECK_MESSAGE( out.find( "(extends \"RecordedParent\")" ) != std::string::npos,
516 "Derived symbol should serialize (extends ...) using the recorded parent name" );
517}
518
519
526BOOST_AUTO_TEST_CASE( SaveCopyAsDerivedSymbolToNewLibrary )
527{
528 wxString tempDir = wxFileName::CreateTempFileName( wxS( "kicad_test_" ) );
529 wxRemoveFile( tempDir );
530 wxFileName::Mkdir( tempDir );
531
532 wxString dstPath = wxFileName( tempDir, wxS( "copy_target.kicad_sym" ) ).GetFullPath();
533
534 // Build an in-memory parent/derived pair like the symbol editor holds while editing.
535 auto parent = std::make_unique<LIB_SYMBOL>( wxS( "BaseSymbol" ) );
536 parent->GetValueField().SetText( wxS( "BaseSymbol" ) );
537 parent->GetReferenceField().SetText( wxS( "U" ) );
538
539 auto derived = std::make_unique<LIB_SYMBOL>( wxS( "DerivedSymbol" ) );
540 derived->SetParent( parent.get() );
541 derived->SetParentName( parent->GetName() );
542 derived->GetValueField().SetText( wxS( "DerivedSymbol" ) );
543
544 // Save the parent then the child to a brand new library, mirroring SYMBOL_SAVE_AS_HANDLER.
545 {
546 IO_RELEASER<SCH_IO> plugin( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_KICAD ) );
547 std::map<std::string, UTF8> properties;
548 properties.emplace( SCH_IO_KICAD_SEXPR::PropBuffering, "" );
549
550 BOOST_CHECK_NO_THROW(
551 plugin->SaveSymbol( dstPath, std::make_unique<LIB_SYMBOL>( *parent ), &properties ) );
552
553 LIB_SYMBOL* dstParent = plugin->LoadSymbol( dstPath, wxS( "BaseSymbol" ), &properties );
554 BOOST_REQUIRE( dstParent != nullptr );
555
556 auto newChild = std::make_unique<LIB_SYMBOL>( *derived );
557 newChild->SetParent( dstParent );
558 newChild->SetParentName( dstParent->GetName() );
559
560 BOOST_CHECK_NO_THROW( plugin->SaveSymbol( dstPath, std::move( newChild ), &properties ) );
561 BOOST_CHECK_NO_THROW( plugin->SaveLibrary( dstPath ) );
562 }
563
564 // Reload and verify the derived symbol survived with a valid parent.
565 {
566 IO_RELEASER<SCH_IO> plugin( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_KICAD ) );
567
568 LIB_SYMBOL* reloadedParent = plugin->LoadSymbol( dstPath, wxS( "BaseSymbol" ) );
569 LIB_SYMBOL* reloadedChild = plugin->LoadSymbol( dstPath, wxS( "DerivedSymbol" ) );
570
571 BOOST_CHECK( reloadedParent != nullptr );
572 BOOST_REQUIRE( reloadedChild != nullptr );
573 BOOST_CHECK( reloadedChild->IsDerived() );
574 BOOST_CHECK_EQUAL( reloadedChild->GetParent().lock().get(), reloadedParent );
575 }
576
577 if( wxFileName::DirExists( tempDir ) )
578 wxFileName::Rmdir( tempDir, wxPATH_RMDIR_RECURSIVE );
579}
580
581
590BOOST_AUTO_TEST_CASE( DerivedSymbolNamesForUnbufferedSymbol )
591{
592 LIB_BUFFER libBuffer( wxS( "TestLibrary" ) );
593
594 auto parent = std::make_unique<LIB_SYMBOL>( wxS( "Parent" ) );
595 LIB_SYMBOL& parentRef = *parent;
596
597 libBuffer.CreateBuffer( std::move( parent ), std::make_unique<SCH_SCREEN>() );
598
599 auto child = std::make_unique<LIB_SYMBOL>( wxS( "Child" ) );
600 child->SetParent( &parentRef );
601 libBuffer.CreateBuffer( std::move( child ), std::make_unique<SCH_SCREEN>() );
602
603 wxArrayString names;
604
605 BOOST_CHECK_EQUAL( libBuffer.GetDerivedSymbolNames( wxS( "NeverBuffered" ), names ), 0 );
606 BOOST_CHECK( names.IsEmpty() );
607
608 // Deleting a symbol drops it from the buffer while the tree selection and the open symbol can
609 // still name it.
610 std::shared_ptr<SYMBOL_BUFFER> parentBuf = libBuffer.GetBuffer( wxS( "Parent" ) );
611 BOOST_REQUIRE( parentBuf != nullptr );
612 BOOST_CHECK( libBuffer.DeleteBuffer( *parentBuf ) );
613 BOOST_CHECK( libBuffer.GetSymbol( wxS( "Parent" ) ) == nullptr );
614
615 names.Clear();
616 BOOST_CHECK_EQUAL( libBuffer.GetDerivedSymbolNames( wxS( "Parent" ), names ), 0 );
617 BOOST_CHECK( names.IsEmpty() );
618}
619
620
629BOOST_AUTO_TEST_CASE( DerivedSymbolNamesAcrossRebufferedParent )
630{
631 // The originals, as the library adapter owns them. The buffer only ever holds copies.
632 auto originalRoot = std::make_unique<LIB_SYMBOL>( wxS( "Root" ) );
633 auto originalMid = std::make_unique<LIB_SYMBOL>( wxS( "Mid" ) );
634 auto originalLeaf = std::make_unique<LIB_SYMBOL>( wxS( "Leaf" ) );
635
636 originalMid->SetParent( originalRoot.get() );
637 originalLeaf->SetParent( originalMid.get() );
638
639 LIB_BUFFER libBuffer( wxS( "TestLibrary" ) );
640
641 // Reaching Leaf first buffers a copy of Mid whose parent link still points at the unbuffered
642 // original Root.
643 auto midCopy = std::make_unique<LIB_SYMBOL>( *originalMid );
644 LIB_SYMBOL* midCopyPtr = midCopy.get();
645
646 libBuffer.CreateBuffer( std::move( midCopy ), std::make_unique<SCH_SCREEN>() );
647
648 auto leafCopy = std::make_unique<LIB_SYMBOL>( *originalLeaf );
649 leafCopy->SetParent( midCopyPtr );
650 libBuffer.CreateBuffer( std::move( leafCopy ), std::make_unique<SCH_SCREEN>() );
651
652 auto rootCopy = std::make_unique<LIB_SYMBOL>( *originalRoot );
653 LIB_SYMBOL* rootCopyPtr = rootCopy.get();
654
655 libBuffer.CreateBuffer( std::move( rootCopy ), std::make_unique<SCH_SCREEN>() );
656
657 // Reaching Mid itself then buffers a second copy, this time parented to the buffered Root.
658 // The duplicate entry is a separate getLibraryBuffer() defect; the walk has to tolerate it.
659 auto midDuplicate = std::make_unique<LIB_SYMBOL>( *originalMid );
660 midDuplicate->SetParent( rootCopyPtr );
661 libBuffer.CreateBuffer( std::move( midDuplicate ), std::make_unique<SCH_SCREEN>() );
662
663 wxArrayString names;
664
665 BOOST_CHECK_EQUAL( libBuffer.GetDerivedSymbolNames( wxS( "Root" ), names ), 2 );
666 BOOST_CHECK( names.Index( wxS( "Mid" ) ) != wxNOT_FOUND );
667 BOOST_CHECK( names.Index( wxS( "Leaf" ) ) != wxNOT_FOUND );
668
669 // HasDerivedSymbols() has always matched on the name. The two have to agree or DeleteBuffer()
670 // reports failure and orphans the children it did not remove.
671 BOOST_CHECK( libBuffer.HasDerivedSymbols( wxS( "Root" ) ) );
672 BOOST_CHECK( libBuffer.HasDerivedSymbols( wxS( "Mid" ) ) );
673}
674
675
684BOOST_AUTO_TEST_CASE( DerivedSymbolNamesExcludeRootFromNameCycle )
685{
686 // Unbuffered originals that close the cycle by name
687 auto originalA = std::make_unique<LIB_SYMBOL>( wxS( "A" ) );
688 auto originalB = std::make_unique<LIB_SYMBOL>( wxS( "B" ) );
689
690 LIB_BUFFER libBuffer( wxS( "TestLibrary" ) );
691
692 auto symbolA = std::make_unique<LIB_SYMBOL>( wxS( "A" ) );
693 symbolA->SetParent( originalB.get() );
694 libBuffer.CreateBuffer( std::move( symbolA ), std::make_unique<SCH_SCREEN>() );
695
696 auto symbolB = std::make_unique<LIB_SYMBOL>( wxS( "B" ) );
697 symbolB->SetParent( originalA.get() );
698 libBuffer.CreateBuffer( std::move( symbolB ), std::make_unique<SCH_SCREEN>() );
699
700 wxArrayString names;
701
702 BOOST_CHECK_EQUAL( libBuffer.GetDerivedSymbolNames( wxS( "A" ), names ), 1 );
703 BOOST_REQUIRE_EQUAL( names.GetCount(), 1 );
704 BOOST_CHECK_EQUAL( names[0], wxS( "B" ) );
705
706 std::shared_ptr<SYMBOL_BUFFER> bufferA = libBuffer.GetBuffer( wxS( "A" ) );
707 BOOST_REQUIRE( bufferA != nullptr );
708 BOOST_CHECK( libBuffer.DeleteBuffer( *bufferA ) );
709 BOOST_CHECK( libBuffer.GetBuffers().empty() );
710}
711
712
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)
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:119
SCH_FIELD & GetDescriptionField()
Return reference to the description field.
Definition lib_symbol.h:453
std::weak_ptr< LIB_SYMBOL > & GetParent()
Definition lib_symbol.h:150
bool IsDerived() const
Definition lib_symbol.h:236
void SetParent(LIB_SYMBOL *aParent=nullptr)
wxString GetName() const override
Definition lib_symbol.h:181
SCH_FIELD & GetValueField()
Return reference to the value field.
Definition lib_symbol.h:437
const wxString & GetParentName() const
Definition lib_symbol.h:985
void SetParentName(const wxString &aParentName)
Definition lib_symbol.h:984
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")