KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_remote_symbol_import.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,
12 * but 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
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 */
19
20#include <boost/test/unit_test.hpp>
21
22#include <eeschema_settings.h>
23#include <io/io_mgr.h>
24#include <lib_symbol.h>
25#include <picosha2.h>
28#include <sch_io/sch_io.h>
29#include <sch_io/sch_io_mgr.h>
31
33
34#include <wx/ffile.h>
35#include <wx/filefn.h>
36#include <wx/filename.h>
37#include <wx/stdpaths.h>
38
39
40namespace
41{
42// The served payload is the shared qa/data library with its symbol renamed, so a download
43// under a different name still yields a parseable .kicad_sym.
44std::string symbolPayload( const char* aName )
45{
46 wxString libPath = wxString::FromUTF8( KI_TEST::GetEeschemaTestDataDir() )
47 + wxS( "remote_symbol_lib.kicad_sym" );
48
49 wxFFile file( libPath, wxS( "rb" ) );
50 BOOST_REQUIRE_MESSAGE( file.IsOpened(), "Could not open " << libPath );
51
52 wxString body;
53 BOOST_REQUIRE( file.ReadAll( &body ) );
54
55 body.Replace( wxS( "TestResistor" ), wxString::FromUTF8( aName ) );
56
57 return std::string( body.ToUTF8() );
58}
59
60
61wxString sha256Hex( const std::string& aPayload )
62{
63 std::string hashHex;
64 picosha2::hash256_hex_string( aPayload.begin(), aPayload.end(), hashHex );
65 return wxString::FromUTF8( hashHex.c_str() );
66}
67
68
69wxString tempDir()
70{
71 wxString path = wxFileName::CreateTempFileName( wxS( "remote-symbol-import" ) );
72 wxRemoveFile( path );
73 wxMkdir( path );
74 return path;
75}
76
77
79{
81 metadata.provider_name = wxString( "Acme" );
82 metadata.provider_version = wxString( "1.0.0" );
83 metadata.api_base_url = wxString( "https://provider.example.test/api" );
84 metadata.max_download_bytes = 4096;
85 metadata.parts_v1 = true;
86 metadata.direct_downloads_v1 = true;
87 return metadata;
88}
89
90REMOTE_SYMBOL_IMPORT_CONTEXT importContext()
91{
93 context.symbol_name = wxString( "R" );
94 context.library_name = wxString( "Device" );
95 return context;
96}
97
98
100{
102 manifest.part_id = wxString( "acme-res-10k" );
103 manifest.display_name = wxString( "RC0603FR-0710KL" );
104
106 const std::string symbolBlob = symbolPayload( "R" );
107 symbol.asset_type = wxString( "symbol" );
108 symbol.name = wxString( "acme-res-10k.kicad_sym" );
109 symbol.content_type = wxString( "application/x-kicad-symbol" );
110 symbol.size_bytes = static_cast<long long>( symbolBlob.size() );
111 symbol.sha256 = sha256Hex( symbolBlob );
112 symbol.download_url = wxString( "https://provider.example.test/downloads/acme-res-10k.kicad_sym" );
113 symbol.required = true;
114 symbol.target_library = wxString( "Device" );
115 symbol.target_name = wxString( "R" );
116
118 footprint.asset_type = wxString( "footprint" );
119 footprint.name = wxString( "R_0603.pretty" );
120 footprint.content_type = wxString( "application/x-kicad-footprint" );
121 footprint.size_bytes = 44;
122 footprint.sha256 = wxString( "8d8090740282c9ec23541a148af0ae57543e0da581e00e714e066dc4a1adefb0" );
123 footprint.download_url = wxString( "https://provider.example.test/downloads/R_0603.pretty" );
124 footprint.required = false;
125 footprint.target_library = wxString( "Resistor_SMD" );
126 footprint.target_name = wxString( "R_0603_1608Metric" );
127
128 manifest.assets = { symbol, footprint };
129 return manifest;
130}
131} // namespace
132
133
134BOOST_AUTO_TEST_SUITE( RemoteSymbolImportTests )
135
136BOOST_AUTO_TEST_CASE( ImportWritesDownloadedAssets )
137{
138 const wxString outputDir = tempDir();
140 BOOST_REQUIRE( settings );
141 settings->m_RemoteSymbol.destination_dir = outputDir;
142 settings->m_RemoteSymbol.library_prefix = wxString( "testremote" );
143 settings->m_RemoteSymbol.add_to_global_table = true;
144
146 [&]( const wxString& aUrl, REMOTE_SYMBOL_FETCH_RESPONSE& aResponse, wxString& aError )
147 {
148 wxUnusedVar( aError );
149 aResponse.status_code = 200;
150
151 if( aUrl.EndsWith( wxString( "acme-res-10k.kicad_sym" ) ) )
152 {
153 aResponse.content_type = wxString( "application/x-kicad-symbol" );
154 const std::string payload = symbolPayload( "R" );
155 aResponse.payload.assign( payload.begin(), payload.end() );
156 return true;
157 }
158
159 aResponse.content_type = wxString( "application/x-kicad-footprint" );
160 const std::string payload = "(module \"R_0603_1608Metric\" (layer \"F.Cu\"))\n";
161 aResponse.payload.assign( payload.begin(), payload.end() );
162 return true;
163 } );
164
165 REMOTE_SYMBOL_IMPORT_JOB job( nullptr, &downloader );
166 wxString error;
167
168 BOOST_REQUIRE( job.Import( provider(), importContext(), manifest(), false, error ) );
169
170 wxFileName symbolPath( outputDir, wxString() );
171 symbolPath.AppendDir( wxString( "symbols" ) );
172 symbolPath.SetFullName( wxString( "testremote_device.kicad_sym" ) );
173 BOOST_CHECK( symbolPath.FileExists() );
174
175 wxFileName footprintPath( outputDir, wxString() );
176 footprintPath.AppendDir( wxString( "footprints" ) );
177 footprintPath.AppendDir( wxString( "testremote_resistor_smd.pretty" ) );
178 footprintPath.SetFullName( wxString( "R_0603_1608Metric.kicad_mod" ) );
179 BOOST_CHECK( footprintPath.FileExists() );
180
181 // The downloaded symbol's Footprint field must point to the local LIB_ID of the
182 // bundled footprint so that subsequent placement / CvPcb sees the link.
183 IO_RELEASER<SCH_IO> plugin( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_KICAD ) );
184 BOOST_REQUIRE( plugin );
185
186 LIB_SYMBOL* loaded = plugin->LoadSymbol( symbolPath.GetFullPath(), wxString( "R" ) );
187 BOOST_REQUIRE( loaded );
188 BOOST_CHECK_EQUAL( loaded->GetFootprintProp().ToStdString(),
189 std::string( "testremote_resistor_smd:R_0603_1608Metric" ) );
190}
191
192BOOST_AUTO_TEST_CASE( ImportRejectsSymbolPayloadThatDoesNotContainExpectedName )
193{
194 const wxString outputDir = tempDir();
196 BOOST_REQUIRE( settings );
197 settings->m_RemoteSymbol.destination_dir = outputDir;
198 settings->m_RemoteSymbol.library_prefix = wxString( "testremote" );
199 settings->m_RemoteSymbol.add_to_global_table = true;
200
202 [&]( const wxString& aUrl, REMOTE_SYMBOL_FETCH_RESPONSE& aResponse, wxString& aError )
203 {
204 wxUnusedVar( aError );
205 aResponse.status_code = 200;
206
207 if( aUrl.EndsWith( wxString( "acme-res-10k.kicad_sym" ) ) )
208 {
209 aResponse.content_type = wxString( "application/x-kicad-symbol" );
210 const std::string payload = symbolPayload( "WrongName" );
211 aResponse.payload.assign( payload.begin(), payload.end() );
212 return true;
213 }
214
215 aResponse.content_type = wxString( "application/x-kicad-footprint" );
216 const std::string payload = "(module \"R_0603_1608Metric\" (layer \"F.Cu\"))\n";
217 aResponse.payload.assign( payload.begin(), payload.end() );
218 return true;
219 } );
220
221 REMOTE_SYMBOL_IMPORT_JOB job( nullptr, &downloader );
222 wxString error;
223
224 BOOST_CHECK( !job.Import( provider(), importContext(), manifest(), false, error ) );
225 BOOST_CHECK( !error.IsEmpty() );
226}
227
228BOOST_AUTO_TEST_CASE( ImportLinksFirstFootprintAndAddsAlternatesAsFilters )
229{
230 const wxString outputDir = tempDir();
232 BOOST_REQUIRE( settings );
233 settings->m_RemoteSymbol.destination_dir = outputDir;
234 settings->m_RemoteSymbol.library_prefix = wxString( "testremote" );
235 settings->m_RemoteSymbol.add_to_global_table = true;
236
237 REMOTE_PROVIDER_PART_MANIFEST multiManifest;
238 multiManifest.part_id = wxString( "acme-res-10k" );
239
240 REMOTE_PROVIDER_PART_ASSET symbolAsset;
241 const std::string symbolBlob = symbolPayload( "R" );
242 symbolAsset.asset_type = wxString( "symbol" );
243 symbolAsset.name = wxString( "acme-res-10k.kicad_sym" );
244 symbolAsset.content_type = wxString( "application/x-kicad-symbol" );
245 symbolAsset.size_bytes = static_cast<long long>( symbolBlob.size() );
246 symbolAsset.sha256 = sha256Hex( symbolBlob );
247 symbolAsset.download_url = wxString( "https://provider.example.test/downloads/acme-res-10k.kicad_sym" );
248 symbolAsset.target_library = wxString( "Device" );
249 symbolAsset.target_name = wxString( "R" );
250
251 const std::string fpBlob0603 = "(module \"R_0603_1608Metric\" (layer \"F.Cu\"))\n";
252 const std::string fpBlob0805 = "(module \"R_0805_2012Metric\" (layer \"F.Cu\"))\n";
253
255 fpPrimary.asset_type = wxString( "footprint" );
256 fpPrimary.name = wxString( "R_0603.pretty" );
257 fpPrimary.content_type = wxString( "application/x-kicad-footprint" );
258 fpPrimary.size_bytes = static_cast<long long>( fpBlob0603.size() );
259 fpPrimary.sha256 = sha256Hex( fpBlob0603 );
260 fpPrimary.download_url = wxString( "https://provider.example.test/downloads/R_0603.kicad_mod" );
261 fpPrimary.target_library = wxString( "Resistor_SMD" );
262 fpPrimary.target_name = wxString( "R_0603_1608Metric" );
263
265 fpAlt.asset_type = wxString( "footprint" );
266 fpAlt.name = wxString( "R_0805.pretty" );
267 fpAlt.content_type = wxString( "application/x-kicad-footprint" );
268 fpAlt.size_bytes = static_cast<long long>( fpBlob0805.size() );
269 fpAlt.sha256 = sha256Hex( fpBlob0805 );
270 fpAlt.download_url = wxString( "https://provider.example.test/downloads/R_0805.kicad_mod" );
271 fpAlt.target_library = wxString( "Resistor_SMD" );
272 fpAlt.target_name = wxString( "R_0805_2012Metric" );
273
274 // Intentionally list the symbol BEFORE the footprints to verify the import job
275 // reorders processing so the symbol's footprint field can resolve.
276 multiManifest.assets = { symbolAsset, fpPrimary, fpAlt };
277
279 [&]( const wxString& aUrl, REMOTE_SYMBOL_FETCH_RESPONSE& aResponse, wxString& aError )
280 {
281 wxUnusedVar( aError );
282 aResponse.status_code = 200;
283
284 if( aUrl.EndsWith( wxString( "acme-res-10k.kicad_sym" ) ) )
285 {
286 aResponse.content_type = wxString( "application/x-kicad-symbol" );
287 aResponse.payload.assign( symbolBlob.begin(), symbolBlob.end() );
288 return true;
289 }
290
291 if( aUrl.EndsWith( wxString( "R_0805.kicad_mod" ) ) )
292 {
293 aResponse.content_type = wxString( "application/x-kicad-footprint" );
294 aResponse.payload.assign( fpBlob0805.begin(), fpBlob0805.end() );
295 return true;
296 }
297
298 aResponse.content_type = wxString( "application/x-kicad-footprint" );
299 aResponse.payload.assign( fpBlob0603.begin(), fpBlob0603.end() );
300 return true;
301 } );
302
303 REMOTE_SYMBOL_IMPORT_JOB job( nullptr, &downloader );
304 wxString error;
305 BOOST_REQUIRE( job.Import( provider(), importContext(), multiManifest, false, error ) );
306
307 wxFileName symbolPath( outputDir, wxString() );
308 symbolPath.AppendDir( wxString( "symbols" ) );
309 symbolPath.SetFullName( wxString( "testremote_device.kicad_sym" ) );
310 BOOST_REQUIRE( symbolPath.FileExists() );
311
312 IO_RELEASER<SCH_IO> plugin( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_KICAD ) );
313 BOOST_REQUIRE( plugin );
314
315 LIB_SYMBOL* loaded = plugin->LoadSymbol( symbolPath.GetFullPath(), wxString( "R" ) );
316 BOOST_REQUIRE( loaded );
317
318 BOOST_CHECK_EQUAL( loaded->GetFootprintProp().ToStdString(),
319 std::string( "testremote_resistor_smd:R_0603_1608Metric" ) );
320
321 const wxArrayString filters = loaded->GetFPFilters();
322 BOOST_CHECK_EQUAL( filters.GetCount(), 1u );
323 if( filters.GetCount() == 1 )
324 BOOST_CHECK_EQUAL( filters[0].ToStdString(), std::string( "R_0805_2012Metric" ) );
325}
326
327
REMOTE_PROVIDER_SETTINGS m_RemoteSymbol
Define a library symbol object.
Definition lib_symbol.h:114
wxArrayString GetFPFilters() const
Definition lib_symbol.h:242
wxString GetFootprintProp() const
Definition lib_symbol.h:483
static std::string ToStdString(const wxString &aStr)
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::string GetEeschemaTestDataDir()
Get the configured location of Eeschema test data.
T * GetAppSettings(const char *aFilename)
std::vector< REMOTE_PROVIDER_PART_ASSET > assets
BOOST_AUTO_TEST_CASE(HorizontalAlignment)
BOOST_AUTO_TEST_SUITE(CadstarPartParser)
BOOST_REQUIRE(intersection.has_value()==c.ExpectedIntersection.has_value())
BOOST_AUTO_TEST_SUITE_END()
std::string path
BOOST_AUTO_TEST_CASE(ImportWritesDownloadedAssets)
BOOST_CHECK_EQUAL(result, "25.4")