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