KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_api_e2e.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
20#include <boost/test/unit_test.hpp>
21#include <wx/filefn.h>
22#include <wx/filename.h>
23
24#include "api_e2e_utils.h"
25
26#include <api/board/board.pb.h>
27#include <api/board/board_commands.pb.h>
28
29
31{
32public:
34 {
35 if( !m_tempDir.IsEmpty() && wxFileName::DirExists( m_tempDir ) )
36 wxFileName::Rmdir( m_tempDir, wxPATH_RMDIR_RECURSIVE );
37 }
38
39 bool Create( wxString* aError )
40 {
41 wxString testDataDir = wxString::FromUTF8( KI_TEST::GetPcbnewTestDataDir() );
42 wxFileName srcPcb( testDataDir, wxS( "api_kitchen_sink.kicad_pcb" ) );
43 wxFileName srcPro( testDataDir, wxS( "api_kitchen_sink.kicad_pro" ) );
44 wxFileName srcDru( testDataDir, wxS( "api_kitchen_sink.kicad_dru" ) );
45
46 wxString tempToken = wxFileName::CreateTempFileName( wxS( "kicad-api-e2e-" ) );
47
48 if( tempToken.IsEmpty() )
49 {
50 if( aError )
51 *aError = wxS( "Failed to create temporary file name" );
52
53 return false;
54 }
55
56 wxRemoveFile( tempToken );
57
58 if( !wxFileName::Mkdir( tempToken, wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL ) )
59 {
60 if( aError )
61 *aError = wxS( "Failed to create temporary directory" );
62
63 return false;
64 }
65
66 m_tempDir = tempToken;
67
68 wxFileName dstPcb( m_tempDir, srcPcb.GetFullName() );
69 wxFileName dstPro( m_tempDir, srcPro.GetFullName() );
70 wxFileName dstDru( m_tempDir, srcDru.GetFullName() );
71
72 if( !wxCopyFile( srcPcb.GetFullPath(), dstPcb.GetFullPath(), true )
73 || !wxCopyFile( srcPro.GetFullPath(), dstPro.GetFullPath(), true )
74 || !wxCopyFile( srcDru.GetFullPath(), dstDru.GetFullPath(), true ) )
75 {
76 if( aError )
77 *aError = wxS( "Failed to copy fixture files into temporary directory" );
78
79 return false;
80 }
81
82 m_boardPath = dstPcb.GetFullPath();
83 return true;
84 }
85
86 const wxString& BoardPath() const { return m_boardPath; }
87
88private:
89 wxString m_tempDir;
90 wxString m_boardPath;
91};
92
93
94bool SendGetBoardDesignRules( API_TEST_CLIENT& aClient, const kiapi::common::types::DocumentSpecifier& aDocument,
95 kiapi::board::commands::BoardDesignRulesResponse* aOut, wxString* aError = nullptr )
96{
97 kiapi::board::commands::GetBoardDesignRules request;
98 *request.mutable_board() = aDocument;
99
100 kiapi::common::ApiResponse response;
101
102 if( !aClient.SendCommand( request, &response ) )
103 {
104 if( aError )
105 *aError = aClient.LastError();
106
107 return false;
108 }
109
110 if( response.status().status() != kiapi::common::AS_OK )
111 {
112 if( aError )
113 *aError = response.status().error_message();
114
115 return false;
116 }
117
118 if( !response.message().UnpackTo( aOut ) )
119 {
120 if( aError )
121 *aError = wxS( "Failed to unpack BoardDesignRulesResponse" );
122
123 return false;
124 }
125
126 return true;
127}
128
129
130bool SendGetCustomDesignRules( API_TEST_CLIENT& aClient, const kiapi::common::types::DocumentSpecifier& aDocument,
131 kiapi::board::commands::CustomRulesResponse* aOut, wxString* aError = nullptr )
132{
133 kiapi::board::commands::GetCustomDesignRules request;
134 *request.mutable_board() = aDocument;
135
136 kiapi::common::ApiResponse response;
137
138 if( !aClient.SendCommand( request, &response ) )
139 {
140 if( aError )
141 *aError = aClient.LastError();
142
143 return false;
144 }
145
146 if( response.status().status() != kiapi::common::AS_OK )
147 {
148 if( aError )
149 *aError = response.status().error_message();
150
151 return false;
152 }
153
154 if( !response.message().UnpackTo( aOut ) )
155 {
156 if( aError )
157 *aError = wxS( "Failed to unpack CustomRulesResponse" );
158
159 return false;
160 }
161
162 return true;
163}
164
165
166BOOST_AUTO_TEST_SUITE( ApiE2E )
167
168
170{
171 BOOST_REQUIRE_MESSAGE( Start(), LastError() );
172
173 BOOST_CHECK_MESSAGE( Client().GetVersion(), "GetVersion failed: " + Client().LastError() );
174}
175
176
178{
179 BOOST_REQUIRE_MESSAGE( Start(), LastError() );
180
181 kiapi::common::types::DocumentSpecifier document;
182 wxString testDataDir = wxString::FromUTF8( KI_TEST::GetPcbnewTestDataDir() );
183 wxFileName boardPath( testDataDir, wxS( "api_kitchen_sink.kicad_pcb" ) );
184
185 BOOST_REQUIRE_MESSAGE( Client().OpenDocument( boardPath.GetFullPath(), &document ),
186 "OpenDocument failed: " + Client().LastError() );
187
188 BOOST_REQUIRE( document.type() == kiapi::common::types::DOCTYPE_PCB );
189 BOOST_REQUIRE( boardPath.GetFullName().Matches( document.board_filename() ) );
190
191 int footprintCount = 0;
192
193 BOOST_REQUIRE_MESSAGE( Client().GetItemsCount( document, kiapi::common::types::KOT_PCB_FOOTPRINT, &footprintCount ),
194 "GetItems failed: " + Client().LastError() );
195
196 BOOST_CHECK_GT( footprintCount, 0 );
197}
198
199
201{
202 BOOST_REQUIRE_MESSAGE( Start(), LastError() );
203
204 wxString testDataDir = wxString::FromUTF8( KI_TEST::GetPcbnewTestDataDir() );
205 wxFileName boardPathA( testDataDir, wxS( "api_kitchen_sink.kicad_pcb" ) );
206 wxFileName boardPathB( testDataDir, wxS( "padstacks.kicad_pcb" ) );
207
208 kiapi::common::types::DocumentSpecifier documentA;
209
210 BOOST_REQUIRE_MESSAGE( Client().OpenDocument( boardPathA.GetFullPath(), &documentA ),
211 "OpenDocument for first board failed: " + Client().LastError() );
212
213 FOOTPRINT footprintA( nullptr );
214
215 BOOST_REQUIRE_MESSAGE( Client().GetFirstFootprint( documentA, &footprintA ),
216 "GetFirstFootprint for first board failed: " + Client().LastError() );
217
218 kiapi::common::types::DocumentSpecifier documentB;
219
220 BOOST_REQUIRE_MESSAGE( Client().OpenDocument( boardPathB.GetFullPath(), &documentB ),
221 "OpenDocument for second board failed: " + Client().LastError() );
222
223 FOOTPRINT footprintB( nullptr );
224
225 BOOST_REQUIRE_MESSAGE( Client().GetFirstFootprint( documentB, &footprintB ),
226 "GetFirstFootprint for second board failed: " + Client().LastError() );
227
228 BOOST_CHECK_NE( footprintA.Similarity( footprintB ), 1.0 );
229
230 kiapi::common::ApiStatusCode closeStatus = kiapi::common::AS_UNKNOWN;
231
232 BOOST_REQUIRE_MESSAGE( Client().CloseDocument( &documentB, &closeStatus ),
233 "CloseDocument failed: " + Client().LastError() );
234 BOOST_CHECK_EQUAL( closeStatus, kiapi::common::AS_OK );
235
236 BOOST_REQUIRE_MESSAGE( Client().CloseDocument( nullptr, &closeStatus ),
237 "CloseDocument after already closed failed: " + Client().LastError() );
238 BOOST_CHECK_EQUAL( closeStatus, kiapi::common::AS_BAD_REQUEST );
239}
240
241
243{
244 BOOST_REQUIRE_MESSAGE( Start(), LastError() );
245
246 kiapi::common::types::DocumentSpecifier document;
247 wxFileName boardPath( wxString::FromUTF8( KI_TEST::GetPcbnewTestDataDir() ), wxS( "api_kitchen_sink.kicad_pcb" ) );
248
249 BOOST_REQUIRE_MESSAGE( Client().OpenDocument( boardPath.GetFullPath(), &document ),
250 "OpenDocument failed: " + Client().LastError() );
251
252 kiapi::board::commands::BoardDesignRulesResponse rulesResponse;
253 wxString error;
254
255 BOOST_REQUIRE_MESSAGE( SendGetBoardDesignRules( Client(), document, &rulesResponse, &error ),
256 "GetBoardDesignRules failed: " + error );
257
258 BOOST_CHECK_GE( rulesResponse.rules().constraints().min_clearance().value_nm(), 0 );
259 BOOST_CHECK_GE( rulesResponse.rules().constraints().min_track_width().value_nm(), 0 );
260 BOOST_CHECK( rulesResponse.custom_rules_status() == kiapi::board::commands::CRS_VALID );
261}
262
263
265{
266 BOOST_REQUIRE_MESSAGE( Start(), LastError() );
267
268 TEMP_KITCHEN_SINK_COPY fixtureCopy;
269 wxString copyError;
270
271 BOOST_REQUIRE_MESSAGE( fixtureCopy.Create( &copyError ), copyError );
272
273 kiapi::common::types::DocumentSpecifier document;
274
275 BOOST_REQUIRE_MESSAGE( Client().OpenDocument( fixtureCopy.BoardPath(), &document ),
276 "OpenDocument failed: " + Client().LastError() );
277
278 kiapi::board::commands::BoardDesignRulesResponse getResponse;
279
280 BOOST_REQUIRE_MESSAGE( SendGetBoardDesignRules( Client(), document, &getResponse, &copyError ),
281 "Initial GetBoardDesignRules failed: " + copyError );
282
283 int newWidth = getResponse.rules().constraints().min_track_width().value_nm() + 1000;
284
285 kiapi::board::commands::SetBoardDesignRules setRequest;
286 *setRequest.mutable_board() = document;
287 *setRequest.mutable_rules()->mutable_constraints() = getResponse.rules().constraints();
288 setRequest.mutable_rules()->mutable_constraints()->mutable_min_track_width()->set_value_nm( newWidth );
289
290 kiapi::common::ApiResponse setApiResponse;
291
292 BOOST_REQUIRE_MESSAGE( Client().SendCommand( setRequest, &setApiResponse ),
293 "SetBoardDesignRules failed to send: " + Client().LastError() );
294 BOOST_REQUIRE( setApiResponse.status().status() == kiapi::common::AS_OK );
295
296 kiapi::board::commands::BoardDesignRulesResponse setResponse;
297 BOOST_REQUIRE( setApiResponse.message().UnpackTo( &setResponse ) );
298
299 BOOST_CHECK_EQUAL( setResponse.rules().constraints().min_track_width().value_nm(), newWidth );
300
301 kiapi::board::commands::BoardDesignRulesResponse verifyResponse;
302
303 BOOST_REQUIRE_MESSAGE( SendGetBoardDesignRules( Client(), document, &verifyResponse, &copyError ),
304 "Verification GetBoardDesignRules failed: " + copyError );
305
306 BOOST_CHECK_EQUAL( verifyResponse.rules().constraints().min_track_width().value_nm(), newWidth );
307}
308
309
310BOOST_FIXTURE_TEST_CASE( SetBoardDesignRules_ValidationFailure, API_SERVER_E2E_FIXTURE )
311{
312 BOOST_REQUIRE_MESSAGE( Start(), LastError() );
313
314 TEMP_KITCHEN_SINK_COPY fixtureCopy;
315 wxString copyError;
316
317 BOOST_REQUIRE_MESSAGE( fixtureCopy.Create( &copyError ), copyError );
318
319 kiapi::common::types::DocumentSpecifier document;
320
321 BOOST_REQUIRE_MESSAGE( Client().OpenDocument( fixtureCopy.BoardPath(), &document ),
322 "OpenDocument failed: " + Client().LastError() );
323
324 kiapi::board::commands::BoardDesignRulesResponse getResponse;
325
326 BOOST_REQUIRE_MESSAGE( SendGetBoardDesignRules( Client(), document, &getResponse, &copyError ),
327 "Initial GetBoardDesignRules failed: " + copyError );
328
329 kiapi::board::commands::SetBoardDesignRules setRequest;
330 *setRequest.mutable_board() = document;
331 *setRequest.mutable_rules()->mutable_constraints() = getResponse.rules().constraints();
332 setRequest.mutable_rules()->mutable_constraints()->mutable_min_track_width()->set_value_nm( 1000000000 );
333
334 kiapi::common::ApiResponse response;
335
336 BOOST_REQUIRE_MESSAGE( Client().SendCommand( setRequest, &response ),
337 "SetBoardDesignRules failed to send: " + Client().LastError() );
338
339 BOOST_CHECK( response.status().status() == kiapi::common::AS_BAD_REQUEST );
340 BOOST_CHECK( !response.status().error_message().empty() );
341}
342
343
344BOOST_FIXTURE_TEST_CASE( SetBoardDesignRules_SeverityOverrides, API_SERVER_E2E_FIXTURE )
345{
346 BOOST_REQUIRE_MESSAGE( Start(), LastError() );
347
348 TEMP_KITCHEN_SINK_COPY fixtureCopy;
349 wxString copyError;
350
351 BOOST_REQUIRE_MESSAGE( fixtureCopy.Create( &copyError ), copyError );
352
353 kiapi::common::types::DocumentSpecifier document;
354
355 BOOST_REQUIRE_MESSAGE( Client().OpenDocument( fixtureCopy.BoardPath(), &document ),
356 "OpenDocument failed: " + Client().LastError() );
357
358 kiapi::board::commands::BoardDesignRulesResponse getResponse;
359 BOOST_REQUIRE_MESSAGE( SendGetBoardDesignRules( Client(), document, &getResponse, &copyError ),
360 "Initial GetBoardDesignRules failed: " + copyError );
361
362 auto applySeverityAndVerify = [&]( kiapi::common::types::RuleSeverity aSeverity )
363 {
364 kiapi::board::commands::SetBoardDesignRules setRequest;
365 *setRequest.mutable_board() = document;
366
367 kiapi::board::DrcSeveritySetting* setting = setRequest.mutable_rules()->add_severities();
368 setting->set_rule_type( kiapi::board::DrcErrorType::DRCET_UNCONNECTED_ITEMS );
369 setting->set_severity( aSeverity );
370
371 kiapi::common::ApiResponse setApiResponse;
372 BOOST_REQUIRE_MESSAGE( Client().SendCommand( setRequest, &setApiResponse ),
373 "SetBoardDesignRules failed to send: " + Client().LastError() );
374 BOOST_REQUIRE( setApiResponse.status().status() == kiapi::common::AS_OK );
375
376 kiapi::board::commands::BoardDesignRulesResponse verifyResponse;
377 BOOST_REQUIRE_MESSAGE( SendGetBoardDesignRules( Client(), document, &verifyResponse, &copyError ),
378 "Verification GetBoardDesignRules failed: " + copyError );
379
380 bool found = false;
381
382 for( const kiapi::board::DrcSeveritySetting& severity : verifyResponse.rules().severities() )
383 {
384 if( severity.rule_type() == kiapi::board::DrcErrorType::DRCET_UNCONNECTED_ITEMS )
385 {
386 found = true;
387 BOOST_CHECK( severity.severity() == aSeverity );
388 break;
389 }
390 }
391
392 BOOST_CHECK( found );
393 };
394
395 applySeverityAndVerify( kiapi::common::types::RS_IGNORE );
396 applySeverityAndVerify( kiapi::common::types::RS_ERROR );
397}
398
399
400BOOST_FIXTURE_TEST_CASE( GetCustomDesignRules_ReturnsFixtureRules, API_SERVER_E2E_FIXTURE )
401{
402 BOOST_REQUIRE_MESSAGE( Start(), LastError() );
403
404 kiapi::common::types::DocumentSpecifier document;
405 wxFileName boardPath( wxString::FromUTF8( KI_TEST::GetPcbnewTestDataDir() ), wxS( "api_kitchen_sink.kicad_pcb" ) );
406
407 BOOST_REQUIRE_MESSAGE( Client().OpenDocument( boardPath.GetFullPath(), &document ),
408 "OpenDocument failed: " + Client().LastError() );
409
410 kiapi::board::commands::CustomRulesResponse response;
411 wxString error;
412
413 BOOST_REQUIRE_MESSAGE( SendGetCustomDesignRules( Client(), document, &response, &error ),
414 "GetCustomDesignRules failed: " + error );
415
416 BOOST_CHECK( response.status() == kiapi::board::commands::CRS_VALID );
417 BOOST_CHECK_GT( response.rules_size(), 0 );
418
419 bool foundFixtureRule = false;
420
421 for( const kiapi::board::CustomRule& rule : response.rules() )
422 {
423 if( rule.name() == "myrule" )
424 {
425 foundFixtureRule = true;
426 BOOST_CHECK_EQUAL( rule.condition(), "A.Name == 'test'" );
427 BOOST_CHECK_EQUAL( rule.constraints_size(), 2 );
428 BOOST_REQUIRE( !rule.comments().empty() );
429 break;
430 }
431 }
432
433 BOOST_CHECK( foundFixtureRule );
434}
435
436
437BOOST_FIXTURE_TEST_CASE( SetCustomDesignRules_RoundTripSingleRule, API_SERVER_E2E_FIXTURE )
438{
439 BOOST_REQUIRE_MESSAGE( Start(), LastError() );
440
441 TEMP_KITCHEN_SINK_COPY fixtureCopy;
442 wxString copyError;
443
444 BOOST_REQUIRE_MESSAGE( fixtureCopy.Create( &copyError ), copyError );
445
446 kiapi::common::types::DocumentSpecifier document;
447
448 BOOST_REQUIRE_MESSAGE( Client().OpenDocument( fixtureCopy.BoardPath(), &document ),
449 "OpenDocument failed: " + Client().LastError() );
450
451 kiapi::board::commands::CustomRulesResponse response;
452 wxString error;
453
454 BOOST_REQUIRE_MESSAGE( SendGetCustomDesignRules( Client(), document, &response, &error ),
455 "GetCustomDesignRules failed: " + error );
456
457 BOOST_CHECK( response.status() == kiapi::board::commands::CRS_VALID );
458 BOOST_CHECK_GT( response.rules_size(), 0 );
459
460 kiapi::board::commands::SetCustomDesignRules setRequest;
461 *setRequest.mutable_board() = document;
462
463 setRequest.mutable_rules()->CopyFrom( response.rules() );
464
465 kiapi::board::CustomRule* rule = setRequest.add_rules();
466 rule->set_name( "api_roundtrip_rule" );
467 rule->set_condition( "A.NetClass == 'HV'" );
468 rule->set_layer_mode( kiapi::board::CRLM_OUTER );
469 rule->set_severity( kiapi::common::types::RS_WARNING );
470
471 kiapi::board::CustomRuleConstraint* annular = rule->add_constraints();
472 annular->set_type( kiapi::board::CRCT_TRACK_WIDTH );
473 annular->mutable_numeric()->set_min( 3000000 );
474 annular->mutable_numeric()->set_opt( 4000000 );
475 annular->mutable_numeric()->set_max( 5000000 );
476
477 kiapi::board::CustomRuleConstraint* disallow = rule->add_constraints();
478 disallow->set_type( kiapi::board::CRCT_DISALLOW );
479 disallow->mutable_disallow()->add_types( kiapi::board::CRDT_BLIND_VIAS );
480
481 kiapi::common::ApiResponse setApiResponse;
482
483 BOOST_REQUIRE_MESSAGE( Client().SendCommand( setRequest, &setApiResponse ),
484 "SetCustomDesignRules failed to send: " + Client().LastError() );
485 BOOST_REQUIRE( setApiResponse.status().status() == kiapi::common::AS_OK );
486
487 kiapi::board::commands::CustomRulesResponse setResponse;
488 BOOST_REQUIRE( setApiResponse.message().UnpackTo( &setResponse ) );
489
490 int num_rules = setResponse.rules_size();
491 BOOST_CHECK( setResponse.status() == kiapi::board::commands::CRS_VALID );
492 BOOST_CHECK_EQUAL( num_rules, response.rules_size() + 1 );
493 BOOST_CHECK_EQUAL( setResponse.rules( num_rules - 1 ).name(), "api_roundtrip_rule" );
494}
495
496
const wxString & LastError() const
bool SendCommand(const T &aMessage, kiapi::common::ApiResponse *aResponse)
double Similarity(const BOARD_ITEM &aOther) const override
Return a measure of how likely the other object is to represent the same object.
bool Create(wxString *aError)
const wxString & BoardPath() const
bool SendCommand(int aService, const std::string &aMessage)
Used by a client to sent (by a socket connection) a data to a server.
Definition eda_dde.cpp:230
std::string GetPcbnewTestDataDir()
Utility which returns a path to the data directory where the test board files are stored.
bool SendGetCustomDesignRules(API_TEST_CLIENT &aClient, const kiapi::common::types::DocumentSpecifier &aDocument, kiapi::board::commands::CustomRulesResponse *aOut, wxString *aError=nullptr)
BOOST_FIXTURE_TEST_CASE(ServerStartsAndResponds, API_SERVER_E2E_FIXTURE)
bool SendGetBoardDesignRules(API_TEST_CLIENT &aClient, const kiapi::common::types::DocumentSpecifier &aDocument, kiapi::board::commands::BoardDesignRulesResponse *aOut, wxString *aError=nullptr)
BOOST_AUTO_TEST_SUITE(CadstarPartParser)
BOOST_REQUIRE(intersection.has_value()==c.ExpectedIntersection.has_value())
BOOST_AUTO_TEST_SUITE_END()
BOOST_CHECK_MESSAGE(totalMismatches==0, std::to_string(totalMismatches)+" board(s) with strategy disagreements")
BOOST_CHECK_EQUAL(result, "25.4")