KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_sprint_layout_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
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your 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
14 * GNU 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
23
26
27#include <board.h>
28#include <footprint.h>
29#include <pad.h>
30#include <pcb_shape.h>
31#include <pcb_track.h>
32#include <netinfo.h>
33#include <zone.h>
34
35
42
43
44BOOST_FIXTURE_TEST_SUITE( SprintLayoutImport, SPRINT_LAYOUT_IMPORT_FIXTURE )
45
46
47// ============================================================================
48// File discrimination tests
49// ============================================================================
50
51BOOST_AUTO_TEST_CASE( CanReadLay6File )
52{
53 std::string path = KI_TEST::GetPcbnewTestDataDir() + "/plugins/sprint_layout/gpio2nesc.lay6";
54 BOOST_CHECK( m_plugin.CanReadBoard( path ) );
55}
56
57
58BOOST_AUTO_TEST_CASE( RejectsNonSprintLayoutFile )
59{
60 std::string kicadPath = KI_TEST::GetPcbnewTestDataDir() + "/plugins/geda/minimal_test.pcb";
61 BOOST_CHECK( !m_plugin.CanReadBoard( kicadPath ) );
62}
63
64
65BOOST_AUTO_TEST_CASE( RejectsNonExistentFile )
66{
67 BOOST_CHECK( !m_plugin.CanReadBoard( wxT( "/nonexistent/path/file.lay6" ) ) );
68}
69
70
71// ============================================================================
72// Basic board loading tests
73// ============================================================================
74
75BOOST_AUTO_TEST_CASE( Gpio2nescBoardLoad )
76{
77 std::string dataPath = KI_TEST::GetPcbnewTestDataDir() + "/plugins/sprint_layout/gpio2nesc.lay6";
78
79 std::unique_ptr<BOARD> board = m_plugin.LoadBoard( dataPath );
80
81 BOOST_REQUIRE( board );
82 BOOST_CHECK( board->Footprints().size() > 0 );
83}
84
85
86BOOST_AUTO_TEST_CASE( ReedDoorbellBoardLoad )
87{
88 std::string dataPath = KI_TEST::GetPcbnewTestDataDir()
89 + "/plugins/sprint_layout/cacazi-a8-zigbee_cr2032_1.2mm.lay6";
90
91 std::unique_ptr<BOARD> board = m_plugin.LoadBoard( dataPath );
92
93 BOOST_REQUIRE( board );
94 BOOST_CHECK( board->Footprints().size() > 0 );
95}
96
97
98BOOST_AUTO_TEST_CASE( MdbRs232BoardLoad )
99{
100 std::string dataPath = KI_TEST::GetPcbnewTestDataDir()
101 + "/plugins/sprint_layout/mdb-rs232.lay6";
102
103 std::unique_ptr<BOARD> board = m_plugin.LoadBoard( dataPath );
104
105 BOOST_REQUIRE( board );
106 BOOST_CHECK( board->Footprints().size() > 0 );
107}
108
109
110BOOST_AUTO_TEST_CASE( LoadBoardAppendToExisting )
111{
112 std::string dataPath = KI_TEST::GetPcbnewTestDataDir() + "/plugins/sprint_layout/gpio2nesc.lay6";
113
114 // Load first into a fresh board
115 std::unique_ptr<BOARD> baseBoard = m_plugin.LoadBoard( dataPath );
116
117 BOOST_REQUIRE( baseBoard );
118
119 size_t originalFootprints = baseBoard->Footprints().size();
120 size_t originalDrawings = baseBoard->Drawings().size();
121
122 BOOST_REQUIRE( originalFootprints > 0 );
123
124 // Load again, appending into the existing board
125 PCB_IO_SPRINT_LAYOUT plugin2;
126 plugin2.LoadAndAppendBoard( dataPath, *baseBoard );
127
128 BOOST_CHECK( baseBoard->Footprints().size() >= originalFootprints * 2 );
129 BOOST_CHECK( baseBoard->Drawings().size() >= originalDrawings * 2 );
130}
131
132
133// ============================================================================
134// Board outline tests
135// ============================================================================
136
137BOOST_AUTO_TEST_CASE( BoardHasOutline )
138{
139 std::string dataPath = KI_TEST::GetPcbnewTestDataDir() + "/plugins/sprint_layout/gpio2nesc.lay6";
140
141 std::unique_ptr<BOARD> board = m_plugin.LoadBoard( dataPath );
142
143 BOOST_REQUIRE( board );
144
145 int edgeCutsCount = 0;
146
147 for( BOARD_ITEM* item : board->Drawings() )
148 {
149 PCB_SHAPE* shape = dynamic_cast<PCB_SHAPE*>( item );
150
151 if( shape && shape->GetLayer() == Edge_Cuts )
152 edgeCutsCount++;
153 }
154
155 BOOST_CHECK_MESSAGE( edgeCutsCount > 0, "Board should have Edge.Cuts outline shapes" );
156}
157
158
159// ============================================================================
160// Pad tests
161// ============================================================================
162
163BOOST_AUTO_TEST_CASE( PadsInsideBoardOutline )
164{
165 std::string dataPath = KI_TEST::GetPcbnewTestDataDir() + "/plugins/sprint_layout/gpio2nesc.lay6";
166
167 std::unique_ptr<BOARD> board = m_plugin.LoadBoard( dataPath );
168
169 BOOST_REQUIRE( board );
170
171 // Get the board bounding box from Edge.Cuts shapes
172 BOX2I boardBox;
173 bool first = true;
174
175 for( BOARD_ITEM* item : board->Drawings() )
176 {
177 PCB_SHAPE* shape = dynamic_cast<PCB_SHAPE*>( item );
178
179 if( !shape || shape->GetLayer() != Edge_Cuts )
180 continue;
181
182 BOX2I shapeBox = shape->GetBoundingBox();
183
184 if( first )
185 {
186 boardBox = shapeBox;
187 first = false;
188 }
189 else
190 {
191 boardBox.Merge( shapeBox );
192 }
193 }
194
195 if( first )
196 {
197 BOOST_TEST_MESSAGE( "No Edge.Cuts found, skipping pad containment test" );
198 return;
199 }
200
201 // Expand for tolerance -- edge connector pads can extend past the board boundary
202 boardBox.Inflate( pcbIUScale.mmToIU( 2.0 ) );
203
204 int outsideCount = 0;
205
206 for( FOOTPRINT* fp : board->Footprints() )
207 {
208 for( PAD* pad : fp->Pads() )
209 {
210 if( !boardBox.Contains( pad->GetPosition() ) )
211 outsideCount++;
212 }
213 }
214
215 BOOST_CHECK_MESSAGE( outsideCount == 0,
216 wxString::Format( "%d pads found outside board outline", outsideCount ) );
217}
218
219
220// ============================================================================
221// Component and layer tests
222// ============================================================================
223
224BOOST_AUTO_TEST_CASE( BoardHasCopperLayers )
225{
226 std::string dataPath = KI_TEST::GetPcbnewTestDataDir() + "/plugins/sprint_layout/gpio2nesc.lay6";
227
228 std::unique_ptr<BOARD> board = m_plugin.LoadBoard( dataPath );
229
230 BOOST_REQUIRE( board );
231 BOOST_CHECK( board->GetCopperLayerCount() >= 2 );
232}
233
234
235BOOST_AUTO_TEST_CASE( CachedLibraryFootprints )
236{
237 std::string dataPath = KI_TEST::GetPcbnewTestDataDir() + "/plugins/sprint_layout/gpio2nesc.lay6";
238
239 std::unique_ptr<BOARD> board = m_plugin.LoadBoard( dataPath );
240
241 BOOST_REQUIRE( board );
242
243 std::vector<FOOTPRINT*> cached = m_plugin.GetImportedCachedLibraryFootprints();
244
245 // We should have at least some cached footprints
246 BOOST_CHECK( cached.size() > 0 );
247
248 for( FOOTPRINT* fp : cached )
249 delete fp;
250}
251
252
253BOOST_AUTO_TEST_CASE( PadsHaveAttributes )
254{
255 std::string dataPath = KI_TEST::GetPcbnewTestDataDir() + "/plugins/sprint_layout/gpio2nesc.lay6";
256
257 std::unique_ptr<BOARD> board = m_plugin.LoadBoard( dataPath );
258
259 BOOST_REQUIRE( board );
260
261 int thtCount = 0;
262 int smdCount = 0;
263
264 for( FOOTPRINT* fp : board->Footprints() )
265 {
266 for( PAD* pad : fp->Pads() )
267 {
268 if( pad->GetAttribute() == PAD_ATTRIB::PTH )
269 thtCount++;
270 else if( pad->GetAttribute() == PAD_ATTRIB::SMD )
271 smdCount++;
272 }
273 }
274
275 // This board should have at least some through-hole pads
276 BOOST_CHECK_MESSAGE( thtCount > 0 || smdCount > 0,
277 "Board should have at least some pads" );
278}
279
280
281// ============================================================================
282// Format-specific feature tests
283// ============================================================================
284
285BOOST_AUTO_TEST_CASE( PadPositionsHavePositiveY )
286{
287 std::string dataPath = KI_TEST::GetPcbnewTestDataDir() + "/plugins/sprint_layout/gpio2nesc.lay6";
288
289 std::unique_ptr<BOARD> board = m_plugin.LoadBoard( dataPath );
290
291 BOOST_REQUIRE( board );
292
293 // After Y-flip conversion, all pads should have non-negative Y coordinates
294 int negativeCount = 0;
295
296 for( FOOTPRINT* fp : board->Footprints() )
297 {
298 for( PAD* pad : fp->Pads() )
299 {
300 if( pad->GetPosition().y < 0 )
301 negativeCount++;
302 }
303 }
304
305 BOOST_CHECK_MESSAGE( negativeCount == 0,
306 wxString::Format( "%d pads have negative Y (Y-flip error)", negativeCount ) );
307}
308
309
310BOOST_AUTO_TEST_CASE( DrawingsExistOnCopperAndSilk )
311{
312 std::string dataPath = KI_TEST::GetPcbnewTestDataDir() + "/plugins/sprint_layout/gpio2nesc.lay6";
313
314 std::unique_ptr<BOARD> board = m_plugin.LoadBoard( dataPath );
315
316 BOOST_REQUIRE( board );
317
318 int copperDrawings = 0;
319 int silkDrawings = 0;
320
321 for( BOARD_ITEM* item : board->Drawings() )
322 {
323 PCB_LAYER_ID layer = item->GetLayer();
324
325 if( layer == F_Cu || layer == B_Cu || layer == In1_Cu || layer == In2_Cu )
326 copperDrawings++;
327 else if( layer == F_SilkS || layer == B_SilkS )
328 silkDrawings++;
329 }
330
331 BOOST_CHECK_MESSAGE( copperDrawings > 0 || silkDrawings > 0,
332 "Board should have drawings on copper or silkscreen layers" );
333}
334
335
336// ============================================================================
337// Multi-file consistency
338// ============================================================================
339
340BOOST_AUTO_TEST_CASE( AllTestFilesLoadWithoutCrash )
341{
342 std::vector<std::string> files = {
343 "/plugins/sprint_layout/gpio2nesc.lay6",
344 "/plugins/sprint_layout/cacazi-a8-zigbee_cr2032_1.2mm.lay6",
345 "/plugins/sprint_layout/mdb-rs232.lay6",
346 "/plugins/sprint_layout/mdb-master-rev2a.lay6",
347 "/plugins/sprint_layout/smalldualrgb-withmask.lay6",
348 "/plugins/sprint_layout/amiga2000-remake.lay6",
349 "/plugins/sprint_layout/karpaty-rx-pcb1-bpf-orig.lay6",
350 "/plugins/sprint_layout/karpaty-rx-pcb2-rfamp-1st-mixer-orig.lay6",
351 "/plugins/sprint_layout/karpaty-rx-pcb3-vfo-orig.lay6",
352 "/plugins/sprint_layout/karpaty-rx-pcb5-buffer-freq-doubler-orig.lay6",
353 "/plugins/sprint_layout/karpaty-rx-pcb6-mainboard-orig.lay6",
354 "/plugins/sprint_layout/karpaty-rx-pcb7-power-supply-orig.lay6",
355 "/plugins/sprint_layout/ku14194revb.lay6",
356 "/plugins/sprint_layout/pcb100x40_v5.lay6",
357 "/plugins/sprint_layout/tfcc.lay6",
358 "/plugins/sprint_layout/12F629_SM.lay6",
359 };
360
361 for( const auto& file : files )
362 {
363 std::string dataPath = KI_TEST::GetPcbnewTestDataDir() + file;
364
365 BOOST_TEST_CONTEXT( "Loading " << file )
366 {
367 std::unique_ptr<BOARD> board = m_plugin.LoadBoard( dataPath );
368 BOOST_CHECK( board != nullptr );
369 }
370 }
371}
372
373
374// ============================================================================
375// Complex board tests
376// ============================================================================
377
378BOOST_AUTO_TEST_CASE( MmJoy2BoardLoad )
379{
380 std::string dataPath = KI_TEST::GetPcbnewTestDataDir()
381 + "/plugins/sprint_layout/mmjoy2-74hc165.lay6";
382
383 std::map<std::string, UTF8> props;
384 props["pcb_id"] = "0";
385
386 std::unique_ptr<BOARD> board = m_plugin.LoadBoard( dataPath, &props );
387
388 BOOST_REQUIRE( board );
389 BOOST_CHECK( board->Footprints().size() > 0 );
390
391 int padCount = 0;
392
393 for( FOOTPRINT* fp : board->Footprints() )
394 padCount += static_cast<int>( fp->Pads().size() );
395
396 BOOST_CHECK_MESSAGE( padCount > 0, "MMJoy2 board should have pads" );
397}
398
399
400BOOST_AUTO_TEST_CASE( SmallDualRgbBoardLoad )
401{
402 std::string dataPath = KI_TEST::GetPcbnewTestDataDir()
403 + "/plugins/sprint_layout/smalldualrgb-withmask.lay6";
404
405 std::unique_ptr<BOARD> board = m_plugin.LoadBoard( dataPath );
406
407 BOOST_REQUIRE( board );
408 BOOST_CHECK( board->Footprints().size() > 0 );
409}
410
411
412BOOST_AUTO_TEST_CASE( MdbMasterRev2aBoardLoad )
413{
414 std::string dataPath = KI_TEST::GetPcbnewTestDataDir()
415 + "/plugins/sprint_layout/mdb-master-rev2a.lay6";
416
417 std::unique_ptr<BOARD> board = m_plugin.LoadBoard( dataPath );
418
419 BOOST_REQUIRE( board );
420 BOOST_CHECK( board->Footprints().size() > 0 );
421 BOOST_CHECK( board->GetCopperLayerCount() >= 2 );
422}
423
424
425// ============================================================================
426// Karpaty-RX HAM receiver boards (6-board set from github.com/UT8IFG/karpaty-rx)
427// ============================================================================
428
429BOOST_AUTO_TEST_CASE( KarpatyBpfBoardLoad )
430{
431 std::string dataPath = KI_TEST::GetPcbnewTestDataDir()
432 + "/plugins/sprint_layout/karpaty-rx-pcb1-bpf-orig.lay6";
433
434 std::unique_ptr<BOARD> board = m_plugin.LoadBoard( dataPath );
435
436 BOOST_REQUIRE( board );
437 BOOST_CHECK( board->Footprints().size() > 0 );
438}
439
440
441BOOST_AUTO_TEST_CASE( KarpatyRfAmpBoardLoad )
442{
443 std::string dataPath = KI_TEST::GetPcbnewTestDataDir()
444 + "/plugins/sprint_layout/karpaty-rx-pcb2-rfamp-1st-mixer-orig.lay6";
445
446 std::unique_ptr<BOARD> board = m_plugin.LoadBoard( dataPath );
447
448 BOOST_REQUIRE( board );
449 BOOST_CHECK( board->Footprints().size() > 0 );
450}
451
452
453BOOST_AUTO_TEST_CASE( KarpatyVfoBoardLoad )
454{
455 std::string dataPath = KI_TEST::GetPcbnewTestDataDir()
456 + "/plugins/sprint_layout/karpaty-rx-pcb3-vfo-orig.lay6";
457
458 std::unique_ptr<BOARD> board = m_plugin.LoadBoard( dataPath );
459
460 BOOST_REQUIRE( board );
461 BOOST_CHECK( board->Footprints().size() > 0 );
462}
463
464
465BOOST_AUTO_TEST_CASE( KarpatyBufferBoardLoad )
466{
467 std::string dataPath = KI_TEST::GetPcbnewTestDataDir()
468 + "/plugins/sprint_layout/karpaty-rx-pcb5-buffer-freq-doubler-orig.lay6";
469
470 std::unique_ptr<BOARD> board = m_plugin.LoadBoard( dataPath );
471
472 BOOST_REQUIRE( board );
473 BOOST_CHECK( board->Footprints().size() > 0 );
474}
475
476
477BOOST_AUTO_TEST_CASE( KarpatyMainboardBoardLoad )
478{
479 std::string dataPath = KI_TEST::GetPcbnewTestDataDir()
480 + "/plugins/sprint_layout/karpaty-rx-pcb6-mainboard-orig.lay6";
481
482 std::unique_ptr<BOARD> board = m_plugin.LoadBoard( dataPath );
483
484 BOOST_REQUIRE( board );
485 BOOST_CHECK( board->Footprints().size() > 0 );
486}
487
488
489BOOST_AUTO_TEST_CASE( KarpatyPowerSupplyBoardLoad )
490{
491 std::string dataPath = KI_TEST::GetPcbnewTestDataDir()
492 + "/plugins/sprint_layout/karpaty-rx-pcb7-power-supply-orig.lay6";
493
494 std::unique_ptr<BOARD> board = m_plugin.LoadBoard( dataPath );
495
496 BOOST_REQUIRE( board );
497 BOOST_CHECK( board->Footprints().size() > 0 );
498}
499
500
501// ============================================================================
502// Additional real-world boards
503// ============================================================================
504
505BOOST_AUTO_TEST_CASE( Ku14194RevBBoardLoad )
506{
507 std::string dataPath = KI_TEST::GetPcbnewTestDataDir()
508 + "/plugins/sprint_layout/ku14194revb.lay6";
509
510 std::unique_ptr<BOARD> board = m_plugin.LoadBoard( dataPath );
511
512 BOOST_REQUIRE( board );
513 BOOST_CHECK( board->Footprints().size() > 0 );
514}
515
516
517BOOST_AUTO_TEST_CASE( AntennaSwitchBoardLoad )
518{
519 std::string dataPath = KI_TEST::GetPcbnewTestDataDir()
520 + "/plugins/sprint_layout/pcb100x40_v5.lay6";
521
522 std::unique_ptr<BOARD> board = m_plugin.LoadBoard( dataPath );
523
524 BOOST_REQUIRE( board );
525 BOOST_CHECK( board->Footprints().size() > 0 );
526}
527
528
529BOOST_AUTO_TEST_CASE( TfccBoardLoad )
530{
531 std::string dataPath = KI_TEST::GetPcbnewTestDataDir()
532 + "/plugins/sprint_layout/tfcc.lay6";
533
534 std::unique_ptr<BOARD> board = m_plugin.LoadBoard( dataPath );
535
536 BOOST_REQUIRE( board );
537 BOOST_CHECK( board->Footprints().size() > 0 );
538}
539
540
541// ============================================================================
542// Large board stress test (Amiga 2000 -- full motherboard recreation, ~4.3MB)
543// ============================================================================
544
545BOOST_AUTO_TEST_CASE( Amiga2000BoardLoad )
546{
547 std::string dataPath = KI_TEST::GetPcbnewTestDataDir()
548 + "/plugins/sprint_layout/amiga2000-remake.lay6";
549
550 std::unique_ptr<BOARD> board = m_plugin.LoadBoard( dataPath );
551
552 BOOST_REQUIRE( board );
553
554 BOOST_CHECK_MESSAGE( board->Footprints().size() > 50,
555 wxString::Format( "Amiga 2000 should have many footprints, got %zu",
556 board->Footprints().size() ) );
557
558 BOOST_CHECK( board->GetCopperLayerCount() >= 2 );
559}
560
561
562// ============================================================================
563// Cross-board consistency checks
564// ============================================================================
565
566BOOST_AUTO_TEST_CASE( AllBoardsHaveConsistentPadCoordinates )
567{
568 std::vector<std::string> files = {
569 "/plugins/sprint_layout/gpio2nesc.lay6",
570 "/plugins/sprint_layout/mdb-rs232.lay6",
571 "/plugins/sprint_layout/mdb-master-rev2a.lay6",
572 "/plugins/sprint_layout/smalldualrgb-withmask.lay6",
573 "/plugins/sprint_layout/amiga2000-remake.lay6",
574 "/plugins/sprint_layout/karpaty-rx-pcb1-bpf-orig.lay6",
575 "/plugins/sprint_layout/karpaty-rx-pcb2-rfamp-1st-mixer-orig.lay6",
576 "/plugins/sprint_layout/karpaty-rx-pcb3-vfo-orig.lay6",
577 "/plugins/sprint_layout/karpaty-rx-pcb5-buffer-freq-doubler-orig.lay6",
578 "/plugins/sprint_layout/karpaty-rx-pcb6-mainboard-orig.lay6",
579 "/plugins/sprint_layout/karpaty-rx-pcb7-power-supply-orig.lay6",
580 "/plugins/sprint_layout/ku14194revb.lay6",
581 "/plugins/sprint_layout/pcb100x40_v5.lay6",
582 "/plugins/sprint_layout/tfcc.lay6",
583 "/plugins/sprint_layout/12F629_SM.lay6",
584 };
585
586 for( const auto& file : files )
587 {
588 std::string dataPath = KI_TEST::GetPcbnewTestDataDir() + file;
589
590 BOOST_TEST_CONTEXT( "Checking coordinate consistency in " << file )
591 {
592 std::unique_ptr<BOARD> board = m_plugin.LoadBoard( dataPath );
593
594 BOOST_REQUIRE( board );
595
596 // All pad coordinates should be finite and within a reasonable range
597 // (no overflow, no NaN from bad float conversion)
598 const int MAX_COORD = pcbIUScale.mmToIU( 1000.0 );
599 int badCount = 0;
600
601 for( FOOTPRINT* fp : board->Footprints() )
602 {
603 for( PAD* pad : fp->Pads() )
604 {
605 VECTOR2I pos = pad->GetPosition();
606
607 if( std::abs( pos.x ) > MAX_COORD || std::abs( pos.y ) > MAX_COORD )
608 badCount++;
609 }
610 }
611
612 BOOST_CHECK_MESSAGE( badCount == 0,
613 wxString::Format( "%d pads have coordinates outside +-10m in %s",
614 badCount, wxString::FromUTF8( file ) ) );
615 }
616 }
617}
618
619
620// ============================================================================
621// Multi-board selection tests
622// ============================================================================
623
624BOOST_AUTO_TEST_CASE( MultiBoardFileHasFiveBoards )
625{
626 std::string dataPath = KI_TEST::GetPcbnewTestDataDir()
627 + "/plugins/sprint_layout/mmjoy2-74hc165.lay6";
628
630 BOOST_REQUIRE( parser.ParseBoard( dataPath ) );
631
632 const auto& fileData = parser.GetFileData();
633 BOOST_CHECK_EQUAL( fileData.boards.size(), 5 );
634}
635
636
637BOOST_AUTO_TEST_CASE( MultiBoardSelectByIndex )
638{
639 std::string dataPath = KI_TEST::GetPcbnewTestDataDir()
640 + "/plugins/sprint_layout/mmjoy2-74hc165.lay6";
641
643 BOOST_REQUIRE( parser.ParseBoard( dataPath ) );
644
645 const auto& fileData = parser.GetFileData();
646 BOOST_REQUIRE( fileData.boards.size() == 5 );
647
648 for( size_t i = 0; i < fileData.boards.size(); i++ )
649 {
650 BOOST_TEST_CONTEXT( "Board index " << i )
651 {
652 std::map<std::string, UTF8> props;
653 props["pcb_id"] = std::to_string( i );
654
656 std::unique_ptr<BOARD> board = plugin.LoadBoard( dataPath, &props );
657
658 BOOST_REQUIRE( board );
659 }
660 }
661}
662
663
664BOOST_AUTO_TEST_CASE( MultiBoardCallbackInvoked )
665{
666 std::string dataPath = KI_TEST::GetPcbnewTestDataDir()
667 + "/plugins/sprint_layout/mmjoy2-74hc165.lay6";
668
670 bool callbackInvoked = false;
671 size_t optionCount = 0;
672
673 plugin.RegisterCallback(
674 [&]( const std::vector<IMPORT_PROJECT_DESC>& aOptions )
675 {
676 callbackInvoked = true;
677 optionCount = aOptions.size();
678
679 // Select the second board
680 std::vector<IMPORT_PROJECT_DESC> chosen;
681 chosen.push_back( aOptions[1] );
682 return chosen;
683 } );
684
685 std::unique_ptr<BOARD> board = plugin.LoadBoard( dataPath );
686
687 BOOST_CHECK( callbackInvoked );
688 BOOST_CHECK_EQUAL( optionCount, 5 );
689 BOOST_REQUIRE( board );
690}
691
692
693BOOST_AUTO_TEST_CASE( MultiBoardCallbackCancelThrows )
694{
695 std::string dataPath = KI_TEST::GetPcbnewTestDataDir()
696 + "/plugins/sprint_layout/mmjoy2-74hc165.lay6";
697
699
700 plugin.RegisterCallback(
701 [&]( const std::vector<IMPORT_PROJECT_DESC>& aOptions )
702 {
703 return std::vector<IMPORT_PROJECT_DESC>();
704 } );
705
706 BOOST_CHECK_THROW( plugin.LoadBoard( dataPath ), IO_ERROR );
707}
708
709
710BOOST_AUTO_TEST_CASE( SingleBoardFileSkipsCallback )
711{
712 std::string dataPath = KI_TEST::GetPcbnewTestDataDir()
713 + "/plugins/sprint_layout/gpio2nesc.lay6";
714
716 bool callbackInvoked = false;
717
718 plugin.RegisterCallback(
719 [&]( const std::vector<IMPORT_PROJECT_DESC>& aOptions )
720 {
721 callbackInvoked = true;
722 return aOptions;
723 } );
724
725 std::unique_ptr<BOARD> board = plugin.LoadBoard( dataPath );
726
727 BOOST_CHECK( !callbackInvoked );
728 BOOST_REQUIRE( board );
729}
730
731
732// ============================================================================
733// Regression tests
734// ============================================================================
735
736BOOST_AUTO_TEST_CASE( Pic12F629SmdPadPositions )
737{
738 // GitLab #23538: SMD pad x,y fields in some Sprint Layout files store
739 // component-relative offsets rather than absolute positions, causing
740 // all SMD pads to pile up near (0,0).
741 std::string dataPath = KI_TEST::GetPcbnewTestDataDir()
742 + "/plugins/sprint_layout/12F629_SM.lay6";
743
744 std::unique_ptr<BOARD> board = m_plugin.LoadBoard( dataPath );
745
746 BOOST_REQUIRE( board );
747
748 // The PIC12F629 is an 8-pin SOIC. After import, its pads should be
749 // spread across the board, not clustered at the origin.
750 BOX2I boardBox;
751 bool first = true;
752
753 for( BOARD_ITEM* item : board->Drawings() )
754 {
755 PCB_SHAPE* shape = dynamic_cast<PCB_SHAPE*>( item );
756
757 if( shape && shape->GetLayer() == Edge_Cuts )
758 {
759 if( first )
760 {
761 boardBox = shape->GetBoundingBox();
762 first = false;
763 }
764 else
765 {
766 boardBox.Merge( shape->GetBoundingBox() );
767 }
768 }
769 }
770
771 BOOST_REQUIRE( !first );
772
773 boardBox.Inflate( pcbIUScale.mmToIU( 2.0 ) );
774
775 int outsideCount = 0;
776 int smdCount = 0;
777
778 for( FOOTPRINT* fp : board->Footprints() )
779 {
780 for( PAD* pad : fp->Pads() )
781 {
782 if( pad->GetAttribute() == PAD_ATTRIB::SMD )
783 {
784 smdCount++;
785
786 if( !boardBox.Contains( pad->GetPosition() ) )
787 outsideCount++;
788 }
789 }
790 }
791
792 BOOST_CHECK_MESSAGE( smdCount > 0,
793 "PIC12F629 board should have SMD pads" );
794
795 BOOST_CHECK_MESSAGE( outsideCount == 0,
796 wxString::Format( "%d of %d SMD pads outside board outline",
797 outsideCount, smdCount ) );
798}
799
800
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:121
General utilities for PCB file IO for QA programs.
BOX2< VECTOR2I > BOX2I
Definition box2.h:927
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition board_item.h:84
constexpr BOX2< Vec > & Inflate(coord_type dx, coord_type dy)
Inflates the rectangle horizontally by dx and vertically by dy.
Definition box2.h:553
constexpr BOX2< Vec > & Merge(const BOX2< Vec > &aRect)
Modify the position and size of the rectangle in order to contain aRect.
Definition box2.h:653
constexpr bool Contains(const Vec &aPoint) const
Definition box2.h:165
Hold an error message and may be used when throwing exceptions containing meaningful error messages.
Definition pad.h:61
void LoadAndAppendBoard(const wxString &aFileName, BOARD &aAppendToMe, const std::map< std::string, UTF8 > *aProperties=nullptr, PROJECT *aProject=nullptr)
Same as LoadBoard(), but appends the loaded board to an existing board, which must already exist.
Definition pcb_io.cpp:85
std::unique_ptr< BOARD > LoadBoard(const wxString &aFileName, const std::map< std::string, UTF8 > *aProperties=nullptr, PROJECT *aProject=nullptr)
Load information from some input file format that this PCB_IO implementation knows about into new BOA...
Definition pcb_io.cpp:72
const BOX2I GetBoundingBox() const override
Return the orthogonal bounding box of this object for display purposes.
PCB_LAYER_ID GetLayer() const override
Return the primary layer this item is on.
Definition pcb_shape.h:68
virtual void RegisterCallback(CHOOSE_PROJECT_HANDLER aChooseProjectHandler)
Register a different handler to be called when a non-KiCad project contains multiple PCB+Schematic co...
const SPRINT_LAYOUT::FILE_DATA & GetFileData() const
bool ParseBoard(const wxString &aFileName)
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:56
@ Edge_Cuts
Definition layer_ids.h:108
@ B_Cu
Definition layer_ids.h:61
@ In2_Cu
Definition layer_ids.h:63
@ F_SilkS
Definition layer_ids.h:96
@ In1_Cu
Definition layer_ids.h:62
@ B_SilkS
Definition layer_ids.h:97
@ F_Cu
Definition layer_ids.h:60
std::string GetPcbnewTestDataDir()
Utility which returns a path to the data directory where the test board files are stored.
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition eda_angle.h:411
@ SMD
Smd pad, appears on the solder paste layer (default)
Definition padstack.h:98
@ PTH
Plated through hole pad.
Definition padstack.h:97
BOOST_AUTO_TEST_CASE(HorizontalAlignment)
BOOST_REQUIRE(intersection.has_value()==c.ExpectedIntersection.has_value())
BOOST_AUTO_TEST_SUITE_END()
std::string path
BOOST_TEST_CONTEXT("Test Clearance")
BOOST_TEST_MESSAGE("Polyline has "<< chain.PointCount()<< " points")
BOOST_AUTO_TEST_CASE(CanReadLay6File)
BOOST_CHECK_EQUAL(result, "25.4")
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683