62 for(
RULE_AREA& ra : aTool->GetData()->m_areas )
64 if( ra.m_ruleName.Contains( ( aName ) ) )
75 if( ra.m_zone && ra.m_zone->GetPlacementAreaSource() == aGroupName )
88 if( zone == aRuleArea.
m_zone )
91 if( zone->GetZoneName() != aZoneName )
106 for(
const ZONE* zone : aBoard->
Zones() )
108 if( zone == aRuleArea.
m_zone )
111 const wxString&
name = zone->GetZoneName();
114 if(
name != aBaseName && !
name.StartsWith( aBaseName + wxT(
"_" ) ) )
129 std::vector<wxString> tests = {
"vme-wren" };
131 for(
const wxString& relPath : tests )
138 toolMgr.
SetEnvironment( m_board.get(),
nullptr,
nullptr,
nullptr, toolsHolder );
147 auto ruleData = mtTool->
GetData();
150 static_cast<int>( ruleData->m_areas.size() ) ) );
156 ruleData->m_replaceExisting =
true;
161 || ra.
m_sheetName == wxT(
"pp_driver_2x.kicad_sch" ) )
175 int n_areas_io = 0, n_areas_pp = 0, n_areas_other = 0;
178 static_cast<int>(ruleData->m_areas.size() ) ) );
180 for(
const RULE_AREA& ra : ruleData->m_areas )
184 if( ra.
m_ruleName.Contains( wxT(
"io_drivers_fp" ) ) )
189 else if( ra.
m_ruleName.Contains( wxT(
"io_drivers_pp" ) ) )
201 n_areas_io, n_areas_pp, n_areas_other ) );
207 const std::vector<wxString> rulesToTest = { wxT(
"io_drivers_fp" ),
208 wxT(
"io_drivers_pp" ) };
210 for(
const wxString& ruleName : rulesToTest )
212 for(
const RULE_AREA& refArea : ruleData->m_areas )
214 if( !refArea.
m_ruleName.Contains( ruleName ) )
219 for(
const RULE_AREA& targetArea : ruleData->m_areas )
224 if( !targetArea.
m_ruleName.Contains( ruleName ) )
227 auto cgRef = CONNECTION_GRAPH::BuildFromFootprintSet( refArea.
m_components,
230 CONNECTION_GRAPH::BuildFromFootprintSet( targetArea.
m_components,
235 std::vector<TMATCH::TOPOLOGY_MISMATCH_REASON> details;
236 bool status = cgRef->FindIsomorphism( cgTarget.get(),
result, details );
239 "topo match: '%s' [%d] -> '%s' [%d] result %d", refArea.
m_ruleName.c_str().AsChar(),
241 static_cast<int>( targetArea.
m_components.size() ), status ? 1 : 0 ) );
243 for(
const auto& iter :
result )
246 iter.second->GetReference(),
247 iter.first->GetReference() ) );
250 BOOST_CHECK( status );
251 BOOST_CHECK( details.empty() );
258 BOOST_ASSERT( refArea );
260 const std::vector<wxString> targetAreaNames( { wxT(
"io_drivers_fp/bank2/io78/" ),
261 wxT(
"io_drivers_fp/bank1/io78/" ),
262 wxT(
"io_drivers_fp/bank0/io01/" ) } );
264 for(
const wxString& targetRaName : targetAreaNames )
268 BOOST_ASSERT( targetRA !=
nullptr );
272 ruleData->m_compatMap[targetRA].m_doCopy =
true;
277 BOOST_ASSERT(
result >= 0 );
293 toolMgr.
SetEnvironment( m_board.get(),
nullptr,
nullptr,
nullptr, toolsHolder );
300 auto ruleData = mtTool->
GetData();
303 static_cast<int>( ruleData->m_areas.size() ) ) );
305 BOOST_CHECK( ruleData->m_areas.size() >= 2 );
307 if( ruleData->m_areas.size() < 2 )
315 if( ra.
m_ruleName.Contains( wxT(
"Untitled Sheet/" ) ) )
317 else if( ra.
m_ruleName.Contains( wxT(
"Untitled Sheet1/" ) ) )
321 if( !refArea || !targetArea )
323 BOOST_TEST_MESSAGE(
"Could not find Untitled Sheet and Untitled Sheet1 rule areas, skipping test" );
351 if( !refFP || !targetFP )
353 BOOST_TEST_MESSAGE(
"Could not find matching footprints in the rule areas, skipping test" );
358 bool refValueVisible = refValueField ? refValueField->
IsVisible() :
true;
360 std::vector<FP_3DMODEL> refModels = refFP->
Models();
364 ruleData->m_compatMap[targetArea].m_doCopy =
true;
365 ruleData->m_options.m_copyPlacement =
true;
369 BOOST_CHECK(
result >= 0 );
373 if( targetValueField && refValueField )
377 refValueVisible, targetValueField->
IsVisible() ) );
382 if( !refModels.empty() )
385 static_cast<int>( refModels.size() ),
386 static_cast<int>( targetFP->
Models().size() ) ) );
405 toolMgr.
SetEnvironment( m_board.get(),
nullptr,
nullptr,
nullptr, toolsHolder );
412 auto ruleData = mtTool->
GetData();
415 static_cast<int>( ruleData->m_areas.size() ) ) );
419 if( ruleData->m_areas.size() < 2 )
436 int refViaCountBefore = 0;
438 for(
PCB_TRACK* track : m_board->Tracks() )
450 BOOST_TEST_MESSAGE( wxString::Format(
"Reference area vias before repeat: %d", refViaCountBefore ) );
451 BOOST_CHECK( refViaCountBefore > 0 );
455 ruleData->m_compatMap[targetArea].m_doCopy =
true;
456 ruleData->m_options.m_copyPlacement =
true;
457 ruleData->m_options.m_copyRouting =
true;
461 BOOST_CHECK(
result >= 0 );
463 int refViaCountAfter = 0;
465 for(
PCB_TRACK* track : m_board->Tracks() )
477 BOOST_TEST_MESSAGE( wxString::Format(
"Reference area vias after repeat: %d", refViaCountAfter ) );
494 toolMgr.
SetEnvironment( m_board.get(),
nullptr,
nullptr,
nullptr, toolsHolder );
559 auto cgRef = std::make_unique<CONNECTION_GRAPH>();
560 auto cgTarget = std::make_unique<CONNECTION_GRAPH>();
563 LIB_ID fpid( wxT(
"Package_SO" ), wxT(
"SOIC-8_3.9x4.9mm_P1.27mm" ) );
575 PAD padRef1( &fpRef );
578 fpRef.
Add( &padRef1 );
580 PAD padRef2( &fpRef );
583 fpRef.
Add( &padRef2 );
585 PAD padTarget1( &fpTarget );
588 fpTarget.
Add( &padTarget1 );
590 PAD padTarget2( &fpTarget );
593 fpTarget.
Add( &padTarget2 );
596 cgRef->AddFootprint( &fpRef,
VECTOR2I( 0, 0 ) );
597 cgTarget->AddFootprint( &fpTarget,
VECTOR2I( 0, 0 ) );
599 cgRef->BuildConnectivity();
600 cgTarget->BuildConnectivity();
606 COMPONENT* cmpRef = cgRef->Components()[0];
607 COMPONENT* cmpTarget = cgTarget->Components()[0];
609 bool sameKind = cmpRef->IsSameKind( *cmpTarget );
611 BOOST_TEST_MESSAGE( wxString::Format(
"TRIM_1.1 and TRIM_2.1 IsSameKind: %d", sameKind ? 1 : 0 ) );
612 BOOST_CHECK( sameKind );
616 std::vector<TMATCH::TOPOLOGY_MISMATCH_REASON> details;
617 bool status = cgRef->FindIsomorphism( cgTarget.get(),
result, details );
619 BOOST_TEST_MESSAGE( wxString::Format(
"Topology match result: %d", status ? 1 : 0 ) );
621 if( !status && !details.empty() )
623 for(
const auto& reason : details )
626 reason.m_reference, reason.m_candidate, reason.m_reason ) );
630 BOOST_CHECK( status );
631 BOOST_CHECK( details.empty() );
634 fpRef.
Pads().clear();
635 fpTarget.
Pads().clear();
654 toolMgr.
SetEnvironment( m_board.get(),
nullptr,
nullptr,
nullptr, toolsHolder );
661 auto ruleData = mtTool->
GetData();
669 if( ra.
m_sheetPath == wxT(
"/io_drivers_fp/bank0/io01/" ) )
671 else if( ra.
m_sheetPath == wxT(
"/io_drivers_fp/bank0/" ) )
673 else if( ra.
m_sheetPath == wxT(
"/io_drivers_fp/" ) )
681 BOOST_TEST_MESSAGE( wxString::Format(
"Leaf /io_drivers_fp/bank0/io01/ components: %d",
720 toolMgr.
SetEnvironment( m_board.get(),
nullptr,
nullptr,
nullptr, toolsHolder );
727 auto ruleData = mtTool->
GetData();
733 if( ra.
m_sheetName == wxT(
"io_driver.kicad_sch" ) )
747 auto cgRef = CONNECTION_GRAPH::BuildFromFootprintSet( refArea->
m_components,
749 auto cgTarget = CONNECTION_GRAPH::BuildFromFootprintSet( targetArea->
m_components,
754 std::atomic<bool> cancelled(
true );
755 std::atomic<int> matched( 0 );
756 std::atomic<int> total( 0 );
764 std::vector<TMATCH::TOPOLOGY_MISMATCH_REASON> details;
766 bool status = cgRef->FindIsomorphism( cgTarget.get(),
result, details, params );
768 BOOST_CHECK( !status );
769 BOOST_CHECK(
result.empty() );
776 std::atomic<bool> cancelled(
false );
777 std::atomic<int> matched( 0 );
778 std::atomic<int> total( 0 );
786 std::vector<TMATCH::TOPOLOGY_MISMATCH_REASON> details;
788 bool status = cgRef->FindIsomorphism( cgTarget.get(),
result, details, params );
790 BOOST_CHECK( status );
792 int finalMatched = matched.load();
793 int finalTotal = total.load();
795 BOOST_TEST_MESSAGE( wxString::Format(
"Progress: matched=%d, total=%d", finalMatched, finalTotal ) );
797 BOOST_CHECK( finalTotal > 0 );
804 std::vector<TMATCH::TOPOLOGY_MISMATCH_REASON> details;
806 bool status = cgRef->FindIsomorphism( cgTarget.get(),
result, details );
808 BOOST_CHECK( status );
809 BOOST_CHECK( !
result.empty() );
835 toolMgr.
SetEnvironment( m_board.get(),
nullptr,
nullptr,
nullptr, toolsHolder );
842 auto ruleData = mtTool->
GetData();
845 static_cast<int>( ruleData->m_areas.size() ) ) );
859 if( source == wxT(
"/i2c_thingy_ch0/" ) )
861 else if( source == wxT(
"/i2c_thingy_ch1/" ) )
865 BOOST_REQUIRE_MESSAGE( ch0Area !=
nullptr,
"Could not find i2c_thingy_ch0 rule area" );
866 BOOST_REQUIRE_MESSAGE( ch1Area !=
nullptr,
"Could not find i2c_thingy_ch1 rule area" );
874 auto cgRef = CONNECTION_GRAPH::BuildFromFootprintSet( ch0Area->
m_components,
876 auto cgTarget = CONNECTION_GRAPH::BuildFromFootprintSet( ch1Area->
m_components,
880 std::vector<TMATCH::TOPOLOGY_MISMATCH_REASON> details;
881 bool status = cgRef->FindIsomorphism( cgTarget.get(),
result, details );
885 for(
const auto& reason : details )
888 reason.m_reference, reason.m_candidate,
894 "Topology match failed for channels with hierarchical pins "
895 "tied to global nets (issue 21739)" );
909 std::unique_ptr<BOARD> board = std::make_unique<BOARD>();
912 auto addNet = [&](
const wxString& aName ) ->
int
919 const int netGnd = addNet( wxT(
"GND" ) );
920 const int netVcc = addNet( wxT(
"+5VP" ) );
921 const int netChainInRef = addNet( wxT(
"Net-(D-RefChainIn)" ) );
922 const int netBridge = addNet( wxT(
"Net-(D38-DOUT)" ) );
923 const int netChainOutTgt = addNet( wxT(
"Net-(D43-DOUT)" ) );
924 const int netD2Dout = addNet( wxT(
"unconnected-(D2-DOUT)" ) );
925 const int netD3Dout = addNet( wxT(
"unconnected-(D3-DOUT)" ) );
926 const int netD5Dout = addNet( wxT(
"unconnected-(D5-DOUT)" ) );
927 const int netD6Dout = addNet( wxT(
"unconnected-(D6-DOUT)" ) );
929 LIB_ID ledId( wxT(
"TestLib" ), wxT(
"WS2812" ) );
932 auto makeLed = [&](
const wxString& aRef,
int aDout,
int aDin ) ->
FOOTPRINT*
939 auto addPad = [&](
const wxString& aNumber,
int aNetCode )
942 pad->SetNumber( aNumber );
947 pad->SetNetCode( aNetCode );
951 addPad( wxT(
"1" ), aDout );
952 addPad( wxT(
"2" ), netGnd );
953 addPad( wxT(
"3" ), aDin );
954 addPad( wxT(
"4" ), netVcc );
961 std::set<FOOTPRINT*> refFps;
962 refFps.insert( makeLed( wxT(
"D1" ), netBridge, netChainInRef ) );
963 refFps.insert( makeLed( wxT(
"D2" ), netD2Dout, netChainInRef ) );
964 refFps.insert( makeLed( wxT(
"D3" ), netD3Dout, netChainInRef ) );
968 std::set<FOOTPRINT*> tgtFps;
969 tgtFps.insert( makeLed( wxT(
"D4" ), netChainOutTgt, netBridge ) );
970 tgtFps.insert( makeLed( wxT(
"D5" ), netD5Dout, netBridge ) );
971 tgtFps.insert( makeLed( wxT(
"D6" ), netD6Dout, netBridge ) );
973 auto cgRef = CONNECTION_GRAPH::BuildFromFootprintSet( refFps, tgtFps );
974 auto cgTarget = CONNECTION_GRAPH::BuildFromFootprintSet( tgtFps, refFps );
977 std::vector<TMATCH::TOPOLOGY_MISMATCH_REASON> details;
978 bool status = cgRef->FindIsomorphism( cgTarget.get(),
result, details );
982 for(
const auto& reason : details )
985 reason.m_reference, reason.m_candidate,
991 "Topology match failed because the design-block boundary signal net "
992 "was misclassified as a global rail and excluded asymmetrically" );
1008 m_board = std::make_unique<BOARD>();
1013 m_board->Add( net );
1015 auto makeFootprint =
1024 pad->SetNumber( wxT(
"1" ) );
1026 pad->SetPosition( aPos );
1038 FOOTPRINT* refFp1 = makeFootprint( wxT(
"R1" ),
1040 FOOTPRINT* refFp2 = makeFootprint( wxT(
"R2" ),
1048 m_board->Add( silkRect );
1051 FOOTPRINT* destFp1 = makeFootprint( wxT(
"R3" ),
1053 FOOTPRINT* destFp2 = makeFootprint( wxT(
"R4" ),
1057 destGroup->
SetName( wxT(
"design-block-dest" ) );
1058 destGroup->
AddItem( destFp1 );
1059 destGroup->
AddItem( destFp2 );
1060 m_board->Add( destGroup );
1069 m_board->Add( unrelatedSilk );
1073 for(
BOARD_ITEM* item : m_board->Drawings() )
1079 BOOST_REQUIRE_EQUAL( silkBefore, 2 );
1112 toolMgr.
SetEnvironment( m_board.get(),
nullptr,
nullptr,
nullptr, toolsHolder );
1118 .m_connectedRoutingOnly =
false,
1119 .m_copyPlacement =
true,
1120 .m_copyOtherItems =
true,
1121 .m_groupItems =
false,
1122 .m_includeLockedItems =
true,
1123 .m_anchorFp =
nullptr };
1135 int silkInGroup = 0;
1136 bool unrelatedSurvived =
false;
1139 for(
BOARD_ITEM* item : m_board->Drawings() )
1146 if( item == unrelatedSilk )
1147 unrelatedSurvived =
true;
1149 if( item->GetParentGroup() == destGroup )
1161 wxString::Format(
"Expected 3 silkscreen shapes after Apply Design Block "
1162 "Layout (2 original + 1 copy), found %d (issue 24372)",
1165 wxString::Format(
"Expected 1 silkscreen shape in destination group, "
1166 "found %d (issue 24372)",
1169 "Unrelated silkscreen drawing outside the design block group was "
1170 "deleted by Apply Design Block Layout (issue 24372)" );
1188 m_board = std::make_unique<BOARD>();
1192 m_board->Add( shared );
1194 auto makeFootprint = [&](
const wxString& aRef,
const VECTOR2I& aPos,
const KIID& aSymbolUuid,
1198 fp->
SetFPID(
LIB_ID( wxT(
"TestLib" ), wxT(
"Receptacle" ) ) );
1204 path.push_back( aSymbolUuid );
1208 pad->SetNumber( wxT(
"1" ) );
1210 pad->SetPosition( aPos );
1215 pad->SetNet( aNet );
1225 FOOTPRINT* refFpA = makeFootprint( wxT(
"J5" ),
VECTOR2I( 0, 0 ), symA,
nullptr );
1236 std::set<FOOTPRINT*> refSet{ refFpA, refFpB };
1237 std::set<FOOTPRINT*> destSet{ destFpA, destFpB };
1238 auto cgRef = CONNECTION_GRAPH::BuildFromFootprintSet( refSet, destSet );
1239 auto cgTarget = CONNECTION_GRAPH::BuildFromFootprintSet( destSet, refSet );
1241 std::vector<TMATCH::TOPOLOGY_MISMATCH_REASON> details;
1242 BOOST_REQUIRE_MESSAGE( !cgRef->FindIsomorphism( cgTarget.get(), topoOnly, details ),
1243 "Test precondition broken: areas are net isomorphic" );
1247 destGroup->
SetName( wxT(
"design-block-dest" ) );
1248 destGroup->
AddItem( destFpA );
1249 destGroup->
AddItem( destFpB );
1250 m_board->Add( destGroup );
1279 toolMgr.
SetEnvironment( m_board.get(),
nullptr,
nullptr,
nullptr, toolsHolder );
1284 .m_connectedRoutingOnly =
false,
1285 .m_copyPlacement =
true,
1286 .m_copyOtherItems =
true,
1287 .m_groupItems =
false,
1288 .m_includeLockedItems =
true,
1289 .m_anchorFp =
nullptr };
1297 "give an unambiguous mapping (No compatible component regression)" );
1302 wxString::Format(
"Destination relative placement wrong, expected "
1303 "(10mm,0) got (%d,%d) IU",
1318 m_board = std::make_unique<BOARD>();
1322 m_board->Add( net );
1324 auto makeFootprint = [&](
const wxString& aRef,
const VECTOR2I& aPos ) ->
FOOTPRINT*
1335 txt->
SetText( wxT(
"${REFERENCE}" ) );
1342 pad->SetNumber( wxT(
"1" ) );
1344 pad->SetPosition( aPos );
1359 if( item->Type() ==
PCB_TEXT_T && item->GetLayer() == layer )
1373 destGroup->
SetName( wxT(
"design-block-dest" ) );
1374 destGroup->
AddItem( destFp1 );
1375 destGroup->
AddItem( destFp2 );
1376 m_board->Add( destGroup );
1378 BOOST_REQUIRE_EQUAL( countSilkText( destFp1,
F_SilkS ), 1 );
1379 BOOST_REQUIRE_EQUAL( countSilkText( destFp1,
B_SilkS ), 1 );
1409 toolMgr.
SetEnvironment( m_board.get(),
nullptr,
nullptr,
nullptr, toolsHolder );
1415 .m_connectedRoutingOnly =
false,
1416 .m_copyPlacement =
true,
1417 .m_copyOtherItems =
true,
1418 .m_groupItems =
false,
1419 .m_includeLockedItems =
true,
1420 .m_anchorFp =
nullptr };
1429 for(
FOOTPRINT* destFp : { destFp1, destFp2 } )
1432 wxString::Format(
"%s lost its F.Silkscreen reference text "
1433 "(issue 24583): F=%d B=%d",
1434 destFp->GetReference(), countSilkText( destFp,
F_SilkS ),
1435 countSilkText( destFp,
B_SilkS ) ) );
1447 m_board = std::make_unique<BOARD>();
1471 m_board->Add( destPlaceholder );
1474 destGroup->
SetName( wxT(
"design-block-dest" ) );
1475 destGroup->
AddItem( destPlaceholder );
1476 m_board->Add( destGroup );
1480 for(
BOARD_ITEM* item : m_board->Drawings() )
1486 BOOST_REQUIRE_EQUAL( silkBefore, 2 );
1514 toolMgr.
SetEnvironment( m_board.get(),
nullptr,
nullptr,
nullptr, toolsHolder );
1520 .m_connectedRoutingOnly =
false,
1521 .m_copyPlacement =
true,
1522 .m_copyOtherItems =
true,
1523 .m_groupItems =
false,
1524 .m_includeLockedItems =
true,
1525 .m_anchorFp =
nullptr };
1533 BOOST_REQUIRE_MESSAGE(
result >= 0, wxString::Format(
"RepeatLayout failed for a footprint-free "
1534 "(graphics-only) design block (issue 24592): %s",
1542 for(
BOARD_ITEM* item : m_board->Drawings() )
1555 BOOST_CHECK_MESSAGE( silkAfter == 2, wxString::Format(
"Expected 2 silkscreen shapes after Apply Design Block "
1556 "Layout (source + 1 copy, placeholder replaced), found "
1559 BOOST_REQUIRE_MESSAGE( copiedRect !=
nullptr,
"Footprint-free block graphic was not copied (issue 24592)" );
1561 "Copied block graphic was not added to the destination group (issue 24592)" );
1572 m_board = std::make_unique<BOARD>();
1576 m_board->Add( net );
1585 m_board->Add( srcTrack );
1587 BOOST_REQUIRE_EQUAL( srcTrack->
GetNetCode(), 1 );
1595 m_board->Add( destPlaceholder );
1598 destGroup->
SetName( wxT(
"design-block-dest" ) );
1599 destGroup->
AddItem( destPlaceholder );
1600 m_board->Add( destGroup );
1628 toolMgr.
SetEnvironment( m_board.get(),
nullptr,
nullptr,
nullptr, toolsHolder );
1634 .m_connectedRoutingOnly =
false,
1635 .m_copyPlacement =
true,
1636 .m_copyOtherItems =
true,
1637 .m_groupItems =
false,
1638 .m_includeLockedItems =
true,
1639 .m_anchorFp =
nullptr };
1647 BOOST_REQUIRE_MESSAGE(
result >= 0, wxString::Format(
"RepeatLayout failed for a footprint-free copper "
1648 "block (issue 24592): %s",
1654 for(
PCB_TRACK* track : m_board->Tracks() )
1656 if( track != srcTrack )
1657 copiedTrack = track;
1660 BOOST_REQUIRE_MESSAGE( copiedTrack !=
nullptr,
"Footprint-free block track was not copied (issue 24592)" );
1662 wxString::Format(
"Copied copper should be no-net, got net code %d "
1666 "Copied copper was not added to the destination group (issue 24592)" );
1681 m_board = std::make_unique<BOARD>();
1690 m_board->Add( srcRect );
1698 m_board->Add( destPlaceholder );
1701 destGroup->
SetName( wxT(
"design-block-dest" ) );
1702 destGroup->
AddItem( destPlaceholder );
1703 m_board->Add( destGroup );
1707 toolMgr.
SetEnvironment( m_board.get(),
nullptr,
nullptr,
nullptr, toolsHolder );
1712 auto applyOnce = [&]() ->
int
1739 .m_connectedRoutingOnly =
false,
1740 .m_copyPlacement =
true,
1741 .m_copyOtherItems =
true,
1742 .m_groupItems =
false,
1743 .m_includeLockedItems =
true,
1744 .m_anchorFp =
nullptr };
1753 BOOST_REQUIRE_MESSAGE( applyOnce() >= 0,
"First apply failed (issue 24592)" );
1754 BOOST_REQUIRE_MESSAGE( applyOnce() >= 0,
"Second apply failed (issue 24592)" );
1760 for(
BOARD_ITEM* item : m_board->Drawings() )
1769 BOOST_CHECK_MESSAGE( rects == 2, wxString::Format(
"Re-apply stacked copies: expected 2 rectangles "
1770 "(source + 1 copy), found %d (issue 24592)",
1784 m_board = std::make_unique<BOARD>();
1788 m_board->Add( net );
1797 m_board->Add( srcTrack );
1805 m_board->Add( destPlaceholder );
1808 destGroup->
SetName( wxT(
"design-block-dest" ) );
1809 destGroup->
AddItem( destPlaceholder );
1810 m_board->Add( destGroup );
1818 m_board->Add( siblingTrack );
1821 siblingGroup->
SetName( wxT(
"design-block-sibling" ) );
1822 siblingGroup->
AddItem( siblingTrack );
1823 m_board->Add( siblingGroup );
1831 m_board->Add( looseTrack );
1859 toolMgr.
SetEnvironment( m_board.get(),
nullptr,
nullptr,
nullptr, toolsHolder );
1865 .m_connectedRoutingOnly =
false,
1866 .m_copyPlacement =
true,
1867 .m_copyOtherItems =
true,
1868 .m_groupItems =
false,
1869 .m_includeLockedItems =
true,
1870 .m_anchorFp =
nullptr };
1878 BOOST_REQUIRE_MESSAGE(
result >= 0, wxString::Format(
"RepeatLayout failed: %s", err ) );
1881 int siblingTracks = 0;
1882 int looseTracks = 0;
1884 for(
PCB_TRACK* track : m_board->Tracks() )
1886 int y = track->GetStart().y;
1895 wxString::Format(
"Sibling group routing was deleted (issue 24767): found %d, expected 1",
1898 wxString::Format(
"Loose routing in the target area should be replaced: found %d, expected 0",
1916 toolMgr.
SetEnvironment( m_board.get(),
nullptr,
nullptr,
nullptr, toolsHolder );
1923 auto ruleData = mtTool->
GetData();
1926 static_cast<int>( ruleData->m_areas.size() ) ) );
1928 BOOST_REQUIRE_EQUAL( ruleData->m_areas.size(), 4 );
1931 const size_t sharedCount = ruleData->m_areas.front().m_components.size();
1935 for(
const RULE_AREA& ra : ruleData->m_areas )
1938 RULE_AREA* refArea = &ruleData->m_areas.front();
1942 BOOST_REQUIRE_EQUAL( ruleData->m_compatMap.size(), ruleData->m_areas.size() - 1 );
1944 for(
const auto& [targetArea, compatData] : ruleData->m_compatMap )
1947 "Duplicate placement area was wrongly reported as a valid copy target "
1949 BOOST_CHECK( !compatData.m_mismatchReasons.empty() );
1953 RULE_AREA* targetArea = &ruleData->m_areas[1];
1961 "RepeatLayout copied a Rule Area onto an identical-component area "
1963 BOOST_CHECK( !err.IsEmpty() );
1985 toolMgr.
SetEnvironment( m_board.get(),
nullptr,
nullptr,
nullptr, toolsHolder );
1992 auto ruleData = mtTool->
GetData();
1995 static_cast<int>( ruleData->m_areas.size() ) ) );
1997 BOOST_REQUIRE_EQUAL( ruleData->m_areas.size(), 4 );
2001 for(
RULE_AREA& ra : ruleData->m_areas )
2009 std::set<KIID> groupUuidsBefore;
2010 std::set<wxString> userGroupNamesBefore;
2014 groupUuidsBefore.insert(
group->m_Uuid );
2016 if( !
group->GetName().IsEmpty() )
2017 userGroupNamesBefore.insert(
group->GetName() );
2022 for(
auto& [targetArea, compatData] : ruleData->m_compatMap )
2023 compatData.m_doCopy =
true;
2025 ruleData->m_options.m_copyPlacement =
true;
2026 ruleData->m_options.m_copyRouting =
true;
2027 ruleData->m_options.m_copyOtherItems =
true;
2028 ruleData->m_options.m_groupItems =
true;
2029 ruleData->m_options.m_includeLockedItems =
true;
2035 int clonedUserGroups = 0;
2039 bool isNew = !groupUuidsBefore.contains(
group->m_Uuid );
2041 if( isNew && userGroupNamesBefore.contains(
group->GetName() ) )
2046 wxString::Format(
"Repeat layout cloned %d unrelated user groups "
2048 clonedUserGroups ) );
2067 toolMgr.
SetEnvironment( m_board.get(),
nullptr,
nullptr,
nullptr, toolsHolder );
2074 auto ruleData = mtTool->
GetData();
2076 BOOST_REQUIRE_EQUAL( ruleData->m_areas.size(), 2 );
2081 for(
RULE_AREA& ra : ruleData->m_areas )
2083 if( ra.
m_ruleName.Contains( wxT(
"Untitled Sheet/" ) ) )
2085 else if( ra.
m_ruleName.Contains( wxT(
"Untitled Sheet1/" ) ) )
2095 auto cgRef = CONNECTION_GRAPH::BuildFromFootprintSet( refArea->
m_components,
2097 auto cgTarget = CONNECTION_GRAPH::BuildFromFootprintSet( targetArea->
m_components,
2101 std::vector<TMATCH::TOPOLOGY_MISMATCH_REASON> details;
2102 bool status = cgRef->FindIsomorphism( cgTarget.get(),
result, details );
2105 BOOST_CHECK( !status );
2110 std::set<wxString> refRefDes;
2111 std::set<wxString> targetRefDes;
2119 bool sawConnectivityReason =
false;
2121 for(
const auto& reason : details )
2124 reason.m_candidate, reason.m_reason ) );
2128 BOOST_CHECK( !reason.m_reason.Contains( wxT(
"No compatible component found" ) ) );
2130 if( reason.m_reason.Contains( wxT(
"connects to" ) )
2131 || reason.m_reason.Contains( wxT(
"connectivity" ) ) )
2133 sawConnectivityReason =
true;
2138 wxString::Format(
"Reason reference '%s' should be a reference-area "
2139 "component", reason.m_reference ) );
2141 wxString::Format(
"Reason candidate '%s' should be a target-area "
2142 "component", reason.m_candidate ) );
2147 "Topology mismatch message should explain the connectivity difference "
2148 "rather than report a generic missing-component error (issue 24192)" );
2162 std::unique_ptr<BOARD> board = std::make_unique<BOARD>();
2164 auto addNet = [&](
const wxString& aName ) ->
int
2171 const int netGnd = addNet( wxT(
"GND" ) );
2172 const int netP3V3 = addNet( wxT(
"+3V3" ) );
2173 const int netCollide = addNet( wxT(
"Net-(D3-A)" ) );
2174 const int netRefY = addNet( wxT(
"Net-(D4-A)" ) );
2175 const int netTgtX = addNet( wxT(
"Net-(D2-A)" ) );
2177 LIB_ID ledAId( wxT(
"TestLib" ), wxT(
"LED_A" ) );
2178 LIB_ID ledBId( wxT(
"TestLib" ), wxT(
"LED_B" ) );
2179 LIB_ID resId( wxT(
"TestLib" ), wxT(
"R" ) );
2181 auto addPad = [&](
FOOTPRINT* fp,
const wxString& aNum,
int aNet )
2184 pad->SetNumber( aNum );
2188 pad->SetNetCode( aNet );
2192 auto addFp = [&](
const LIB_ID& aId,
const wxString& aRef ) ->
FOOTPRINT*
2202 auto addLedAndRes = [&](
const LIB_ID& aLedId,
const wxString& aLedRef,
const wxString& aResRef,
int aAnodeNet,
2203 std::set<FOOTPRINT*>& aSet )
2205 FOOTPRINT* led = addFp( aLedId, aLedRef );
2206 addPad( led, wxT(
"1" ), netGnd );
2207 addPad( led, wxT(
"2" ), aAnodeNet );
2211 addPad(
res, wxT(
"1" ), aAnodeNet );
2212 addPad(
res, wxT(
"2" ), netP3V3 );
2217 std::set<FOOTPRINT*> refFps;
2218 addLedAndRes( ledAId, wxT(
"D3" ), wxT(
"R27" ), netCollide, refFps );
2219 addLedAndRes( ledBId, wxT(
"D4" ), wxT(
"R30" ), netRefY, refFps );
2222 std::set<FOOTPRINT*> tgtFps;
2223 addLedAndRes( ledAId, wxT(
"D2" ), wxT(
"R1" ), netTgtX, tgtFps );
2224 addLedAndRes( ledBId, wxT(
"D3" ), wxT(
"R2" ), netCollide, tgtFps );
2227 std::unordered_set<EDA_ITEM*> refItems( refFps.begin(), refFps.end() );
2230 auto cgRef = CONNECTION_GRAPH::BuildFromFootprintSet( refFps, tgtFps );
2231 auto cgTarget = CONNECTION_GRAPH::BuildFromFootprintSet( tgtFps, refFps );
2234 std::vector<TMATCH::TOPOLOGY_MISMATCH_REASON> details;
2235 bool status = cgRef->FindIsomorphism( cgTarget.get(),
result, details );
2239 for(
const auto& reason : details )
2241 BOOST_TEST_MESSAGE( wxString::Format(
"Mismatch: %s to %s: %s", reason.m_reference, reason.m_candidate,
2242 reason.m_reason ) );
2246 BOOST_CHECK_MESSAGE( status,
"Topology match failed even after isolating the block's auto nets "
2261 std::unique_ptr<BOARD> board = std::make_unique<BOARD>();
2263 const LIB_ID receptacleId( wxT(
"Don-Con" ), wxT(
"Mill-Max-Pin_Receptacle" ) );
2266 auto makeReceptacle = [&](
const wxString& aRef,
const wxString& aValue,
bool aWithSymbolPath ) ->
FOOTPRINT*
2273 if( aWithSymbolPath )
2284 pad->SetNumber( wxT(
"1" ) );
2296 FOOTPRINT* refNC0 = makeReceptacle( wxT(
"J3" ), wxT(
"NC_0" ),
false );
2297 FOOTPRINT* refNC1 = makeReceptacle( wxT(
"J5" ), wxT(
"NC_1" ),
false );
2298 FOOTPRINT* refNO0 = makeReceptacle( wxT(
"J6" ), wxT(
"NO_0" ),
false );
2299 FOOTPRINT* refNO1 = makeReceptacle( wxT(
"J7" ), wxT(
"NO_1" ),
false );
2302 FOOTPRINT* tgtNO1 = makeReceptacle( wxT(
"J20" ), wxT(
"NO_1" ),
true );
2303 FOOTPRINT* tgtNO0 = makeReceptacle( wxT(
"J21" ), wxT(
"NO_0" ),
true );
2304 FOOTPRINT* tgtNC1 = makeReceptacle( wxT(
"J22" ), wxT(
"NC_1" ),
true );
2305 FOOTPRINT* tgtNC0 = makeReceptacle( wxT(
"J23" ), wxT(
"NC_0" ),
true );
2307 auto cgRef = std::make_unique<CONNECTION_GRAPH>();
2308 cgRef->AddFootprint( refNC0,
VECTOR2I( 0, 0 ) );
2309 cgRef->AddFootprint( refNC1,
VECTOR2I( 0, 0 ) );
2310 cgRef->AddFootprint( refNO0,
VECTOR2I( 0, 0 ) );
2311 cgRef->AddFootprint( refNO1,
VECTOR2I( 0, 0 ) );
2312 cgRef->BuildConnectivity();
2314 auto cgTarget = std::make_unique<CONNECTION_GRAPH>();
2315 cgTarget->AddFootprint( tgtNO1,
VECTOR2I( 0, 0 ) );
2316 cgTarget->AddFootprint( tgtNO0,
VECTOR2I( 0, 0 ) );
2317 cgTarget->AddFootprint( tgtNC1,
VECTOR2I( 0, 0 ) );
2318 cgTarget->AddFootprint( tgtNC0,
VECTOR2I( 0, 0 ) );
2319 cgTarget->BuildConnectivity();
2322 std::vector<TMATCH::TOPOLOGY_MISMATCH_REASON> details;
2323 bool status = cgRef->FindIsomorphism( cgTarget.get(),
result, details );
2325 BOOST_CHECK( status );
2329 for(
const auto& [refFp, targetFp] :
result )
2331 BOOST_TEST_MESSAGE( wxString::Format(
"%s (%s) -> %s (%s)", refFp->GetReference(), refFp->GetValue(),
2332 targetFp->GetReference(), targetFp->GetValue() ) );
2345 m_board = std::make_unique<BOARD>();
2348 auto makeReceptacle = [&](
const wxString& aRef,
const wxString& aValue,
const VECTOR2I& aPos,
2352 fp->
SetFPID(
LIB_ID( wxT(
"Don-Con" ), wxT(
"Mill-Max-Pin_Receptacle" ) ) );
2357 if( aWithSymbolPath )
2365 m_board->Add( net );
2368 pad->SetNumber( wxT(
"1" ) );
2373 pad->SetPosition( aPos );
2380 auto mm = [](
double a,
double b )
2388 return (
double) ( aNC1.x - aNC0.
x ) * ( aNO0.y - aNC0.
y ) - (double) ( aNC1.y - aNC0.
y ) * ( aNO0.x - aNC0.
x );
2392 FOOTPRINT* srcNC0 = makeReceptacle( wxT(
"J3" ), wxT(
"NC_0" ), mm( 0, 0 ),
false );
2393 FOOTPRINT* srcNC1 = makeReceptacle( wxT(
"J5" ), wxT(
"NC_1" ), mm( 0, 7.9 ),
false );
2394 FOOTPRINT* srcNO0 = makeReceptacle( wxT(
"J6" ), wxT(
"NO_0" ), mm( -3.58, 2.28 ),
false );
2395 FOOTPRINT* srcNO1 = makeReceptacle( wxT(
"J7" ), wxT(
"NO_1" ), mm( 3.58, 5.62 ),
false );
2400 FOOTPRINT* dstNC0 = makeReceptacle( wxT(
"J8" ), wxT(
"NC_0" ), mm( 40, 40 ),
true );
2401 FOOTPRINT* dstNC1 = makeReceptacle( wxT(
"J9" ), wxT(
"NC_1" ), mm( 40, 47.9 ),
true );
2402 FOOTPRINT* dstNO0 = makeReceptacle( wxT(
"J10" ), wxT(
"NO_0" ), mm( 43.58, 42.28 ),
true );
2403 FOOTPRINT* dstNO1 = makeReceptacle( wxT(
"J11" ), wxT(
"NO_1" ), mm( 36.42, 45.62 ),
true );
2408 destGroup->
SetName( wxT(
"Pin receptacles for safety switch" ) );
2410 for(
FOOTPRINT* fp : { dstNC0, dstNC1, dstNO0, dstNO1 } )
2413 m_board->Add( destGroup );
2418 for(
FOOTPRINT* fp : { srcNC0, srcNC1, srcNO0, srcNO1 } )
2432 for(
FOOTPRINT* fp : { dstNC0, dstNC1, dstNO0, dstNO1 } )
2442 toolMgr.
SetEnvironment( m_board.get(),
nullptr,
nullptr,
nullptr, toolsHolder );
2448 .m_connectedRoutingOnly =
false,
2449 .m_copyPlacement =
true,
2450 .m_copyOtherItems =
false,
2451 .m_groupItems =
false,
2452 .m_includeLockedItems =
true,
2453 .m_anchorFp =
nullptr };
2456 BOOST_REQUIRE_MESSAGE(
result >= 0,
"RepeatLayout failed" );
2464 wxString::Format(
"block chirality %.0f, destination chirality after apply %.0f", srcChir, dstChir ) );
2466 BOOST_CHECK_MESSAGE( srcChir * dstChir > 0.0,
"Applied layout left the receptacles mirrored (NC/NO swapped)" );
constexpr EDA_IU_SCALE pcbIUScale
virtual bool SetNetCode(int aNetCode, bool aNoAssert)
Set net using a net code.
virtual void SetNet(NETINFO_ITEM *aNetInfo)
Set a NET_INFO object for the item.
void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
virtual void SetLayer(PCB_LAYER_ID aLayer)
Set the layer this item is on.
Information pertinent to a Pcbnew printed circuit board.
const ZONES & Zones() const
static constexpr BOX2< VECTOR2I > ByCorners(const VECTOR2I &aCorner1, const VECTOR2I &aCorner2)
Store all of the related component information found in a netlist.
void AddItem(EDA_ITEM *aItem)
Add item to group.
void SetName(const wxString &aName)
virtual EDA_GROUP * GetParentGroup() const
const VECTOR2I & GetStart() const
Return the starting point of the graphic.
virtual bool IsVisible() const
virtual void SetText(const wxString &aText)
A logical library item identifier and consists of various portions much like a URI.
LSET is a set of PCB_LAYER_IDs.
static const LSET & AllTechMask()
Return a mask holding all technical layers (no CU layer) on both side.
static LSET AllCuMask(int aCuLayerCount)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
Handle the data for a net.
static constexpr PCB_LAYER_ID ALL_LAYERS
! Temporary layer identifier to identify code that is not padstack-aware
void SetNumber(const wxString &aNumber)
Set the pad number (note that it can be alphanumeric, such as the array reference "AA12").
A set of BOARD_ITEMs (i.e., without duplicates).
void SetEnd(const VECTOR2I &aEnd) override
void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
void SetStart(const VECTOR2I &aStart) override
void SetStroke(const STROKE_PARAMS &aStroke) override
virtual void SetPosition(const VECTOR2I &aPos) override
void SetEnd(const VECTOR2I &aEnd)
void SetStart(const VECTOR2I &aStart)
const VECTOR2I & GetStart() const
virtual void SetWidth(int aWidth)
bool Contains(const VECTOR2I &aP, int aSubpolyIndex=-1, int aAccuracy=0, bool aUseBBoxCaches=false) const
Return true if a given subpolygon contains the point aP.
const SHAPE_LINE_CHAIN & COutline(int aIndex) const
virtual VECTOR2I Centre() const
Compute a center-of-mass of the shape.
Simple container to manage line stroke parameters.
Handle a list of polygons defining a copper zone.
void AddPolygon(std::vector< VECTOR2I > &aPolygon)
Add a polygon to the zone outline.
wxString GetPlacementAreaSource() const
SHAPE_POLY_SET * Outline()
void SetIsRuleArea(bool aEnable)
void SetLayerSet(const LSET &aLayerSet) override
@ RECTANGLE
Use RECTANGLE instead of RECT to avoid collision in a Windows header.
PCB_LAYER_ID
A quick note on layer IDs:
SHAPE_LINE_CHAIN BoxToLineChain(const BOX2I &aBox)
void LoadBoard(SETTINGS_MANAGER &aSettingsManager, const wxString &aRelPath, std::unique_ptr< BOARD > &aBoard)
std::map< FOOTPRINT *, FOOTPRINT * > COMPONENT_MATCHES
Class to handle a set of BOARD_ITEMs.
Utility functions for working with shapes.
MULTICHANNEL_TEST_FIXTURE()
SETTINGS_MANAGER m_settingsManager
std::unique_ptr< BOARD > m_board
bool m_includeLockedItems
std::vector< RULE_AREA > m_areas
std::unordered_set< EDA_ITEM * > m_designBlockItems
PLACEMENT_SOURCE_T m_sourceType
std::set< FOOTPRINT * > m_components
std::atomic< bool > * m_cancelled
std::atomic< int > * m_matchedComponents
std::atomic< int > * m_totalComponents
@ VALUE
Field Value of part, i.e. "3.3K".
BOOST_REQUIRE(intersection.has_value()==c.ExpectedIntersection.has_value())
BOOST_AUTO_TEST_SUITE_END()
RULE_AREA * findRuleAreaByPartialName(MULTICHANNEL_TOOL *aTool, const wxString &aName)
int countZonesByNamePrefixInRuleArea(BOARD *aBoard, const wxString &aBaseName, const RULE_AREA &aRuleArea)
BOOST_FIXTURE_TEST_CASE(MultichannelToolRegressions, MULTICHANNEL_TEST_FIXTURE)
int countZonesByNameInRuleArea(BOARD *aBoard, const wxString &aZoneName, const RULE_AREA &aRuleArea)
RULE_AREA * findRuleAreaByPlacementGroup(MULTICHANNEL_TOOL *aTool, const wxString &aGroupName)
BOOST_CHECK_MESSAGE(totalMismatches==0, std::to_string(totalMismatches)+" board(s) with strategy disagreements")
BOOST_TEST_MESSAGE("\n=== Real-World Polygon PIP Benchmark ===\n"<< formatTable(table))
wxString result
Test unit parsing edge cases and error handling.
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_TEXT_T
class PCB_TEXT, text on a layer
VECTOR2< int32_t > VECTOR2I