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, see <https://www.gnu.org/licenses/>.
18 */
19
24
28
31
32#include <board.h>
34#include <footprint.h>
35#include <netclass.h>
37#include <pad.h>
38#include <pcb_track.h>
39#include <pcb_shape.h>
40
41#include <wx/ffile.h>
42#include <wx/filefn.h>
43#include <wx/filename.h>
44
45
47{
49
51
52 std::string GetTestDataDir()
53 {
54 return KI_TEST::GetPcbnewTestDataDir() + "plugins/diptrace/";
55 }
56};
57
58
59BOOST_FIXTURE_TEST_SUITE( DipTracePcbImport, DIPTRACE_PCB_IMPORT_FIXTURE )
60
61
62
66BOOST_AUTO_TEST_CASE( CanReadBoard )
67{
68 BOOST_CHECK( m_plugin.CanReadBoard( GetTestDataDir() + "keyboard.dip" ) );
69 BOOST_CHECK( m_plugin.CanReadBoard( GetTestDataDir() + "156bus_narrow.dip" ) );
70 BOOST_CHECK( m_plugin.CanReadBoard( GetTestDataDir() + "z80_board.dip" ) );
71 BOOST_CHECK( m_plugin.CanReadBoard( GetTestDataDir() + "logic_probe.dip" ) );
72 BOOST_CHECK( m_plugin.CanReadBoard( GetTestDataDir() + "project4.dip" ) );
73
74 wxString tempBase = wxFileName::CreateTempFileName( wxS( "kicad_diptrace_legacy_" ) );
75 wxRemoveFile( tempBase );
76 wxString legacyPath = tempBase + wxS( ".dip" );
77
78 {
79 const uint8_t legacyHeader[] = {
80 0x0B, 'D', 'T', 'B', 'O', 'A', 'R', 'D', '2', '.', '2', '1'
81 };
82
83 wxFFile file( legacyPath, wxS( "wb" ) );
84 BOOST_REQUIRE( file.IsOpened() );
85 BOOST_REQUIRE_EQUAL( file.Write( legacyHeader, sizeof( legacyHeader ) ),
86 sizeof( legacyHeader ) );
87 }
88
89 BOOST_CHECK( m_plugin.CanReadBoard( legacyPath ) );
90 wxRemoveFile( legacyPath );
91}
92
93
94BOOST_AUTO_TEST_CASE( InvalidComponentHeaderFailsDeterministically )
95{
96 const std::string sourcePath = GetTestDataDir() + "z80_board.dip";
97 wxString tempBase = wxFileName::CreateTempFileName( wxS( "kicad_diptrace_bad_pcb_comp_" ) );
98 wxRemoveFile( tempBase );
99 wxString tempPath = tempBase + wxS( ".dip" );
100
101 BOOST_REQUIRE( wxCopyFile( sourcePath, tempPath ) );
102
103 {
104 static constexpr wxFileOffset FIRST_COMPONENT_FLAGS_OFFSET = 0x491;
105 const uint8_t invalidFlags[] = { 0xFF, 0x00, 0x00, 0x00 };
106
107 wxFFile file( tempPath, wxS( "r+b" ) );
108 BOOST_REQUIRE( file.IsOpened() );
109 BOOST_REQUIRE( file.Seek( FIRST_COMPONENT_FLAGS_OFFSET ) );
110 BOOST_REQUIRE_EQUAL( file.Write( invalidFlags, sizeof( invalidFlags ) ), sizeof( invalidFlags ) );
111 }
112
113 BOOST_CHECK_THROW( m_plugin.LoadBoard( tempPath.ToStdString() ), IO_ERROR );
114
115 wxRemoveFile( tempPath );
116}
117
118
119BOOST_AUTO_TEST_CASE( InvalidRouteChainNodeCountFailsDeterministically )
120{
121 const std::string sourcePath = GetTestDataDir() + "z80_board.dip";
122 wxString tempBase = wxFileName::CreateTempFileName( wxS( "kicad_diptrace_bad_route_chain_" ) );
123 wxRemoveFile( tempBase );
124 wxString tempPath = tempBase + wxS( ".dip" );
125
126 BOOST_REQUIRE( wxCopyFile( sourcePath, tempPath ) );
127
128 {
129 static constexpr wxFileOffset FIRST_ROUTE_CHAIN_NODE_COUNT_OFFSET = 0x1CEA7;
130 const uint8_t invalidNodeCount[] = { 0x0F, 0x69, 0x51 }; // int3 value 10001
131
132 wxFFile file( tempPath, wxS( "r+b" ) );
133 BOOST_REQUIRE( file.IsOpened() );
134 BOOST_REQUIRE( file.Seek( FIRST_ROUTE_CHAIN_NODE_COUNT_OFFSET ) );
135 BOOST_REQUIRE_EQUAL( file.Write( invalidNodeCount, sizeof( invalidNodeCount ) ), sizeof( invalidNodeCount ) );
136 }
137
138 BOOST_CHECK_THROW( m_plugin.LoadBoard( tempPath.ToStdString() ), IO_ERROR );
139
140 wxRemoveFile( tempPath );
141}
142
143
144BOOST_AUTO_TEST_CASE( InvalidNetNameLengthFailsDeterministically )
145{
146 const std::string sourcePath = GetTestDataDir() + "z80_board.dip";
147 wxString tempBase = wxFileName::CreateTempFileName( wxS( "kicad_diptrace_bad_net_name_" ) );
148 wxRemoveFile( tempBase );
149 wxString tempPath = tempBase + wxS( ".dip" );
150
151 BOOST_REQUIRE( wxCopyFile( sourcePath, tempPath ) );
152
153 {
154 static constexpr wxFileOffset FIRST_NET_NAME_LENGTH_OFFSET = 0x1CE2D;
155 const uint8_t invalidNameLength[] = { 0x01, 0xF5 }; // UTF-16 char count 501
156
157 wxFFile file( tempPath, wxS( "r+b" ) );
158 BOOST_REQUIRE( file.IsOpened() );
159 BOOST_REQUIRE( file.Seek( FIRST_NET_NAME_LENGTH_OFFSET ) );
160 BOOST_REQUIRE_EQUAL( file.Write( invalidNameLength, sizeof( invalidNameLength ) ),
161 sizeof( invalidNameLength ) );
162 }
163
164 BOOST_CHECK_THROW( m_plugin.LoadBoard( tempPath.ToStdString() ), IO_ERROR );
165
166 wxRemoveFile( tempPath );
167}
168
169
170BOOST_AUTO_TEST_CASE( InvalidZoneMinWidthFailsDeterministically )
171{
172 const std::string sourcePath = GetTestDataDir() + "z80_board.dip";
173 wxString tempBase = wxFileName::CreateTempFileName( wxS( "kicad_diptrace_bad_zone_width_" ) );
174 wxRemoveFile( tempBase );
175 wxString tempPath = tempBase + wxS( ".dip" );
176
177 BOOST_REQUIRE( wxCopyFile( sourcePath, tempPath ) );
178
179 {
180 static constexpr wxFileOffset FIRST_ZONE_MIN_WIDTH_OFFSET = 0x382CB;
181 const uint8_t zeroMinWidth[] = { 0x3B, 0x9A, 0xCA, 0x00 }; // int4 value 0
182
183 wxFFile file( tempPath, wxS( "r+b" ) );
184 BOOST_REQUIRE( file.IsOpened() );
185 BOOST_REQUIRE( file.Seek( FIRST_ZONE_MIN_WIDTH_OFFSET ) );
186 BOOST_REQUIRE_EQUAL( file.Write( zeroMinWidth, sizeof( zeroMinWidth ) ), sizeof( zeroMinWidth ) );
187 }
188
189 BOOST_CHECK_THROW( m_plugin.LoadBoard( tempPath.ToStdString() ), IO_ERROR );
190
191 wxRemoveFile( tempPath );
192}
193
194
200BOOST_AUTO_TEST_CASE( LoadKeyboard )
201{
202 std::unique_ptr<BOARD> board = m_plugin.LoadBoard( GetTestDataDir() + "keyboard.dip" );
203
204 BOOST_REQUIRE( board );
205
206 // keyboard.dip has approximately 103 components
207 BOOST_CHECK_GT( board->Footprints().size(), 50 );
208
209 // keyboard.dip has ~70 nets (key matrix: col1-6, row1-8, diode nets, etc.)
210 BOOST_CHECK_GT( board->GetNetCount(), 20 );
211}
212
213
227BOOST_AUTO_TEST_CASE( ObjectsAreFieldLocatedNotScanned )
228{
229 const std::vector<std::string> files = {
230 "keyboard.dip",
231 "156bus_narrow.dip",
232 "logic_probe.dip",
233 "project4.dip",
234 "z80_board.dip"
235 };
236
237 for( const std::string& name : files )
238 {
239 const std::string path = GetTestDataDir() + name;
240 std::unique_ptr<BOARD> board = std::make_unique<BOARD>();
241 board->SetFileName( wxString::FromUTF8( path ) );
242
243 DIPTRACE::PCB_PARSER parser( wxString::FromUTF8( path ), board.get() );
244 parser.Parse();
245
246 // Per-category regression guards for the categories already converted to a
247 // deterministic field-walk. Pad records are field-located from the component
248 // header; mount holes carry no scan in this corpus.
249 BOOST_CHECK_MESSAGE( parser.PadLocatorScans() == 0,
250 name + ": pad located by scan (" + std::to_string( parser.PadLocatorScans() )
251 + ")" );
252 BOOST_CHECK_MESSAGE( parser.MountHoleLocatorScans() == 0,
253 name + ": mount hole located by scan ("
254 + std::to_string( parser.MountHoleLocatorScans() ) + ")" );
255 BOOST_CHECK_MESSAGE( parser.ShapeLocatorScans() == 0,
256 name + ": shape located by scan ("
257 + std::to_string( parser.ShapeLocatorScans() ) + ")" );
258 BOOST_CHECK_MESSAGE( parser.ComponentLocatorScans() == 0,
259 name + ": component located by scan ("
260 + std::to_string( parser.ComponentLocatorScans() ) + ")" );
261 BOOST_CHECK_MESSAGE( parser.SectionLocatorScans() == 0,
262 name + ": section located by scan ("
263 + std::to_string( parser.SectionLocatorScans() ) + ")" );
264
265 BOOST_CHECK_MESSAGE( parser.ScanLocatorUseCount() == 0,
266 name + ": object/section located by byte-pattern scan (total="
267 + std::to_string( parser.ScanLocatorUseCount() )
268 + " components=" + std::to_string( parser.ComponentLocatorScans() )
269 + " pads=" + std::to_string( parser.PadLocatorScans() )
270 + " shapes=" + std::to_string( parser.ShapeLocatorScans() )
271 + " holes=" + std::to_string( parser.MountHoleLocatorScans() )
272 + " sections=" + std::to_string( parser.SectionLocatorScans() ) + ")" );
273 }
274}
275
276
282{
283 std::unique_ptr<BOARD> board = m_plugin.LoadBoard( GetTestDataDir() + "156bus_narrow.dip" );
284
285 BOOST_REQUIRE( board );
286 BOOST_CHECK_GT( board->Footprints().size(), 0 );
287
288 // 156bus_narrow has a board outline with 12 vertices including arcs
289 bool hasOutline = false;
290
291 for( BOARD_ITEM* drawing : board->Drawings() )
292 {
293 if( drawing->GetLayer() == Edge_Cuts )
294 {
295 hasOutline = true;
296 break;
297 }
298 }
299
300 BOOST_CHECK( hasOutline );
301}
302
303
309{
310 std::unique_ptr<BOARD> board = m_plugin.LoadBoard( GetTestDataDir() + "project4.dip" );
311
312 BOOST_REQUIRE( board );
313 BOOST_CHECK_GT( board->Footprints().size(), 0 );
314}
315
316
320BOOST_AUTO_TEST_CASE( LoadLogicProbe )
321{
322 std::unique_ptr<BOARD> board = m_plugin.LoadBoard( GetTestDataDir() + "logic_probe.dip" );
323
324 BOOST_REQUIRE( board );
325 BOOST_CHECK_GT( board->Footprints().size(), 0 );
326}
327
328
333BOOST_AUTO_TEST_CASE( LoadZ80Board )
334{
335 std::unique_ptr<BOARD> board = m_plugin.LoadBoard( GetTestDataDir() + "z80_board.dip" );
336
337 BOOST_REQUIRE( board );
338
339 // Z80 computer board should have a healthy number of components
340 BOOST_CHECK_GT( board->Footprints().size(), 10 );
341
342 // Verify reference designators are present on imported footprints
343 int refsFound = 0;
344 int totalPads = 0;
345
346 for( const FOOTPRINT* fp : board->Footprints() )
347 {
348 if( !fp->GetReference().IsEmpty() )
349 refsFound++;
350
351 totalPads += static_cast<int>( fp->Pads().size() );
352 }
353
354 BOOST_CHECK_GT( refsFound, 10 );
355 BOOST_CHECK_GT( totalPads, 0 );
356
357 // Z80 board has ~97 nets (address bus A0-A15, data bus D0-D7, control, etc.)
358 BOOST_CHECK_GT( board->GetNetCount(), 20 );
359}
360
361
366{
367 std::unique_ptr<BOARD> board = m_plugin.LoadBoard( GetTestDataDir() + "project4.dip" );
368
369 BOOST_REQUIRE( board );
370
371 int totalPads = 0;
372
373 for( const FOOTPRINT* fp : board->Footprints() )
374 totalPads += static_cast<int>( fp->Pads().size() );
375
376 // project4.dip has 27 components, mostly 2-pin through-hole parts
377 BOOST_CHECK_GT( totalPads, 0 );
378}
379
380
384BOOST_AUTO_TEST_CASE( LoadKeyboardPads )
385{
386 std::unique_ptr<BOARD> board = m_plugin.LoadBoard( GetTestDataDir() + "keyboard.dip" );
387
388 BOOST_REQUIRE( board );
389
390 int totalPads = 0;
391
392 for( const FOOTPRINT* fp : board->Footprints() )
393 totalPads += static_cast<int>( fp->Pads().size() );
394
395 BOOST_CHECK_GT( totalPads, 0 );
396}
397
398
405BOOST_AUTO_TEST_CASE( KeyboardFootprintGraphics )
406{
407 std::unique_ptr<BOARD> board = m_plugin.LoadBoard( GetTestDataDir() + "keyboard.dip" );
408
409 BOOST_REQUIRE( board );
410
411 int totalGraphics = 0;
412
413 for( const FOOTPRINT* fp : board->Footprints() )
414 {
415 for( const BOARD_ITEM* item : fp->GraphicalItems() )
416 {
417 if( item->Type() == PCB_SHAPE_T )
418 totalGraphics++;
419 }
420 }
421
422 // keyboard.dip has 123 components; keyboard switches have 20 line segments each,
423 // capacitors have 14 each. A healthy import should produce many hundreds of shapes.
424 BOOST_CHECK_GT( totalGraphics, 100 );
425}
426
427
432BOOST_AUTO_TEST_CASE( LogicProbeFootprintGraphics )
433{
434 std::unique_ptr<BOARD> board = m_plugin.LoadBoard( GetTestDataDir() + "logic_probe.dip" );
435
436 BOOST_REQUIRE( board );
437
438 int totalGraphics = 0;
439
440 for( const FOOTPRINT* fp : board->Footprints() )
441 {
442 for( const BOARD_ITEM* item : fp->GraphicalItems() )
443 {
444 if( item->Type() == PCB_SHAPE_T )
445 totalGraphics++;
446 }
447 }
448
449 BOOST_CHECK_GT( totalGraphics, 50 );
450}
451
452
457BOOST_AUTO_TEST_CASE( Z80FootprintGraphics )
458{
459 std::unique_ptr<BOARD> board = m_plugin.LoadBoard( GetTestDataDir() + "z80_board.dip" );
460
461 BOOST_REQUIRE( board );
462
463 int totalGraphics = 0;
464
465 for( const FOOTPRINT* fp : board->Footprints() )
466 {
467 for( const BOARD_ITEM* item : fp->GraphicalItems() )
468 {
469 if( item->Type() == PCB_SHAPE_T )
470 totalGraphics++;
471 }
472 }
473
474 // v45 uses fixed-record shapes; the Z80 board has fewer shape-bearing footprints
475 // than the keyboard/logic_probe boards since many components are simple 2-pin DIP.
476 BOOST_CHECK_GT( totalGraphics, 10 );
477}
478
479
485BOOST_AUTO_TEST_CASE( LogicProbeTextPositioning )
486{
487 std::unique_ptr<BOARD> board = m_plugin.LoadBoard( GetTestDataDir() + "logic_probe.dip" );
488
489 BOOST_REQUIRE( board );
490
491 int nonZeroRefY = 0;
492 int nonZeroValY = 0;
493
494 for( const FOOTPRINT* fp : board->Footprints() )
495 {
496 VECTOR2I refPos = fp->Reference().GetPosition();
497 VECTOR2I valPos = fp->Value().GetPosition();
498 VECTOR2I fpPos = fp->GetPosition();
499
500 if( refPos.y != fpPos.y )
501 nonZeroRefY++;
502
503 if( valPos.y != fpPos.y )
504 nonZeroValY++;
505 }
506
507 // logic_probe.dip has 113 components; the component tail parser finds text
508 // offsets for components where the 37-byte tail is intact at the expected position.
509 BOOST_CHECK_GT( nonZeroRefY, 30 );
510 BOOST_CHECK_GT( nonZeroValY, 3 );
511}
512
513
517BOOST_AUTO_TEST_CASE( Z80TextPositioning )
518{
519 std::unique_ptr<BOARD> board = m_plugin.LoadBoard( GetTestDataDir() + "z80_board.dip" );
520
521 BOOST_REQUIRE( board );
522
523 int nonZeroRefY = 0;
524 int nonZeroValY = 0;
525
526 for( const FOOTPRINT* fp : board->Footprints() )
527 {
528 VECTOR2I refPos = fp->Reference().GetPosition();
529 VECTOR2I valPos = fp->Value().GetPosition();
530 VECTOR2I fpPos = fp->GetPosition();
531
532 if( refPos.y != fpPos.y )
533 nonZeroRefY++;
534
535 if( valPos.y != fpPos.y )
536 nonZeroValY++;
537 }
538
539 BOOST_CHECK_GT( nonZeroRefY, 1 );
540 BOOST_CHECK_GT( nonZeroValY, 1 );
541}
542
543
547BOOST_AUTO_TEST_CASE( KeyboardTextPositioning )
548{
549 std::unique_ptr<BOARD> board = m_plugin.LoadBoard( GetTestDataDir() + "keyboard.dip" );
550
551 BOOST_REQUIRE( board );
552
553 int nonZeroRefY = 0;
554
555 for( const FOOTPRINT* fp : board->Footprints() )
556 {
557 VECTOR2I refPos = fp->Reference().GetPosition();
558 VECTOR2I fpPos = fp->GetPosition();
559
560 if( refPos.y != fpPos.y )
561 nonZeroRefY++;
562 }
563
564 BOOST_CHECK_GT( nonZeroRefY, 30 );
565}
566
567
571BOOST_AUTO_TEST_CASE( V37TextPositioning )
572{
573 std::unique_ptr<BOARD> board = m_plugin.LoadBoard( GetTestDataDir() + "project4.dip" );
574
575 BOOST_REQUIRE( board );
576
577 int nonZeroValY = 0;
578
579 for( const FOOTPRINT* fp : board->Footprints() )
580 {
581 VECTOR2I valPos = fp->Value().GetPosition();
582 VECTOR2I fpPos = fp->GetPosition();
583
584 if( valPos.y != fpPos.y )
585 nonZeroValY++;
586 }
587
588 // project4.dip v37 has 27 components; at least some should have value Y offsets
589 BOOST_CHECK_GT( nonZeroValY, 5 );
590}
591
592
597BOOST_AUTO_TEST_CASE( FootprintGraphicShapeTypes )
598{
599 std::vector<std::string> files = { "logic_probe.dip",
600 "keyboard.dip" };
601
602 int totalSegments = 0;
603 int totalCircles = 0;
604 int totalArcs = 0;
605
606 for( const std::string& file : files )
607 {
608 std::unique_ptr<BOARD> board = m_plugin.LoadBoard( GetTestDataDir() + file );
609
610 BOOST_REQUIRE( board );
611
612 int segments = 0;
613 int circles = 0;
614 int arcs = 0;
615
616 for( const FOOTPRINT* fp : board->Footprints() )
617 {
618 for( const BOARD_ITEM* item : fp->GraphicalItems() )
619 {
620 if( item->Type() != PCB_SHAPE_T )
621 continue;
622
623 const PCB_SHAPE* shape = static_cast<const PCB_SHAPE*>( item );
624
625 switch( shape->GetShape() )
626 {
627 case SHAPE_T::SEGMENT: segments++; break;
628 case SHAPE_T::CIRCLE: circles++; break;
629 case SHAPE_T::ARC: arcs++; break;
630 default: break;
631 }
632 }
633 }
634
635 BOOST_TEST_MESSAGE( file << ": SEGMENT=" << segments
636 << " CIRCLE=" << circles << " ARC=" << arcs );
637
638 BOOST_CHECK_GT( segments + circles + arcs, 0 );
639
640 totalSegments += segments;
641 totalCircles += circles;
642 totalArcs += arcs;
643 }
644
645 // Across both boards, verify we see a healthy mix of shape types and not just segments
646 BOOST_CHECK_GT( totalSegments, 200 );
647 BOOST_CHECK_GT( totalCircles + totalArcs, 0 );
648}
649
650
656BOOST_AUTO_TEST_CASE( Z80PolygonPads )
657{
658 std::unique_ptr<BOARD> board = m_plugin.LoadBoard( GetTestDataDir() + "z80_board.dip" );
659
660 BOOST_REQUIRE( board );
661
662 int customPads = 0;
663
664 for( const FOOTPRINT* fp : board->Footprints() )
665 {
666 for( const PAD* pad : fp->Pads() )
667 {
668 if( pad->GetShape( PADSTACK::ALL_LAYERS ) == PAD_SHAPE::CUSTOM )
669 customPads++;
670 }
671 }
672
673 BOOST_CHECK_GT( customPads, 0 );
674}
675
676
681BOOST_AUTO_TEST_CASE( Z80RectangularPads )
682{
683 std::unique_ptr<BOARD> board = m_plugin.LoadBoard( GetTestDataDir() + "z80_board.dip" );
684
685 BOOST_REQUIRE( board );
686
687 int rectPads = 0;
688
689 for( const FOOTPRINT* fp : board->Footprints() )
690 {
691 for( const PAD* pad : fp->Pads() )
692 {
693 if( pad->GetShape( PADSTACK::ALL_LAYERS ) == PAD_SHAPE::RECTANGLE )
694 rectPads++;
695 }
696 }
697
698 // The Z80 board has SMD components with rectangular pads (padStyleC=2)
699 BOOST_CHECK_GT( rectPads, 0 );
700}
701
702
707BOOST_AUTO_TEST_CASE( KeyboardPadShapes )
708{
709 std::unique_ptr<BOARD> board = m_plugin.LoadBoard( GetTestDataDir() + "keyboard.dip" );
710
711 BOOST_REQUIRE( board );
712
713 int circlePads = 0;
714 int ovalPads = 0;
715 int rectPads = 0;
716
717 for( const FOOTPRINT* fp : board->Footprints() )
718 {
719 for( const PAD* pad : fp->Pads() )
720 {
721 switch( pad->GetShape( PADSTACK::ALL_LAYERS ) )
722 {
723 case PAD_SHAPE::CIRCLE: circlePads++; break;
724 case PAD_SHAPE::OVAL: ovalPads++; break;
725 case PAD_SHAPE::RECTANGLE: rectPads++; break;
726 default: break;
727 }
728 }
729 }
730
731 BOOST_TEST_MESSAGE( "keyboard.dip pads: CIRCLE=" << circlePads
732 << " OVAL=" << ovalPads << " RECT=" << rectPads );
733
734 // Should have at least some circular pads (THT switch pins)
735 BOOST_CHECK_GT( circlePads, 0 );
736}
737
738
743BOOST_AUTO_TEST_CASE( LogicProbeBoardSettings )
744{
745 std::unique_ptr<BOARD> board = m_plugin.LoadBoard( GetTestDataDir() + "logic_probe.dip" );
746
747 BOOST_REQUIRE( board );
748
749 BOARD_DESIGN_SETTINGS& bds = board->GetDesignSettings();
750
751 // Board should have at least 2 copper layers
752 BOOST_CHECK_GE( board->GetCopperLayerCount(), 2 );
753
754 // Default track width and clearance should be non-zero (from design rules)
755 std::shared_ptr<NETCLASS> defNc = bds.m_NetSettings->GetDefaultNetclass();
756 BOOST_CHECK_GT( defNc->GetTrackWidth(), 0 );
757 BOOST_CHECK_GT( defNc->GetClearance(), 0 );
758
759 // logic_probe.dip has ViaStyles, so via diameter should be set
760 BOOST_CHECK_GT( defNc->GetViaDiameter(), 0 );
761 BOOST_CHECK_GT( defNc->GetViaDrill(), 0 );
762}
763
764
768BOOST_AUTO_TEST_CASE( Z80BoardSettings )
769{
770 std::unique_ptr<BOARD> board = m_plugin.LoadBoard( GetTestDataDir() + "z80_board.dip" );
771
772 BOOST_REQUIRE( board );
773
774 // Z80 board is a 2-layer board
775 BOOST_CHECK_EQUAL( board->GetCopperLayerCount(), 2 );
776
777 BOARD_DESIGN_SETTINGS& bds = board->GetDesignSettings();
778 std::shared_ptr<NETCLASS> defNc = bds.m_NetSettings->GetDefaultNetclass();
779
780 // Default track width should have been set from design rules
781 BOOST_CHECK_GT( defNc->GetTrackWidth(), 0 );
782}
783
784
795BOOST_AUTO_TEST_CASE( ReArmPlacementRotationsMatchDipTrace )
796{
797 std::unique_ptr<BOARD> board = m_plugin.LoadBoard( GetTestDataDir() + "re-arm_pub.dip" );
798
799 std::map<wxString, int> orient;
800
801 for( FOOTPRINT* fp : board->Footprints() )
802 orient[fp->GetReference()] = KiROUND( fp->GetOrientation().Normalize().AsDegrees() ) % 360;
803
804 const std::vector<std::pair<wxString, int>> expected = {
805 { wxT( "A1" ), 0 }, { wxT( "C1" ), 270 }, { wxT( "C2" ), 180 }, { wxT( "C12" ), 180 },
806 { wxT( "C25" ), 90 }, { wxT( "C32" ), 270 }, { wxT( "D2" ), 90 }, { wxT( "J3" ), 180 },
807 { wxT( "J5" ), 90 }, { wxT( "J7" ), 270 }, { wxT( "J12" ), 180 }, { wxT( "R2" ), 270 },
808 { wxT( "R11" ), 90 }, { wxT( "U1" ), 270 }, { wxT( "U2" ), 180 }, { wxT( "X1" ), 90 },
809 };
810
811 for( const auto& [ref, deg] : expected )
812 {
813 auto it = orient.find( ref );
814 BOOST_REQUIRE_MESSAGE( it != orient.end(), "missing footprint " << ref.ToStdString() );
815 BOOST_CHECK_MESSAGE( it->second == deg, ref.ToStdString() << " orientation " << it->second
816 << " != expected " << deg );
817 }
818
819 // The board is heavily rotated; the old defect left almost every component at 0 degrees.
820 int rotated = 0;
821
822 for( const auto& [ref, deg] : orient )
823 {
824 if( deg != 0 )
825 rotated++;
826 }
827
828 BOOST_CHECK_GT( rotated, 50 );
829}
830
const char * name
General utilities for PCB file IO for QA programs.
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:995
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:175
Hold an error message and may be used when throwing exceptions containing meaningful error messages.
std::shared_ptr< NETCLASS > GetDefaultNetclass() const
Gets the default netclass for the project.
static constexpr PCB_LAYER_ID ALL_LAYERS
! The layer identifier to use for the single defintion on normal padstacks
Definition padstack.h:179
Definition pad.h:61
Parser for DipTrace binary .dip board files.
@ SEGMENT
Definition eda_shape.h:56
@ Edge_Cuts
Definition layer_ids.h:108
std::string GetPcbnewTestDataDir()
Utility which returns a path to the data directory where the test board files are stored.
@ RECTANGLE
Definition padstack.h:53
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
VECTOR3I expected(15, 30, 45)
BOOST_TEST_MESSAGE("Polyline has "<< chain.PointCount()<< " points")
BOOST_CHECK_EQUAL(result, "25.4")
@ PCB_SHAPE_T
class PCB_SHAPE, a segment not on copper layers
Definition typeinfo.h:80
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683