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 along
18 * with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
25
27
28// Code under test
31#include <sch_field.h>
33#include <sch_io/sch_io_mgr.h>
34
35#include <wx/filename.h>
36
44
45
46BOOST_FIXTURE_TEST_SUITE( SymbolLibraryManager, SYMBOL_LIBRARY_MANAGER_TEST_FIXTURE )
47
48
49
52BOOST_AUTO_TEST_CASE( SymbolBuffer )
53{
54 std::unique_ptr<LIB_SYMBOL> symbol = std::make_unique<LIB_SYMBOL>( wxS( "Symbol" ) );
55 std::unique_ptr<SCH_SCREEN> screen = std::make_unique<SCH_SCREEN>();
56
57 LIB_SYMBOL& symbolRef = *symbol;
58
59 SYMBOL_BUFFER buffer( std::move( symbol ), std::move( screen ) );
60
61 BOOST_CHECK( !buffer.IsModified() );
62 BOOST_CHECK( &buffer.GetSymbol() == &symbolRef );
63 BOOST_CHECK( buffer.GetOriginal() == symbolRef );
64
65 buffer.GetScreen()->SetContentModified();
66 BOOST_CHECK( buffer.IsModified() );
67
68 std::unique_ptr<LIB_SYMBOL> originalSymbol =
69 std::make_unique<LIB_SYMBOL>( wxS( "OriginalSymbol" ) );
70 LIB_SYMBOL& originalSymbolRef = *originalSymbol;
71
72 buffer.SetOriginal( std::move( originalSymbol ) );
73
74 BOOST_CHECK( &buffer.GetOriginal() == &originalSymbolRef );
75 BOOST_CHECK( buffer.GetOriginal() == originalSymbolRef );
76 BOOST_CHECK( &buffer.GetSymbol() != &buffer.GetOriginal() );
77}
78
79
84{
85 wxArrayString symbolNames;
86 LIB_BUFFER libBuffer( wxS( "TestLibrary" ) );
87
88 BOOST_CHECK( !libBuffer.IsModified() );
89 BOOST_CHECK_EQUAL( libBuffer.GetHash(), 1 );
90
91 auto parentSymbol1 = std::make_unique<LIB_SYMBOL>( wxS( "Parent1" ) );
92 // A but clunky, but real code would get symbol from the LIB_BUFFER
93 // via GetSymbol etc, rather than retaining a reference after construction.
94 LIB_SYMBOL& parentSymbol1Ref = *parentSymbol1;
95
96 BOOST_CHECK_EQUAL( libBuffer.GetSymbol( parentSymbol1->GetName() ), nullptr );
97
98 parentSymbol1->GetValueField().SetText( parentSymbol1->GetName() );
99 libBuffer.CreateBuffer( std::move( parentSymbol1 ), std::make_unique<SCH_SCREEN>() );
100 BOOST_CHECK( libBuffer.GetSymbol( parentSymbol1Ref.GetName() ) == &parentSymbol1Ref );
101 BOOST_CHECK( !libBuffer.HasDerivedSymbols( parentSymbol1Ref.GetName() ) );
102 BOOST_CHECK_EQUAL( libBuffer.GetHash(), 2 );
103
104 libBuffer.GetSymbolNames( symbolNames );
105 BOOST_CHECK_EQUAL( symbolNames.GetCount(), 1 );
106 BOOST_CHECK_EQUAL( symbolNames[0], parentSymbol1Ref.GetName() );
107
108 symbolNames.Clear();
109 libBuffer.GetSymbolNames( symbolNames, SYMBOL_NAME_FILTER::ROOT_ONLY );
110 BOOST_CHECK_EQUAL( symbolNames.GetCount(), 1 );
111 BOOST_CHECK_EQUAL( symbolNames[0], parentSymbol1Ref.GetName() );
112
113 symbolNames.Clear();
114 libBuffer.GetSymbolNames( symbolNames, SYMBOL_NAME_FILTER::DERIVED_ONLY );
115 BOOST_CHECK_EQUAL( symbolNames.GetCount(), 0 );
116
117 auto childSymbol1 = std::make_unique<LIB_SYMBOL>( wxS( "Child1" ) );
118 LIB_SYMBOL& childSymbol1Ref = *childSymbol1;
119
120 childSymbol1->SetParent( &parentSymbol1Ref );
121 childSymbol1->GetValueField().SetText( childSymbol1->GetName() );
122 libBuffer.CreateBuffer( std::move( childSymbol1 ), std::make_unique<SCH_SCREEN>() );
123 BOOST_CHECK( libBuffer.GetSymbol( childSymbol1Ref.GetName() ) == &childSymbol1Ref );
124 BOOST_CHECK( libBuffer.HasDerivedSymbols( parentSymbol1Ref.GetName() ) );
125 BOOST_CHECK_EQUAL( libBuffer.GetHash(), 3 );
126
127 symbolNames.Clear();
128 libBuffer.GetSymbolNames( symbolNames );
129 BOOST_CHECK_EQUAL( symbolNames.GetCount(), 2 );
130 BOOST_CHECK_EQUAL( symbolNames[0], parentSymbol1Ref.GetName() );
131 BOOST_CHECK_EQUAL( symbolNames[1], childSymbol1Ref.GetName() );
132
133 symbolNames.Clear();
134 libBuffer.GetSymbolNames( symbolNames, SYMBOL_NAME_FILTER::DERIVED_ONLY );
135 BOOST_CHECK_EQUAL( symbolNames.GetCount(), 1 );
136 BOOST_CHECK_EQUAL( symbolNames[0], childSymbol1Ref.GetName() );
137
138 symbolNames.Clear();
139
140 BOOST_CHECK_EQUAL( libBuffer.GetDerivedSymbolNames( parentSymbol1Ref.GetName(), symbolNames ),
141 1 );
142 BOOST_CHECK_EQUAL( symbolNames[0], childSymbol1Ref.GetName() );
143
144 std::shared_ptr<SYMBOL_BUFFER> buf = libBuffer.GetBuffer( parentSymbol1Ref.GetName() );
145
146 LIB_SYMBOL tmp( parentSymbol1Ref );
147 tmp.GetDescriptionField().SetText( wxS( "Description" ) );
148
149 libBuffer.UpdateBuffer( *buf, tmp );
150 BOOST_CHECK_EQUAL( libBuffer.GetHash(), 4 );
151 BOOST_CHECK( *libBuffer.GetSymbol( parentSymbol1Ref.GetName() ) == tmp );
152
153 const bool deletedOk = libBuffer.DeleteBuffer( *buf );
154 BOOST_CHECK( deletedOk );
155 BOOST_CHECK( libBuffer.GetBuffers().empty() );
156}
157
158
162BOOST_AUTO_TEST_CASE( NewSymbolCreation )
163{
165
166 props.name = wxS( "Standalone" );
167 props.reference = wxS( "U" );
168 props.unitCount = 2;
169 props.pinNameInside = true;
170 props.pinTextPosition = 2;
171 props.powerSymbol = false;
172 props.showPinNumber = true;
173 props.showPinName = true;
174 props.unitsInterchangeable = false;
175 props.includeInBom = true;
176 props.includeOnBoard = true;
177 props.alternateBodyStyle = false;
178
179 std::unique_ptr<LIB_SYMBOL> standalone =
181
182 BOOST_CHECK_EQUAL( standalone->GetReferenceField().GetText(), props.reference );
183 BOOST_CHECK_EQUAL( standalone->GetUnitCount(), props.unitCount );
184 BOOST_CHECK( standalone->GetPinNameOffset() > 0 );
185
186 auto parent = std::make_unique<LIB_SYMBOL>( wxS( "Parent" ) );
187 parent->GetValueField().SetText( parent->GetName() );
188 SCH_FIELD* user = new SCH_FIELD( parent.get(), FIELD_T::USER, wxS( "UF" ) );
189 user->SetText( wxS( "V" ) );
190 parent->AddField( user );
191
192 props.name = wxS( "Child" );
193 props.parentSymbolName = parent->GetName();
194 props.keepFootprint = false;
195 props.keepDatasheet = false;
196 props.transferUserFields = true;
197 props.keepContentUserFields = false;
198
199 std::unique_ptr<LIB_SYMBOL> child =
200 LIB_SYMBOL_LIBRARY_MANAGER::CreateSymbol( props, parent.get() );
201
202 BOOST_CHECK( child->GetParent().lock().get() == parent.get() );
203 BOOST_CHECK( child->GetFootprintField().GetText().IsEmpty() );
204 BOOST_CHECK( child->GetDatasheetField().GetText().IsEmpty() );
205
206 std::vector<SCH_FIELD*> childFields;
207 child->GetFields( childFields );
208
209 bool found = false;
210
211 for( SCH_FIELD* field : childFields )
212 {
213 if( field->GetId() == FIELD_T::USER )
214 {
215 found = true;
216 BOOST_CHECK( field->GetText().IsEmpty() );
217 }
218 }
219
220 BOOST_CHECK( found );
221}
222
223
232BOOST_AUTO_TEST_CASE( DeletedSymbolsAreRemovedFromFile )
233{
234 // Create a temporary directory and library file
235 wxString tempDir = wxFileName::CreateTempFileName( wxS( "kicad_test_" ) );
236 wxRemoveFile( tempDir );
237 wxFileName::Mkdir( tempDir );
238 wxString libPath = wxFileName( tempDir, wxS( "test_lib.kicad_sym" ) ).GetFullPath();
239
240 // Step 1: Create a library with a parent and derived symbol using the plugin directly
241 {
242 IO_RELEASER<SCH_IO> plugin( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_KICAD ) );
243 plugin->CreateLibrary( libPath );
244
245 // Create parent symbol
246 std::unique_ptr<LIB_SYMBOL> parentSymbol = std::make_unique<LIB_SYMBOL>( wxS( "Parent" ) );
247 parentSymbol->GetValueField().SetText( wxS( "Parent" ) );
248 parentSymbol->GetReferenceField().SetText( wxS( "U" ) );
249
250 LIB_SYMBOL* parentPtr = parentSymbol.get();
251 plugin->SaveSymbol( libPath, new LIB_SYMBOL( *parentSymbol ) );
252
253 // Create derived symbol
254 std::unique_ptr<LIB_SYMBOL> derivedSymbol = std::make_unique<LIB_SYMBOL>( wxS( "Derived" ) );
255 derivedSymbol->GetValueField().SetText( wxS( "Derived" ) );
256 derivedSymbol->SetParent( parentPtr );
257
258 plugin->SaveSymbol( libPath, new LIB_SYMBOL( *derivedSymbol ) );
259 plugin->SaveLibrary( libPath );
260 }
261
262 // Step 2: Verify both symbols exist in the library
263 {
264 IO_RELEASER<SCH_IO> plugin( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_KICAD ) );
265 LIB_SYMBOL* parent = plugin->LoadSymbol( libPath, wxS( "Parent" ) );
266 LIB_SYMBOL* derived = plugin->LoadSymbol( libPath, wxS( "Derived" ) );
267
268 BOOST_REQUIRE( parent != nullptr );
269 BOOST_REQUIRE( derived != nullptr );
270 BOOST_CHECK( derived->IsDerived() );
271 }
272
273 // Step 3: Load symbols into LIB_BUFFER and delete the derived symbol
274 LIB_BUFFER libBuffer( wxS( "TestLibrary" ) );
275
276 {
277 IO_RELEASER<SCH_IO> plugin( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_KICAD ) );
278 LIB_SYMBOL* loadedParent = plugin->LoadSymbol( libPath, wxS( "Parent" ) );
279 LIB_SYMBOL* loadedDerived = plugin->LoadSymbol( libPath, wxS( "Derived" ) );
280
281 BOOST_REQUIRE( loadedParent != nullptr );
282 BOOST_REQUIRE( loadedDerived != nullptr );
283
284 // Set up parent relationship
285 loadedDerived->SetParent( loadedParent );
286
287 // Create buffers (parent first, then derived)
288 libBuffer.CreateBuffer( std::make_unique<LIB_SYMBOL>( *loadedParent ),
289 std::make_unique<SCH_SCREEN>() );
290
291 std::unique_ptr<LIB_SYMBOL> derivedCopy = std::make_unique<LIB_SYMBOL>( *loadedDerived );
292 derivedCopy->SetParent( libBuffer.GetSymbol( wxS( "Parent" ) ) );
293 libBuffer.CreateBuffer( std::move( derivedCopy ), std::make_unique<SCH_SCREEN>() );
294 }
295
296 // Verify buffer state before deletion
297 BOOST_CHECK_EQUAL( libBuffer.GetBuffers().size(), 2 );
298 BOOST_CHECK( libBuffer.GetSymbol( wxS( "Parent" ) ) != nullptr );
299 BOOST_CHECK( libBuffer.GetSymbol( wxS( "Derived" ) ) != nullptr );
300
301 // Delete the derived symbol from the buffer
302 std::shared_ptr<SYMBOL_BUFFER> derivedBuf = libBuffer.GetBuffer( wxS( "Derived" ) );
303 BOOST_REQUIRE( derivedBuf != nullptr );
304
305 bool deleteResult = libBuffer.DeleteBuffer( *derivedBuf );
306 BOOST_CHECK( deleteResult );
307
308 // Verify buffer state after deletion
309 BOOST_CHECK_EQUAL( libBuffer.GetBuffers().size(), 1 );
310 BOOST_CHECK( libBuffer.GetSymbol( wxS( "Parent" ) ) != nullptr );
311 BOOST_CHECK( libBuffer.GetSymbol( wxS( "Derived" ) ) == nullptr );
312
313 // Step 4: Save the library using the same pattern as SYMBOL_LIBRARY_MANAGER::SaveLibrary
314 {
315 IO_RELEASER<SCH_IO> plugin( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_KICAD ) );
316 std::map<std::string, UTF8> properties;
317 properties.emplace( SCH_IO_KICAD_SEXPR::PropBuffering, "" );
318
319 // Save remaining buffers (just Parent)
320 for( const std::shared_ptr<SYMBOL_BUFFER>& symbolBuf : libBuffer.GetBuffers() )
321 {
322 libBuffer.SaveBuffer( *symbolBuf, libPath, &*plugin, true );
323 }
324
325 // Delete symbols that were removed from the buffer (this is the fix for the bug)
326 for( const std::shared_ptr<SYMBOL_BUFFER>& deletedBuf : libBuffer.GetDeletedBuffers() )
327 {
328 const wxString& originalName = deletedBuf->GetOriginal().GetName();
329
330 if( plugin->LoadSymbol( libPath, originalName ) )
331 plugin->DeleteSymbol( libPath, originalName, &properties );
332 }
333
334 plugin->SaveLibrary( libPath );
335 libBuffer.ClearDeletedBuffer();
336 }
337
338 // Step 5: Reload the library and verify the derived symbol is gone
339 {
340 IO_RELEASER<SCH_IO> plugin( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_KICAD ) );
341
342 LIB_SYMBOL* parent = plugin->LoadSymbol( libPath, wxS( "Parent" ) );
343 LIB_SYMBOL* derived = plugin->LoadSymbol( libPath, wxS( "Derived" ) );
344
345 BOOST_CHECK( parent != nullptr );
346 // This is the actual check for the bug - the derived symbol should be deleted
347 BOOST_CHECK_MESSAGE( derived == nullptr,
348 "Derived symbol should have been deleted from the library file" );
349 }
350
351 // Cleanup
352 if( wxFileName::DirExists( tempDir ) )
353 {
354 wxFileName::Rmdir( tempDir, wxPATH_RMDIR_RECURSIVE );
355 }
356}
357
358
void SetContentModified(bool aModified=true)
Definition base_screen.h:59
Store a working copy of a library.
size_t GetDerivedSymbolNames(const wxString &aSymbolName, wxArrayString &aList)
Fetch all of the symbols derived from a aSymbolName into aList.
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.
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:83
SCH_FIELD & GetDescriptionField()
Return reference to the description field.
Definition lib_symbol.h:349
bool IsDerived() const
Definition lib_symbol.h:204
void SetParent(LIB_SYMBOL *aParent=nullptr)
wxString GetName() const override
Definition lib_symbol.h:146
void SetText(const wxString &aText) override
static const char * PropBuffering
The property used internally by the plugin to enable cache buffering which prevents the library file ...
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")