KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_http_lib_plugin.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 General
14 * 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 <chrono>
23#include <thread>
24
28#include <lib_symbol.h>
30
32
34
35
36BOOST_AUTO_TEST_SUITE( HttpLibPlugin )
37
38
39static const std::string RootUrl = "http://fake.test/v1/";
40static const std::string SettingsFile = "test_http_lib.kicad_httplib";
41
42
43static wxString httpLibSettingsPath()
44{
45 return wxString::FromUTF8( KI_TEST::GetEeschemaTestDataDir() ) + SettingsFile;
46}
47
48
50{
51 aPlugin.SetSourcePatcher( []( HTTP_LIB_SOURCE& aSource )
52 {
53 aSource.timeout_parts = 1;
54 aSource.timeout_categories = 1;
55 } );
56}
57
58
60{
62 {
63 plugin.SetLibraryManagerAdapter( &adapter );
64 server.InstallStandardLibrary( RootUrl );
65 plugin.SetConnectionBuilder( [this]( const HTTP_LIB_SOURCE& aSource )
66 {
67 return std::make_unique<HTTP_LIB_CONNECTION>(
68 aSource, true, server.MakeRetriever() );
69 } );
70 }
71
76};
77
78
79BOOST_AUTO_TEST_CASE( EnumerateSymbolLibListsAllParts )
80{
81 HTTP_PLUGIN_HARNESS harness;
82 const wxString settingsPath = httpLibSettingsPath();
83
84 wxArrayString names;
85 harness.plugin.EnumerateSymbolLib( names, settingsPath );
86
87 BOOST_CHECK( names.Index( wxS( "R_10K" ) ) != wxNOT_FOUND );
88 BOOST_CHECK( names.Index( wxS( "R_100K" ) ) != wxNOT_FOUND );
89 BOOST_CHECK( names.Index( wxS( "C_100n" ) ) != wxNOT_FOUND );
90
91 BOOST_CHECK( harness.server.RequestCount( RootUrl + "categories.json" ) >= 1 );
92 BOOST_CHECK( harness.server.RequestCount( RootUrl + "parts/category/1.json" ) >= 1 );
93
94}
95
96
97BOOST_AUTO_TEST_CASE( EnumerateSymbolLibMaterializesFields )
98{
99 HTTP_PLUGIN_HARNESS harness;
100 const wxString settingsPath = httpLibSettingsPath();
101
102 std::vector<LIB_SYMBOL*> symbols;
103 harness.plugin.EnumerateSymbolLib( symbols, settingsPath );
104
105 BOOST_REQUIRE_EQUAL( symbols.size(), 3u );
106
107 const LIB_SYMBOL* r10k = nullptr;
108
109 for( const LIB_SYMBOL* symbol : symbols )
110 {
111 if( symbol->GetName() == wxS( "R_10K" ) )
112 r10k = symbol;
113 }
114
115 BOOST_REQUIRE( r10k );
117 wxString( wxS( "Resistor_SMD:R_0603_1608Metric" ) ) );
118 BOOST_CHECK_EQUAL( r10k->GetValueField().GetText(), wxString( wxS( "10k" ) ) );
119 BOOST_CHECK_EQUAL( r10k->GetDescription(), wxString( wxS( "10 kOhms resistor" ) ) );
120
121}
122
123
124BOOST_AUTO_TEST_CASE( SubLibrariesExposeCategories )
125{
126 HTTP_PLUGIN_HARNESS harness;
127 const wxString settingsPath = httpLibSettingsPath();
128
129 // GetSubLibraryNames relies on settings established by an earlier connection.
130 BOOST_REQUIRE_NO_THROW( harness.plugin.CheckLibrary( settingsPath ) );
131
132 std::vector<wxString> names = { wxS( "stale" ) };
133 harness.plugin.GetSubLibraryNames( names );
134
135 BOOST_REQUIRE_EQUAL( names.size(), 2u );
136 BOOST_CHECK_EQUAL( names[0], wxString( wxS( "Resistors" ) ) );
137 BOOST_CHECK_EQUAL( names[1], wxString( wxS( "Capacitors" ) ) );
138
139 BOOST_CHECK_EQUAL( harness.plugin.GetSubLibraryDescription( wxS( "Resistors" ) ),
140 wxString( wxS( "Resistors" ) ) );
141}
142
143
144BOOST_AUTO_TEST_CASE( LoadSymbolReturnsMaterializedPart )
145{
146 HTTP_PLUGIN_HARNESS harness;
147 const wxString settingsPath = httpLibSettingsPath();
148
149 BOOST_REQUIRE_NO_THROW( harness.plugin.CheckLibrary( settingsPath ) );
150
151 LIB_SYMBOL* symbol = harness.plugin.LoadSymbol( settingsPath, wxS( "R_10K" ) );
152 BOOST_REQUIRE( symbol );
153
155 wxString( wxS( "Resistor_SMD:R_0603_1608Metric" ) ) );
156 BOOST_CHECK_EQUAL( symbol->GetValueField().GetText(), wxString( wxS( "10k" ) ) );
157
158 delete symbol;
159}
160
161
162BOOST_AUTO_TEST_CASE( CheckLibraryDoesNotEnumerateParts )
163{
164 HTTP_PLUGIN_HARNESS harness;
165 const wxString settingsPath = httpLibSettingsPath();
166
167 BOOST_REQUIRE_NO_THROW( harness.plugin.CheckLibrary( settingsPath ) );
168
169 // Category sync happens as part of endpoint validation; part enumeration must not.
170 BOOST_CHECK_EQUAL( harness.server.RequestCount( RootUrl + "categories.json" ), 1 );
171 BOOST_CHECK_EQUAL( harness.server.RequestCount( RootUrl + "parts/category/1.json" ), 0 );
172 BOOST_CHECK_EQUAL( harness.server.RequestCount( RootUrl + "parts/res-10k.json" ), 0 );
173}
174
175
177BOOST_AUTO_TEST_CASE( IdenticalMetadataDoesNotRematerialize )
178{
179 HTTP_PLUGIN_HARNESS harness;
180 useFastCacheLifetimes( harness.plugin );
181 const wxString settingsPath = httpLibSettingsPath();
182
183 wxArrayString names;
184 harness.plugin.EnumerateSymbolLib( names, settingsPath );
185 BOOST_REQUIRE( names.Index( wxS( "R_10K" ) ) != wxNOT_FOUND );
186
187 const int hashBefore = harness.plugin.GetModifyHash();
188
189 // Give the background refresh at least one full cycle with identical data.
190 std::this_thread::sleep_for( std::chrono::milliseconds( 1100 ) );
191
192 BOOST_CHECK_EQUAL( harness.plugin.GetModifyHash(), hashBefore );
193
194 wxArrayString namesAgain;
195 harness.plugin.EnumerateSymbolLib( namesAgain, settingsPath );
196 BOOST_CHECK( namesAgain.Index( wxS( "R_10K" ) ) != wxNOT_FOUND );
197}
198
199
200BOOST_AUTO_TEST_CASE( MetadataChangeRematerializes )
201{
202 HTTP_PLUGIN_HARNESS harness;
203 useFastCacheLifetimes( harness.plugin );
204 const wxString settingsPath = httpLibSettingsPath();
205
206 wxArrayString names;
207 harness.plugin.EnumerateSymbolLib( names, settingsPath );
208 BOOST_REQUIRE( names.Index( wxS( "R_10K" ) ) != wxNOT_FOUND );
209
210 const int hashBefore = harness.plugin.GetModifyHash();
211
212 harness.server.SetResponse( RootUrl + "parts/category/1.json",
213 { 200, R"([
214 { "id": "res-20k", "name": "R_20K", "description": "20 kOhms resistor" },
215 { "id": "res-100k", "name": "R_100K", "description": "100 kOhms resistor" }
216])" } );
217
218 harness.server.SetResponse( RootUrl + "parts/res-20k.json",
219 { 200, R"({
220 "id": "res-20k",
221 "name": "R_20K",
222 "description": "20 kOhms resistor",
223 "fields": {
224 "footprint": { "value": "Resistor_SMD:R_0603_1608Metric", "visible": "False" },
225 "value": { "value": "20k" },
226 "reference": { "value": "R" }
227 }
228})" } );
229
230 std::this_thread::sleep_for( std::chrono::milliseconds( 1100 ) );
231
232 LIB_SYMBOL* newSymbol = harness.plugin.LoadSymbol( settingsPath, wxS( "R_20K" ) );
233
234 BOOST_REQUIRE( newSymbol );
235 BOOST_CHECK_EQUAL( newSymbol->GetValueField().GetText(), wxString( wxS( "20k" ) ) );
236 delete newSymbol;
237
238 BOOST_CHECK( harness.plugin.LoadSymbol( settingsPath, wxS( "R_10K" ) ) == nullptr );
239 BOOST_CHECK( harness.plugin.GetModifyHash() != hashBefore );
240}
241
242
245BOOST_AUTO_TEST_CASE( ListingDescriptionSurvivesDetailWithoutDescription )
246{
247 HTTP_PLUGIN_HARNESS harness;
248 const wxString settingsPath = httpLibSettingsPath();
249
250 harness.server.SetResponse( RootUrl + "parts/category/1.json",
251 { 200, R"([
252 { "id": "res-10k", "name": "R_10K", "description": "Listing description" }
253])" } );
254
255 harness.server.SetResponse( RootUrl + "parts/res-10k.json",
256 { 200, R"({
257 "id": "res-10k",
258 "name": "R_10K",
259 "fields": {
260 "value": { "value": "10k" }
261 }
262})" } );
263
264 std::vector<LIB_SYMBOL*> symbols;
265 harness.plugin.EnumerateSymbolLib( symbols, settingsPath );
266
267 const LIB_SYMBOL* r10k = nullptr;
268
269 for( const LIB_SYMBOL* symbol : symbols )
270 {
271 if( symbol->GetName() == wxS( "R_10K" ) )
272 r10k = symbol;
273 }
274
275 BOOST_REQUIRE( r10k );
276 BOOST_CHECK_EQUAL( r10k->GetDescription(), wxString( wxS( "Listing description" ) ) );
277}
278
279
282BOOST_AUTO_TEST_CASE( DescriptionFieldNotClobberedByEmptySummary )
283{
284 HTTP_PLUGIN_HARNESS harness;
285 const wxString settingsPath = httpLibSettingsPath();
286
287 harness.server.SetResponse( RootUrl + "parts/category/1.json",
288 { 200, R"([
289 { "id": "res-10k", "name": "R_10K" }
290])" } );
291
292 harness.server.SetResponse( RootUrl + "parts/res-10k.json",
293 { 200, R"({
294 "id": "res-10k",
295 "name": "R_10K",
296 "fields": {
297 "value": { "value": "10k" },
298 "description": { "value": "Field description", "visible": "False" }
299 }
300})" } );
301
302 std::vector<LIB_SYMBOL*> symbols;
303 harness.plugin.EnumerateSymbolLib( symbols, settingsPath );
304
305 const LIB_SYMBOL* r10k = nullptr;
306
307 for( const LIB_SYMBOL* symbol : symbols )
308 {
309 if( symbol->GetName() == wxS( "R_10K" ) )
310 r10k = symbol;
311 }
312
313 BOOST_REQUIRE( r10k );
314 BOOST_CHECK_EQUAL( r10k->GetDescription(), wxString( wxS( "Field description" ) ) );
315}
316
317
In-process HTTP library server used by QA tests.
int RequestCount(const std::string &aUrl) const
Number of times aUrl has been requested.
void SetResponse(const std::string &aUrl, RESPONSE aResponse)
Replace the response served for an exact URL.
Define a library symbol object.
Definition lib_symbol.h:119
wxString GetDescription() const override
Definition lib_symbol.h:200
SCH_FIELD & GetFootprintField()
Return reference to the footprint field.
Definition lib_symbol.h:445
SCH_FIELD & GetValueField()
Return reference to the value field.
Definition lib_symbol.h:437
virtual const wxString & GetText() const override
Return the string associated with the text object.
Definition sch_field.h:138
A KiCad HTTP library provides both symbol and footprint metadata, so there are "shim" plugins on both...
LIB_SYMBOL * LoadSymbol(const wxString &aLibraryPath, const wxString &aAliasName, const std::map< std::string, UTF8 > *aProperties=nullptr) override
Load a LIB_SYMBOL object having aPartName from the aLibraryPath containing a library format that this...
void SetSourcePatcher(SOURCE_PATCHER aOverride)
Allows updating a source after initial load; used for QA testing.
wxString GetSubLibraryDescription(const wxString &aName) override
Gets a description of a sublibrary.
int GetModifyHash() const override
Return the modification hash from the library cache.
void CheckLibrary(const wxString &aLibraryPath, const std::map< std::string, UTF8 > *aProperties=nullptr) override
Validate that the library at aLibraryPath is reachable and well-formed, without necessarily loading s...
void EnumerateSymbolLib(wxArrayString &aSymbolNameList, const wxString &aLibraryPath, const std::map< std::string, UTF8 > *aProperties=nullptr) override
Populate a list of LIB_SYMBOL alias names contained within the library aLibraryPath.
void GetSubLibraryNames(std::vector< wxString > &aNames) override
Retrieves a list of sub-libraries in this library.
An interface to the global shared library manager that is schematic-specific and linked to one projec...
std::string GetEeschemaTestDataDir()
Get the configured location of Eeschema test data.
STL namespace.
Connection parameters for one HTTP library.
SYMBOL_LIBRARY_ADAPTER adapter
HTTP_LIB_FAKE_SERVER server
BOOST_AUTO_TEST_SUITE(CadstarPartParser)
BOOST_REQUIRE(intersection.has_value()==c.ExpectedIntersection.has_value())
BOOST_AUTO_TEST_SUITE_END()
static const std::string RootUrl
BOOST_AUTO_TEST_CASE(EnumerateSymbolLibListsAllParts)
static const std::string SettingsFile
static void useFastCacheLifetimes(SCH_IO_HTTP_LIB &aPlugin)
static wxString httpLibSettingsPath()
static const std::string RootUrl
BOOST_CHECK_EQUAL(result, "25.4")