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 SCH_SHEET* rootSheet = new SCH_SHEET( &schematic );
196 schematic.SetRoot( rootSheet );
197
198 SCH_SCREEN* screen = new SCH_SCREEN( &schematic );
199 rootSheet->SetScreen( screen );
200
201 IO_RELEASER<SCH_IO> plugin( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_KICAD ) );
202 LIB_SYMBOL* loadedParent = plugin->LoadSymbol( GetLibPath(), wxS( "ParentSymbol" ) );
203 LIB_SYMBOL* loadedDerived = plugin->LoadSymbol( GetLibPath(), wxS( "DerivedSymbol" ) );
204
205 loadedDerived->SetParent( loadedParent );
206
207 SCH_SHEET_PATH rootPath;
208 rootPath.push_back( &schematic.Root() );
209
210 SCH_SYMBOL* schSymbol = new SCH_SYMBOL( *loadedDerived, LIB_ID( "test_lib", "DerivedSymbol" ),
211 &rootPath, 0 );
212 screen->Append( schSymbol );
213
214 // Verify derived file
215 BOOST_CHECK( schSymbol->GetLibSymbolRef()->HasFile( wxS( "derived_datasheet.pdf" ) ) );
216
217 // Verify parent file (should be flattened into the symbol)
218 BOOST_CHECK( schSymbol->GetLibSymbolRef()->HasFile( wxS( "parent_datasheet.pdf" ) ) );
219 }
220}
221
222
226BOOST_AUTO_TEST_CASE( CopySymbolPreservesEmbeddedFiles )
227{
228 // Create a symbol with an embedded file
229 std::unique_ptr<LIB_SYMBOL> original = std::make_unique<LIB_SYMBOL>( wxS( "Original" ) );
230 original->GetValueField().SetText( wxS( "Original" ) );
231
233 CreateTestEmbeddedFile( wxS( "test.pdf" ), "Test content" );
234 original->AddFile( file );
235
236 BOOST_CHECK_EQUAL( original->EmbeddedFileMap().size(), 1 );
237
238 // Copy the symbol using copy constructor
239 std::unique_ptr<LIB_SYMBOL> copy = std::make_unique<LIB_SYMBOL>( *original );
240
241 // Verify the copy has the embedded file
242 BOOST_CHECK_EQUAL( copy->EmbeddedFileMap().size(), 1 );
243 BOOST_CHECK( copy->HasFile( wxS( "test.pdf" ) ) );
244
245 EMBEDDED_FILES::EMBEDDED_FILE* copiedFile = copy->GetEmbeddedFile( wxS( "test.pdf" ) );
246 BOOST_REQUIRE( copiedFile );
247 BOOST_CHECK( copiedFile->Validate() );
248}
249
250
254BOOST_AUTO_TEST_CASE( AssignmentPreservesEmbeddedFiles )
255{
256 // Create a symbol with an embedded file
257 std::unique_ptr<LIB_SYMBOL> source = std::make_unique<LIB_SYMBOL>( wxS( "Source" ) );
258 source->GetValueField().SetText( wxS( "Source" ) );
259
261 CreateTestEmbeddedFile( wxS( "source.pdf" ), "Source content" );
262 source->AddFile( file );
263
264 // Create a destination symbol
265 std::unique_ptr<LIB_SYMBOL> dest = std::make_unique<LIB_SYMBOL>( wxS( "Dest" ) );
266 dest->GetValueField().SetText( wxS( "Dest" ) );
267
268 // Assign source to destination
269 *dest = *source;
270
271 // Verify destination has the embedded file
272 BOOST_CHECK_EQUAL( dest->EmbeddedFileMap().size(), 1 );
273 BOOST_CHECK( dest->HasFile( wxS( "source.pdf" ) ) );
274
275 EMBEDDED_FILES::EMBEDDED_FILE* destFile = dest->GetEmbeddedFile( wxS( "source.pdf" ) );
276 BOOST_REQUIRE( destFile );
277 BOOST_CHECK( destFile->Validate() );
278}
279
280
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 SetRoot(SCH_SHEET *aRootSheet)
Initialize the schematic with a new root sheet.
SCH_SHEET & Root() const
Definition schematic.h:125
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:47
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")