KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_diptrace_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
28
32
35
36#include <board.h>
38#include <footprint.h>
39#include <netclass.h>
41#include <pad.h>
42#include <pcb_track.h>
43#include <pcb_shape.h>
44
45#include <wx/ffile.h>
46#include <wx/filefn.h>
47#include <wx/filename.h>
48
49
51{
53
55
56 std::string GetTestDataDir()
57 {
58 return KI_TEST::GetPcbnewTestDataDir() + "plugins/diptrace/";
59 }
60};
61
62
63BOOST_FIXTURE_TEST_SUITE( DipTracePcbImport, DIPTRACE_PCB_IMPORT_FIXTURE )
64
65
66
70BOOST_AUTO_TEST_CASE( CanReadBoard )
71{
72 BOOST_CHECK( m_plugin.CanReadBoard( GetTestDataDir() + "keyboard.dip" ) );
73 BOOST_CHECK( m_plugin.CanReadBoard( GetTestDataDir() + "156bus_narrow.dip" ) );
74 BOOST_CHECK( m_plugin.CanReadBoard( GetTestDataDir() + "z80_board.dip" ) );
75 BOOST_CHECK( m_plugin.CanReadBoard( GetTestDataDir() + "logic_probe.dip" ) );
76 BOOST_CHECK( m_plugin.CanReadBoard( GetTestDataDir() + "project4.dip" ) );
77
78 wxString tempBase = wxFileName::CreateTempFileName( wxS( "kicad_diptrace_legacy_" ) );
79 wxRemoveFile( tempBase );
80 wxString legacyPath = tempBase + wxS( ".dip" );
81
82 {
83 const uint8_t legacyHeader[] = {
84 0x0B, 'D', 'T', 'B', 'O', 'A', 'R', 'D', '2', '.', '2', '1'
85 };
86
87 wxFFile file( legacyPath, wxS( "wb" ) );
88 BOOST_REQUIRE( file.IsOpened() );
89 BOOST_REQUIRE_EQUAL( file.Write( legacyHeader, sizeof( legacyHeader ) ),
90 sizeof( legacyHeader ) );
91 }
92
93 BOOST_CHECK( m_plugin.CanReadBoard( legacyPath ) );
94 wxRemoveFile( legacyPath );
95}
96
97
98BOOST_AUTO_TEST_CASE( InvalidComponentHeaderFailsDeterministically )
99{
100 const std::string sourcePath = GetTestDataDir() + "z80_board.dip";
101 wxString tempBase = wxFileName::CreateTempFileName( wxS( "kicad_diptrace_bad_pcb_comp_" ) );
102 wxRemoveFile( tempBase );
103 wxString tempPath = tempBase + wxS( ".dip" );
104
105 BOOST_REQUIRE( wxCopyFile( sourcePath, tempPath ) );
106
107 {
108 static constexpr wxFileOffset FIRST_COMPONENT_FLAGS_OFFSET = 0x491;
109 const uint8_t invalidFlags[] = { 0xFF, 0x00, 0x00, 0x00 };
110
111 wxFFile file( tempPath, wxS( "r+b" ) );
112 BOOST_REQUIRE( file.IsOpened() );
113 BOOST_REQUIRE( file.Seek( FIRST_COMPONENT_FLAGS_OFFSET ) );
114 BOOST_REQUIRE_EQUAL( file.Write( invalidFlags, sizeof( invalidFlags ) ),
115 sizeof( invalidFlags ) );
116 }
117
118 std::unique_ptr<BOARD> board = std::make_unique<BOARD>();
119
120 BOOST_CHECK_THROW( m_plugin.LoadBoard( tempPath.ToStdString(), board.get() ), IO_ERROR );
121
122 wxRemoveFile( tempPath );
123}
124
125
126BOOST_AUTO_TEST_CASE( InvalidRouteChainNodeCountFailsDeterministically )
127{
128 const std::string sourcePath = GetTestDataDir() + "z80_board.dip";
129 wxString tempBase = wxFileName::CreateTempFileName( wxS( "kicad_diptrace_bad_route_chain_" ) );
130 wxRemoveFile( tempBase );
131 wxString tempPath = tempBase + wxS( ".dip" );
132
133 BOOST_REQUIRE( wxCopyFile( sourcePath, tempPath ) );
134
135 {
136 static constexpr wxFileOffset FIRST_ROUTE_CHAIN_NODE_COUNT_OFFSET = 0x1CEA7;
137 const uint8_t invalidNodeCount[] = { 0x0F, 0x69, 0x51 }; // int3 value 10001
138
139 wxFFile file( tempPath, wxS( "r+b" ) );
140 BOOST_REQUIRE( file.IsOpened() );
141 BOOST_REQUIRE( file.Seek( FIRST_ROUTE_CHAIN_NODE_COUNT_OFFSET ) );
142 BOOST_REQUIRE_EQUAL( file.Write( invalidNodeCount, sizeof( invalidNodeCount ) ),
143 sizeof( invalidNodeCount ) );
144 }
145
146 std::unique_ptr<BOARD> board = std::make_unique<BOARD>();
147
148 BOOST_CHECK_THROW( m_plugin.LoadBoard( tempPath.ToStdString(), board.get() ), IO_ERROR );
149
150 wxRemoveFile( tempPath );
151}
152
153
154BOOST_AUTO_TEST_CASE( InvalidNetNameLengthFailsDeterministically )
155{
156 const std::string sourcePath = GetTestDataDir() + "z80_board.dip";
157 wxString tempBase = wxFileName::CreateTempFileName( wxS( "kicad_diptrace_bad_net_name_" ) );
158 wxRemoveFile( tempBase );
159 wxString tempPath = tempBase + wxS( ".dip" );
160
161 BOOST_REQUIRE( wxCopyFile( sourcePath, tempPath ) );
162
163 {
164 static constexpr wxFileOffset FIRST_NET_NAME_LENGTH_OFFSET = 0x1CE2D;
165 const uint8_t invalidNameLength[] = { 0x01, 0xF5 }; // UTF-16 char count 501
166
167 wxFFile file( tempPath, wxS( "r+b" ) );
168 BOOST_REQUIRE( file.IsOpened() );
169 BOOST_REQUIRE( file.Seek( FIRST_NET_NAME_LENGTH_OFFSET ) );
170 BOOST_REQUIRE_EQUAL( file.Write( invalidNameLength, sizeof( invalidNameLength ) ),
171 sizeof( invalidNameLength ) );
172 }
173
174 std::unique_ptr<BOARD> board = std::make_unique<BOARD>();
175
176 BOOST_CHECK_THROW( m_plugin.LoadBoard( tempPath.ToStdString(), board.get() ), IO_ERROR );
177
178 wxRemoveFile( tempPath );
179}
180
181
182BOOST_AUTO_TEST_CASE( InvalidZoneMinWidthFailsDeterministically )
183{
184 const std::string sourcePath = GetTestDataDir() + "z80_board.dip";
185 wxString tempBase = wxFileName::CreateTempFileName( wxS( "kicad_diptrace_bad_zone_width_" ) );
186 wxRemoveFile( tempBase );
187 wxString tempPath = tempBase + wxS( ".dip" );
188
189 BOOST_REQUIRE( wxCopyFile( sourcePath, tempPath ) );
190
191 {
192 static constexpr wxFileOffset FIRST_ZONE_MIN_WIDTH_OFFSET = 0x382CB;
193 const uint8_t zeroMinWidth[] = { 0x3B, 0x9A, 0xCA, 0x00 }; // int4 value 0
194
195 wxFFile file( tempPath, wxS( "r+b" ) );
196 BOOST_REQUIRE( file.IsOpened() );
197 BOOST_REQUIRE( file.Seek( FIRST_ZONE_MIN_WIDTH_OFFSET ) );
198 BOOST_REQUIRE_EQUAL( file.Write( zeroMinWidth, sizeof( zeroMinWidth ) ),
199 sizeof( zeroMinWidth ) );
200 }
201
202 std::unique_ptr<BOARD> board = std::make_unique<BOARD>();
203
204 BOOST_CHECK_THROW( m_plugin.LoadBoard( tempPath.ToStdString(), board.get() ), IO_ERROR );
205
206 wxRemoveFile( tempPath );
207}
208
209
215BOOST_AUTO_TEST_CASE( LoadKeyboard )
216{
217 std::unique_ptr<BOARD> board = std::make_unique<BOARD>();
218
219 m_plugin.LoadBoard( GetTestDataDir() + "keyboard.dip", board.get() );
220
221 BOOST_REQUIRE( board );
222
223 // keyboard.dip has approximately 103 components
224 BOOST_CHECK_GT( board->Footprints().size(), 50 );
225
226 // keyboard.dip has ~70 nets (key matrix: col1-6, row1-8, diode nets, etc.)
227 BOOST_CHECK_GT( board->GetNetCount(), 20 );
228}
229
230
244BOOST_AUTO_TEST_CASE( ObjectsAreFieldLocatedNotScanned )
245{
246 const std::vector<std::string> files = {
247 "keyboard.dip", "156bus_narrow.dip", "logic_probe.dip", "project4.dip", "z80_board.dip"
248 };
249
250 for( const std::string& name : files )
251 {
252 const std::string path = GetTestDataDir() + name;
253 std::unique_ptr<BOARD> board = std::make_unique<BOARD>();
254 board->SetFileName( wxString::FromUTF8( path ) );
255
256 DIPTRACE::PCB_PARSER parser( wxString::FromUTF8( path ), board.get() );
257 parser.Parse();
258
259 // Per-category regression guards for the categories already converted to a
260 // deterministic field-walk. Pad records are field-located from the component
261 // header; mount holes carry no scan in this corpus.
263 name + ": pad located by scan (" + std::to_string( parser.PadLocatorScans() )
264 + ")" );
266 name + ": mount hole located by scan ("
267 + std::to_string( parser.MountHoleLocatorScans() ) + ")" );
269 name + ": shape located by scan ("
270 + std::to_string( parser.ShapeLocatorScans() ) + ")" );
272 name + ": component located by scan ("
273 + std::to_string( parser.ComponentLocatorScans() ) + ")" );
275 name + ": section located by scan ("
276 + std::to_string( parser.SectionLocatorScans() ) + ")" );
277
279 parser.ScanLocatorUseCount() == 0,
280 name + ": object/section located by byte-pattern scan (total="
281 + std::to_string( parser.ScanLocatorUseCount() )
282 + " components=" + std::to_string( parser.ComponentLocatorScans() )
283 + " pads=" + std::to_string( parser.PadLocatorScans() )
284 + " shapes=" + std::to_string( parser.ShapeLocatorScans() )
285 + " holes=" + std::to_string( parser.MountHoleLocatorScans() )
286 + " sections=" + std::to_string( parser.SectionLocatorScans() ) + ")" );
287 }
288}
289
290
296{
297 std::unique_ptr<BOARD> board = std::make_unique<BOARD>();
298
299 m_plugin.LoadBoard( GetTestDataDir() + "156bus_narrow.dip", board.get() );
300
301 BOOST_REQUIRE( board );
302 BOOST_CHECK_GT( board->Footprints().size(), 0 );
303
304 // 156bus_narrow has a board outline with 12 vertices including arcs
305 bool hasOutline = false;
306
307 for( BOARD_ITEM* drawing : board->Drawings() )
308 {
309 if( drawing->GetLayer() == Edge_Cuts )
310 {
311 hasOutline = true;
312 break;
313 }
314 }
315
316 BOOST_CHECK( hasOutline );
317}
318
319
325{
326 std::unique_ptr<BOARD> board = std::make_unique<BOARD>();
327
328 m_plugin.LoadBoard( GetTestDataDir() + "project4.dip", board.get() );
329
330 BOOST_REQUIRE( board );
331 BOOST_CHECK_GT( board->Footprints().size(), 0 );
332}
333
334
338BOOST_AUTO_TEST_CASE( LoadLogicProbe )
339{
340 std::unique_ptr<BOARD> board = std::make_unique<BOARD>();
341
342 m_plugin.LoadBoard( GetTestDataDir() + "logic_probe.dip", board.get() );
343
344 BOOST_REQUIRE( board );
345 BOOST_CHECK_GT( board->Footprints().size(), 0 );
346}
347
348
353BOOST_AUTO_TEST_CASE( LoadZ80Board )
354{
355 std::unique_ptr<BOARD> board = std::make_unique<BOARD>();
356
357 m_plugin.LoadBoard( GetTestDataDir() + "z80_board.dip", board.get() );
358
359 BOOST_REQUIRE( board );
360
361 // Z80 computer board should have a healthy number of components
362 BOOST_CHECK_GT( board->Footprints().size(), 10 );
363
364 // Verify reference designators are present on imported footprints
365 int refsFound = 0;
366 int totalPads = 0;
367
368 for( const FOOTPRINT* fp : board->Footprints() )
369 {
370 if( !fp->GetReference().IsEmpty() )
371 refsFound++;
372
373 totalPads += static_cast<int>( fp->Pads().size() );
374 }
375
376 BOOST_CHECK_GT( refsFound, 10 );
377 BOOST_CHECK_GT( totalPads, 0 );
378
379 // Z80 board has ~97 nets (address bus A0-A15, data bus D0-D7, control, etc.)
380 BOOST_CHECK_GT( board->GetNetCount(), 20 );
381}
382
383
388{
389 std::unique_ptr<BOARD> board = std::make_unique<BOARD>();
390
391 m_plugin.LoadBoard( GetTestDataDir() + "project4.dip", board.get() );
392
393 BOOST_REQUIRE( board );
394
395 int totalPads = 0;
396
397 for( const FOOTPRINT* fp : board->Footprints() )
398 totalPads += static_cast<int>( fp->Pads().size() );
399
400 // project4.dip has 27 components, mostly 2-pin through-hole parts
401 BOOST_CHECK_GT( totalPads, 0 );
402}
403
404
408BOOST_AUTO_TEST_CASE( LoadKeyboardPads )
409{
410 std::unique_ptr<BOARD> board = std::make_unique<BOARD>();
411
412 m_plugin.LoadBoard( GetTestDataDir() + "keyboard.dip", board.get() );
413
414 BOOST_REQUIRE( board );
415
416 int totalPads = 0;
417
418 for( const FOOTPRINT* fp : board->Footprints() )
419 totalPads += static_cast<int>( fp->Pads().size() );
420
421 BOOST_CHECK_GT( totalPads, 0 );
422}
423
424
431BOOST_AUTO_TEST_CASE( KeyboardFootprintGraphics )
432{
433 std::unique_ptr<BOARD> board = std::make_unique<BOARD>();
434
435 m_plugin.LoadBoard( GetTestDataDir() + "keyboard.dip", board.get() );
436
437 BOOST_REQUIRE( board );
438
439 int totalGraphics = 0;
440
441 for( const FOOTPRINT* fp : board->Footprints() )
442 {
443 for( const BOARD_ITEM* item : fp->GraphicalItems() )
444 {
445 if( item->Type() == PCB_SHAPE_T )
446 totalGraphics++;
447 }
448 }
449
450 // keyboard.dip has 123 components; keyboard switches have 20 line segments each,
451 // capacitors have 14 each. A healthy import should produce many hundreds of shapes.
452 BOOST_CHECK_GT( totalGraphics, 100 );
453}
454
455
460BOOST_AUTO_TEST_CASE( LogicProbeFootprintGraphics )
461{
462 std::unique_ptr<BOARD> board = std::make_unique<BOARD>();
463
464 m_plugin.LoadBoard( GetTestDataDir() + "logic_probe.dip", board.get() );
465
466 BOOST_REQUIRE( board );
467
468 int totalGraphics = 0;
469
470 for( const FOOTPRINT* fp : board->Footprints() )
471 {
472 for( const BOARD_ITEM* item : fp->GraphicalItems() )
473 {
474 if( item->Type() == PCB_SHAPE_T )
475 totalGraphics++;
476 }
477 }
478
479 BOOST_CHECK_GT( totalGraphics, 50 );
480}
481
482
487BOOST_AUTO_TEST_CASE( Z80FootprintGraphics )
488{
489 std::unique_ptr<BOARD> board = std::make_unique<BOARD>();
490
491 m_plugin.LoadBoard( GetTestDataDir() + "z80_board.dip", board.get() );
492
493 BOOST_REQUIRE( board );
494
495 int totalGraphics = 0;
496
497 for( const FOOTPRINT* fp : board->Footprints() )
498 {
499 for( const BOARD_ITEM* item : fp->GraphicalItems() )
500 {
501 if( item->Type() == PCB_SHAPE_T )
502 totalGraphics++;
503 }
504 }
505
506 // v45 uses fixed-record shapes; the Z80 board has fewer shape-bearing footprints
507 // than the keyboard/logic_probe boards since many components are simple 2-pin DIP.
508 BOOST_CHECK_GT( totalGraphics, 10 );
509}
510
511
517BOOST_AUTO_TEST_CASE( LogicProbeTextPositioning )
518{
519 std::unique_ptr<BOARD> board = std::make_unique<BOARD>();
520
521 m_plugin.LoadBoard( GetTestDataDir() + "logic_probe.dip", board.get() );
522
523 BOOST_REQUIRE( board );
524
525 int nonZeroRefY = 0;
526 int nonZeroValY = 0;
527
528 for( const FOOTPRINT* fp : board->Footprints() )
529 {
530 VECTOR2I refPos = fp->Reference().GetPosition();
531 VECTOR2I valPos = fp->Value().GetPosition();
532 VECTOR2I fpPos = fp->GetPosition();
533
534 if( refPos.y != fpPos.y )
535 nonZeroRefY++;
536
537 if( valPos.y != fpPos.y )
538 nonZeroValY++;
539 }
540
541 // logic_probe.dip has 113 components; the component tail parser finds text
542 // offsets for components where the 37-byte tail is intact at the expected position.
543 BOOST_CHECK_GT( nonZeroRefY, 30 );
544 BOOST_CHECK_GT( nonZeroValY, 3 );
545}
546
547
551BOOST_AUTO_TEST_CASE( Z80TextPositioning )
552{
553 std::unique_ptr<BOARD> board = std::make_unique<BOARD>();
554
555 m_plugin.LoadBoard( GetTestDataDir() + "z80_board.dip", board.get() );
556
557 BOOST_REQUIRE( board );
558
559 int nonZeroRefY = 0;
560 int nonZeroValY = 0;
561
562 for( const FOOTPRINT* fp : board->Footprints() )
563 {
564 VECTOR2I refPos = fp->Reference().GetPosition();
565 VECTOR2I valPos = fp->Value().GetPosition();
566 VECTOR2I fpPos = fp->GetPosition();
567
568 if( refPos.y != fpPos.y )
569 nonZeroRefY++;
570
571 if( valPos.y != fpPos.y )
572 nonZeroValY++;
573 }
574
575 BOOST_CHECK_GT( nonZeroRefY, 1 );
576 BOOST_CHECK_GT( nonZeroValY, 1 );
577}
578
579
583BOOST_AUTO_TEST_CASE( KeyboardTextPositioning )
584{
585 std::unique_ptr<BOARD> board = std::make_unique<BOARD>();
586
587 m_plugin.LoadBoard( GetTestDataDir() + "keyboard.dip", board.get() );
588
589 BOOST_REQUIRE( board );
590
591 int nonZeroRefY = 0;
592
593 for( const FOOTPRINT* fp : board->Footprints() )
594 {
595 VECTOR2I refPos = fp->Reference().GetPosition();
596 VECTOR2I fpPos = fp->GetPosition();
597
598 if( refPos.y != fpPos.y )
599 nonZeroRefY++;
600 }
601
602 BOOST_CHECK_GT( nonZeroRefY, 30 );
603}
604
605
609BOOST_AUTO_TEST_CASE( V37TextPositioning )
610{
611 std::unique_ptr<BOARD> board = std::make_unique<BOARD>();
612
613 m_plugin.LoadBoard( GetTestDataDir() + "project4.dip", board.get() );
614
615 BOOST_REQUIRE( board );
616
617 int nonZeroValY = 0;
618
619 for( const FOOTPRINT* fp : board->Footprints() )
620 {
621 VECTOR2I valPos = fp->Value().GetPosition();
622 VECTOR2I fpPos = fp->GetPosition();
623
624 if( valPos.y != fpPos.y )
625 nonZeroValY++;
626 }
627
628 // project4.dip v37 has 27 components; at least some should have value Y offsets
629 BOOST_CHECK_GT( nonZeroValY, 5 );
630}
631
632
637BOOST_AUTO_TEST_CASE( FootprintGraphicShapeTypes )
638{
639 std::vector<std::string> files = { "logic_probe.dip", "keyboard.dip" };
640
641 int totalSegments = 0;
642 int totalCircles = 0;
643 int totalArcs = 0;
644
645 for( const std::string& file : files )
646 {
647 std::unique_ptr<BOARD> board = std::make_unique<BOARD>();
648
649 m_plugin.LoadBoard( GetTestDataDir() + file, board.get() );
650
651 BOOST_REQUIRE( board );
652
653 int segments = 0;
654 int circles = 0;
655 int arcs = 0;
656
657 for( const FOOTPRINT* fp : board->Footprints() )
658 {
659 for( const BOARD_ITEM* item : fp->GraphicalItems() )
660 {
661 if( item->Type() != PCB_SHAPE_T )
662 continue;
663
664 const PCB_SHAPE* shape = static_cast<const PCB_SHAPE*>( item );
665
666 switch( shape->GetShape() )
667 {
668 case SHAPE_T::SEGMENT: segments++; break;
669 case SHAPE_T::CIRCLE: circles++; break;
670 case SHAPE_T::ARC: arcs++; break;
671 default: break;
672 }
673 }
674 }
675
676 BOOST_TEST_MESSAGE( file << ": SEGMENT=" << segments
677 << " CIRCLE=" << circles << " ARC=" << arcs );
678
679 BOOST_CHECK_GT( segments + circles + arcs, 0 );
680
681 totalSegments += segments;
682 totalCircles += circles;
683 totalArcs += arcs;
684 }
685
686 // Across both boards, verify we see a healthy mix of shape types and not just segments
687 BOOST_CHECK_GT( totalSegments, 200 );
688 BOOST_CHECK_GT( totalCircles + totalArcs, 0 );
689}
690
691
697BOOST_AUTO_TEST_CASE( Z80PolygonPads )
698{
699 std::unique_ptr<BOARD> board = std::make_unique<BOARD>();
700
701 m_plugin.LoadBoard( GetTestDataDir() + "z80_board.dip", board.get() );
702
703 BOOST_REQUIRE( board );
704
705 int customPads = 0;
706
707 for( const FOOTPRINT* fp : board->Footprints() )
708 {
709 for( const PAD* pad : fp->Pads() )
710 {
711 if( pad->GetShape( PADSTACK::ALL_LAYERS ) == PAD_SHAPE::CUSTOM )
712 customPads++;
713 }
714 }
715
716 BOOST_CHECK_GT( customPads, 0 );
717}
718
719
724BOOST_AUTO_TEST_CASE( Z80RectangularPads )
725{
726 std::unique_ptr<BOARD> board = std::make_unique<BOARD>();
727
728 m_plugin.LoadBoard( GetTestDataDir() + "z80_board.dip", board.get() );
729
730 BOOST_REQUIRE( board );
731
732 int rectPads = 0;
733
734 for( const FOOTPRINT* fp : board->Footprints() )
735 {
736 for( const PAD* pad : fp->Pads() )
737 {
738 if( pad->GetShape( PADSTACK::ALL_LAYERS ) == PAD_SHAPE::RECTANGLE )
739 rectPads++;
740 }
741 }
742
743 // The Z80 board has SMD components with rectangular pads (padStyleC=2)
744 BOOST_CHECK_GT( rectPads, 0 );
745}
746
747
752BOOST_AUTO_TEST_CASE( KeyboardPadShapes )
753{
754 std::unique_ptr<BOARD> board = std::make_unique<BOARD>();
755
756 m_plugin.LoadBoard( GetTestDataDir() + "keyboard.dip", board.get() );
757
758 BOOST_REQUIRE( board );
759
760 int circlePads = 0;
761 int ovalPads = 0;
762 int rectPads = 0;
763
764 for( const FOOTPRINT* fp : board->Footprints() )
765 {
766 for( const PAD* pad : fp->Pads() )
767 {
768 switch( pad->GetShape( PADSTACK::ALL_LAYERS ) )
769 {
770 case PAD_SHAPE::CIRCLE: circlePads++; break;
771 case PAD_SHAPE::OVAL: ovalPads++; break;
772 case PAD_SHAPE::RECTANGLE: rectPads++; break;
773 default: break;
774 }
775 }
776 }
777
778 BOOST_TEST_MESSAGE( "keyboard.dip pads: CIRCLE=" << circlePads
779 << " OVAL=" << ovalPads << " RECT=" << rectPads );
780
781 // Should have at least some circular pads (THT switch pins)
782 BOOST_CHECK_GT( circlePads, 0 );
783}
784
785
790BOOST_AUTO_TEST_CASE( LogicProbeBoardSettings )
791{
792 std::unique_ptr<BOARD> board = std::make_unique<BOARD>();
793
794 m_plugin.LoadBoard( GetTestDataDir() + "logic_probe.dip", board.get() );
795
796 BOOST_REQUIRE( board );
797
798 BOARD_DESIGN_SETTINGS& bds = board->GetDesignSettings();
799
800 // Board should have at least 2 copper layers
801 BOOST_CHECK_GE( board->GetCopperLayerCount(), 2 );
802
803 // Default track width and clearance should be non-zero (from design rules)
804 std::shared_ptr<NETCLASS> defNc = bds.m_NetSettings->GetDefaultNetclass();
805 BOOST_CHECK_GT( defNc->GetTrackWidth(), 0 );
806 BOOST_CHECK_GT( defNc->GetClearance(), 0 );
807
808 // logic_probe.dip has ViaStyles, so via diameter should be set
809 BOOST_CHECK_GT( defNc->GetViaDiameter(), 0 );
810 BOOST_CHECK_GT( defNc->GetViaDrill(), 0 );
811}
812
813
817BOOST_AUTO_TEST_CASE( Z80BoardSettings )
818{
819 std::unique_ptr<BOARD> board = std::make_unique<BOARD>();
820
821 m_plugin.LoadBoard( GetTestDataDir() + "z80_board.dip", board.get() );
822
823 BOOST_REQUIRE( board );
824
825 // Z80 board is a 2-layer board
826 BOOST_CHECK_EQUAL( board->GetCopperLayerCount(), 2 );
827
828 BOARD_DESIGN_SETTINGS& bds = board->GetDesignSettings();
829 std::shared_ptr<NETCLASS> defNc = bds.m_NetSettings->GetDefaultNetclass();
830
831 // Default track width should have been set from design rules
832 BOOST_CHECK_GT( defNc->GetTrackWidth(), 0 );
833}
834
835
const char * name
General utilities for PCB file IO for QA programs.
Container for design settings for a BOARD object.
std::shared_ptr< NET_SETTINGS > m_NetSettings
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition board_item.h:84
Parses a DipTrace .dip binary board file and populates a KiCad BOARD.
int ScanLocatorUseCount() const
Number of objects or sections that were located by byte-pattern scanning rather than a deterministic ...
void Parse()
Parse the file and populate the board. Throws IO_ERROR on failure.
SHAPE_T GetShape() const
Definition eda_shape.h:189
Hold an error message and may be used when throwing exceptions containing meaningful error messages.
std::shared_ptr< NETCLASS > GetDefaultNetclass()
Gets the default netclass for the project.
static constexpr PCB_LAYER_ID ALL_LAYERS
! Temporary layer identifier to identify code that is not padstack-aware
Definition padstack.h:177
Definition pad.h:65
Parser for DipTrace binary .dip board files.
@ SEGMENT
Definition eda_shape.h:50
@ Edge_Cuts
Definition layer_ids.h:112
std::string GetPcbnewTestDataDir()
Utility which returns a path to the data directory where the test board files are stored.
@ RECTANGLE
Definition padstack.h:54
Pcbnew PCB_IO for DipTrace binary .dip board files.
BOOST_AUTO_TEST_CASE(HorizontalAlignment)
BOOST_AUTO_TEST_CASE(CanReadBoard)
Test that CanReadBoard correctly identifies DipTrace .dip files by their magic header bytes (0x07 "DT...
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_CHECK_EQUAL(result, "25.4")
@ PCB_SHAPE_T
class PCB_SHAPE, a segment not on copper layers
Definition typeinfo.h:85
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:687