81 std::unique_ptr<BOARD> board = LoadAllegroBoard(
"TRS80_POWER/TRS80_POWER.brd" );
85 int emptyRefDesCount = 0;
86 int validRefDesCount = 0;
88 for(
FOOTPRINT* fp : board->Footprints() )
90 wxString refdes = fp->GetReference();
92 if( refdes.IsEmpty() )
98 BOOST_TEST_MESSAGE(
"Valid RefDes: " << validRefDesCount <<
", Empty: " << emptyRefDesCount );
101 BOOST_CHECK_GT( validRefDesCount, emptyRefDesCount );
110 std::unique_ptr<BOARD> board = LoadAllegroBoard(
"TRS80_POWER/TRS80_POWER.brd" );
114 int unreferenced = 0;
116 for(
FOOTPRINT* fp : board->Footprints() )
118 if( !fp->GetReference().IsEmpty() )
125 BOOST_CHECK_GT( unreferenced, 0 );
134 std::unique_ptr<BOARD> board = LoadAllegroBoard(
"TRS80_POWER/TRS80_POWER.brd" );
140 for(
FOOTPRINT* fp : board->Footprints() )
142 BOOST_TEST_MESSAGE( fp->GetReference() <<
" -> " << fp->GetFPID().GetLibItemName().wx_str() );
144 if( !fp->GetReference().IsEmpty() && fp->GetFPID().GetLibItemName().empty() )
147 BOOST_CHECK( fp->GetFPID().GetLibNickname().empty() );
159 std::unique_ptr<BOARD> board = LoadAllegroBoard(
"EVK_BaseBoard/EVK_BaseBoard.brd" );
165 for(
FOOTPRINT* fp : board->Footprints() )
167 for(
PAD*
pad : fp->Pads() )
169 pad->Padstack().ForEachUniqueLayer(
172 if(
pad->GetSize( layer ).x <= 0 ||
pad->GetSize( layer ).y <= 0 )
187 const char* corpusEnv = std::getenv(
"KICAD_ORCAD_CORPUS" );
189 if( !corpusEnv || !*corpusEnv )
192 std::filesystem::path brd;
194 for(
const std::filesystem::directory_entry& entry : std::filesystem::recursive_directory_iterator( corpusEnv ) )
196 if( entry.is_regular_file() && entry.path().filename() ==
"EDA-02862-V1-0.brd" )
203 std::unique_ptr<BOARD> board = m_allegroPlugin.LoadBoard( brd.string() );
207 for(
FOOTPRINT* fp : board->Footprints() )
209 std::unique_ptr<FOOTPRINT>
copy(
static_cast<FOOTPRINT*
>( fp->Clone() ) );
220 const char* corpusEnv = std::getenv(
"KICAD_ORCAD_CORPUS" );
222 if( !corpusEnv || !*corpusEnv )
225 std::filesystem::path brd;
227 for(
const std::filesystem::directory_entry& entry : std::filesystem::recursive_directory_iterator( corpusEnv ) )
229 if( entry.is_regular_file() && entry.path().filename() ==
"EDA-02152-V1-0.brd" )
236 std::unique_ptr<BOARD> board = m_allegroPlugin.LoadBoard( brd.string() );
248 std::unique_ptr<BOARD> board = LoadAllegroBoard(
"EVK_BaseBoard/EVK_BaseBoard.brd" );
270 const char* corpusEnv = std::getenv(
"KICAD_ORCAD_CORPUS" );
272 if( !corpusEnv || !*corpusEnv )
275 std::filesystem::path brd;
277 for(
const std::filesystem::directory_entry& entry : std::filesystem::recursive_directory_iterator( corpusEnv ) )
279 if( entry.is_regular_file() && entry.path().filename() ==
"EDA-02152-V1-0.brd" )
286 std::unique_ptr<BOARD> board = m_allegroPlugin.LoadBoard( brd.string() );
310 const char* corpusEnv = std::getenv(
"KICAD_ORCAD_CORPUS" );
312 if( !corpusEnv || !*corpusEnv )
315 std::filesystem::path brd;
317 for(
const std::filesystem::directory_entry& entry : std::filesystem::recursive_directory_iterator( corpusEnv ) )
319 if( entry.is_regular_file() && entry.path().filename() ==
"USB-PD w QC3_0.brd" )
326 std::unique_ptr<BOARD> board = m_allegroPlugin.LoadBoard( brd.string() );
330 FOOTPRINT* tie = board->FindFootprintByReference( wxS(
"NT1" ) );
343 const char* corpusEnv = std::getenv(
"KICAD_ORCAD_CORPUS" );
345 if( !corpusEnv || !*corpusEnv )
350 const std::map<std::string, std::pair<int, int>>
expected = { {
"EDA-02016-V8-0.brd", { 43, 4 } },
351 {
"EDA-03470-V1-0.brd", { 138, 0 } } };
353 std::map<std::string, std::filesystem::path> boards;
355 for(
const std::filesystem::directory_entry& entry : std::filesystem::recursive_directory_iterator( corpusEnv ) )
357 if( entry.is_regular_file() &&
expected.count( entry.path().filename().string() ) )
358 boards[entry.path().filename().string()] = entry.path();
361 for(
const auto& [
name, brd] : boards )
365 std::unique_ptr<BOARD> board = m_allegroPlugin.LoadBoard( brd.string() );
372 for(
FOOTPRINT* fp : board->Footprints() )
374 for(
PAD*
pad : fp->Pads() )
379 const LSET layers =
pad->GetLayerSet();
401 const char* corpusEnv = std::getenv(
"KICAD_ORCAD_CORPUS" );
403 if( !corpusEnv || !*corpusEnv )
406 std::filesystem::path brd;
408 for(
const std::filesystem::directory_entry& entry : std::filesystem::recursive_directory_iterator( corpusEnv ) )
410 if( entry.is_regular_file() && entry.path().filename() ==
"600-20192-01_CYW920822M2P4XXI040_REV1.0.brd" )
417 std::unique_ptr<BOARD> board = m_allegroPlugin.LoadBoard( brd.string() );
421 FOOTPRINT* edge = board->FindFootprintByReference( wxS(
"J1" ) );
440 const char* corpusEnv = std::getenv(
"KICAD_ORCAD_CORPUS" );
442 if( !corpusEnv || !*corpusEnv )
445 std::filesystem::path brd;
447 for(
const std::filesystem::directory_entry& entry : std::filesystem::recursive_directory_iterator( corpusEnv ) )
449 if( entry.is_regular_file() && entry.path().filename() ==
"EDA-02067-V1-0.brd" )
456 std::unique_ptr<BOARD> board = m_allegroPlugin.LoadBoard( brd.string() );
480 const char* corpusEnv = std::getenv(
"KICAD_ORCAD_CORPUS" );
482 if( !corpusEnv || !*corpusEnv )
485 std::filesystem::path brd;
487 for(
const std::filesystem::directory_entry& entry : std::filesystem::recursive_directory_iterator( corpusEnv ) )
489 if( entry.is_regular_file() && entry.path().filename() ==
"EDA-02153-V1-0.brd" )
496 std::unique_ptr<BOARD> board = m_allegroPlugin.LoadBoard( brd.string() );
500 int planePolygons = 0;
512 for(
ZONE* zone : board->Zones() )
514 if( !zone->GetIsRuleArea() &&
IsInnerCopperLayer( zone->GetFirstLayer() ) && zone->GetNetCode() > 0 )
519 BOOST_CHECK( zone->GetLocalClearance().has_value() );
526 BOOST_CHECK_GT( planeZones, 0 );
530 int filledPlanes = 0;
533 for(
ZONE* zone : board->Zones() )
537 if( !zone->GetIsRuleArea() &&
IsInnerCopperLayer( layer ) && zone->HasFilledPolysForLayer( layer )
538 && !zone->GetFilledPolysList( layer )->IsEmpty() )
544 for(
PCB_TRACK* track : board->Tracks() )
549 for(
ZONE* zone : board->Zones() )
553 if( zone->GetIsRuleArea() || zone->GetNetCode() == track->GetNetCode() || !
IsInnerCopperLayer( layer )
554 || !track->IsOnLayer( layer ) || !zone->HasFilledPolysForLayer( layer ) )
559 if( zone->GetFilledPolysList( layer )->Collide( track->GetEffectiveShape( layer ).get() ) )
564 BOOST_CHECK_GT( filledPlanes, 0 );
574 std::unique_ptr<BOARD> board = LoadAllegroBoard(
"EVK_BaseBoard/EVK_BaseBoard.brd" );
578 std::vector<ZONE*> voids;
580 for(
ZONE* zone : board->Zones() )
582 if( zone->GetIsRuleArea() && zone->GetZoneName() == wxS(
"Plane void" ) )
583 voids.push_back( zone );
589 for(
ZONE* area : voids )
596 for(
ZONE* area : voids )
600 for(
ZONE* zone : board->Zones() )
602 if( zone->GetIsRuleArea() || !zone->IsOnLayer( layer ) || !zone->HasFilledPolysForLayer( layer ) )
607 overlap += common.
Area();
612 BOOST_CHECK_LT( overlap, 1e10 );
621 for(
const char*
name : {
"EVK_BaseBoard/EVK_BaseBoard.brd",
"EVK_SOM/EVK_SOM.brd",
622 "BeagleBone-AI/BeagleBone-AI.brd",
"CutiePi_V2_3_v166/CutiePi_V2_3.brd",
623 "mainBoard/mainBoard.brd",
"ABX00162_UNOQ/20250806_UNOQ.brd" } )
625 std::unique_ptr<BOARD> board = LoadAllegroBoard(
name );
631 for(
FOOTPRINT* fp : board->Footprints() )
633 for(
PAD*
pad : fp->Pads() )
640 if(
pad->GetEffectivePolygon( layer )->OutlineCount() != 1 )
656 std::unique_ptr<BOARD> board = LoadAllegroBoard(
"TRS80_POWER/TRS80_POWER.brd" );
660 int validPadCount = 0;
661 int zeroPadCount = 0;
662 int hugePadCount = 0;
664 for(
FOOTPRINT* fp : board->Footprints() )
666 for(
PAD*
pad : fp->Pads() )
670 if( size.
x == 0 || size.
y == 0 )
674 <<
pad->GetNumber() );
676 else if( size.
x > 50000000 || size.
y > 50000000 )
680 <<
": " << size.
x / 1000000.0 <<
"mm x "
681 << size.
y / 1000000.0 <<
"mm" );
691 <<
", Huge: " << hugePadCount );
703 std::unique_ptr<BOARD> board = LoadAllegroBoard(
"TRS80_POWER/TRS80_POWER.brd" );
707 int validViaCount = 0;
708 int suspiciousViaCount = 0;
711 const int HARDCODED_SIZE = 1000000;
713 for(
PCB_TRACK* track : board->Tracks() )
718 int width =
via->GetWidth(
F_Cu );
720 if( width == HARDCODED_SIZE )
722 suspiciousViaCount++;
724 else if( width > 0 && width < 10000000 )
731 BOOST_TEST_MESSAGE(
"Valid vias: " << validViaCount <<
", Hardcoded-size vias: " << suspiciousViaCount );
734 if( suspiciousViaCount > 0 )
736 BOOST_WARN_MESSAGE(
false,
"Found " << suspiciousViaCount <<
" vias with hardcoded 1mm size" );
746 std::unique_ptr<BOARD> board = LoadAllegroBoard(
"TRS80_POWER/TRS80_POWER.brd" );
750 int validTrackCount = 0;
751 int zeroTrackCount = 0;
753 for(
PCB_TRACK* track : board->Tracks() )
757 int width = track->GetWidth();
761 else if( width > 0 && width < 10000000 )
766 BOOST_TEST_MESSAGE(
"Valid tracks: " << validTrackCount <<
", Zero-width: " << zeroTrackCount );
777 std::unique_ptr<BOARD> board = LoadAllegroBoard(
"TRS80_POWER/TRS80_POWER.brd" );
781 int numberedPads = 0;
782 int unnumberedPads = 0;
784 for(
FOOTPRINT* fp : board->Footprints() )
786 for(
PAD*
pad : fp->Pads() )
788 if(
pad->GetNumber().IsEmpty() )
795 BOOST_TEST_MESSAGE(
"Numbered pads: " << numberedPads <<
", Unnumbered: " << unnumberedPads );
799 if( unnumberedPads > 0 )
801 BOOST_WARN_MESSAGE(
false,
"Found " << unnumberedPads <<
" pads without numbers" );
823 std::vector<SEG> outlineSegs;
847 outlineSegs.push_back( seg );
855 for(
size_t i = 0; i < polyPoints.size() - 1; i++ )
858 VECTOR2I end = polyPoints[( i + 1 ) % polyPoints.size()];
859 outlineSegs.emplace_back( start,
end );
869 false,
"Unexpected shape type in board outline: " <<
static_cast<int>( shape->
GetShape() ) );
874 if( !outlineSegs.empty() )
878 int connectedCount = 0;
880 for(
const SEG& seg : outlineSegs )
882 for(
const SEG& other : outlineSegs )
888 if( seg.A == other.A || seg.A == other.B )
892 if( seg.B == other.A || seg.B == other.B )
900 BOOST_CHECK_GE( connectedCount, outlineSegs.size() * 2 );
910 std::unique_ptr<BOARD> board = LoadAllegroBoard(
"TRS80_POWER/TRS80_POWER.brd" );
920 BOOST_CHECK_GE( outlineSegmentCount, 1 );
931 std::unique_ptr<BOARD> board = LoadAllegroBoard(
"TRS80_POWER/TRS80_POWER.brd" );
937 bool hasBbox =
false;
945 boardBbox = item->GetBoundingBox();
950 boardBbox.
Merge( item->GetBoundingBox() );
955 BOOST_REQUIRE_MESSAGE( hasBbox,
"Board should have an outline" );
961 BOX2I testBbox = boardBbox;
964 for(
FOOTPRINT* fp : board->Footprints() )
966 for(
PAD*
pad : fp->Pads() )
970 if( testBbox.
Contains( padCenter ) )
978 <<
pad->GetNumber() <<
" at ("
979 << padCenter.
x / 1000000.0 <<
", "
980 << padCenter.
y / 1000000.0 <<
") mm" );
985 BOOST_TEST_MESSAGE(
"Pads inside outline: " << padsInside <<
", outside: " << padsOutside );
989 if( padsOutside > 0 )
991 BOOST_WARN_MESSAGE(
false,
"Found " << padsOutside <<
" pads outside board outline" );
995 BOOST_CHECK_GT( padsInside, padsOutside );
1005 BOOST_CHECK_EXCEPTION(
1006 LoadAllegroBoard(
"v13_header/v13_header.brd" ),
IO_ERROR,
1009 wxString msg = e.
What();
1011 return msg.Contains( wxS(
"predates Allegro 16.0" ) )
1012 && msg.Contains( wxS(
"Allegro PCB Design" ) );
1024 std::unique_ptr<BOARD> board = LoadAllegroBoard(
"rects/rects.brd" );
1030 ZONE* zone = board->Zones().front();
1036 int copperPolyCount = 0;
1037 int copperPolyWithNet = 0;
1050 copperPolyWithNet++;
1065 std::unique_ptr<BOARD> board = LoadAllegroBoard(
"copper_text/copper_text.brd" );
1068 int copperTextCount = 0;
1069 bool foundTestingText =
false;
1081 if(
text->GetText() == wxS(
"TESTING" ) )
1083 foundTestingText =
true;
1090 BOOST_CHECK_MESSAGE( foundTestingText,
"Board should contain 'TESTING' text on F.Cu" );
1099 std::unique_ptr<BOARD> board = LoadAllegroBoard(
"TRS80_POWER/TRS80_POWER.brd" );
1101 const auto netOf = [](
const BOARD_ITEM* aItem )
1109 if( aFirst->
Type() != aSecond->Type() || aFirst->
GetLayer() != aSecond->GetLayer()
1110 || netOf( aFirst ) != netOf( aSecond ) )
1117 return static_cast<const PCB_SHAPE*
>( aFirst )->Compare(
1118 static_cast<const PCB_SHAPE*
>( aSecond ) ) == 0;
1123 return static_cast<const PCB_TEXT*
>( aFirst )->Compare(
1124 static_cast<const PCB_TEXT*
>( aSecond ) ) == 0;
1130 const auto countCoincident = [&](
const DRAWINGS& aItems )
1134 for(
size_t ii = 0; ii < aItems.size(); ++ii )
1136 for(
size_t jj = ii + 1; jj < aItems.size(); ++jj )
1138 if( coincident( aItems[ii], aItems[jj] ) )
1151 for(
FOOTPRINT* footprint : board->Footprints() )
1161 const auto collectIds = [](
const BOARD& aBoard )
1163 std::vector<wxString> ids;
1165 std::function<void(
const BOARD_ITEM* )> walk =
1168 ids.push_back( aItem->m_Uuid.AsString() );
1174 aItem->RunOnChildren(
1182 for(
const BOARD_ITEM* item :
const_cast<BOARD&
>( aBoard ).GetItemSet() )
1185 std::sort( ids.begin(), ids.end() );
1189 std::unique_ptr<BOARD> first = LoadAllegroBoard(
"ProiectBoard/ProiectBoard.brd" );
1190 std::unique_ptr<BOARD> second = LoadAllegroBoard(
"ProiectBoard/ProiectBoard.brd" );
1192 const std::vector<wxString> firstIds = collectIds( *first );
1193 const std::vector<wxString> secondIds = collectIds( *second );
1197 BOOST_CHECK( firstIds == secondIds );
1199 BOOST_CHECK( std::adjacent_find( firstIds.begin(), firstIds.end() ) == firstIds.end() );
1207 std::unique_ptr<BOARD> board = LoadAllegroBoard(
"led_youtube/led_youtube.brd" );
1210 std::map<wxString, FP_3DMODEL> models;
1212 for(
FOOTPRINT* fp : board->Footprints() )
1214 BOOST_REQUIRE_EQUAL( fp->Models().size(), 1u );
1215 models.emplace( fp->Models().front().m_Filename, fp->Models().front() );
1218 BOOST_REQUIRE_EQUAL( models.size(), 3u );
1221 BOOST_REQUIRE_EQUAL( models.count( wxS(
"22272021.stp" ) ), 1u );
1224 const FP_3DMODEL& conn = models.at( wxS(
"22272021.stp" ) );
1226 BOOST_CHECK_CLOSE( conn.
m_Offset.
x, 0.02, 1e-6 );
1227 BOOST_CHECK_CLOSE( conn.
m_Offset.
y, -1.27, 1e-6 );
1228 BOOST_CHECK_CLOSE( conn.
m_Offset.
z, 1.580007, 1e-6 );
1229 BOOST_CHECK_CLOSE( conn.
m_Rotation.
x, -90.0, 1e-6 );
1231 BOOST_CHECK_CLOSE( conn.
m_Rotation.
z, -90.0, 1e-6 );
1270 std::vector<std::string> boards;
1276 for(
const auto& boardDir : std::filesystem::directory_iterator( dataPath ) )
1278 if( !boardDir.is_directory() )
1281 for(
const auto& entry : std::filesystem::directory_iterator( boardDir ) )
1283 if( entry.is_regular_file() && entry.path().extension() ==
".brd" && entry.file_size() > 0 )
1285 std::string
name = entry.path().filename().string();
1288 if(
name !=
"v13_header.brd" )
1290 boards.push_back( boardDir.path().string() +
"/" +
name );
1296 catch(
const std::filesystem::filesystem_error& e )
1301 std::sort( boards.begin(), boards.end() );
1320 BOARD* board = GetCachedBoard( dataPath );
1323 const std::vector<wxString> expectedLayers = { wxS(
"TOP" ), wxS(
"LYR2_GND" ),
1324 wxS(
"LYR5_PWR" ), wxS(
"BOTTOM" ) };
1326 for(
const wxString& layerName : expectedLayers )
1333 "Layer " << layerName <<
" should exist" );
1335 const ZONE* largest =
nullptr;
1336 double largestArea = 0;
1338 for(
const ZONE* zone : board->
Zones() )
1340 if( zone->GetIsRuleArea() )
1343 if( zone->GetNetCode() == 0 )
1346 if( !zone->GetLayerSet().Contains( layerId ) )
1349 BOX2I bbox = zone->GetBoundingBox();
1350 double area =
static_cast<double>( bbox.
GetWidth() )
1351 *
static_cast<double>( bbox.
GetHeight() );
1353 if( area > largestArea )
1360 BOOST_REQUIRE_MESSAGE( largest !=
nullptr,
1361 "Should find a netted copper zone on " << layerName );
1373 std::vector<std::string> boards = GetAllBoardFiles();
1375 for(
const std::string& boardPath : boards )
1377 std::string boardName = std::filesystem::path( boardPath ).filename().string();
1378 BOARD* board = GetCachedBoard( boardPath );
1385 int negativePadCount = 0;
1389 for(
PAD*
pad : fp->Pads() )
1393 if( size.
x < 0 || size.
y < 0 )
1397 <<
" pad " <<
pad->GetNumber() <<
": " << size.
x <<
" x "
1415 std::vector<std::string> boards = GetAllBoardFiles();
1417 for(
const std::string& boardPath : boards )
1419 std::string boardName = std::filesystem::path( boardPath ).filename().string();
1420 BOARD* board = GetCachedBoard( boardPath );
1427 int invalidViaCount = 0;
1434 int drill =
via->GetDrill();
1435 int width =
via->GetWidth(
F_Cu );
1441 <<
via->GetPosition().x / 1000000.0 <<
", "
1442 <<
via->GetPosition().y / 1000000.0
1443 <<
") has drill " << drill / 1000000.0
1444 <<
"mm > width " << width / 1000000.0 <<
"mm" );
1461 std::vector<std::string> boards = GetAllBoardFiles();
1463 for(
const std::string& boardPath : boards )
1465 std::string boardName = std::filesystem::path( boardPath ).filename().string();
1466 BOARD* board = GetCachedBoard( boardPath );
1473 int misclassifiedSmdCount = 0;
1474 int correctSmdCount = 0;
1475 int correctThCount = 0;
1479 for(
PAD*
pad : fp->Pads() )
1481 bool hasDrill =
pad->GetDrillSize().x > 0 &&
pad->GetDrillSize().y > 0;
1486 misclassifiedSmdCount++;
1488 <<
"." <<
pad->GetNumber()
1489 <<
" has no drill but is marked as PTH (should be SMD)" );
1503 <<
", Correct TH=" << correctThCount
1504 <<
", Misclassified=" << misclassifiedSmdCount );
1518 std::vector<std::string> boards = GetAllBoardFiles();
1520 for(
const std::string& boardPath : boards )
1522 std::string boardName = std::filesystem::path( boardPath ).filename().string();
1523 BOARD* board = GetCachedBoard( boardPath );
1530 int quadPackageCount = 0;
1531 int packagesWithRotatedPads = 0;
1532 int packagesWithUnrotatedPads = 0;
1536 wxString refdes = fp->GetReference().Upper();
1539 if( !refdes.StartsWith(
"U" ) )
1543 if( fp->Pads().size() < 16 )
1550 for(
PAD*
pad : fp->Pads() )
1561 padBounds.
Merge( pos );
1569 if( width == 0 || height == 0 )
1572 double aspectRatio =
static_cast<double>( std::max( width, height ) ) /
1573 static_cast<double>( std::min( width, height ) );
1575 if( aspectRatio > 2.0 )
1581 std::set<int> uniqueAngles;
1583 for(
PAD*
pad : fp->Pads() )
1587 int degrees =
static_cast<int>( angle.
AsDegrees() + 0.5 ) % 360;
1588 uniqueAngles.insert( degrees );
1593 if( uniqueAngles.size() >= 2 )
1595 packagesWithRotatedPads++;
1597 <<
" has " << uniqueAngles.size() <<
" unique pad orientations" );
1601 packagesWithUnrotatedPads++;
1603 <<
" has only " << uniqueAngles.size()
1604 <<
" unique pad orientation (may be missing rotation)" );
1608 if( quadPackageCount > 0 )
1611 <<
" potential quad packages, "
1612 << packagesWithRotatedPads <<
" with rotated pads, "
1613 << packagesWithUnrotatedPads <<
" without" );
1617 if( packagesWithRotatedPads == 0 && quadPackageCount > 0 )
1619 BOOST_WARN_MESSAGE(
false, boardName <<
" has no packages with rotated pads" );
1636 BOARD* board = GetCachedBoard( dataPath );
1637 BOOST_REQUIRE_MESSAGE( board !=
nullptr,
"BeagleBone_Black_RevC.brd should load successfully" );
1644 if( fp->GetReference() ==
"C78" )
1651 BOOST_REQUIRE_MESSAGE( c78 !=
nullptr,
"Footprint C78 should exist in BeagleBone Black" );
1658 BOOST_CHECK_MESSAGE( fpLayer ==
B_Cu,
"C78 should be on the bottom copper layer (B_Cu), got "
1660 BOOST_CHECK_MESSAGE( c78->
IsFlipped(),
"C78 should be flipped (IsFlipped() == true)" );
1664 int bottomCount = 0;
1668 if( fp->GetLayer() ==
F_Cu )
1670 else if( fp->GetLayer() ==
B_Cu )
1674 BOOST_TEST_MESSAGE(
"Footprints on top: " << topCount <<
", on bottom: " << bottomCount );
1677 BOOST_CHECK_GT( topCount, 0 );
1678 BOOST_CHECK_GT( bottomCount, 0 );
1688 std::vector<std::string> boards = GetAllBoardFiles();
1690 for(
const std::string& boardPath : boards )
1692 std::string boardName = std::filesystem::path( boardPath ).filename().string();
1693 BOARD* board = GetCachedBoard( boardPath );
1701 int disconnectedArcs = 0;
1704 std::map<int, std::vector<VECTOR2I>> netEndpoints;
1708 int netCode = track->GetNetCode();
1712 netEndpoints[netCode].push_back( track->GetStart() );
1713 netEndpoints[netCode].push_back( track->GetEnd() );
1717 netEndpoints[netCode].push_back( track->GetPosition() );
1724 for(
PAD*
pad : fp->Pads() )
1726 int netCode =
pad->GetNetCode();
1729 netEndpoints[netCode].push_back(
pad->GetPosition() );
1747 bool startConnected =
false;
1748 bool endConnected =
false;
1749 const int tolerance = 1000;
1751 for(
const VECTOR2I& pt : netEndpoints[netCode] )
1753 if( ( pt - arcStart ).EuclideanNorm() < tolerance )
1754 startConnected =
true;
1756 if( ( pt - arcEnd ).EuclideanNorm() < tolerance )
1757 endConnected =
true;
1762 if( !startConnected && !endConnected && netEndpoints[netCode].size() > 2 )
1766 << arcStart.
x / 1000000.0 <<
", "
1767 << arcStart.
y / 1000000.0 <<
") to ("
1768 << arcEnd.
x / 1000000.0 <<
", "
1769 << arcEnd.
y / 1000000.0
1770 <<
") appears disconnected from net " << netCode );
1777 << disconnectedArcs <<
" disconnected" );
1784 BOOST_CHECK_LE( disconnectedArcs, arcCount / 5 );
1836 std::vector<wxString> fields;
1839 for(
size_t i = 0; i < aLine.size(); ++i )
1841 if( aLine[i] ==
'!' )
1843 fields.push_back( current );
1848 current += aLine[i];
1852 if( !current.empty() )
1853 fields.push_back( current );
1865 wxString tag = aTag.BeforeFirst(
' ' );
1867 return static_cast<int>( val );
1873 std::ifstream file( aPath );
1875 if( !file.is_open() )
1886 SECTION currentSection = SECTION::UNKNOWN;
1892 while( std::getline( file, line ) )
1894 if( line.empty() || line[0] ==
'J' )
1897 if( line[0] ==
'A' )
1899 if( line.find(
"NET_NAME_SORT!NODE_SORT!NET_NAME!REFDES!" ) != std::string::npos )
1900 currentSection = SECTION::NET_NODES;
1901 else if( line.find(
"SYM_TYPE!SYM_NAME!REFDES!SYM_MIRROR!" ) != std::string::npos )
1902 currentSection = SECTION::SYM_PLACEMENT;
1903 else if( line.find(
"CLASS!SUBCLASS!RECORD_TAG!GRAPHIC_DATA_NAME!" ) != std::string::npos )
1904 currentSection = SECTION::GRAPHICS;
1906 currentSection = SECTION::UNKNOWN;
1911 if( line[0] !=
'S' )
1914 auto fields =
SplitAlgLine( wxString::FromUTF8( line ) );
1916 switch( currentSection )
1918 case SECTION::NET_NODES:
1921 if( fields.size() >= 5 )
1923 wxString netName = fields[3];
1924 wxString refdes = fields[4];
1926 if( !netName.empty() )
1930 if( !refdes.empty() )
1937 case SECTION::SYM_PLACEMENT:
1940 if( fields.size() >= 4 )
1942 wxString symType = fields[1];
1943 wxString symName = fields[2];
1944 wxString refdes = fields[3];
1946 if( symType == wxT(
"PACKAGE" ) && !refdes.empty() )
1948 data.
refDes.insert( refdes );
1955 case SECTION::GRAPHICS:
1961 if( fields.size() < 16 || fields[1] != wxT(
"BOUNDARY" ) )
1964 wxString closureType = fields[15];
1966 if( closureType != wxT(
"SHAPE" ) )
1969 wxString layer = fields[2];
1977 if( fields.size() > 23 )
1978 netName = fields[23];
1980 auto key = std::make_pair( layer, recordId );
1981 auto& zone = zoneMap[key];
1983 zone.recordId = recordId;
1985 if( !netName.empty() )
1986 zone.netName = netName;
1988 double x1 = 0, y1 = 0, x2 = 0, y2 = 0;
1990 if( fields.size() > 9 )
1992 fields[6].ToDouble( &x1 );
1993 fields[7].ToDouble( &y1 );
1994 fields[8].ToDouble( &x2 );
1995 fields[9].ToDouble( &y2 );
1996 zone.AddPoint( x1, y1 );
1997 zone.AddPoint( x2, y2 );
2007 for(
auto& [key, zone] : zoneMap )
2029 std::vector<BRD_ALG_PAIR> boardsWithAlg;
2031 for(
const auto& boardDir : std::filesystem::directory_iterator( dataPath ) )
2033 if( !boardDir.is_directory() )
2036 std::filesystem::path boardPath;
2037 std::filesystem::path algPath;
2039 for(
const auto& entry : std::filesystem::directory_iterator( boardDir ) )
2041 if( !entry.is_regular_file() )
2044 if( entry.path().extension() ==
".brd" )
2045 boardPath = entry.path();
2046 else if( entry.path().extension() ==
".alg" )
2047 algPath = entry.path();
2049 if( !boardPath.empty() && !algPath.empty() )
2051 boardsWithAlg.push_back( { boardPath.string(), algPath.string() } );
2057 return boardsWithAlg;
2068 BOOST_REQUIRE_GT( boardsWithAlg.size(), 0u );
2070 for(
const auto& [brdFile, algFile] : boardsWithAlg )
2075 BOOST_REQUIRE_GT( algData.
netNames.size(), 0u );
2077 BOARD* board = GetCachedBoard( brdFile );
2080 std::set<wxString> boardNets;
2084 if( net->GetNetCode() > 0 )
2085 boardNets.insert( net->GetNetname() );
2088 int missingNets = 0;
2090 for(
const wxString& algNet : algData.
netNames )
2092 if( boardNets.find( algNet ) == boardNets.end() )
2096 if( missingNets <= 10 )
2102 << boardNets.size() <<
", missing " << missingNets );
2116 BOOST_REQUIRE_GT( boardsWithAlg.size(), 0u );
2118 for(
const auto& [brdFile, algFile] : boardsWithAlg )
2123 BOOST_REQUIRE_GT( algData.
refDes.size(), 0u );
2125 BOARD* board = GetCachedBoard( brdFile );
2128 std::set<wxString> boardRefDes;
2131 boardRefDes.insert( fp->GetReference() );
2133 int missingRefDes = 0;
2134 int extraRefDes = 0;
2136 for(
const wxString& algRef : algData.
refDes )
2138 if( boardRefDes.find( algRef ) == boardRefDes.end() )
2142 if( missingRefDes <= 10 )
2147 for(
const wxString& boardRef : boardRefDes )
2149 if( algData.
refDes.find( boardRef ) == algData.
refDes.end() )
2153 if( extraRefDes <= 10 )
2159 <<
" components, board has " << boardRefDes.size()
2160 <<
", missing " << missingRefDes
2161 <<
", extra " << extraRefDes );
2173 std::vector<std::string> boards = GetAllBoardFiles();
2175 for(
const std::string& boardPath : boards )
2177 std::string boardName = std::filesystem::path( boardPath ).filename().string();
2178 BOARD* board = GetCachedBoard( boardPath );
2185 int zeroWidthCount = 0;
2194 if( track->GetWidth() <= 0 )
2198 if( zeroWidthCount <= 5 )
2201 << track->GetStart().x / 1000000.0 <<
", "
2202 << track->GetStart().y / 1000000.0 <<
")" );
2242 double minX = std::numeric_limits<double>::max();
2243 double minY = std::numeric_limits<double>::max();
2244 double maxX = std::numeric_limits<double>::lowest();
2245 double maxY = std::numeric_limits<double>::lowest();
2258 std::ifstream file( aPath );
2260 if( !file.is_open() )
2265 while( std::getline( file, line ) )
2267 if( line.empty() || line[0] !=
'S' )
2272 if( fields.size() < 10 )
2275 bool isDesignOutline = ( fields[1] == wxT(
"BOARD GEOMETRY" )
2276 && fields[2] == wxT(
"DESIGN_OUTLINE" ) );
2278 bool isOutline = ( fields[1] == wxT(
"BOARD GEOMETRY" )
2279 && fields[2] == wxT(
"OUTLINE" ) );
2281 if( !isDesignOutline && !isOutline )
2284 wxString shapeType = fields[4];
2287 if( shapeType == wxT(
"LINE" ) && fields.size() >= 10 )
2290 fields[6].ToCDouble( &seg.
x1 );
2291 fields[7].ToCDouble( &seg.
y1 );
2292 fields[8].ToCDouble( &seg.
x2 );
2293 fields[9].ToCDouble( &seg.
y2 );
2298 else if( shapeType == wxT(
"ARC" ) && fields.size() >= 15 )
2301 fields[6].ToCDouble( &seg.
x1 );
2302 fields[7].ToCDouble( &seg.
y1 );
2303 fields[8].ToCDouble( &seg.
x2 );
2304 fields[9].ToCDouble( &seg.
y2 );
2305 fields[10].ToCDouble( &seg.
centerX );
2306 fields[11].ToCDouble( &seg.
centerY );
2307 fields[12].ToCDouble( &seg.
radius );
2308 seg.
clockwise = ( fields[14] == wxT(
"CLOCKWISE" ) );
2313 else if( shapeType == wxT(
"RECTANGLE" ) && fields.size() >= 10 )
2316 fields[6].ToCDouble( &seg.
x1 );
2317 fields[7].ToCDouble( &seg.
y1 );
2318 fields[8].ToCDouble( &seg.
x2 );
2319 fields[9].ToCDouble( &seg.
y2 );
2329 if( isDesignOutline )
2373 BOOST_REQUIRE_GT( testBoards.size(), 0u );
2375 for(
const auto& [brdFile, algFile] : testBoards )
2387 BOARD* board = GetCachedBoard( brdFile );
2390 int edgeCutsCount = 0;
2401 <<
" -> expected Edge_Cuts segments: " << expectedCount );
2419 BOOST_REQUIRE_GT( testBoards.size(), 0u );
2422 const double milToNm = 25400.0;
2425 const int toleranceNm =
static_cast<int>( 2.0 * milToNm );
2427 for(
const auto& [brdFile, algFile] : testBoards )
2439 BOARD* board = GetCachedBoard( brdFile );
2443 bool hasBbox =
false;
2451 boardBbox = item->GetBoundingBox();
2456 boardBbox.
Merge( item->GetBoundingBox() );
2461 BOOST_REQUIRE_MESSAGE( hasBbox,
"Board should have Edge_Cuts outline" );
2464 int algMinXnm =
static_cast<int>( algOutlines.
minX * milToNm );
2465 int algMinYnm =
static_cast<int>( algOutlines.
minY * milToNm );
2466 int algMaxXnm =
static_cast<int>( algOutlines.
maxX * milToNm );
2467 int algMaxYnm =
static_cast<int>( algOutlines.
maxY * milToNm );
2468 int algWidthNm = algMaxXnm - algMinXnm;
2469 int algHeightNm = algMaxYnm - algMinYnm;
2471 int boardWidth = boardBbox.
GetWidth();
2472 int boardHeight = boardBbox.
GetHeight();
2475 << algOutlines.
minX <<
"," << algOutlines.
minY <<
" to "
2476 << algOutlines.
maxX <<
"," << algOutlines.
maxY
2477 <<
" = " << ( algOutlines.
maxX - algOutlines.
minX ) <<
" x "
2478 << ( algOutlines.
maxY - algOutlines.
minY ) );
2480 << boardBbox.
GetLeft() <<
"," << boardBbox.
GetTop() <<
" to "
2482 <<
" = " << boardWidth <<
" x " << boardHeight );
2486 BOOST_CHECK_CLOSE(
static_cast<double>( boardWidth ),
2487 static_cast<double>( algWidthNm ), 3.0 );
2488 BOOST_CHECK_CLOSE(
static_cast<double>( boardHeight ),
2489 static_cast<double>( algHeightNm ), 3.0 );
2506 BOOST_REQUIRE_GT( testBoards.size(), 0u );
2508 const double milToNm = 25400.0;
2509 const int toleranceNm =
static_cast<int>( 2.0 * milToNm );
2511 for(
const auto& [brdFile, algFile] : testBoards )
2521 bool allLines =
true;
2539 BOARD* board = GetCachedBoard( brdFile );
2543 struct ENDPOINT_PAIR
2549 std::vector<ENDPOINT_PAIR> boardSegments;
2559 boardSegments.push_back( { shape->
GetStart(), shape->
GetEnd() } );
2563 std::vector<ENDPOINT_PAIR> algSegments;
2569 VECTOR2I start(
static_cast<int>( seg.x1 * milToNm ),
2570 static_cast<int>( seg.y1 * milToNm ) );
2571 VECTOR2I end(
static_cast<int>( seg.x2 * milToNm ),
2572 static_cast<int>( seg.y2 * milToNm ) );
2573 algSegments.push_back( { start,
end } );
2577 int x1 =
static_cast<int>( seg.x1 * milToNm );
2578 int y1 =
static_cast<int>( seg.y1 * milToNm );
2579 int x2 =
static_cast<int>( seg.x2 * milToNm );
2580 int y2 =
static_cast<int>( seg.y2 * milToNm );
2582 algSegments.push_back( { { x1, y1 }, { x2, y1 } } );
2583 algSegments.push_back( { { x2, y1 }, { x2, y2 } } );
2584 algSegments.push_back( { { x2, y2 }, { x1, y2 } } );
2585 algSegments.push_back( { { x1, y2 }, { x1, y1 } } );
2591 if( boardSegments.size() != algSegments.size() )
2597 int matchedCount = 0;
2599 std::vector<bool> used( boardSegments.size(),
false );
2601 for(
size_t ai = 0; ai < algSegments.size(); ++ai )
2603 const auto& algSeg = algSegments[ai];
2605 int64_t bestDist = std::numeric_limits<int64_t>::max();
2607 for(
size_t bi = 0; bi < boardSegments.size(); ++bi )
2612 const auto& bSeg = boardSegments[bi];
2615 auto dist = [&](
const VECTOR2I& aAlgPt,
const VECTOR2I& aBoardPt ) -> int64_t
2617 int64_t dx =
std::abs(
static_cast<int64_t
>( aAlgPt.
x )
2618 -
static_cast<int64_t
>( aBoardPt.x ) );
2619 int64_t dy =
std::abs(
static_cast<int64_t
>( aAlgPt.
y )
2620 -
static_cast<int64_t
>( aBoardPt.y ) );
2624 int64_t d1 = dist( algSeg.start, bSeg.start ) + dist( algSeg.end, bSeg.end );
2625 int64_t d2 = dist( algSeg.start, bSeg.end ) + dist( algSeg.end, bSeg.start );
2626 int64_t d = std::min( d1, d2 );
2631 bestIdx =
static_cast<int>( bi );
2635 if( bestIdx >= 0 && bestDist < 2LL * toleranceNm )
2637 used[bestIdx] =
true;
2643 << algSeg.start.x / 1000000.0 <<
", "
2644 << algSeg.start.y / 1000000.0 <<
") -> ("
2645 << algSeg.end.x / 1000000.0 <<
", "
2646 << algSeg.end.y / 1000000.0 <<
") mm"
2647 <<
" bestDist=" << bestDist );
2652 <<
" outline segments" );
2666 std::vector<std::string> boards = GetAllBoardFiles();
2668 for(
const std::string& boardPath : boards )
2670 std::string boardName = std::filesystem::path( boardPath ).filename().string();
2671 BOARD* board = GetCachedBoard( boardPath );
2679 int smdWithDrill = 0;
2683 for(
PAD*
pad : fp->Pads() )
2686 bool hasDrill =
pad->GetDrillSize().x > 0;
2692 if( pthNoDrill <= 5 )
2695 << fp->GetReference() <<
"."
2696 <<
pad->GetNumber() );
2704 if( smdWithDrill <= 5 )
2707 << fp->GetReference() <<
"."
2708 <<
pad->GetNumber() );
2728 BOOST_REQUIRE_GT( boardsWithAlg.size(), 0u );
2730 for(
const auto& [brdFile, algFile] : boardsWithAlg )
2736 BOARD* board = GetCachedBoard( brdFile );
2739 size_t boardCopperZoneLayers = 0;
2741 for(
const ZONE* zone : board->
Zones() )
2743 if( !zone->GetIsRuleArea() )
2744 boardCopperZoneLayers += ( zone->GetLayerSet() &
LSET::AllCuMask() ).count();
2750 <<
" zone polygons, board has " << boardCopperZoneLayers
2751 <<
" copper zone-layers" );
2753 BOOST_CHECK_EQUAL(
static_cast<size_t>( boardCopperZoneLayers ), algZoneCount );
2766 BOOST_REQUIRE_GT( boardsWithAlg.size(), 0u );
2768 for(
const auto& [brdFile, algFile] : boardsWithAlg )
2774 BOARD* board = GetCachedBoard( brdFile );
2777 std::map<wxString, int> algLayerCounts;
2780 algLayerCounts[zone.
layer]++;
2782 std::map<wxString, int> boardLayerCounts;
2784 for(
const ZONE* zone : board->
Zones() )
2786 if( zone->GetIsRuleArea() )
2798 for(
const auto& [layer, count] : algLayerCounts )
2800 auto it = boardLayerCounts.find( layer );
2801 int boardCount = ( it != boardLayerCounts.end() ) ? it->second : 0;
2819 BOOST_REQUIRE_GT( boardsWithAlg.size(), 0u );
2821 for(
const auto& [brdFile, algFile] : boardsWithAlg )
2827 BOARD* board = GetCachedBoard( brdFile );
2831 const double milsToNm = 25400.0;
2833 std::map<wxString, std::vector<double>> algAreas;
2837 double w = ( zone.
maxX - zone.
minX ) * milsToNm;
2838 double h = ( zone.
maxY - zone.
minY ) * milsToNm;
2839 algAreas[zone.
layer].push_back( w * h );
2842 std::map<wxString, std::vector<double>> boardAreas;
2844 for(
const ZONE* zone : board->
Zones() )
2846 if( zone->GetIsRuleArea() )
2849 BOX2I bbox = zone->GetBoundingBox();
2850 double area =
static_cast<double>( bbox.
GetWidth() )
2851 *
static_cast<double>( bbox.
GetHeight() );
2856 boardAreas[board->
GetLayerName( layer )].push_back( area );
2863 for(
auto& [layer, algList] : algAreas )
2865 std::sort( algList.begin(), algList.end() );
2866 auto it = boardAreas.find( layer );
2868 if( it == boardAreas.end() || it->second.size() != algList.size() )
2871 std::sort( it->second.begin(), it->second.end() );
2873 for(
size_t i = 0; i < algList.size(); ++i )
2875 double ref = std::max( algList[i], 1.0 );
2876 double err =
std::abs( it->second[i] - algList[i] ) / ref;
2886 if( mismatched <= 5 )
2889 <<
": alg area " << algList[i] / ( milsToNm * milsToNm )
2890 <<
" sq mils vs board area "
2891 << it->second[i] / ( milsToNm * milsToNm )
2899 << mismatched <<
" mismatched" );
2901 int total = matched + mismatched;
2905 BOOST_CHECK_GT( matched, total * 8 / 10 );
2922 BOARD* board = GetCachedBoard( dataPath );
2928 const std::set<wxString> targetRefs = { wxS(
"J1" ), wxS(
"P5" ), wxS(
"U5" ),
2929 wxS(
"U13" ), wxS(
"C78" ) };
2931 int testedCount = 0;
2932 int failedCount = 0;
2936 if( targetRefs.find( fp->GetReference() ) == targetRefs.end() )
2942 bool hasFab =
false;
2944 for(
BOARD_ITEM* item : fp->GraphicalItems() )
2946 if( item->GetLayer() == fabLayer )
2950 fabBbox = item->GetBoundingBox();
2955 fabBbox.
Merge( item->GetBoundingBox() );
2960 if( !hasFab || fp->Pads().empty() )
2965 BOX2I testBbox = fabBbox;
2972 for(
PAD*
pad : fp->Pads() )
2976 if( !testBbox.
Contains( padCenter ) )
2980 <<
" at (" << padCenter.
x / 1e6 <<
", "
2981 << padCenter.
y / 1e6 <<
") mm is outside F.Fab bbox" );
2988 BOOST_CHECK_GE( testedCount, 4 );
3002 BOARD* board = GetCachedBoard( dataPath );
3007 wxString ref = fp->GetReference();
3009 if( ref != wxS(
"P6" ) && ref != wxS(
"P10" ) )
3014 for(
PAD*
pad : fp->Pads() )
3018 if( !
pad->GetNumber().ToLong( &padNum ) )
3022 if( padNum < 1 || padNum > 19 )
3026 BOX2I bbox =
pad->GetBoundingBox();
3031 if( bboxW == bboxH )
3036 BOOST_CHECK_MESSAGE( bboxW > bboxH,
3037 ref <<
" pad " <<
pad->GetNumber()
3038 <<
" should be visually wider than tall: "
3039 << bboxW / 1e6 <<
" x " << bboxH / 1e6 <<
" mm" );
3058 BOARD* board = GetCachedBoard( dataPath );
3061 int oblongCount = 0;
3065 for(
PAD*
pad : fp->Pads() )
3069 if( drillSize.
x <= 0 || drillSize.
y <= 0 )
3072 if( drillSize.
x == drillSize.
y )
3082 <<
" slot: " << drillSize.
x / 1e6 <<
" x "
3083 << drillSize.
y / 1e6 <<
" mm"
3084 <<
" attr=" <<
static_cast<int>(
pad->GetAttribute() ) );
3103 BOARD* board = GetCachedBoard( dataPath );
3111 if( fp->GetReference() == wxT(
"J1" ) )
3118 BOOST_REQUIRE_MESSAGE( j1 !=
nullptr,
"Footprint J1 must exist in BeagleBone_Black_RevC" );
3122 BOOST_CHECK_CLOSE( orientation.
AsDegrees(), 90.0, 0.1 );
3138 std::unique_ptr<BOARD> board;
3148 catch(
const std::exception& e )
3153 reporter.PrintAllMessages(
"UIImportPath_NullBoard" );
3155 BOOST_REQUIRE_MESSAGE( board !=
nullptr,
"LoadBoard must return a valid board" );
3157 BOOST_CHECK_GT( board->GetNetCount(), 0 );
3158 BOOST_CHECK_GT( board->Footprints().size(), 0 );
3159 BOOST_CHECK_GT( board->Tracks().size(), 0 );
3176 std::vector<std::string> boards = GetAllBoardFiles();
3178 for(
const std::string& boardPath : boards )
3180 std::string boardName = std::filesystem::path( boardPath ).filename().string();
3181 BOARD* board = GetCachedBoard( boardPath );
3188 int inconsistentCount = 0;
3192 const bool onBottom = fp->IsFlipped();
3194 for(
PAD*
pad : fp->Pads() )
3199 LSET layers =
pad->GetLayerSet();
3203 if( onBottom && hasTopCopper && !hasBotCopper )
3205 inconsistentCount++;
3207 <<
pad->GetNumber() <<
" is on bottom footprint but SMD "
3208 <<
"pad has F.Cu without B.Cu" );
3210 else if( !onBottom && hasBotCopper && !hasTopCopper )
3212 inconsistentCount++;
3214 <<
pad->GetNumber() <<
" is on top footprint but SMD "
3215 <<
"pad has B.Cu without F.Cu" );
3238 std::vector<std::string> boards = {
3242 for(
const std::string& boardPath : boards )
3244 std::string boardName = std::filesystem::path( boardPath ).filename().string();
3245 BOARD* board = GetCachedBoard( boardPath );
3251 int inconsistentCount = 0;
3252 int checkedFootprints = 0;
3257 bool hasSmd =
false;
3260 for(
PAD*
pad : fp->Pads() )
3268 if( !hasSmd || hasTH )
3271 checkedFootprints++;
3273 const bool onBottom = fp->IsFlipped();
3275 for(
BOARD_ITEM* item : fp->GraphicalItems() )
3282 bool wrongSide =
false;
3291 inconsistentCount++;
3293 boardName <<
": " << fp->GetReference() <<
" is "
3294 << ( onBottom ?
"bottom" :
"top" ) <<
"-side SMD but has "
3295 << item->GetClass() <<
" on "
3302 <<
" SMD-only footprints for tech layer consistency" );
3319 BOARD* board = GetCachedBoard( dataPath );
3329 std::vector<SLOT_CHECK> checks = {
3330 { wxS(
"P6" ), wxS(
"20" ) },
3331 { wxS(
"P6" ), wxS(
"21" ) },
3332 { wxS(
"P3" ), wxS(
"6" ) },
3333 { wxS(
"P1" ), wxS(
"1" ) },
3334 { wxS(
"P1" ), wxS(
"2" ) },
3335 { wxS(
"P1" ), wxS(
"3" ) },
3338 for(
const auto& check : checks )
3346 if( candidate->GetReference() == check.fpRef )
3353 BOOST_REQUIRE_MESSAGE( fp !=
nullptr,
"Footprint " << check.fpRef <<
" should exist" );
3357 for(
PAD* candidate : fp->
Pads() )
3359 if( candidate->GetNumber() == check.padNum )
3366 BOOST_REQUIRE_MESSAGE(
pad !=
nullptr,
3367 "Pad " << check.padNum <<
" should exist on " << check.fpRef );
3373 <<
": pad=" << padSize.
x <<
"x" << padSize.
y
3374 <<
" drill=" << drillSize.
x <<
"x" << drillSize.
y );
3377 if( drillSize.
x != drillSize.
y )
3379 bool padIsTaller = ( padSize.
y > padSize.
x );
3380 bool drillIsTaller = ( drillSize.
y > drillSize.
x );
3382 BOOST_CHECK_MESSAGE( padIsTaller == drillIsTaller,
3383 "Drill slot should match pad orientation" );
3398 BOARD* board = GetCachedBoard( dataPath );
3401 int filledZoneCount = 0;
3402 int totalCopperZones = 0;
3404 for(
const ZONE* zone : board->
Zones() )
3406 if( zone->GetIsRuleArea() || zone->GetNetCode() == 0 )
3411 if( zone->IsFilled() )
3416 <<
" layers=" << zone->GetLayerSet().count() );
3421 <<
", filled: " << filledZoneCount );
3423 BOOST_CHECK_GT( totalCopperZones, 0 );
3424 BOOST_CHECK_GT( filledZoneCount, 0 );
3436 BOARD* board = GetCachedBoard( dataPath );
3439 int teardropZones = 0;
3441 for(
const ZONE* zone : board->
Zones() )
3443 if( zone->IsTeardropArea() )
3447 BOOST_CHECK_GT( teardropZones, 1000 );
3451 int padsWithTeardrops = 0;
3456 for(
const PAD*
pad : fp->Pads() )
3460 if(
pad->GetTeardropsEnabled() )
3461 padsWithTeardrops++;
3465 BOOST_CHECK_GT( padsWithTeardrops, 0 );
3466 BOOST_TEST_MESSAGE(
"Pads with teardrops enabled: " << padsWithTeardrops <<
" / " << totalPads );
3468 int viasWithTeardrops = 0;
3478 if(
static_cast<const PCB_VIA*
>( track )->GetTeardropsEnabled() )
3479 viasWithTeardrops++;
3482 BOOST_CHECK_GT( viasWithTeardrops, 0 );
3483 BOOST_TEST_MESSAGE(
"Vias with teardrops enabled: " << viasWithTeardrops <<
" / " << totalVias );
3491 BOARD* board = GetCachedBoard( dataPath );
3496 for(
const ZONE* zone : board->
Zones() )
3498 if( !zone->IsTeardropArea() )
3503 if( !zone->HasFilledPolysForLayer( layer ) )
3506 const std::shared_ptr<SHAPE_POLY_SET> fill = zone->GetFilledPolysList( layer );
3508 for(
int i = 0; i < fill->OutlineCount(); ++i )
3510 if( fill->COutline( i ).ArcCount() > 0 )
3528 BOARD* board = GetCachedBoard( dataPath );
3531 int teardropZones = 0;
3533 for(
const ZONE* zone : board->
Zones() )
3535 if( zone->IsTeardropArea() )
3541 int padsWithTeardrops = 0;
3545 for(
const PAD*
pad : fp->Pads() )
3547 if(
pad->GetTeardropsEnabled() )
3548 padsWithTeardrops++;
3569 std::unique_ptr<BOARD> board = plugin.
LoadBoard( dataPath );
3573 BOOST_CHECK_MESSAGE( board->m_LegacyNetclassesLoaded,
3574 "m_LegacyNetclassesLoaded must be true after Allegro import" );
3575 BOOST_CHECK_MESSAGE( board->m_LegacyDesignSettingsLoaded,
3576 "m_LegacyDesignSettingsLoaded must be true after Allegro import" );
3586 std::vector<std::string> boards = GetAllBoardFiles();
3588 for(
const std::string& boardPath : boards )
3590 std::string boardName = std::filesystem::path( boardPath ).filename().string();
3591 BOARD* board = GetCachedBoard( boardPath );
3599 const auto& netclasses = netSettings->GetNetclasses();
3603 for(
const auto& [
name, nc] : netclasses )
3606 << nc->GetTrackWidth() <<
" clearance="
3607 << nc->GetClearance() );
3611 if( !
name.StartsWith( wxS(
"DP_" ) )
3612 && !
name.StartsWith( wxS(
"MG_" ) )
3613 && !
name.StartsWith( wxS(
"W" ) ) )
3615 BOOST_CHECK_MESSAGE( nc->HasTrackWidth(),
3616 name <<
" should have a track width" );
3617 BOOST_CHECK_MESSAGE( nc->GetTrackWidth() > 0,
3618 name <<
" track width should be positive" );
General utilities for PCB file IO for QA programs.
A base class derived from BOARD_ITEM for items that can be connected and have a net,...
wxString GetNetname() const
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...
virtual PCB_LAYER_ID GetLayer() const
Return the primary layer this item is on.
Information pertinent to a Pcbnew printed circuit board.
const NETINFO_LIST & GetNetInfo() const
const ZONES & Zones() const
PCB_LAYER_ID GetLayerID(const wxString &aLayerName) const
Return the ID of a layer.
const FOOTPRINTS & Footprints() const
const TRACKS & Tracks() const
const wxString GetLayerName(PCB_LAYER_ID aLayer) const
Return the name of a aLayer.
BOARD_DESIGN_SETTINGS & GetDesignSettings()
const DRAWINGS & Drawings() const
constexpr BOX2< Vec > & Inflate(coord_type dx, coord_type dy)
Inflates the rectangle horizontally by dx and vertically by dy.
constexpr size_type GetWidth() const
constexpr BOX2< Vec > & Merge(const BOX2< Vec > &aRect)
Modify the position and size of the rectangle in order to contain aRect.
constexpr size_type GetHeight() const
constexpr coord_type GetLeft() const
constexpr bool Contains(const Vec &aPoint) const
constexpr coord_type GetRight() const
constexpr coord_type GetTop() const
constexpr coord_type GetBottom() const
KICAD_T Type() const
Returns the type of object.
std::vector< VECTOR2I > GetPolyPoints() const
Duplicate the polygon outlines into a flat list of VECTOR2I points.
const VECTOR2I & GetEnd() const
Return the ending point of the graphic.
const VECTOR2I & GetStart() const
Return the starting point of the graphic.
VECTOR3D m_Offset
3D model offset (mm)
VECTOR3D m_Rotation
3D model rotation (degrees)
virtual void SetReporter(REPORTER *aReporter)
Set an optional reporter for warnings/errors.
Hold an error message and may be used when throwing exceptions containing meaningful error messages.
virtual const wxString What() const
A composite of Problem() and Where()
virtual const char * what() const override
std::exception interface, returned as UTF-8
static ALLEGRO_CACHED_LOADER & GetInstance()
Get the singleton instance of the Allegro board cache loader.
BOARD * GetCachedBoard(const std::string &aFilePath)
Get a cached board for the given file path, or load it if not already cached, without forcing a reloa...
Custom REPORTER that captures all messages for later analysis in the unit test framework.
LSET is a set of PCB_LAYER_IDs.
static LSET AllCuMask(int aCuLayerCount)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
bool Contains(PCB_LAYER_ID aLayer) const
See if the layer set contains a PCB layer.
Handle the data for a net.
static const int UNCONNECTED
Constant that holds the "unconnected net" number (typically 0) all items "connected" to this net are ...
bool IsOnLayer(PCB_LAYER_ID aLayer) const override
Test to see if this object is on the given layer.
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...
PCB_LAYER_ID GetLayer() const override
Return the primary layer this item is on.
const VECTOR2I & GetStart() const
const VECTOR2I & GetEnd() const
Represent a set of closed polygons.
double Area()
Return the area of this poly set.
void BooleanIntersection(const SHAPE_POLY_SET &b)
Perform boolean polyset intersection.
Handle a list of polygons defining a copper zone.
PCB_LAYER_ID GetFirstLayer() const
static constexpr EDA_ANGLE ANGLE_0
@ RECTANGLE
Use RECTANGLE instead of RECT to avoid collision in a Windows header.
bool IsFrontLayer(PCB_LAYER_ID aLayerId)
Layer classification: check if it's a front layer.
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.
bool IsInnerCopperLayer(int aLayerId)
Test whether a layer is an inner (In1_Cu to In30_Cu) copper layer.
PCB_LAYER_ID
A quick note on layer IDs:
std::array< SEG, 4 > BoxToSegs(const BOX2I &aBox)
Decompose a BOX2 into four segments.
Test utilities for timestamps.
std::string AllegroBoardDataDir(const std::string &aBoardName)
void PrintBoardStats(const BOARD *aBoard, const std::string &aBoardName)
Print detailed board statistics for debugging using test-framework logging.
void FillZones(BOARD *m_board)
std::string AllegroBoardFile(const std::string &aFileName)
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
PAD_ATTRIB
The set of pad shapes, used with PAD::{Set,Get}Attribute().
@ NPTH
like PAD_PTH, but not plated mechanical use only, no connection allowed
@ SMD
Smd pad, appears on the solder paste layer (default)
@ PTH
Plated through hole pad.
std::deque< BOARD_ITEM * > DRAWINGS
Utility functions for working with shapes.
Parse board outline geometry from a .alg ASCII reference file.
void updateBounds(double aX, double aY)
static ALG_OUTLINE_DATA ParseAlgOutlines(const std::string &aPath)
std::vector< OUTLINE_SEGMENT > designOutlineSegments
std::vector< OUTLINE_SEGMENT > outlineSegments
int expectedEdgeCutsSegments() const
Expected number of Edge_Cuts segments when translating to KiCad.
std::set< wxString > netNames
static ALG_REFERENCE_DATA ParseAlgFile(const std::string &aPath)
std::map< wxString, wxString > refDesToSymName
std::set< wxString > refDes
static std::vector< wxString > SplitAlgLine(const wxString &aLine)
std::vector< ALG_ZONE_POLYGON > zonePolygons
static int ParseRecordId(const wxString &aTag)
Extract the integer record ID from a RECORD_TAG field like "36 1 0".
std::map< wxString, std::set< wxString > > netToRefDes
Parse a FabMaster .alg file and extract reference data for cross-validation.
void AddPoint(double aX, double aY)
Data for parameterized all-boards test.
Fixture for comprehensive board import tests with error capturing.
PCB_IO_ALLEGRO m_allegroPlugin
BOARD * GetCachedBoard(const std::string &aFilePath)
Get a cached board, loading it on first access.
static std::vector< std::string > GetAllBoardFiles()
Get list of all .brd files in the Allegro test data directory.
ALLEGRO_COMPREHENSIVE_FIXTURE()
std::unique_ptr< BOARD > LoadAllegroBoard(const std::string &aFileName)
PCB_IO_ALLEGRO m_allegroPlugin
BOOST_AUTO_TEST_CASE(FootprintRefDes)
Test that footprints have valid reference designators.
static void AssertOutlineValid(const BOARD &aBoard)
static std::vector< BRD_ALG_PAIR > getBoardsWithAlg()
Get a list of all board files in the test data that have a corresponding .alg reference file.
static unsigned CountOutlineElements(const BOARD &board)
BOOST_AUTO_TEST_CASE(HorizontalAlignment)
BOOST_REQUIRE(intersection.has_value()==c.ExpectedIntersection.has_value())
BOOST_AUTO_TEST_SUITE_END()
IbisParser parser & reporter
KIBIS top(path, &reporter)
VECTOR3I expected(15, 30, 45)
BOOST_TEST_CONTEXT("Test Clearance")
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
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
@ PCB_GROUP_T
class PCB_GROUP, a set of BOARD_ITEMs
@ 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