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