KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_symbol_embedded_files.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
27// Code under test
28#include <lib_symbol.h>
30#include <sch_io/sch_io_mgr.h>
31#include <embedded_files.h>
32#include <mmh3_hash.h>
33#include <schematic.h>
34#include <sch_screen.h>
35#include <sch_sheet.h>
36#include <sch_symbol.h>
37#include <project.h>
38#include <lib_id.h>
39
40#include <wx/filename.h>
41#include <wx/dir.h>
42
44{
45public:
47 {
48 m_tempDir = wxFileName::CreateTempFileName( wxS( "kicad_test_" ) );
49 wxRemoveFile( m_tempDir );
50 wxFileName::Mkdir( m_tempDir );
51
52 m_libPath = wxFileName( m_tempDir, wxS( "test_lib.kicad_sym" ) ).GetFullPath();
53 }
54
56 {
57 // Clean up temporary directory
58 if( wxFileName::DirExists( m_tempDir ) )
59 {
60 wxFileName::Rmdir( m_tempDir, wxPATH_RMDIR_RECURSIVE );
61 }
62 }
63
64 wxString GetTempDir() const { return m_tempDir; }
65 wxString GetLibPath() const { return m_libPath; }
66
71 const std::string& aContent )
72 {
74 file->name = aName;
75 file->decompressedData.assign( aContent.begin(), aContent.end() );
76
78 hash.add( file->decompressedData );
79 file->data_hash = hash.digest().ToString();
80
83
84 return file;
85 }
86
87private:
88 wxString m_tempDir;
89 wxString m_libPath;
90};
91
92
93BOOST_FIXTURE_TEST_SUITE( SymbolEmbeddedFiles, SYMBOL_EMBEDDED_FILES_TEST_FIXTURE )
94
95
96
108BOOST_AUTO_TEST_CASE( DerivedSymbolEmbeddedFiles )
109{
110 // Step 1: Create a parent symbol with an embedded file and save
111 std::unique_ptr<LIB_SYMBOL> parentSymbol = std::make_unique<LIB_SYMBOL>( wxS( "ParentSymbol" ) );
112 parentSymbol->GetValueField().SetText( wxS( "ParentSymbol" ) );
113 parentSymbol->GetReferenceField().SetText( wxS( "U" ) );
114
115 // Add an embedded file to the parent
117 CreateTestEmbeddedFile( wxS( "parent_datasheet.pdf" ), "Parent datasheet content" );
118 parentSymbol->AddFile( parentFile );
119
120 BOOST_CHECK( parentSymbol->HasFile( wxS( "parent_datasheet.pdf" ) ) );
121 BOOST_CHECK_EQUAL( parentSymbol->EmbeddedFileMap().size(), 1 );
122
123 // Save the parent symbol to the library
124 {
125 IO_RELEASER<SCH_IO> plugin( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_KICAD ) );
126 plugin->CreateLibrary( GetLibPath() );
127 plugin->SaveSymbol( GetLibPath(), new LIB_SYMBOL( *parentSymbol ) );
128 plugin->SaveLibrary( GetLibPath() );
129 }
130
131 // Step 2: Close and reload the symbol to verify that the embedded file still exists.
132 {
133 IO_RELEASER<SCH_IO> plugin( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_KICAD ) );
134 LIB_SYMBOL* loadedParent = plugin->LoadSymbol( GetLibPath(), wxS( "ParentSymbol" ) );
135 BOOST_REQUIRE( loadedParent );
136 BOOST_CHECK( loadedParent->HasFile( wxS( "parent_datasheet.pdf" ) ) );
137 }
138
139 // Step 3: Create a derived symbol based on the first symbol and save
140 {
141 IO_RELEASER<SCH_IO> plugin( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_KICAD ) );
142 LIB_SYMBOL* loadedParent = plugin->LoadSymbol( GetLibPath(), wxS( "ParentSymbol" ) );
143 BOOST_REQUIRE( loadedParent );
144
145 std::unique_ptr<LIB_SYMBOL> derivedSymbol = std::make_unique<LIB_SYMBOL>( wxS( "DerivedSymbol" ) );
146 derivedSymbol->GetValueField().SetText( wxS( "DerivedSymbol" ) );
147 derivedSymbol->SetParent( loadedParent );
148
149 plugin->SaveSymbol( GetLibPath(), new LIB_SYMBOL( *derivedSymbol ) );
150 plugin->SaveLibrary( GetLibPath() );
151 }
152
153 // Step 4: Close and re-open the derived symbol. Ensure that the embedded file does not exist in the derived symbol.
154 {
155 IO_RELEASER<SCH_IO> plugin( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_KICAD ) );
156 LIB_SYMBOL* loadedDerived = plugin->LoadSymbol( GetLibPath(), wxS( "DerivedSymbol" ) );
157 BOOST_REQUIRE( loadedDerived );
158
159 // The derived symbol itself should not have the file in its map
160 BOOST_CHECK( !loadedDerived->HasFile( wxS( "parent_datasheet.pdf" ) ) );
161 BOOST_CHECK_EQUAL( loadedDerived->EmbeddedFileMap().size(), 0 );
162 }
163
164 // Step 5: Embed a new file in the derived symbol
165 {
166 IO_RELEASER<SCH_IO> plugin( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_KICAD ) );
167 // We need to load parent to set it again, because LoadSymbol doesn't automatically link parents from disk without a library table
168 LIB_SYMBOL* loadedParent = plugin->LoadSymbol( GetLibPath(), wxS( "ParentSymbol" ) );
169 LIB_SYMBOL* loadedDerived = plugin->LoadSymbol( GetLibPath(), wxS( "DerivedSymbol" ) );
170 BOOST_REQUIRE( loadedDerived );
171 loadedDerived->SetParent( loadedParent );
172
173 EMBEDDED_FILES::EMBEDDED_FILE* derivedFile =
174 CreateTestEmbeddedFile( wxS( "derived_datasheet.pdf" ), "Derived datasheet content" );
175 loadedDerived->AddFile( derivedFile );
176
177 BOOST_CHECK( loadedDerived->HasFile( wxS( "derived_datasheet.pdf" ) ) );
178
179 plugin->SaveSymbol( GetLibPath(), new LIB_SYMBOL( *loadedDerived ) );
180 plugin->SaveLibrary( GetLibPath() );
181 }
182
183 // Step 6: Close and reopen the derived symbol. Ensure that the new embedded file exists
184 {
185 IO_RELEASER<SCH_IO> plugin( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_KICAD ) );
186 LIB_SYMBOL* loadedDerived = plugin->LoadSymbol( GetLibPath(), wxS( "DerivedSymbol" ) );
187 BOOST_REQUIRE( loadedDerived );
188 BOOST_CHECK( loadedDerived->HasFile( wxS( "derived_datasheet.pdf" ) ) );
189 }
190
191 // Step 7: Add only the derived symbol to a new schematic sheet. Ensure that both embedded files are present in the schematic.
192 {
193 SCHEMATIC schematic( nullptr );
194
195 schematic.Reset();
196 SCH_SHEET* defaultSheet = schematic.GetTopLevelSheet( 0 );
197
198 SCH_SHEET* rootSheet = new SCH_SHEET( &schematic );
199 SCH_SCREEN* screen = new SCH_SCREEN( &schematic );
200 rootSheet->SetScreen( screen );
201
202 schematic.AddTopLevelSheet( rootSheet );
203 schematic.RemoveTopLevelSheet( defaultSheet );
204 delete defaultSheet;
205
206 IO_RELEASER<SCH_IO> plugin( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_KICAD ) );
207 LIB_SYMBOL* loadedParent = plugin->LoadSymbol( GetLibPath(), wxS( "ParentSymbol" ) );
208 LIB_SYMBOL* loadedDerived = plugin->LoadSymbol( GetLibPath(), wxS( "DerivedSymbol" ) );
209
210 loadedDerived->SetParent( loadedParent );
211
212 SCH_SHEET_PATH rootPath;
213 rootPath.push_back( &schematic.Root() );
214 rootPath.push_back( rootSheet );
215
216 SCH_SYMBOL* schSymbol = new SCH_SYMBOL( *loadedDerived, LIB_ID( "test_lib", "DerivedSymbol" ),
217 &rootPath, 0 );
218 screen->Append( schSymbol );
219
220 // Verify derived file
221 BOOST_CHECK( schSymbol->GetLibSymbolRef()->HasFile( wxS( "derived_datasheet.pdf" ) ) );
222
223 // Verify parent file (should be flattened into the symbol)
224 BOOST_CHECK( schSymbol->GetLibSymbolRef()->HasFile( wxS( "parent_datasheet.pdf" ) ) );
225 }
226}
227
228
232BOOST_AUTO_TEST_CASE( CopySymbolPreservesEmbeddedFiles )
233{
234 // Create a symbol with an embedded file
235 std::unique_ptr<LIB_SYMBOL> original = std::make_unique<LIB_SYMBOL>( wxS( "Original" ) );
236 original->GetValueField().SetText( wxS( "Original" ) );
237
239 CreateTestEmbeddedFile( wxS( "test.pdf" ), "Test content" );
240 original->AddFile( file );
241
242 BOOST_CHECK_EQUAL( original->EmbeddedFileMap().size(), 1 );
243
244 // Copy the symbol using copy constructor
245 std::unique_ptr<LIB_SYMBOL> copy = std::make_unique<LIB_SYMBOL>( *original );
246
247 // Verify the copy has the embedded file
248 BOOST_CHECK_EQUAL( copy->EmbeddedFileMap().size(), 1 );
249 BOOST_CHECK( copy->HasFile( wxS( "test.pdf" ) ) );
250
251 EMBEDDED_FILES::EMBEDDED_FILE* copiedFile = copy->GetEmbeddedFile( wxS( "test.pdf" ) );
252 BOOST_REQUIRE( copiedFile );
253 BOOST_CHECK( copiedFile->Validate() );
254}
255
256
260BOOST_AUTO_TEST_CASE( AssignmentPreservesEmbeddedFiles )
261{
262 // Create a symbol with an embedded file
263 std::unique_ptr<LIB_SYMBOL> source = std::make_unique<LIB_SYMBOL>( wxS( "Source" ) );
264 source->GetValueField().SetText( wxS( "Source" ) );
265
267 CreateTestEmbeddedFile( wxS( "source.pdf" ), "Source content" );
268 source->AddFile( file );
269
270 // Create a destination symbol
271 std::unique_ptr<LIB_SYMBOL> dest = std::make_unique<LIB_SYMBOL>( wxS( "Dest" ) );
272 dest->GetValueField().SetText( wxS( "Dest" ) );
273
274 // Assign source to destination
275 *dest = *source;
276
277 // Verify destination has the embedded file
278 BOOST_CHECK_EQUAL( dest->EmbeddedFileMap().size(), 1 );
279 BOOST_CHECK( dest->HasFile( wxS( "source.pdf" ) ) );
280
281 EMBEDDED_FILES::EMBEDDED_FILE* destFile = dest->GetEmbeddedFile( wxS( "source.pdf" ) );
282 BOOST_REQUIRE( destFile );
283 BOOST_CHECK( destFile->Validate() );
284}
285
286
bool HasFile(const wxString &name) const
EMBEDDED_FILE * AddFile(const wxFileName &aName, bool aOverwrite)
Load a file from disk and adds it to the collection.
static uint32_t Seed()
const std::map< wxString, EMBEDDED_FILE * > & EmbeddedFileMap() const
static RETURN_CODE CompressAndEncode(EMBEDDED_FILE &aFile)
Take data from the #decompressedData buffer and compresses it using ZSTD into the #compressedEncodedD...
A logical library item identifier and consists of various portions much like a URI.
Definition lib_id.h:49
Define a library symbol object.
Definition lib_symbol.h:83
void SetParent(LIB_SYMBOL *aParent=nullptr)
A streaming C++ equivalent for MurmurHash3_x64_128.
Definition mmh3_hash.h:60
FORCE_INLINE void add(const std::string &input)
Definition mmh3_hash.h:95
FORCE_INLINE HASH_128 digest()
Definition mmh3_hash.h:114
Holds all the data relating to one schematic.
Definition schematic.h:88
void Reset()
Initialize this schematic to a blank one, unloading anything existing.
void AddTopLevelSheet(SCH_SHEET *aSheet)
Add a new top-level sheet to the schematic.
SCH_SHEET * GetTopLevelSheet(int aIndex=0) const
bool RemoveTopLevelSheet(SCH_SHEET *aSheet)
Remove a top-level sheet from the schematic.
SCH_SHEET & Root() const
Definition schematic.h:132
void Append(SCH_ITEM *aItem, bool aUpdateLibSymbol=true)
Handle access to a stack of flattened SCH_SHEET objects by way of a path for creating a flattened sch...
void push_back(SCH_SHEET *aSheet)
Forwarded method from std::vector.
Sheet symbol placed in a schematic, and is the entry point for a sub schematic.
Definition sch_sheet.h:48
void SetScreen(SCH_SCREEN *aScreen)
Set the SCH_SCREEN associated with this sheet to aScreen.
Schematic symbol object.
Definition sch_symbol.h:76
std::unique_ptr< LIB_SYMBOL > & GetLibSymbolRef()
Definition sch_symbol.h:184
EMBEDDED_FILES::EMBEDDED_FILE * CreateTestEmbeddedFile(const wxString &aName, const std::string &aContent)
Create a test embedded file with the given name and content.
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::vector< char > decompressedData
std::string ToString() const
Definition hash_128.h:47
BOOST_AUTO_TEST_CASE(HorizontalAlignment)
BOOST_REQUIRE(intersection.has_value()==c.ExpectedIntersection.has_value())
BOOST_AUTO_TEST_SUITE_END()
BOOST_AUTO_TEST_CASE(DerivedSymbolEmbeddedFiles)
Test embedded files in parent and derived symbols: 1) Create a symbol and add an embedded file and sa...
wxString result
Test unit parsing edge cases and error handling.
BOOST_CHECK_EQUAL(result, "25.4")