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
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 */
19
20#include "api_e2e_utils.h"
21
22#include <utility>
23
24#include <boost/test/unit_test.hpp>
25#include <qa_utils/file_utils.h>
26
27#include <magic_enum.hpp>
28
29#include <wx/filefn.h>
30#include <wx/filename.h>
31
32#include <api/board/board.pb.h>
33#include <api/board/board_commands.pb.h>
34#include <api/board/board_rules.pb.h>
35#include <pcb_field.h>
36
37
39{
40public:
42 m_tempRoot( "kicad_api_e2e" )
43 {
44 }
45
46 bool Create( wxString* aError )
47 {
48 wxString testDataDir = wxString::FromUTF8( KI_TEST::GetPcbnewTestDataDir() );
49 wxFileName srcPcb( testDataDir, wxS( "api_kitchen_sink.kicad_pcb" ) );
50 wxFileName srcPro( testDataDir, wxS( "api_kitchen_sink.kicad_pro" ) );
51 wxFileName srcDru( testDataDir, wxS( "api_kitchen_sink.kicad_dru" ) );
52
53 wxFileName dstPcb( m_tempRoot.PathStr(), srcPcb.GetFullName() );
54 wxFileName dstPro( m_tempRoot.PathStr(), srcPro.GetFullName() );
55 wxFileName dstDru( m_tempRoot.PathStr(), srcDru.GetFullName() );
56
57 if( !wxCopyFile( srcPcb.GetFullPath(), dstPcb.GetFullPath(), true )
58 || !wxCopyFile( srcPro.GetFullPath(), dstPro.GetFullPath(), true )
59 || !wxCopyFile( srcDru.GetFullPath(), dstDru.GetFullPath(), true ) )
60 {
61 if( aError )
62 *aError = wxS( "Failed to copy fixture files into temporary directory" );
63
64 return false;
65 }
66
67 m_boardPath = dstPcb.GetFullPath();
68 return true;
69 }
70
71 const wxString& BoardPath() const { return m_boardPath; }
72
73private:
75 wxString m_boardPath;
76};
77
78
79bool SendGetBoardDesignRules( API_TEST_CLIENT& aClient, const kiapi::common::types::DocumentSpecifier& aDocument,
80 kiapi::board::commands::BoardDesignRulesResponse* aOut, wxString* aError = nullptr )
81{
82 kiapi::board::commands::GetBoardDesignRules request;
83 *request.mutable_board() = aDocument;
84
85 kiapi::common::ApiResponse response;
86
87 if( !aClient.SendCommand( request, &response ) )
88 {
89 if( aError )
90 *aError = aClient.LastError();
91
92 return false;
93 }
94
95 if( response.status().status() != kiapi::common::AS_OK )
96 {
97 if( aError )
98 *aError = response.status().error_message();
99
100 return false;
101 }
102
103 if( !response.message().UnpackTo( aOut ) )
104 {
105 if( aError )
106 *aError = wxS( "Failed to unpack BoardDesignRulesResponse" );
107
108 return false;
109 }
110
111 return true;
112}
113
114
115bool SendGetCustomDesignRules( API_TEST_CLIENT& aClient, const kiapi::common::types::DocumentSpecifier& aDocument,
116 kiapi::board::commands::CustomRulesResponse* aOut, wxString* aError = nullptr )
117{
118 kiapi::board::commands::GetCustomDesignRules request;
119 *request.mutable_board() = aDocument;
120
121 kiapi::common::ApiResponse response;
122
123 if( !aClient.SendCommand( request, &response ) )
124 {
125 if( aError )
126 *aError = aClient.LastError();
127
128 return false;
129 }
130
131 if( response.status().status() != kiapi::common::AS_OK )
132 {
133 if( aError )
134 *aError = response.status().error_message();
135
136 return false;
137 }
138
139 if( !response.message().UnpackTo( aOut ) )
140 {
141 if( aError )
142 *aError = wxS( "Failed to unpack CustomRulesResponse" );
143
144 return false;
145 }
146
147 return true;
148}
149
150
151BOOST_AUTO_TEST_SUITE( ApiE2E )
152
153
155{
156 BOOST_REQUIRE_MESSAGE( Start(), LastError() );
157
158 BOOST_CHECK_MESSAGE( Client().GetVersion(), "GetVersion failed: " + Client().LastError() );
159}
160
161
163{
164 BOOST_REQUIRE_MESSAGE( Start(), LastError() );
165
166 kiapi::common::types::DocumentSpecifier document;
167 wxString testDataDir = wxString::FromUTF8( KI_TEST::GetPcbnewTestDataDir() );
168 wxFileName boardPath( testDataDir, wxS( "api_kitchen_sink.kicad_pcb" ) );
169
170 BOOST_REQUIRE_MESSAGE( Client().OpenDocument( boardPath.GetFullPath(), &document ),
171 "OpenDocument failed: " + Client().LastError() );
172
173 BOOST_REQUIRE( document.type() == kiapi::common::types::DOCTYPE_PCB );
174 BOOST_REQUIRE( boardPath.GetFullName().Matches( document.board_filename() ) );
175
176 int footprintCount = 0;
177
178 BOOST_REQUIRE_MESSAGE( Client().GetItemsCount( document, kiapi::common::types::KOT_PCB_FOOTPRINT, &footprintCount ),
179 "GetItems failed: " + Client().LastError() );
180
181 BOOST_CHECK_GT( footprintCount, 0 );
182}
183
184
185// A footprint stripped of its reference or value is a state neither the editor nor the const
186// field accessors can cope with, so the deletion has to be refused outright
187BOOST_FIXTURE_TEST_CASE( DeleteMandatoryFieldIsRefused, API_SERVER_E2E_FIXTURE )
188{
189 BOOST_REQUIRE_MESSAGE( Start(), LastError() );
190
191 kiapi::common::types::DocumentSpecifier document;
192 wxFileName boardPath( wxString::FromUTF8( KI_TEST::GetPcbnewTestDataDir() ), wxS( "api_kitchen_sink.kicad_pcb" ) );
193
194 BOOST_REQUIRE_MESSAGE( Client().OpenDocument( boardPath.GetFullPath(), &document ),
195 "OpenDocument failed: " + Client().LastError() );
196
197 FOOTPRINT footprint( nullptr );
198
199 BOOST_REQUIRE_MESSAGE( Client().GetFirstFootprint( document, &footprint ),
200 "GetFirstFootprint failed: " + Client().LastError() );
201
202 const PCB_FIELD* reference = std::as_const( footprint ).GetField( FIELD_T::REFERENCE );
203 BOOST_REQUIRE( reference );
204
205 kiapi::common::commands::DeleteItems request;
206 *request.mutable_header()->mutable_document() = document;
207 request.add_item_ids()->set_value( reference->m_Uuid.AsStdString() );
208
209 kiapi::common::ApiResponse response;
210
211 BOOST_REQUIRE_MESSAGE( Client().SendCommand( request, &response ),
212 "DeleteItems failed: " + Client().LastError() );
213 BOOST_REQUIRE_EQUAL( response.status().status(), kiapi::common::AS_OK );
214
215 kiapi::common::commands::DeleteItemsResponse deleteResponse;
216
217 BOOST_REQUIRE( response.message().UnpackTo( &deleteResponse ) );
218 BOOST_REQUIRE_EQUAL( deleteResponse.deleted_items_size(), 1 );
219 BOOST_CHECK_EQUAL( deleteResponse.deleted_items( 0 ).status(),
220 kiapi::common::commands::ItemDeletionStatus::IDS_IMMUTABLE );
221
222 FOOTPRINT after( nullptr );
223
224 BOOST_REQUIRE_MESSAGE( Client().GetFirstFootprint( document, &after ),
225 "GetFirstFootprint after delete failed: " + Client().LastError() );
226
227 const PCB_FIELD* survivor = std::as_const( after ).GetField( FIELD_T::REFERENCE );
228
229 BOOST_REQUIRE( survivor );
230 BOOST_CHECK( survivor->m_Uuid == reference->m_Uuid );
231}
232
233
235{
236 BOOST_REQUIRE_MESSAGE( Start(), LastError() );
237
238 wxString testDataDir = wxString::FromUTF8( KI_TEST::GetPcbnewTestDataDir() );
239 wxFileName boardPathA( testDataDir, wxS( "api_kitchen_sink.kicad_pcb" ) );
240 wxFileName boardPathB( testDataDir, wxS( "padstacks.kicad_pcb" ) );
241
242 kiapi::common::types::DocumentSpecifier documentA;
243
244 BOOST_REQUIRE_MESSAGE( Client().OpenDocument( boardPathA.GetFullPath(), &documentA ),
245 "OpenDocument for first board failed: " + Client().LastError() );
246
247 FOOTPRINT footprintA( nullptr );
248
249 BOOST_REQUIRE_MESSAGE( Client().GetFirstFootprint( documentA, &footprintA ),
250 "GetFirstFootprint for first board failed: " + Client().LastError() );
251
252 // Switching projects requires an explicit close
253 kiapi::common::ApiStatusCode closeStatusA = kiapi::common::AS_UNKNOWN;
254
255 BOOST_REQUIRE_MESSAGE( Client().CloseDocument( &documentA, &closeStatusA ),
256 "CloseDocument for first board failed: " + Client().LastError() );
257 BOOST_CHECK_EQUAL( closeStatusA, kiapi::common::AS_OK );
258
259 kiapi::common::types::DocumentSpecifier documentB;
260
261 BOOST_REQUIRE_MESSAGE( Client().OpenDocument( boardPathB.GetFullPath(), &documentB ),
262 "OpenDocument for second board failed: " + Client().LastError() );
263
264 FOOTPRINT footprintB( nullptr );
265
266 BOOST_REQUIRE_MESSAGE( Client().GetFirstFootprint( documentB, &footprintB ),
267 "GetFirstFootprint for second board failed: " + Client().LastError() );
268
269 BOOST_CHECK_NE( footprintA.Similarity( footprintB ), 1.0 );
270
271 kiapi::common::ApiStatusCode closeStatus = kiapi::common::AS_UNKNOWN;
272
273 BOOST_REQUIRE_MESSAGE( Client().CloseDocument( &documentB, &closeStatus ),
274 "CloseDocument failed: " + Client().LastError() );
275 BOOST_CHECK_EQUAL( closeStatus, kiapi::common::AS_OK );
276
277 BOOST_REQUIRE_MESSAGE( Client().CloseDocument( nullptr, &closeStatus ),
278 "CloseDocument after already closed failed: " + Client().LastError() );
279 BOOST_CHECK_EQUAL( closeStatus, kiapi::common::AS_BAD_REQUEST );
280}
281
282
284{
285 BOOST_REQUIRE_MESSAGE( Start(), LastError() );
286
287 kiapi::common::types::DocumentSpecifier document;
288 wxFileName boardPath( wxString::FromUTF8( KI_TEST::GetPcbnewTestDataDir() ), wxS( "api_kitchen_sink.kicad_pcb" ) );
289
290 BOOST_REQUIRE_MESSAGE( Client().OpenDocument( boardPath.GetFullPath(), &document ),
291 "OpenDocument failed: " + Client().LastError() );
292
293 kiapi::board::commands::BoardDesignRulesResponse rulesResponse;
294 wxString error;
295
296 BOOST_REQUIRE_MESSAGE( SendGetBoardDesignRules( Client(), document, &rulesResponse, &error ),
297 "GetBoardDesignRules failed: " + error );
298
299 BOOST_CHECK_GE( rulesResponse.rules().constraints().min_clearance().value_nm(), 0 );
300 BOOST_CHECK_GE( rulesResponse.rules().constraints().min_track_width().value_nm(), 0 );
301 BOOST_CHECK( rulesResponse.custom_rules_status() == kiapi::board::commands::CRS_VALID );
302}
303
304
306{
307 BOOST_REQUIRE_MESSAGE( Start(), LastError() );
308
309 TEMP_KITCHEN_SINK_COPY fixtureCopy;
310 wxString copyError;
311
312 BOOST_REQUIRE_MESSAGE( fixtureCopy.Create( &copyError ), copyError );
313
314 kiapi::common::types::DocumentSpecifier document;
315
316 BOOST_REQUIRE_MESSAGE( Client().OpenDocument( fixtureCopy.BoardPath(), &document ),
317 "OpenDocument failed: " + Client().LastError() );
318
319 kiapi::board::commands::BoardDesignRulesResponse getResponse;
320
321 BOOST_REQUIRE_MESSAGE( SendGetBoardDesignRules( Client(), document, &getResponse, &copyError ),
322 "Initial GetBoardDesignRules failed: " + copyError );
323
324 int newWidth = getResponse.rules().constraints().min_track_width().value_nm() + 1000;
325
326 kiapi::board::commands::SetBoardDesignRules setRequest;
327 *setRequest.mutable_board() = document;
328 *setRequest.mutable_rules()->mutable_constraints() = getResponse.rules().constraints();
329 setRequest.mutable_rules()->mutable_constraints()->mutable_min_track_width()->set_value_nm( newWidth );
330
331 kiapi::common::ApiResponse setApiResponse;
332
333 BOOST_REQUIRE_MESSAGE( Client().SendCommand( setRequest, &setApiResponse ),
334 "SetBoardDesignRules failed to send: " + Client().LastError() );
335 BOOST_REQUIRE( setApiResponse.status().status() == kiapi::common::AS_OK );
336
337 kiapi::board::commands::BoardDesignRulesResponse setResponse;
338 BOOST_REQUIRE( setApiResponse.message().UnpackTo( &setResponse ) );
339
340 BOOST_CHECK_EQUAL( setResponse.rules().constraints().min_track_width().value_nm(), newWidth );
341
342 kiapi::board::commands::BoardDesignRulesResponse verifyResponse;
343
344 BOOST_REQUIRE_MESSAGE( SendGetBoardDesignRules( Client(), document, &verifyResponse, &copyError ),
345 "Verification GetBoardDesignRules failed: " + copyError );
346
347 BOOST_CHECK_EQUAL( verifyResponse.rules().constraints().min_track_width().value_nm(), newWidth );
348}
349
350
351BOOST_FIXTURE_TEST_CASE( SetBoardDesignRules_ValidationFailure, API_SERVER_E2E_FIXTURE )
352{
353 BOOST_REQUIRE_MESSAGE( Start(), LastError() );
354
355 TEMP_KITCHEN_SINK_COPY fixtureCopy;
356 wxString copyError;
357
358 BOOST_REQUIRE_MESSAGE( fixtureCopy.Create( &copyError ), copyError );
359
360 kiapi::common::types::DocumentSpecifier document;
361
362 BOOST_REQUIRE_MESSAGE( Client().OpenDocument( fixtureCopy.BoardPath(), &document ),
363 "OpenDocument failed: " + Client().LastError() );
364
365 kiapi::board::commands::BoardDesignRulesResponse getResponse;
366
367 BOOST_REQUIRE_MESSAGE( SendGetBoardDesignRules( Client(), document, &getResponse, &copyError ),
368 "Initial GetBoardDesignRules failed: " + copyError );
369
370 kiapi::board::commands::SetBoardDesignRules setRequest;
371 *setRequest.mutable_board() = document;
372 *setRequest.mutable_rules()->mutable_constraints() = getResponse.rules().constraints();
373 setRequest.mutable_rules()->mutable_constraints()->mutable_min_track_width()->set_value_nm( 1000000000 );
374
375 kiapi::common::ApiResponse response;
376
377 BOOST_REQUIRE_MESSAGE( Client().SendCommand( setRequest, &response ),
378 "SetBoardDesignRules failed to send: " + Client().LastError() );
379
380 BOOST_CHECK( response.status().status() == kiapi::common::AS_BAD_REQUEST );
381 BOOST_CHECK( !response.status().error_message().empty() );
382}
383
384
385BOOST_FIXTURE_TEST_CASE( SetBoardDesignRules_SeverityOverrides, API_SERVER_E2E_FIXTURE )
386{
387 BOOST_REQUIRE_MESSAGE( Start(), LastError() );
388
389 TEMP_KITCHEN_SINK_COPY fixtureCopy;
390 wxString copyError;
391
392 BOOST_REQUIRE_MESSAGE( fixtureCopy.Create( &copyError ), copyError );
393
394 kiapi::common::types::DocumentSpecifier document;
395
396 BOOST_REQUIRE_MESSAGE( Client().OpenDocument( fixtureCopy.BoardPath(), &document ),
397 "OpenDocument failed: " + Client().LastError() );
398
399 kiapi::board::commands::BoardDesignRulesResponse getResponse;
400 BOOST_REQUIRE_MESSAGE( SendGetBoardDesignRules( Client(), document, &getResponse, &copyError ),
401 "Initial GetBoardDesignRules failed: " + copyError );
402
403 auto applySeverityAndVerify = [&]( kiapi::common::types::RuleSeverity aSeverity )
404 {
405 kiapi::board::commands::SetBoardDesignRules setRequest;
406 *setRequest.mutable_board() = document;
407
408 kiapi::board::DrcSeveritySetting* setting = setRequest.mutable_rules()->add_severities();
409 setting->set_rule_type( kiapi::board::DrcErrorType::DRCET_UNCONNECTED_ITEMS );
410 setting->set_severity( aSeverity );
411
412 kiapi::common::ApiResponse setApiResponse;
413 BOOST_REQUIRE_MESSAGE( Client().SendCommand( setRequest, &setApiResponse ),
414 "SetBoardDesignRules failed to send: " + Client().LastError() );
415 BOOST_REQUIRE( setApiResponse.status().status() == kiapi::common::AS_OK );
416
417 kiapi::board::commands::BoardDesignRulesResponse verifyResponse;
418 BOOST_REQUIRE_MESSAGE( SendGetBoardDesignRules( Client(), document, &verifyResponse, &copyError ),
419 "Verification GetBoardDesignRules failed: " + copyError );
420
421 bool found = false;
422
423 for( const kiapi::board::DrcSeveritySetting& severity : verifyResponse.rules().severities() )
424 {
425 if( severity.rule_type() == kiapi::board::DrcErrorType::DRCET_UNCONNECTED_ITEMS )
426 {
427 found = true;
428 BOOST_CHECK( severity.severity() == aSeverity );
429 break;
430 }
431 }
432
433 BOOST_CHECK( found );
434 };
435
436 applySeverityAndVerify( kiapi::common::types::RS_IGNORE );
437 applySeverityAndVerify( kiapi::common::types::RS_ERROR );
438}
439
440
441BOOST_FIXTURE_TEST_CASE( GetCustomDesignRules_ReturnsFixtureRules, API_SERVER_E2E_FIXTURE )
442{
443 BOOST_REQUIRE_MESSAGE( Start(), LastError() );
444
445 kiapi::common::types::DocumentSpecifier document;
446 wxFileName boardPath( wxString::FromUTF8( KI_TEST::GetPcbnewTestDataDir() ), wxS( "api_kitchen_sink.kicad_pcb" ) );
447
448 BOOST_REQUIRE_MESSAGE( Client().OpenDocument( boardPath.GetFullPath(), &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 bool foundFixtureRule = false;
461
462 for( const kiapi::board::CustomRule& rule : response.rules() )
463 {
464 if( rule.name() == "myrule" )
465 {
466 foundFixtureRule = true;
467 BOOST_CHECK_EQUAL( rule.condition(), "A.Name == 'test'" );
468 BOOST_CHECK_EQUAL( rule.constraints_size(), 2 );
469 BOOST_REQUIRE( !rule.comments().empty() );
470 break;
471 }
472 }
473
474 BOOST_CHECK( foundFixtureRule );
475}
476
477
478BOOST_FIXTURE_TEST_CASE( SetCustomDesignRules_RoundTripSingleRule, API_SERVER_E2E_FIXTURE )
479{
480 BOOST_REQUIRE_MESSAGE( Start(), LastError() );
481
482 TEMP_KITCHEN_SINK_COPY fixtureCopy;
483 wxString copyError;
484
485 BOOST_REQUIRE_MESSAGE( fixtureCopy.Create( &copyError ), copyError );
486
487 kiapi::common::types::DocumentSpecifier document;
488
489 BOOST_REQUIRE_MESSAGE( Client().OpenDocument( fixtureCopy.BoardPath(), &document ),
490 "OpenDocument failed: " + Client().LastError() );
491
492 kiapi::board::commands::CustomRulesResponse response;
493 wxString error;
494
495 BOOST_REQUIRE_MESSAGE( SendGetCustomDesignRules( Client(), document, &response, &error ),
496 "GetCustomDesignRules failed: " + error );
497
498 BOOST_CHECK( response.status() == kiapi::board::commands::CRS_VALID );
499 BOOST_CHECK_GT( response.rules_size(), 0 );
500
501 kiapi::board::commands::SetCustomDesignRules setRequest;
502 *setRequest.mutable_board() = document;
503
504 setRequest.mutable_rules()->CopyFrom( response.rules() );
505
506 kiapi::board::CustomRule* rule = setRequest.add_rules();
507 rule->set_name( "api_roundtrip_rule" );
508 rule->set_condition( "A.NetClass == 'HV'" );
509 rule->set_layer_mode( kiapi::board::CRLM_OUTER );
510 rule->set_severity( kiapi::common::types::RS_WARNING );
511
512 kiapi::board::CustomRuleConstraint* annular = rule->add_constraints();
513 annular->set_type( kiapi::board::CRCT_TRACK_WIDTH );
514 annular->mutable_numeric()->set_min( 3000000 );
515 annular->mutable_numeric()->set_opt( 4000000 );
516 annular->mutable_numeric()->set_max( 5000000 );
517
518 kiapi::board::CustomRuleConstraint* disallow = rule->add_constraints();
519 disallow->set_type( kiapi::board::CRCT_DISALLOW );
520 disallow->mutable_disallow()->add_types( kiapi::board::CRDT_BLIND_VIAS );
521
522 kiapi::common::ApiResponse setApiResponse;
523
524 BOOST_REQUIRE_MESSAGE( Client().SendCommand( setRequest, &setApiResponse ),
525 "SetCustomDesignRules failed to send: " + Client().LastError() );
526 BOOST_REQUIRE( setApiResponse.status().status() == kiapi::common::AS_OK );
527
528 kiapi::board::commands::CustomRulesResponse setResponse;
529 BOOST_REQUIRE( setApiResponse.message().UnpackTo( &setResponse ) );
530
531 int num_rules = setResponse.rules_size();
532 BOOST_CHECK( setResponse.status() == kiapi::board::commands::CRS_VALID );
533 BOOST_CHECK_EQUAL( num_rules, response.rules_size() + 1 );
534 BOOST_CHECK_EQUAL( setResponse.rules( num_rules - 1 ).name(), "api_roundtrip_rule" );
535}
536
537
538BOOST_FIXTURE_TEST_CASE( OpenProjectWithBoardAndSchematic, API_SERVER_E2E_FIXTURE )
539{
540 BOOST_REQUIRE_MESSAGE( Start(), LastError() );
541
542 wxString testDataDir = wxString::FromUTF8( KI_TEST::GetTestDataRootDir() ) + wxS( "cli/basic_test/" );
543
544 kiapi::common::types::DocumentSpecifier projectDoc;
545
546 BOOST_REQUIRE_MESSAGE( Client().OpenDocument( testDataDir + wxS( "basic_test.kicad_pro" ),
547 kiapi::common::types::DOCTYPE_PROJECT, &projectDoc ),
548 "OpenDocument for project failed: " + Client().LastError() );
549
550 BOOST_REQUIRE( projectDoc.type() == kiapi::common::types::DOCTYPE_PROJECT );
551
552 kiapi::common::types::DocumentSpecifier boardDoc;
553
554 BOOST_REQUIRE_MESSAGE( Client().OpenDocument( testDataDir + wxS( "basic_test.kicad_pcb" ),
555 kiapi::common::types::DOCTYPE_PCB, &boardDoc ),
556 "OpenDocument for board failed: " + Client().LastError() );
557
558 BOOST_REQUIRE( boardDoc.type() == kiapi::common::types::DOCTYPE_PCB );
559 BOOST_REQUIRE( !boardDoc.board_filename().empty() );
560
561 int footprintCount = 0;
562 BOOST_REQUIRE_MESSAGE( Client().GetItemsCount( boardDoc, kiapi::common::types::KOT_PCB_FOOTPRINT, &footprintCount ),
563 "GetItems for board failed: " + Client().LastError() );
564 BOOST_CHECK_GT( footprintCount, 0 );
565
566 kiapi::common::types::DocumentSpecifier schDoc;
567
568 BOOST_REQUIRE_MESSAGE( Client().OpenDocument( testDataDir + wxS( "basic_test.kicad_sch" ),
569 kiapi::common::types::DOCTYPE_SCHEMATIC, &schDoc ),
570 "OpenDocument for schematic failed: " + Client().LastError() );
571
572 BOOST_REQUIRE( schDoc.type() == kiapi::common::types::DOCTYPE_SCHEMATIC );
573
574 // Verify the board is still open by querying it again.
575 footprintCount = 0;
576 BOOST_REQUIRE_MESSAGE( Client().GetItemsCount( boardDoc, kiapi::common::types::KOT_PCB_FOOTPRINT, &footprintCount ),
577 "GetItems for board after opening schematic failed: " + Client().LastError() );
578 BOOST_CHECK_GT( footprintCount, 0 );
579
580 // Verify that opening a document from a different project is rejected.
581 wxString otherDataDir = wxString::FromUTF8( KI_TEST::GetPcbnewTestDataDir() );
582
583 kiapi::common::types::DocumentSpecifier otherBoardDoc;
584
585 BOOST_REQUIRE_MESSAGE(
586 !Client().OpenDocument( otherDataDir + wxS( "api_kitchen_sink.kicad_pcb" ),
587 kiapi::common::types::DOCTYPE_PCB, &otherBoardDoc ),
588 "OpenDocument for a different-project board should have failed" );
589
590 BOOST_CHECK( Client().LastError().Contains( wxS( "already open" ) ) );
591}
592
593
595{
596 BOOST_REQUIRE_MESSAGE( Start(), LastError() );
597
598 auto test =
599 [&]( kiapi::common::types::DocumentType aType, const wxString& aExpectedExt )
600 {
601 KI_TEST::SCOPED_TEMP_DIR tempDir( "kicad_api_e2e_createdoc" );
602
603 wxString tempFn = tempDir.ChildPathStr( "new_doc" );
604 wxFileName fileWithExt( tempDir.ChildPathStr( "new_doc" + aExpectedExt ) );
605 wxFileName projectWithExt( tempDir.ChildPathStr( "new_doc.kicad_pro" ) );
606
607 kiapi::common::types::DocumentSpecifier document;
608
609 BOOST_REQUIRE_MESSAGE( Client().CreateDocument( tempFn, aType, &document ),
610 "CreateDocument failed: " + Client().LastError() );
611
612 BOOST_CHECK( document.type() == aType );
613 BOOST_CHECK( document.project().name() == projectWithExt.GetName() );
614 BOOST_CHECK( document.project().path() == projectWithExt.GetPath( true ) );
615
616 kiapi::common::ApiResponse response;
617 kiapi::common::commands::GetDocumentModifiedState state;
618 state.mutable_document()->CopyFrom( document );
619 BOOST_CHECK( Client().SendCommand( state, &response ) );
620
621 // Just creating the document doesn't save it
622 BOOST_CHECK( !wxFileName( fileWithExt ).FileExists() );
623 BOOST_CHECK( !wxFileName( projectWithExt ).FileExists() );
624
625 kiapi::common::commands::SaveDocument save;
626 save.mutable_document()->CopyFrom( document );
627 BOOST_CHECK( Client().SendCommand( save, &response ) );
628
629 BOOST_CHECK( wxFileName( fileWithExt ).FileExists() );
630 BOOST_CHECK( wxFileName( projectWithExt ).FileExists() );
631 };
632
633 std::map<kiapi::common::types::DocumentType, wxString> cases = {
634 { kiapi::common::types::DOCTYPE_PCB, wxS( ".kicad_pcb" )},
635 { kiapi::common::types::DOCTYPE_SCHEMATIC, wxS( ".kicad_sch" ) }
636 };
637
638 for( const auto& [docType, ext] : cases )
639 {
640 BOOST_TEST_CONTEXT( magic_enum::enum_name( docType ) )
641 {
642 test( docType, ext );
643 }
644 }
645}
646
647
648BOOST_FIXTURE_TEST_CASE( CreateDocumentRejectsUnsavedModifications, API_SERVER_E2E_FIXTURE )
649{
650 using namespace kiapi::common::commands;
651
652 BOOST_REQUIRE_MESSAGE( Start(), LastError() );
653
654 auto test =
655 [&]( kiapi::common::types::DocumentType aType, const wxString& aExistingPath )
656 {
657 KI_TEST::SCOPED_TEMP_DIR tempDir( "kicad_api_e2e_createdoc" );
658 wxString docFileName = tempDir.ChildPathStr( "new_doc" );
659
660 kiapi::common::types::DocumentSpecifier existingDoc;
661
662 BOOST_REQUIRE_MESSAGE( Client().OpenDocument( aExistingPath, aType, &existingDoc ),
663 "OpenDocument failed: " + Client().LastError() );
664
665 SetPageSettings modify;
666 modify.mutable_document()->CopyFrom( existingDoc );
667 modify.mutable_page_settings()->set_orientation( kiapi::common::types::PO_PORTRAIT );
668 kiapi::common::ApiResponse response;
669 BOOST_CHECK( Client().SendCommand( modify, &response ) );
670
671 GetDocumentModifiedState stateQuery;
672 stateQuery.mutable_document()->CopyFrom( existingDoc );
673 BOOST_CHECK( Client().SendCommand( stateQuery, &response ) );
674 GetDocumentModifiedStateResponse state;
675 BOOST_CHECK( response.message().UnpackTo( &state ) );
676 BOOST_CHECK( state.state() == DocumentModifiedState::DMS_MODIFIED );
677
678 kiapi::common::types::DocumentSpecifier newDoc;
679
680 BOOST_REQUIRE( !Client().CreateDocument( docFileName, aType, &newDoc ) );
681
682 BOOST_CHECK( Client().LastError().Contains( wxS( "save or revert" ) ) );
683
684 kiapi::common::commands::RevertDocument revert;
685 revert.mutable_document()->CopyFrom( existingDoc );
686 BOOST_CHECK( Client().SendCommand( revert, &response ) );
687
688 BOOST_REQUIRE( Client().CreateDocument( docFileName, aType, &newDoc ) );
689
690 BOOST_CHECK( Client().CloseAllDocuments() );
691 };
692
693 std::map<kiapi::common::types::DocumentType, wxString> cases = {
694 { kiapi::common::types::DOCTYPE_PCB,
695 wxString::FromUTF8( KI_TEST::GetPcbnewTestDataDir() ) + wxS( "api_kitchen_sink.kicad_pcb" ) },
696 { kiapi::common::types::DOCTYPE_SCHEMATIC,
697 wxString::FromUTF8( KI_TEST::GetEeschemaTestDataDir() ) + wxS( "api_kitchen_sink.kicad_sch" ) }
698 };
699
700 for( const auto& [docType, path] : cases )
701 {
702 BOOST_TEST_CONTEXT( magic_enum::enum_name( docType ) )
703 {
704 test( docType, path );
705 }
706 }
707}
708
Per-test fixture for API E2E tests.
const wxString & LastError() const
bool SendCommand(const T &aMessage, kiapi::common::ApiResponse *aResponse)
const KIID m_Uuid
Definition eda_item.h:597
double Similarity(const BOARD_ITEM &aOther) const override
Return a measure of how likely the other object is to represent the same object.
std::string AsStdString() const
Definition kiid.cpp:270
wxString ChildPathStr(const wxString &aName) const
Get the path to a direct child of the temporary directory as a wxString, without creating the child.
bool Create(wxString *aError)
const wxString & BoardPath() const
KI_TEST::SCOPED_TEMP_DIR m_tempRoot
std::string GetTestDataRootDir()
std::string GetPcbnewTestDataDir()
Utility which returns a path to the data directory where the test board files are stored.
std::string GetEeschemaTestDataDir()
Get the configured location of Eeschema test data.
@ REFERENCE
Field Reference of part, i.e. "IC21".
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()
std::string path
BOOST_TEST_CONTEXT("Test Clearance")
BOOST_CHECK_EQUAL(result, "25.4")