53 {
"ClaySight_MK1",
"ClaySight_MK1.asc" },
54 {
"TMS1mmX19",
"TMS1mmX19.asc" },
55 {
"MC4_PLUS_CSHAPE",
"MC4_PLUS_CSHAPE.asc" },
56 {
"MC2_PLUS_REV1",
"MC2_PLUS_REV1.asc" },
57 {
"Ems4_Rev2",
"Ems4_Rev2.asc" },
58 {
"LCORE_4",
"LCORE_4.asc" },
59 {
"LCORE_2",
"LCORE_2.asc" },
60 {
"Dexter_MotorCtrl",
"Dexter_MotorCtrl.asc" },
61 {
"MAIS_FC",
"MAIS_FC.asc" },
62 {
"ClaySight_MK2",
"ClaySight_MK2.asc" },
82 aBoard.
dir <<
" should be a readable PADS file" );
84 std::unique_ptr<BOARD> board;
88 board = plugin.
LoadBoard( filename,
nullptr,
nullptr );
90 catch(
const std::exception& e )
92 BOOST_WARN_MESSAGE(
false,
93 aBoard.
dir <<
" threw exception during load: " << e.what() );
97 BOOST_REQUIRE_MESSAGE( board !=
nullptr, aBoard.
dir <<
" failed to load" );
98 BOOST_CHECK_MESSAGE( board->Footprints().size() > 0,
99 aBoard.
dir <<
" should have footprints" );
119 BOOST_WARN_MESSAGE( board->Tracks().size() > 0,
120 aBoard.
dir <<
" has no tracks (parser may not support this format version)" );
122 if( board->Tracks().size() > 0 && board->Footprints().size() > 0 )
127 for(
FOOTPRINT* fp : board->Footprints() )
128 fpBbox.
Merge( fp->GetBoundingBox() );
134 trackBbox.
Merge( trk->GetBoundingBox() );
136 BOOST_CHECK_MESSAGE( fpBbox.
Intersects( trackBbox ),
137 aBoard.
dir <<
" footprint and track bounding boxes should overlap" );
141 std::set<std::pair<int, int>> viaPositions;
142 bool hasDuplicate =
false;
151 auto key = std::make_pair(
via->GetPosition().x,
via->GetPosition().y );
153 if( viaPositions.count( key ) )
159 viaPositions.insert( key );
162 BOOST_CHECK_MESSAGE( !hasDuplicate,
163 aBoard.
dir <<
" should have no duplicate through-hole vias" );
171 aBoard.
dir <<
" track on non-copper layer " << trk->GetLayer() );
176 for(
FOOTPRINT* fp : board->Footprints() )
178 for(
PAD*
pad : fp->Pads() )
182 aBoard.
dir <<
" " << fp->GetReference() <<
" pad has zero size" );
187 for(
ZONE* zone : board->Zones() )
190 BOOST_REQUIRE_MESSAGE( outline !=
nullptr,
191 aBoard.
dir <<
" zone has null outline" );
196 aBoard.
dir <<
" zone outline " << ii <<
" has "
231 for(
FOOTPRINT* fp : board->Footprints() )
232 totalPads += fp->Pads().size();
257 int edgeCutsCount = 0;
275 if(
dynamic_cast<PCB_TEXT*
>( item ) )
282 std::set<wxString> trackNets;
300 BOOST_CHECK_MESSAGE( layer ==
F_Cu || layer ==
B_Cu,
301 "trace on unexpected layer " << layer );
362 std::unique_ptr<BOARD> board = plugin.
LoadBoard( filename,
nullptr,
nullptr );
368 for(
PCB_TRACK* track : board->Tracks() )
377 BOOST_CHECK( track_count > 0 );
388 std::unique_ptr<BOARD> board = plugin.
LoadBoard( filename,
nullptr,
nullptr );
392 int silkscreen_count = 0;
393 int comments_count = 0;
394 int copper_count = 0;
408 else if( layer ==
F_Cu )
444 for(
FOOTPRINT* fp : board->Footprints() )
445 totalPads += fp->Pads().size();
465 std::set<wxString> trackNets;
483 "trace on non-copper layer " << trk->GetLayer() );
497 if( shape->GetLayer() ==
F_SilkS )
513 BOOST_CHECK( defaultNc->GetViaDiameter() > 0 );
514 BOOST_CHECK( defaultNc->GetViaDrill() > 0 );
515 BOOST_CHECK( defaultNc->GetViaDiameter() > defaultNc->GetViaDrill() );
522 int edgeCutsCount = 0;
537 wxString textContent;
544 textContent =
text->GetText();
553 BOOST_CHECK( textContent.Contains( wxT(
"\n" ) ) );
554 BOOST_CHECK( !textContent.Contains( wxT(
"_" ) ) );
555 BOOST_CHECK( textContent.Contains( wxT(
"CLAYSIGHT MCU V.2" ) ) );
556 BOOST_CHECK( textContent.Contains( wxT(
"The Ohio State University" ) ) );
577 bool foundCopperThickness =
false;
578 bool foundDielectric =
false;
584 if( item->GetThickness() > 0 )
585 foundCopperThickness =
true;
589 if( item->GetEpsilonR() > 3.0 )
590 foundDielectric =
true;
594 BOOST_CHECK_MESSAGE( foundCopperThickness,
"stackup should have non-zero copper thickness" );
595 BOOST_CHECK_MESSAGE( foundDielectric,
"stackup should have dielectric constant > 3.0" );
613 +
"plugins/pads/synthetic_degenerate_pour.asc";
615 std::unique_ptr<BOARD> board = plugin.
LoadBoard( filename,
nullptr,
nullptr );
625 if( board->Zones().size() == 1 )
627 ZONE* zone = board->Zones()[0];
647 +
"plugins/pads/synthetic_filled_copper.asc";
649 std::unique_ptr<BOARD> board = plugin.
LoadBoard( filename,
nullptr,
nullptr );
652 BOOST_REQUIRE_EQUAL( board->Zones().size(), 1 );
654 ZONE* zone = board->Zones()[0];
675 std::unique_ptr<BOARD> board = plugin.
LoadBoard( filename,
nullptr,
nullptr );
683 int dwgsUserCount = 0;
694 BOOST_CHECK_MESSAGE( dwgsUserCount > 0,
695 "graphics on PADS layer 18 (drill drawing) should map to Dwgs_User" );
702 for(
FOOTPRINT* fp : board->Footprints() )
704 if( fp->GetReference() == wxT(
"U1" ) )
711 BOOST_REQUIRE_MESSAGE( u1 !=
nullptr,
"U1 footprint should exist" );
713 bool foundOvalPad =
false;
726 BOOST_CHECK_MESSAGE( foundOvalPad,
"U1 should have oval pads (OF shape, not RT thermal)" );
731 int pourZoneCount = 0;
732 int filledZoneCount = 0;
734 for(
ZONE* zone : board->Zones() )
736 if( !zone->GetIsRuleArea() )
740 if( zone->IsFilled() )
745 BOOST_CHECK_MESSAGE( pourZoneCount <= 13,
746 "should not have duplicate zones from HATOUT; got " << pourZoneCount );
748 BOOST_CHECK_MESSAGE( filledZoneCount > 0,
"HATOUT records should produce filled zones" );
764 BOOST_CHECK_MESSAGE( start.
y ==
end.y,
765 "horizontal dimension endpoints should have equal Y coordinates; "
766 "start.y=" << start.
y <<
" end.y=" <<
end.y );
770 BOOST_CHECK_MESSAGE( dimCount > 0,
"should have at least one dimension" );
792 std::unique_ptr<BOARD> board = plugin.
LoadBoard( filename,
nullptr,
nullptr );
797 std::map<std::pair<int, int>,
int> viaPositionCount;
799 int throughCount = 0;
805 const int maxExpectedViaWidth = 1200000;
807 int oversizedViaCount = 0;
809 for(
PCB_TRACK* track : board->Tracks() )
817 auto key = std::make_pair( pos.
x, pos.
y );
818 viaPositionCount[key]++;
825 if(
via->GetWidth(
F_Cu ) > maxExpectedViaWidth )
830 BOOST_CHECK_MESSAGE( blindCount == 0,
831 "no vias should be blind; STANDARDVIA spans all copper layers; got "
832 << blindCount <<
" blind vias" );
834 BOOST_CHECK_MESSAGE( throughCount > 0,
"should have through-hole vias" );
837 BOOST_CHECK_MESSAGE( oversizedViaCount == 0,
838 "via size should use copper pad, not soldermask opening; got "
839 << oversizedViaCount <<
" oversized vias" );
842 int duplicateCount = 0;
844 for(
const auto& [pos, count] : viaPositionCount )
850 BOOST_CHECK_MESSAGE( duplicateCount == 0,
851 "should not have duplicate vias at the same position; got "
852 << duplicateCount <<
" positions with duplicates" );
857 int backTentedCount = 0;
860 for(
PCB_TRACK* track : board->Tracks() )
873 BOOST_CHECK_MESSAGE( backTentedCount == totalVias,
874 "vias without soldermask opening should be tented; "
875 << backTentedCount <<
" of " << totalVias <<
" back-tented" );
893 std::unique_ptr<BOARD> board = plugin.
LoadBoard( filename,
nullptr,
nullptr );
899 for(
FOOTPRINT* fp : board->Footprints() )
901 if( fp->GetReference() ==
"U1" )
908 BOOST_REQUIRE_MESSAGE( u1,
"U1 not found on board" );
913 const int expectedMajor = 6000000;
914 const int expectedMinor = 1500000;
915 const int tolerance = 10000;
921 wxString padNum =
pad->GetNumber();
923 if( padNum ==
"1" || padNum ==
"2" || padNum ==
"3"
924 || padNum ==
"4" || padNum ==
"5" )
927 "pad " << padNum <<
" should have oblong drill" );
930 int major = std::max( drillSize.
x, drillSize.
y );
931 int minor = std::min( drillSize.
x, drillSize.
y );
933 BOOST_CHECK_MESSAGE(
std::abs( major - expectedMajor ) < tolerance,
934 "pad " << padNum <<
" drill major axis " << major
935 <<
" should be ~" << expectedMajor );
937 BOOST_CHECK_MESSAGE(
std::abs( minor - expectedMinor ) < tolerance,
938 "pad " << padNum <<
" drill minor axis " << minor
939 <<
" should be ~" << expectedMinor );
946 BOOST_CHECK_MESSAGE( oblongCount == 5,
947 "expected 5 pads with oblong drill, got " << oblongCount );
965 std::unique_ptr<BOARD> board = plugin.
LoadBoard( filename,
nullptr,
nullptr );
971 for(
FOOTPRINT* fp : board->Footprints() )
973 if( fp->GetReference() ==
"M4" )
980 BOOST_REQUIRE_MESSAGE( m4,
"M4 not found on board" );
984 const int expectedPadSize = 6350000;
985 const int expectedDrill = 3175000;
986 const int tolerance = 10000;
988 BOOST_REQUIRE_MESSAGE( m4->
Pads().size() == 1,
989 "MTHOLEAAAB has 1 terminal; got " << m4->
Pads().size() );
994 "M4 pad 1 drill should be circular" );
997 int padDim = std::max( padSize.
x, padSize.
y );
999 BOOST_CHECK_MESSAGE(
std::abs( padDim - expectedPadSize ) < tolerance,
1000 "M4 pad size " << padDim <<
" should be ~" << expectedPadSize
1004 int drillDim = std::max( drillSize.
x, drillSize.
y );
1006 BOOST_CHECK_MESSAGE(
std::abs( drillDim - expectedDrill ) < tolerance,
1007 "M4 drill size " << drillDim <<
" should be ~" << expectedDrill
1026 std::unique_ptr<BOARD> board = plugin.
LoadBoard( filename,
nullptr,
nullptr );
1034 auto hasTrueCrossing = [](
const SHAPE_POLY_SET& aPoly,
int aIdx ) ->
bool
1036 std::vector<SEG> segs;
1039 segs.emplace_back( *it );
1041 for(
size_t i = 0; i < segs.size(); i++ )
1043 for(
size_t j = i + 1; j < segs.size(); j++ )
1047 if( segs[i].
A == segs[j].
A || segs[i].
A == segs[j].
B
1048 || segs[i].
B == segs[j].
A || segs[i].
B == segs[j].
B )
1053 if( segs[i].Intersects( segs[j] ) )
1061 int zonesChecked = 0;
1063 for(
ZONE* zone : board->Zones() )
1065 if( !zone->IsFilled() )
1070 if( !zone->HasFilledPolysForLayer( layer ) )
1073 std::shared_ptr<SHAPE_POLY_SET> fill = zone->GetFilledPolysList( layer );
1075 if( !fill || fill->OutlineCount() == 0 )
1080 for(
int pi = 0; pi < fill->OutlineCount(); pi++ )
1082 if( fill->Outline( pi ).PointCount() < 3 )
1085 BOOST_CHECK_MESSAGE(
1086 !hasTrueCrossing( *fill, pi ),
1087 "zone \"" << zone->GetNetname() <<
"\" on "
1088 << board->GetLayerName( layer )
1089 <<
" outline " << pi
1090 <<
" has self-intersecting fill polygon" );
1095 BOOST_CHECK_MESSAGE( zonesChecked > 0,
"no filled zones found to check" );
1120 std::unique_ptr<BOARD> board = plugin.
LoadBoard( filename,
nullptr,
nullptr );
1123 BOOST_REQUIRE_EQUAL( board->Footprints().size(), 5 );
1125 auto findFP = [&](
const wxString& aRef ) ->
FOOTPRINT*
1127 for(
FOOTPRINT* fp : board->Footprints() )
1128 if( fp->GetReference() == aRef )
1136 BOOST_REQUIRE_MESSAGE( u1,
"U1 should exist" );
1137 BOOST_REQUIRE_EQUAL( u1->
Pads().size(), 1 );
1140 BOOST_CHECK_MESSAGE(
pad->IsOnLayer(
F_Cu ),
"U1 pad should be on F.Cu" );
1141 BOOST_CHECK_MESSAGE(
pad->IsOnLayer(
F_Mask ),
"U1 pad should have F.Mask (explicit in stack)" );
1142 BOOST_CHECK_MESSAGE(
pad->IsOnLayer(
F_Paste ),
"U1 pad should have F.Paste (explicit in stack)" );
1143 BOOST_CHECK_MESSAGE( !
pad->IsOnLayer(
B_Cu ),
"U1 SMD pad should not be on B.Cu" );
1149 BOOST_REQUIRE_MESSAGE( u2,
"U2 should exist" );
1150 BOOST_REQUIRE_EQUAL( u2->
Pads().size(), 1 );
1153 BOOST_CHECK_MESSAGE(
pad->IsOnLayer(
F_Cu ),
"U2 pad should be on F.Cu" );
1154 BOOST_CHECK_MESSAGE(
pad->IsOnLayer(
F_Mask ),
"U2 pad should have F.Mask (explicit in stack)" );
1155 BOOST_CHECK_MESSAGE(
pad->IsOnLayer(
F_Paste ),
"U2 pad should have F.Paste (fallback for SMD)" );
1156 BOOST_CHECK_MESSAGE( !
pad->IsOnLayer(
B_Cu ),
"U2 SMD pad should not be on B.Cu" );
1162 BOOST_REQUIRE_MESSAGE( u3,
"U3 should exist" );
1163 BOOST_REQUIRE_EQUAL( u3->
Pads().size(), 1 );
1166 BOOST_CHECK_MESSAGE(
pad->IsOnLayer(
F_Cu ),
"U3 pad should be on F.Cu" );
1167 BOOST_CHECK_MESSAGE(
pad->IsOnLayer(
F_Mask ),
"U3 pad should have F.Mask (SMD fallback)" );
1168 BOOST_CHECK_MESSAGE(
pad->IsOnLayer(
F_Paste ),
"U3 pad should have F.Paste (SMD fallback)" );
1169 BOOST_CHECK_MESSAGE( !
pad->IsOnLayer(
B_Cu ),
"U3 SMD pad should not be on B.Cu" );
1176 BOOST_REQUIRE_MESSAGE( u4,
"U4 should exist" );
1177 BOOST_REQUIRE_EQUAL( u4->
Pads().size(), 1 );
1180 BOOST_CHECK_MESSAGE(
pad->IsOnLayer(
F_Cu ),
"U4 PTH pad should be on F.Cu" );
1181 BOOST_CHECK_MESSAGE(
pad->IsOnLayer(
B_Cu ),
"U4 PTH pad should be on B.Cu" );
1182 BOOST_CHECK_MESSAGE(
pad->IsOnLayer(
F_Mask ),
"U4 pad should have F.Mask (explicit in stack)" );
1183 BOOST_CHECK_MESSAGE(
pad->IsOnLayer(
B_Mask ),
"U4 pad should have B.Mask (explicit in stack)" );
1184 BOOST_CHECK_MESSAGE( !
pad->IsOnLayer(
F_Paste ),
"U4 PTH pad should not have F.Paste" );
1192 BOOST_REQUIRE_MESSAGE( u5,
"U5 should exist" );
1193 BOOST_REQUIRE_EQUAL( u5->
Pads().size(), 1 );
1196 BOOST_CHECK_MESSAGE(
pad->IsOnLayer(
F_Cu ),
"U5 pad should be on F.Cu" );
1197 BOOST_CHECK_MESSAGE(
pad->IsOnLayer(
F_Mask ),
"U5 pad should have F.Mask (SMD fallback)" );
1198 BOOST_CHECK_MESSAGE( !
pad->IsOnLayer(
F_Paste ),
"U5 pad should NOT have F.Paste (explicitly zero-size)" );
1199 BOOST_CHECK_MESSAGE( !
pad->IsOnLayer(
B_Cu ),
"U5 SMD pad should not be on B.Cu" );
1215 std::unique_ptr<BOARD> board = plugin.
LoadBoard( filename,
nullptr,
nullptr );
1219 bool foundSmdWithMask =
false;
1221 for(
FOOTPRINT* fp : board->Footprints() )
1223 for(
PAD*
pad : fp->Pads() )
1228 foundSmdWithMask =
true;
1233 if( foundSmdWithMask )
1237 BOOST_CHECK_MESSAGE( foundSmdWithMask,
1238 "At least one SMD pad in issue23254.asc should have F.Mask and F.Paste" );
1251 std::unique_ptr<BOARD> board;
1252 board = plugin.
LoadBoard( filename,
nullptr,
nullptr );
1257 bool foundSquarePad =
false;
1259 for(
FOOTPRINT* fp : board->Footprints() )
1261 for(
PAD*
pad : fp->Pads() )
1265 foundSquarePad =
true;
1270 if( foundSquarePad )
1274 BOOST_CHECK_MESSAGE( foundSquarePad,
1275 "At least one pad should have RECTANGLE shape (square pad import)" );
1279 bool foundZoneWithFull =
false;
1280 bool foundPadWithThermal =
false;
1281 bool foundPadWithoutThermal =
false;
1283 for(
ZONE* zone : board->Zones() )
1287 foundZoneWithFull =
true;
1292 BOOST_CHECK_MESSAGE( foundZoneWithFull,
1293 "Zones should default to FULL (solid) connection" );
1295 for(
FOOTPRINT* fp : board->Footprints() )
1297 for(
PAD*
pad : fp->Pads() )
1300 foundPadWithThermal =
true;
1302 foundPadWithoutThermal =
true;
1306 BOOST_CHECK_MESSAGE( foundPadWithThermal,
1307 "Pads with RT/ST entries should have per-pad THERMAL connection" );
1308 BOOST_CHECK_MESSAGE( foundPadWithoutThermal,
1309 "Pads without RT/ST entries should not have per-pad THERMAL override" );
1315 auto nc1It = netclasses.find( wxT(
"NETTCLASS1" ) );
1316 auto nc2It = netclasses.find( wxT(
"NETTCLASS2" ) );
1318 BOOST_CHECK_MESSAGE( nc1It != netclasses.end(),
"NETTCLASS1 should exist" );
1319 BOOST_CHECK_MESSAGE( nc2It != netclasses.end(),
"NETTCLASS2 should exist" );
1321 if( nc1It != netclasses.end() )
1323 BOOST_CHECK_MESSAGE( nc1It->second->HasTrackWidth(),
1324 "NETTCLASS1 should have a track width rule" );
1327 if( nc2It != netclasses.end() )
1329 BOOST_CHECK_MESSAGE( nc2It->second->HasTrackWidth(),
1330 "NETTCLASS2 should have a track width rule" );
1331 BOOST_CHECK_MESSAGE( nc2It->second->HasClearance(),
1332 "NETTCLASS2 should have a clearance rule" );
1337 std::map<wxString, wxString> netAssignments;
1339 for(
const auto& [matcher, ncName] : patterns )
1340 netAssignments[matcher->GetPattern()] = ncName;
1342 BOOST_CHECK_MESSAGE( netAssignments.count( wxT(
"+24V0" ) ),
1343 "+24V0 should be assigned to a net class" );
1344 BOOST_CHECK_MESSAGE( netAssignments.count( wxT(
"+24V0_FILTER" ) ),
1345 "+24V0_FILTER should be assigned to a net class" );
1346 BOOST_CHECK_MESSAGE( netAssignments.count( wxT(
"+24V0_FILTER_RTN" ) ),
1347 "+24V0_FILTER_RTN should be assigned to a net class" );
1349 if( netAssignments.count( wxT(
"+24V0" ) ) )
1354 if( netAssignments.count( wxT(
"+24V0_FILTER_RTN" ) ) )
1356 BOOST_CHECK_EQUAL( netAssignments[wxT(
"+24V0_FILTER_RTN" )], wxT(
"NETTCLASS2" ) );
1374 std::unique_ptr<BOARD> board = plugin.
LoadBoard( filename,
nullptr,
nullptr );
1380 BOOST_CHECK_MESSAGE( netclasses.find( wxT(
"NETTCLASS1" ) ) != netclasses.end(),
1381 "NETTCLASS1 should be imported" );
1382 BOOST_CHECK_MESSAGE( netclasses.find( wxT(
"NETTCLASS2" ) ) != netclasses.end(),
1383 "NETTCLASS2 should be imported" );
1387 std::map<wxString, wxString> netAssignments;
1389 for(
const auto& [matcher, ncName] : patterns )
1390 netAssignments[matcher->GetPattern()] = ncName;
1394 BOOST_CHECK_EQUAL( netAssignments[wxT(
"+24V0_FILTER" )], wxT(
"NETTCLASS1" ) );
1397 BOOST_CHECK_EQUAL( netAssignments[wxT(
"+24V0_FILTER_RTN" )], wxT(
"NETTCLASS2" ) );
1402 auto nc2It = netclasses.find( wxT(
"NETTCLASS2" ) );
1404 if( nc2It != netclasses.end() )
1406 BOOST_CHECK_MESSAGE( nc2It->second->HasClearance(),
1407 "NETTCLASS2 should have clearance from RULE_SET" );
1408 BOOST_CHECK_MESSAGE( nc2It->second->HasTrackWidth(),
1409 "NETTCLASS2 should have track width from RULE_SET" );
1429 +
"plugins/pads/issue23540/test_import.asc";
1431 std::unique_ptr<BOARD> board = plugin.
LoadBoard( filename,
nullptr,
nullptr );
1450 BOOST_CHECK_MESSAGE( absDeg > 170.0 && absDeg < 190.0,
1451 "route arc angle " << absDeg <<
" should be ~180 degrees (semicircle)" );
1459 int chordY = ( start.
y +
end.y ) / 2;
1461 BOOST_CHECK_MESSAGE( mid.
y < chordY,
1462 "arc midpoint Y=" << mid.
y <<
" should be above (less than) "
1463 "chord center Y=" << chordY );
1466 BOOST_REQUIRE_MESSAGE( arcCount == 1,
1467 "expected exactly 1 PCB_ARC from route CW/CCW arc, got " << arcCount );
1471 int straightOnArcNet = 0;
1479 BOOST_CHECK_MESSAGE( straightOnArcNet == 0,
1480 "route arc net should contain no straight track remnant, got "
1481 << straightOnArcNet );
1505 +
"plugins/pads/issue23425/controlCARDDockingStation.asc";
1507 std::unique_ptr<BOARD> board = plugin.
LoadBoard( filename,
nullptr,
nullptr );
1513 for(
FOOTPRINT* fp : board->Footprints() )
1515 if( fp->GetReference() == wxT(
"J3" ) )
1522 BOOST_REQUIRE_MESSAGE( j3,
"J3 (HSEC8 edge connector) not found on board" );
1525 const int expectedOffset = 762000;
1526 const int tolerance = 1000;
1528 int offsetPadCount = 0;
1540 BOOST_CHECK_MESSAGE( offset.
y == 0,
1541 "J3 pad " <<
pad->GetNumber()
1542 <<
" offset Y should be 0 (unrotated pad-local), got " << offset.
y );
1544 BOOST_CHECK_MESSAGE(
std::abs(
std::abs( offset.
x ) - expectedOffset ) < tolerance,
1545 "J3 pad " <<
pad->GetNumber()
1546 <<
" offset X magnitude " <<
std::abs( offset.
x )
1547 <<
" should be ~" << expectedOffset );
1553 BOOST_CHECK_MESSAGE( offsetPadCount >= 90,
1554 "expected the HSEC8 finger pads to carry a finger offset; got "
1555 << offsetPadCount );
1576 std::unique_ptr<BOARD> board = plugin.
LoadBoard( filename,
nullptr,
nullptr );
1582 PAD* mtg_top_pad =
nullptr;
1583 PAD* mtg_bot_pad =
nullptr;
1584 PAD* conn_top_pin1 =
nullptr;
1585 PAD* conn_bot_pin1 =
nullptr;
1587 for(
FOOTPRINT* fp : board->Footprints() )
1589 wxString ref = fp->GetReference();
1591 for(
PAD*
pad : fp->Pads() )
1595 else if( ref ==
"E3" )
1597 else if( ref ==
"X1" &&
pad->GetNumber() ==
"1" )
1598 conn_top_pin1 =
pad;
1599 else if( ref ==
"X2" &&
pad->GetNumber() ==
"1" )
1600 conn_bot_pin1 =
pad;
1604 BOOST_REQUIRE_MESSAGE( mtg_top_pad,
"E1 (top mounting hole) not found" );
1605 BOOST_REQUIRE_MESSAGE( mtg_bot_pad,
"E3 (bottom mounting hole) not found" );
1606 BOOST_REQUIRE_MESSAGE( conn_top_pin1,
"X1 pin 1 (top connector square pad) not found" );
1607 BOOST_REQUIRE_MESSAGE( conn_bot_pin1,
"X2 pin 1 (bottom connector square pad) not found" );
1610 BOOST_CHECK_MESSAGE(
1612 "Mounting hole with same shape but different sizes should use NORMAL padstack mode" );
1614 BOOST_CHECK_MESSAGE(
1616 "Bottom-placed mounting hole should also use NORMAL padstack mode" );
1622 BOOST_CHECK_MESSAGE(
1623 top_size == bot_size,
1624 "Top and bottom mounting holes should have equal pad size on F_Cu; "
1625 "top=" << top_size.
x <<
" bot=" << bot_size.
x );
1630 BOOST_CHECK_MESSAGE(
1632 "Mounting hole pad size must be non-zero" );
1635 BOOST_CHECK_MESSAGE(
1637 "Connector pin-1 with square-on-top / round-on-bottom must use FRONT_INNER_BACK" );
1640 BOOST_CHECK_MESSAGE(
1642 "Top connector pin-1 must have RECTANGLE shape on F_Cu" );
1644 BOOST_CHECK_MESSAGE(
1646 "Top connector pin-1 must have CIRCLE shape on B_Cu" );
1649 BOOST_CHECK_MESSAGE(
1651 "Bottom connector pin-1 must have CIRCLE shape on F_Cu after flip" );
1653 BOOST_CHECK_MESSAGE(
1655 "Bottom connector pin-1 must have RECTANGLE shape on B_Cu after flip" );
1682 std::unique_ptr<BOARD> board = plugin.
LoadBoard( filename,
nullptr,
nullptr );
1692 for(
FOOTPRINT* fp : board->Footprints() )
1694 if( fp->GetValue() == wxT(
"TP_BOTTOM_SMD" ) )
1696 else if( fp->GetValue() == wxT(
"TP_TOP_SMD" ) )
1701 BOOST_REQUIRE_MESSAGE( tpBottom,
"TP_BOTTOM_SMD test point footprint should exist" );
1704 if( tpBottom->
Pads().size() == 1 )
1707 BOOST_CHECK_MESSAGE(
pad->IsOnLayer(
B_Cu ),
1708 "TP_BOTTOM_SMD pad should be on B.Cu" );
1709 BOOST_CHECK_MESSAGE( !
pad->IsOnLayer(
F_Cu ),
1710 "TP_BOTTOM_SMD pad should not be on F.Cu" );
1714 BOOST_CHECK_MESSAGE( padSize > 700000 && padSize < 900000,
1715 "TP_BOTTOM_SMD pad size " << padSize <<
" should be ~800000 nm" );
1719 BOOST_REQUIRE_MESSAGE( tpTop,
"TP_TOP_SMD test point footprint should exist" );
1722 if( tpTop->
Pads().size() == 1 )
1725 BOOST_CHECK_MESSAGE(
pad->IsOnLayer(
F_Cu ),
1726 "TP_TOP_SMD pad should be on F.Cu" );
1727 BOOST_CHECK_MESSAGE( !
pad->IsOnLayer(
B_Cu ),
1728 "TP_TOP_SMD pad should not be on B.Cu" );
1731 BOOST_CHECK_MESSAGE( padSize > 700000 && padSize < 900000,
1732 "TP_TOP_SMD pad size " << padSize <<
" should be ~800000 nm" );
1756 BOOST_CHECK_MESSAGE( pos != tpBottomPos,
1757 "TP_BOTTOM_SMD position should not have a bare PCB_VIA" );
1758 BOOST_CHECK_MESSAGE( pos != tpTopPos,
1759 "TP_TOP_SMD position should not have a bare PCB_VIA" );
1779 std::unique_ptr<BOARD> board;
1780 board = plugin.
LoadBoard( filename,
nullptr,
nullptr );
1785 int copyrightCount = 0;
1786 bool frontCopyrightNotMirrored =
false;
1787 bool backCopyrightMirrored =
false;
1797 BOOST_CHECK_MESSAGE( !t->
GetText().IsEmpty(),
1798 "imported free text should not be empty" );
1800 if( t->
GetText().Contains( wxT(
"TEXMATE" ) ) )
1805 BOOST_CHECK_MESSAGE( t->
GetText().Contains( wxString::FromUTF8(
"©" ) ),
1806 "copyright text should retain the (c) character" );
1811 "front silkscreen text should not be mirrored" );
1812 frontCopyrightNotMirrored =
true;
1817 "back-side text should be mirrored" );
1818 backCopyrightMirrored =
true;
1823 BOOST_CHECK_MESSAGE( copyrightCount >= 2,
1824 "expected the copyright text on both front and back, got " << copyrightCount );
1825 BOOST_CHECK( frontCopyrightNotMirrored );
1826 BOOST_CHECK( backCopyrightMirrored );
1830 bool foundCN1 =
false;
1832 for(
FOOTPRINT* fp : board->Footprints() )
1834 if( fp->GetReference() != wxT(
"CN1" ) )
1839 BOOST_CHECK_MESSAGE( fp->Pads().size() >= 10,
1840 "CN1 should have at least 10 pads, got " << fp->Pads().size() );
1842 for(
PAD*
pad : fp->Pads() )
1848 BOOST_CHECK_MESSAGE(
1850 "CN1 finger pad " <<
pad->GetNumber().ToStdString()
1851 <<
" should be oriented 90 degrees, got "
1852 <<
pad->GetOrientation().AsDegrees() );
1857 BOOST_CHECK_MESSAGE( foundCN1,
"CN1 footprint should be imported" );
1873 std::unique_ptr<BOARD> board = plugin.
LoadBoard( filename,
nullptr,
nullptr );
1876 auto findFP = [&](
const wxString& aRef ) ->
FOOTPRINT*
1878 for(
FOOTPRINT* fp : board->Footprints() )
1880 if( fp->GetReference() == aRef )
1887 const int tolerance = 5000;
1894 BOOST_REQUIRE_MESSAGE( l1,
"L1 footprint should be imported" );
1900 "L1 pad " <<
pad->GetNumber().ToStdString()
1901 <<
" should have THERMAL zone connection" );
1903 std::optional<int> spoke =
pad->GetLocalThermalSpokeWidthOverride();
1904 BOOST_REQUIRE_MESSAGE( spoke.has_value(),
1905 "L1 pad " <<
pad->GetNumber().ToStdString()
1906 <<
" should have a spoke-width override" );
1907 BOOST_CHECK_MESSAGE(
std::abs( spoke.value() - 1000000 ) < tolerance,
1908 "L1 pad " <<
pad->GetNumber().ToStdString() <<
" spoke width "
1909 << spoke.value() <<
" should be ~1000000 nm" );
1911 std::optional<int> gap =
pad->GetLocalThermalGapOverride();
1912 BOOST_REQUIRE_MESSAGE( gap.has_value(),
1913 "L1 pad " <<
pad->GetNumber().ToStdString()
1914 <<
" should have a thermal gap override" );
1915 BOOST_CHECK_MESSAGE(
std::abs( gap.value() - 120000 ) < tolerance,
1916 "L1 pad " <<
pad->GetNumber().ToStdString() <<
" thermal gap "
1917 << gap.value() <<
" should be ~120000 nm" );
1925 BOOST_REQUIRE_MESSAGE( e1,
"E1 footprint should be imported" );
1930 "E1 pad should have THERMAL zone connection" );
1931 BOOST_CHECK_MESSAGE( !
pad->GetLocalThermalGapOverride().has_value(),
1932 "E1 pad should NOT have a thermal gap override (outer == pad size)" );
1951 std::unique_ptr<BOARD> board = plugin.
LoadBoard( filename,
nullptr,
nullptr );
1957 std::set<wxString> refDes;
1959 for(
FOOTPRINT* fp : board->Footprints() )
1960 refDes.insert( fp->GetReference() );
1962 BOOST_CHECK_MESSAGE( refDes.count( wxT(
"J1" ) ),
"J1 should be present" );
1963 BOOST_CHECK_MESSAGE( refDes.count( wxT(
"J2" ) ),
"J2 should be present" );
1964 BOOST_CHECK_MESSAGE( refDes.count( wxT(
"J3" ) ),
"J3 should be present" );
1965 BOOST_CHECK_MESSAGE( refDes.count( wxT(
"J4" ) ),
"J4 should be present" );
1966 BOOST_CHECK_MESSAGE( refDes.count( wxT(
"J5" ) ),
"J5 should be present" );
1967 BOOST_CHECK_MESSAGE( refDes.count( wxT(
"U1" ) ),
"U1 should be present" );
1968 BOOST_CHECK_MESSAGE( refDes.count( wxT(
"U2" ) ),
"U2 should be present" );
1969 BOOST_CHECK_MESSAGE( refDes.count( wxT(
"U3" ) ),
"U3 should be present" );
1970 BOOST_CHECK_MESSAGE( refDes.count( wxT(
"U4" ) ),
"U4 should be present" );
1987 parser.
Parse( filename );
1991 auto it = decals.find(
"104130-6" );
1992 BOOST_REQUIRE_MESSAGE( it != decals.end(),
"104130-6 decal should exist" );
1993 BOOST_REQUIRE_EQUAL( it->second.terminals.size(), 34u );
1995 auto sop_it = decals.find(
"SOP16" );
1996 BOOST_REQUIRE_MESSAGE( sop_it != decals.end(),
"SOP16 decal should exist" );
1997 BOOST_REQUIRE_EQUAL( sop_it->second.terminals.size(), 16u );
2025 std::unique_ptr<BOARD> board = plugin.
LoadBoard( filename,
nullptr,
nullptr );
2029 const double tolerance = 0.005;
2037 const std::map<wxString, EXPECTED>
expected = {
2038 { wxT(
"R1" ), { 2, 105000.0 / 900000.0 } },
2039 { wxT(
"D1" ), { 6, 225000.0 / 825000.0 } },
2042 int checkedRefs = 0;
2044 for(
FOOTPRINT* fp : board->Footprints() )
2046 auto it =
expected.find( fp->GetReference() );
2053 int roundRectCount = 0;
2055 for(
PAD*
pad : fp->Pads() )
2058 fp->GetReference() <<
" pad " <<
pad->GetNumber()
2059 <<
" should import as roundrect" );
2064 BOOST_CHECK_MESSAGE(
2065 std::abs(
pad->GetRoundRectRadiusRatio(
F_Cu ) - it->second.ratio ) < tolerance,
2066 fp->GetReference() <<
" pad " <<
pad->GetNumber() <<
" ratio "
2067 <<
pad->GetRoundRectRadiusRatio(
F_Cu ) <<
" should be ~" << it->second.ratio );
2072 BOOST_CHECK_MESSAGE( roundRectCount == it->second.padCount,
2073 fp->GetReference() <<
" should have " << it->second.padCount
2074 <<
" roundrect pads, got " << roundRectCount );
2077 BOOST_CHECK_MESSAGE( checkedRefs == (
int)
expected.size(),
2078 "expected R1 and D1 footprints to be imported, found " << checkedRefs );
2099 auto it = partTypes.find(
"QUARZ_32.768KHZ_12.5PF_1.2X1" );
2101 BOOST_REQUIRE_MESSAGE( it != partTypes.end(),
"dotted part type name should be registered" );
2105 auto prev = partTypes.find(
"LT3481" );
2124 std::unique_ptr<BOARD> board = plugin.
LoadBoard( filename,
nullptr,
nullptr );
2130 for(
FOOTPRINT* fp : board->Footprints() )
2132 if( fp->GetReference() == wxT(
"X1" ) )
2136 BOOST_REQUIRE_MESSAGE( quartz !=
nullptr,
"X1 should be imported" );
2139 BOOST_REQUIRE_EQUAL( quartz->
Pads().size(), 4u );
2142 std::set<VECTOR2I> padOffsets;
2147 BOOST_CHECK_MESSAGE( padOffsets.size() == 4,
"pads should sit on 4 distinct positions" );
2149 const int tolerance =
pcbIUScale.mmToIU( 0.01 );
2151 for(
const VECTOR2I& offset : padOffsets )
constexpr EDA_IU_SCALE pcbIUScale
General utilities for PCB file IO for QA programs.
@ BS_ITEM_TYPE_DIELECTRIC
Container for design settings for a BOARD object.
std::shared_ptr< NET_SETTINGS > m_NetSettings
int m_CopperEdgeClearance
int GetBoardThickness() const
The full thickness of the board including copper and masks.
BOARD_STACKUP & GetStackupDescriptor()
std::vector< VIA_DIMENSION > m_ViasDimensionsList
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
virtual PCB_LAYER_ID GetLayer() const
Return the primary layer this item is on.
Manage one layer needed to make a physical board.
Manage layers needed to make a physical board.
const std::vector< BOARD_STACKUP_ITEM * > & GetList() const
constexpr void SetMaximum()
constexpr BOX2< Vec > & Merge(const BOX2< Vec > &aRect)
Modify the position and size of the rectangle in order to contain aRect.
constexpr bool Intersects(const BOX2< Vec > &aRect) const
virtual const wxString & GetText() const
Return the string associated with the text object.
const UTF8 & GetLibItemName() const
Handle the data for a net.
const wxString & GetNetname() const
const std::map< wxString, std::shared_ptr< NETCLASS > > & GetNetclasses() const
Gets all netclasses.
std::shared_ptr< NETCLASS > GetDefaultNetclass() const
Gets the default netclass for the project.
std::vector< std::pair< std::unique_ptr< EDA_COMBINED_MATCHER >, wxString > > & GetNetclassPatternAssignments()
Gets the netclass pattern assignments.
@ NORMAL
Shape is the same on all layers.
@ FRONT_INNER_BACK
Up to three shapes can be defined (F_Cu, inner copper layers, B_Cu)
static constexpr PCB_LAYER_ID ALL_LAYERS
! The layer identifier to use for the single defintion on normal padstacks
const std::map< std::string, PART_TYPE > & GetPartTypes() const
const std::map< std::string, PART_DECAL > & GetPartDecals() const
void Parse(const wxString &aFileName)
PAD_SHAPE GetShape(PCB_LAYER_ID aLayer) const
VECTOR2I GetSize(PCB_LAYER_ID aLayer) const
const PADSTACK & Padstack() const
EDA_ANGLE GetAngle() const
const VECTOR2I & GetMid() const
For better understanding of the points that make a dimension:
bool CanReadBoard(const wxString &aFileName) const override
Checks if this PCB_IO can read the specified board file.
std::unique_ptr< BOARD > LoadBoard(const wxString &aFileName, const std::map< std::string, UTF8 > *aProperties=nullptr, PROJECT *aProject=nullptr)
Load information from some input file format that this PCB_IO implementation knows about into new BOA...
const VECTOR2I & GetStart() const
const VECTOR2I & GetEnd() const
int PointCount() const
Return the number of points (vertices) in this line chain.
Represent a set of closed polygons.
SHAPE_LINE_CHAIN & Outline(int aIndex)
Return the reference to aIndex-th outline in the set.
CONST_SEGMENT_ITERATOR CIterateSegmentsWithHoles() const
Return an iterator object, for the aOutline-th outline in the set (with holes).
int OutlineCount() const
Return the number of outlines in the set.
const SHAPE_LINE_CHAIN & COutline(int aIndex) const
Handle a list of polygons defining a copper zone.
SHAPE_POLY_SET * Outline()
@ RECTANGLE
Use RECTANGLE instead of RECT to avoid collision in a Windows header.
bool IsBackLayer(PCB_LAYER_ID aLayerId)
Layer classification: check if it's a back layer.
bool IsCopperLayer(int aLayerId)
Test whether a layer is a copper layer.
PCB_LAYER_ID
A quick note on layer IDs:
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)
@ SMD
Smd pad, appears on the solder paste layer (default)
BOOST_AUTO_TEST_CASE(HorizontalAlignment)
BOOST_AUTO_TEST_SUITE(CadstarPartParser)
BOOST_REQUIRE(intersection.has_value()==c.ExpectedIntersection.has_value())
BOOST_AUTO_TEST_SUITE_END()
VECTOR3I expected(15, 30, 45)
static void RunStructuralChecks(const PADS_BOARD_INFO &aBoard)
Run structural integrity checks on a successfully loaded board.
static wxString GetBoardPath(const PADS_BOARD_INFO &aBoard)
static const PADS_BOARD_INFO PADS_BOARDS[]
static std::unique_ptr< BOARD > LoadAndVerify(const PADS_BOARD_INFO &aBoard)
Verify that the PADS file is recognized and loads without crashing.
BOOST_AUTO_TEST_CASE(ImportClaySight_MK1)
BOOST_CHECK_EQUAL(result, "25.4")
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
@ PCB_TEXT_T
class PCB_TEXT, text on a layer
@ PCB_ARC_T
class PCB_ARC, an arc track segment on a copper layer
@ PCB_TRACE_T
class PCB_TRACK, a track segment (segment on a copper layer)
VECTOR2< int32_t > VECTOR2I
@ THERMAL
Use thermal relief for pads.
@ FULL
pads are covered by copper