65 for(
RULE_AREA& ra : aTool->GetData()->m_areas )
67 if( ra.m_ruleName.Contains( ( aName ) ) )
76 if( aPath.EndsWith( wxT(
"/" ) ) )
84 wxString
path = ra.m_sheetPath;
86 if(
path.EndsWith( wxT(
"/" ) ) )
111 if( ra.m_zone && ra.m_zone->GetPlacementAreaSource() == aGroupName )
125 if( gen->GetGeneratorType() != wxT(
"tuning_pattern" ) )
128 if( gen->HitTest( outline,
false ) )
145 if( aRAZone->
Outline()->
Contains( zone->Outline()->COutline( 0 ).Centre() ) )
157 for(
const ZONE* zone : aBoard->
Zones() )
159 if( zone == aRuleArea.
m_zone )
162 if( zone->GetZoneName() != aZoneName )
177 for(
const ZONE* zone : aBoard->
Zones() )
179 if( zone == aRuleArea.
m_zone )
182 const wxString&
name = zone->GetZoneName();
185 if(
name != aBaseName && !
name.StartsWith( aBaseName + wxT(
"_" ) ) )
200 std::vector<wxString> tests = {
"vme-wren" };
202 for(
const wxString& relPath : tests )
209 toolMgr.
SetEnvironment( m_board.get(),
nullptr,
nullptr,
nullptr, toolsHolder );
218 auto ruleData = mtTool->
GetData();
221 static_cast<int>( ruleData->m_areas.size() ) ) );
228 ruleData->m_replaceExisting =
true;
233 || ra.
m_sheetName == wxT(
"pp_driver_2x.kicad_sch" ) )
247 int n_areas_io = 0, n_areas_pp = 0, n_areas_other = 0;
250 static_cast<int>(ruleData->m_areas.size() ) ) );
252 for(
const RULE_AREA& ra : ruleData->m_areas )
256 if( ra.
m_ruleName.Contains( wxT(
"io_drivers_fp" ) ) )
261 else if( ra.
m_ruleName.Contains( wxT(
"io_drivers_pp" ) ) )
273 n_areas_io, n_areas_pp, n_areas_other ) );
279 const std::vector<wxString> rulesToTest = { wxT(
"io_drivers_fp" ),
280 wxT(
"io_drivers_pp" ) };
282 for(
const wxString& ruleName : rulesToTest )
284 for(
const RULE_AREA& refArea : ruleData->m_areas )
286 if( !refArea.
m_ruleName.Contains( ruleName ) )
291 for(
const RULE_AREA& targetArea : ruleData->m_areas )
296 if( !targetArea.
m_ruleName.Contains( ruleName ) )
299 auto cgRef = CONNECTION_GRAPH::BuildFromFootprintSet( refArea.
m_components,
302 CONNECTION_GRAPH::BuildFromFootprintSet( targetArea.
m_components,
307 std::vector<TMATCH::TOPOLOGY_MISMATCH_REASON> details;
308 bool status = cgRef->FindIsomorphism( cgTarget.get(),
result, details );
311 "topo match: '%s' [%d] -> '%s' [%d] result %d", refArea.
m_ruleName.c_str().AsChar(),
313 static_cast<int>( targetArea.
m_components.size() ), status ? 1 : 0 ) );
315 for(
const auto& iter :
result )
318 iter.second->GetReference(),
319 iter.first->GetReference() ) );
322 BOOST_CHECK( status );
323 BOOST_CHECK( details.empty() );
330 BOOST_ASSERT( refArea );
332 const std::vector<wxString> targetAreaNames( { wxT(
"io_drivers_fp/bank2/io78/" ),
333 wxT(
"io_drivers_fp/bank1/io78/" ),
334 wxT(
"io_drivers_fp/bank0/io01/" ) } );
336 for(
const wxString& targetRaName : targetAreaNames )
340 BOOST_ASSERT( targetRA !=
nullptr );
344 ruleData->m_compatMap[targetRA].m_doCopy =
true;
349 BOOST_ASSERT(
result >= 0 );
365 toolMgr.
SetEnvironment( m_board.get(),
nullptr,
nullptr,
nullptr, toolsHolder );
372 auto ruleData = mtTool->
GetData();
375 static_cast<int>( ruleData->m_areas.size() ) ) );
377 BOOST_CHECK( ruleData->m_areas.size() >= 2 );
379 if( ruleData->m_areas.size() < 2 )
387 if( ra.
m_ruleName.Contains( wxT(
"Untitled Sheet/" ) ) )
389 else if( ra.
m_ruleName.Contains( wxT(
"Untitled Sheet1/" ) ) )
393 if( !refArea || !targetArea )
395 BOOST_TEST_MESSAGE(
"Could not find Untitled Sheet and Untitled Sheet1 rule areas, skipping test" );
423 if( !refFP || !targetFP )
425 BOOST_TEST_MESSAGE(
"Could not find matching footprints in the rule areas, skipping test" );
430 bool refValueVisible = refValueField ? refValueField->
IsVisible() :
true;
432 std::vector<FP_3DMODEL> refModels = refFP->
Models();
436 ruleData->m_compatMap[targetArea].m_doCopy =
true;
437 ruleData->m_options.m_copyPlacement =
true;
441 BOOST_CHECK(
result >= 0 );
445 if( targetValueField && refValueField )
449 refValueVisible, targetValueField->
IsVisible() ) );
454 if( !refModels.empty() )
457 static_cast<int>( refModels.size() ),
458 static_cast<int>( targetFP->
Models().size() ) ) );
477 toolMgr.
SetEnvironment( m_board.get(),
nullptr,
nullptr,
nullptr, toolsHolder );
484 auto ruleData = mtTool->
GetData();
487 static_cast<int>( ruleData->m_areas.size() ) ) );
491 if( ruleData->m_areas.size() < 2 )
508 int refViaCountBefore = 0;
510 for(
PCB_TRACK* track : m_board->Tracks() )
522 BOOST_TEST_MESSAGE( wxString::Format(
"Reference area vias before repeat: %d", refViaCountBefore ) );
523 BOOST_CHECK( refViaCountBefore > 0 );
527 ruleData->m_compatMap[targetArea].m_doCopy =
true;
528 ruleData->m_options.m_copyPlacement =
true;
529 ruleData->m_options.m_copyRouting =
true;
533 BOOST_CHECK(
result >= 0 );
535 int refViaCountAfter = 0;
537 for(
PCB_TRACK* track : m_board->Tracks() )
549 BOOST_TEST_MESSAGE( wxString::Format(
"Reference area vias after repeat: %d", refViaCountAfter ) );
566 toolMgr.
SetEnvironment( m_board.get(),
nullptr,
nullptr,
nullptr, toolsHolder );
631 auto cgRef = std::make_unique<CONNECTION_GRAPH>();
632 auto cgTarget = std::make_unique<CONNECTION_GRAPH>();
635 LIB_ID fpid( wxT(
"Package_SO" ), wxT(
"SOIC-8_3.9x4.9mm_P1.27mm" ) );
647 PAD padRef1( &fpRef );
650 fpRef.
Add( &padRef1 );
652 PAD padRef2( &fpRef );
655 fpRef.
Add( &padRef2 );
657 PAD padTarget1( &fpTarget );
660 fpTarget.
Add( &padTarget1 );
662 PAD padTarget2( &fpTarget );
665 fpTarget.
Add( &padTarget2 );
668 cgRef->AddFootprint( &fpRef,
VECTOR2I( 0, 0 ) );
669 cgTarget->AddFootprint( &fpTarget,
VECTOR2I( 0, 0 ) );
671 cgRef->BuildConnectivity();
672 cgTarget->BuildConnectivity();
678 COMPONENT* cmpRef = cgRef->Components()[0];
679 COMPONENT* cmpTarget = cgTarget->Components()[0];
681 bool sameKind = cmpRef->IsSameKind( *cmpTarget );
683 BOOST_TEST_MESSAGE( wxString::Format(
"TRIM_1.1 and TRIM_2.1 IsSameKind: %d", sameKind ? 1 : 0 ) );
684 BOOST_CHECK( sameKind );
688 std::vector<TMATCH::TOPOLOGY_MISMATCH_REASON> details;
689 bool status = cgRef->FindIsomorphism( cgTarget.get(),
result, details );
691 BOOST_TEST_MESSAGE( wxString::Format(
"Topology match result: %d", status ? 1 : 0 ) );
693 if( !status && !details.empty() )
695 for(
const auto& reason : details )
698 reason.m_reference, reason.m_candidate, reason.m_reason ) );
702 BOOST_CHECK( status );
703 BOOST_CHECK( details.empty() );
706 fpRef.
Pads().clear();
707 fpTarget.
Pads().clear();
722 auto cgRef = std::make_unique<CONNECTION_GRAPH>();
723 auto cgTarget = std::make_unique<CONNECTION_GRAPH>();
725 LIB_ID fpid( wxT(
"_BugFpLib" ), wxT(
"SW_Hotswap" ) );
738 PAD padRef1( &fpRef );
741 fpRef.
Add( &padRef1 );
743 PAD padRef2( &fpRef );
746 fpRef.
Add( &padRef2 );
748 PAD padTarget1( &fpTarget );
751 fpTarget.
Add( &padTarget1 );
753 PAD padTarget2( &fpTarget );
756 fpTarget.
Add( &padTarget2 );
758 cgRef->AddFootprint( &fpRef,
VECTOR2I( 0, 0 ) );
759 cgTarget->AddFootprint( &fpTarget,
VECTOR2I( 0, 0 ) );
761 cgRef->BuildConnectivity();
762 cgTarget->BuildConnectivity();
767 COMPONENT* cmpRef = cgRef->Components()[0];
768 COMPONENT* cmpTarget = cgTarget->Components()[0];
770 BOOST_CHECK_MESSAGE( cmpRef->IsSameKind( *cmpTarget ),
771 "REF** placeholder must be the same kind as an annotated footprint "
772 "with matching FPID (issue 24585)" );
775 std::vector<TMATCH::TOPOLOGY_MISMATCH_REASON> details;
776 bool status = cgRef->FindIsomorphism( cgTarget.get(),
result, details );
780 for(
const auto& reason : details )
783 reason.m_reference, reason.m_candidate, reason.m_reason ) );
787 BOOST_CHECK_MESSAGE( status,
788 "Design block layout with an un-annotated REF** footprint must match "
789 "an annotated destination (issue 24585)" );
790 BOOST_CHECK( details.empty() );
792 fpRef.
Pads().clear();
793 fpTarget.
Pads().clear();
806 LIB_ID fpid( wxT(
"_BugFpLib" ), wxT(
"SW_Hotswap" ) );
807 LIB_ID otherFpid( wxT(
"_BugFpLib" ), wxT(
"R_0402" ) );
820 BOOST_CHECK_MESSAGE( !cmpUnrelated.IsSameKind( cmpTarget ),
821 "Two annotated footprints with unrelated prefixes must not match even "
822 "with the same FPID (issue 24585)" );
826 fpPlaceholder.
SetFPID( otherFpid );
830 BOOST_CHECK_MESSAGE( !cmpPlaceholder.IsSameKind( cmpTarget ),
831 "A REF** placeholder must not match a target with a different FPID "
851 toolMgr.
SetEnvironment( m_board.get(),
nullptr,
nullptr,
nullptr, toolsHolder );
858 auto ruleData = mtTool->
GetData();
866 if( ra.
m_sheetPath == wxT(
"/io_drivers_fp/bank0/io01/" ) )
868 else if( ra.
m_sheetPath == wxT(
"/io_drivers_fp/bank0/" ) )
870 else if( ra.
m_sheetPath == wxT(
"/io_drivers_fp/" ) )
878 BOOST_TEST_MESSAGE( wxString::Format(
"Leaf /io_drivers_fp/bank0/io01/ components: %d",
917 toolMgr.
SetEnvironment( m_board.get(),
nullptr,
nullptr,
nullptr, toolsHolder );
924 auto ruleData = mtTool->
GetData();
930 if( ra.
m_sheetName == wxT(
"io_driver.kicad_sch" ) )
944 auto cgRef = CONNECTION_GRAPH::BuildFromFootprintSet( refArea->
m_components,
946 auto cgTarget = CONNECTION_GRAPH::BuildFromFootprintSet( targetArea->
m_components,
951 std::atomic<bool> cancelled(
true );
952 std::atomic<int> matched( 0 );
953 std::atomic<int> total( 0 );
961 std::vector<TMATCH::TOPOLOGY_MISMATCH_REASON> details;
963 bool status = cgRef->FindIsomorphism( cgTarget.get(),
result, details, params );
965 BOOST_CHECK( !status );
966 BOOST_CHECK(
result.empty() );
973 std::atomic<bool> cancelled(
false );
974 std::atomic<int> matched( 0 );
975 std::atomic<int> total( 0 );
983 std::vector<TMATCH::TOPOLOGY_MISMATCH_REASON> details;
985 bool status = cgRef->FindIsomorphism( cgTarget.get(),
result, details, params );
987 BOOST_CHECK( status );
989 int finalMatched = matched.load();
990 int finalTotal = total.load();
992 BOOST_TEST_MESSAGE( wxString::Format(
"Progress: matched=%d, total=%d", finalMatched, finalTotal ) );
994 BOOST_CHECK( finalTotal > 0 );
1001 std::vector<TMATCH::TOPOLOGY_MISMATCH_REASON> details;
1003 bool status = cgRef->FindIsomorphism( cgTarget.get(),
result, details );
1005 BOOST_CHECK( status );
1006 BOOST_CHECK( !
result.empty() );
1032 toolMgr.
SetEnvironment( m_board.get(),
nullptr,
nullptr,
nullptr, toolsHolder );
1039 auto ruleData = mtTool->
GetData();
1042 static_cast<int>( ruleData->m_areas.size() ) ) );
1049 for(
RULE_AREA& ra : ruleData->m_areas )
1056 if( source == wxT(
"/i2c_thingy_ch0/" ) )
1058 else if( source == wxT(
"/i2c_thingy_ch1/" ) )
1062 BOOST_REQUIRE_MESSAGE( ch0Area !=
nullptr,
"Could not find i2c_thingy_ch0 rule area" );
1063 BOOST_REQUIRE_MESSAGE( ch1Area !=
nullptr,
"Could not find i2c_thingy_ch1 rule area" );
1071 auto cgRef = CONNECTION_GRAPH::BuildFromFootprintSet( ch0Area->
m_components,
1073 auto cgTarget = CONNECTION_GRAPH::BuildFromFootprintSet( ch1Area->
m_components,
1077 std::vector<TMATCH::TOPOLOGY_MISMATCH_REASON> details;
1078 bool status = cgRef->FindIsomorphism( cgTarget.get(),
result, details );
1082 for(
const auto& reason : details )
1085 reason.m_reference, reason.m_candidate,
1086 reason.m_reason ) );
1090 BOOST_CHECK_MESSAGE( status,
1091 "Topology match failed for channels with hierarchical pins "
1092 "tied to global nets (issue 21739)" );
1106 std::unique_ptr<BOARD> board = std::make_unique<BOARD>();
1109 auto addNet = [&](
const wxString& aName ) ->
int
1116 const int netGnd = addNet( wxT(
"GND" ) );
1117 const int netVcc = addNet( wxT(
"+5VP" ) );
1118 const int netChainInRef = addNet( wxT(
"Net-(D-RefChainIn)" ) );
1119 const int netBridge = addNet( wxT(
"Net-(D38-DOUT)" ) );
1120 const int netChainOutTgt = addNet( wxT(
"Net-(D43-DOUT)" ) );
1121 const int netD2Dout = addNet( wxT(
"unconnected-(D2-DOUT)" ) );
1122 const int netD3Dout = addNet( wxT(
"unconnected-(D3-DOUT)" ) );
1123 const int netD5Dout = addNet( wxT(
"unconnected-(D5-DOUT)" ) );
1124 const int netD6Dout = addNet( wxT(
"unconnected-(D6-DOUT)" ) );
1126 LIB_ID ledId( wxT(
"TestLib" ), wxT(
"WS2812" ) );
1129 auto makeLed = [&](
const wxString& aRef,
int aDout,
int aDin ) ->
FOOTPRINT*
1136 auto addPad = [&](
const wxString& aNumber,
int aNetCode )
1139 pad->SetNumber( aNumber );
1144 pad->SetNetCode( aNetCode );
1148 addPad( wxT(
"1" ), aDout );
1149 addPad( wxT(
"2" ), netGnd );
1150 addPad( wxT(
"3" ), aDin );
1151 addPad( wxT(
"4" ), netVcc );
1158 std::set<FOOTPRINT*> refFps;
1159 refFps.insert( makeLed( wxT(
"D1" ), netBridge, netChainInRef ) );
1160 refFps.insert( makeLed( wxT(
"D2" ), netD2Dout, netChainInRef ) );
1161 refFps.insert( makeLed( wxT(
"D3" ), netD3Dout, netChainInRef ) );
1165 std::set<FOOTPRINT*> tgtFps;
1166 tgtFps.insert( makeLed( wxT(
"D4" ), netChainOutTgt, netBridge ) );
1167 tgtFps.insert( makeLed( wxT(
"D5" ), netD5Dout, netBridge ) );
1168 tgtFps.insert( makeLed( wxT(
"D6" ), netD6Dout, netBridge ) );
1170 auto cgRef = CONNECTION_GRAPH::BuildFromFootprintSet( refFps, tgtFps );
1171 auto cgTarget = CONNECTION_GRAPH::BuildFromFootprintSet( tgtFps, refFps );
1174 std::vector<TMATCH::TOPOLOGY_MISMATCH_REASON> details;
1175 bool status = cgRef->FindIsomorphism( cgTarget.get(),
result, details );
1179 for(
const auto& reason : details )
1182 reason.m_reference, reason.m_candidate,
1183 reason.m_reason ) );
1187 BOOST_CHECK_MESSAGE( status,
1188 "Topology match failed because the design-block boundary signal net "
1189 "was misclassified as a global rail and excluded asymmetrically" );
1205 m_board = std::make_unique<BOARD>();
1210 m_board->Add( net );
1212 auto makeFootprint =
1221 pad->SetNumber( wxT(
"1" ) );
1223 pad->SetPosition( aPos );
1235 FOOTPRINT* refFp1 = makeFootprint( wxT(
"R1" ),
1237 FOOTPRINT* refFp2 = makeFootprint( wxT(
"R2" ),
1245 m_board->Add( silkRect );
1248 FOOTPRINT* destFp1 = makeFootprint( wxT(
"R3" ),
1250 FOOTPRINT* destFp2 = makeFootprint( wxT(
"R4" ),
1254 destGroup->
SetName( wxT(
"design-block-dest" ) );
1255 destGroup->
AddItem( destFp1 );
1256 destGroup->
AddItem( destFp2 );
1257 m_board->Add( destGroup );
1266 m_board->Add( unrelatedSilk );
1270 for(
BOARD_ITEM* item : m_board->Drawings() )
1276 BOOST_REQUIRE_EQUAL( silkBefore, 2 );
1309 toolMgr.
SetEnvironment( m_board.get(),
nullptr,
nullptr,
nullptr, toolsHolder );
1315 .m_connectedRoutingOnly =
false,
1316 .m_copyPlacement =
true,
1317 .m_copyOtherItems =
true,
1318 .m_groupItems =
false,
1319 .m_includeLockedItems =
true,
1320 .m_anchorFp =
nullptr };
1323 BOOST_CHECK_MESSAGE(
result >= 0,
"RepeatLayout failed" );
1332 int silkInGroup = 0;
1333 bool unrelatedSurvived =
false;
1336 for(
BOARD_ITEM* item : m_board->Drawings() )
1343 if( item == unrelatedSilk )
1344 unrelatedSurvived =
true;
1346 if( item->GetParentGroup() == destGroup )
1357 BOOST_CHECK_MESSAGE( silkAfter == 3,
1358 wxString::Format(
"Expected 3 silkscreen shapes after Apply Design Block "
1359 "Layout (2 original + 1 copy), found %d (issue 24372)",
1361 BOOST_CHECK_MESSAGE( silkInGroup == 1,
1362 wxString::Format(
"Expected 1 silkscreen shape in destination group, "
1363 "found %d (issue 24372)",
1365 BOOST_CHECK_MESSAGE( unrelatedSurvived,
1366 "Unrelated silkscreen drawing outside the design block group was "
1367 "deleted by Apply Design Block Layout (issue 24372)" );
1383 m_board = std::make_unique<BOARD>();
1387 m_board->Add( net );
1389 auto makeFootprint = [&](
const wxString& aRef,
const VECTOR2I& aPos ) ->
FOOTPRINT*
1397 pad->SetNumber( wxT(
"1" ) );
1399 pad->SetPosition( aPos );
1415 for(
FOOTPRINT* fp : { refFp1, refFp2 } )
1416 fp->GetField(
FIELD_T::VALUE )->SetTextAngle( sourceFieldAngle );
1422 for(
FOOTPRINT* fp : { destFp1, destFp2 } )
1423 fp->SetOrientation( blockRotation );
1426 destGroup->
SetName( wxT(
"design-block-dest" ) );
1427 destGroup->
AddItem( destFp1 );
1428 destGroup->
AddItem( destFp2 );
1429 m_board->Add( destGroup );
1459 toolMgr.
SetEnvironment( m_board.get(),
nullptr,
nullptr,
nullptr, toolsHolder );
1465 .m_connectedRoutingOnly =
false,
1466 .m_copyPlacement =
true,
1467 .m_copyOtherItems =
true,
1468 .m_groupItems =
false,
1469 .m_includeLockedItems =
true,
1470 .m_anchorFp =
nullptr };
1473 BOOST_REQUIRE_MESSAGE(
result >= 0,
"RepeatLayout failed" );
1480 const double expected = ( sourceFieldAngle + blockRotation ).AsDegrees();
1482 for(
FOOTPRINT* fp : { destFp1, destFp2 } )
1490 wxString::Format(
"Field on %s rotated to %.3f deg, expected %.3f deg "
1491 "(field double-rotation regression)",
1506 m_board = std::make_unique<BOARD>();
1510 m_board->Add( shared );
1512 auto makeFootprint = [&](
const wxString& aRef,
const VECTOR2I& aPos,
const KIID& aSymbolUuid,
1516 fp->
SetFPID(
LIB_ID( wxT(
"TestLib" ), wxT(
"Receptacle" ) ) );
1522 path.push_back( aSymbolUuid );
1526 pad->SetNumber( wxT(
"1" ) );
1528 pad->SetPosition( aPos );
1533 pad->SetNet( aNet );
1543 FOOTPRINT* refFpA = makeFootprint( wxT(
"J5" ),
VECTOR2I( 0, 0 ), symA,
nullptr );
1554 std::set<FOOTPRINT*> refSet{ refFpA, refFpB };
1555 std::set<FOOTPRINT*> destSet{ destFpA, destFpB };
1556 auto cgRef = CONNECTION_GRAPH::BuildFromFootprintSet( refSet, destSet );
1557 auto cgTarget = CONNECTION_GRAPH::BuildFromFootprintSet( destSet, refSet );
1559 std::vector<TMATCH::TOPOLOGY_MISMATCH_REASON> details;
1560 BOOST_REQUIRE_MESSAGE( !cgRef->FindIsomorphism( cgTarget.get(), topoOnly, details ),
1561 "Test precondition broken: areas are net isomorphic" );
1565 destGroup->
SetName( wxT(
"design-block-dest" ) );
1566 destGroup->
AddItem( destFpA );
1567 destGroup->
AddItem( destFpB );
1568 m_board->Add( destGroup );
1597 toolMgr.
SetEnvironment( m_board.get(),
nullptr,
nullptr,
nullptr, toolsHolder );
1602 .m_connectedRoutingOnly =
false,
1603 .m_copyPlacement =
true,
1604 .m_copyOtherItems =
true,
1605 .m_groupItems =
false,
1606 .m_includeLockedItems =
true,
1607 .m_anchorFp =
nullptr };
1614 BOOST_CHECK_MESSAGE(
result >= 0,
"Apply Design Block Layout aborted even though the symbol instance paths "
1615 "give an unambiguous mapping (No compatible component regression)" );
1620 wxString::Format(
"Destination relative placement wrong, expected "
1621 "(10mm,0) got (%d,%d) IU",
1636 m_board = std::make_unique<BOARD>();
1640 m_board->Add( net );
1642 auto makeFootprint = [&](
const wxString& aRef,
const VECTOR2I& aPos ) ->
FOOTPRINT*
1653 txt->
SetText( wxT(
"${REFERENCE}" ) );
1660 pad->SetNumber( wxT(
"1" ) );
1662 pad->SetPosition( aPos );
1677 if( item->Type() ==
PCB_TEXT_T && item->GetLayer() == layer )
1691 destGroup->
SetName( wxT(
"design-block-dest" ) );
1692 destGroup->
AddItem( destFp1 );
1693 destGroup->
AddItem( destFp2 );
1694 m_board->Add( destGroup );
1696 BOOST_REQUIRE_EQUAL( countSilkText( destFp1,
F_SilkS ), 1 );
1697 BOOST_REQUIRE_EQUAL( countSilkText( destFp1,
B_SilkS ), 1 );
1727 toolMgr.
SetEnvironment( m_board.get(),
nullptr,
nullptr,
nullptr, toolsHolder );
1733 .m_connectedRoutingOnly =
false,
1734 .m_copyPlacement =
true,
1735 .m_copyOtherItems =
true,
1736 .m_groupItems =
false,
1737 .m_includeLockedItems =
true,
1738 .m_anchorFp =
nullptr };
1742 BOOST_CHECK_MESSAGE(
result >= 0, wxString::Format(
"RepeatLayout failed: %s", err ) );
1747 for(
FOOTPRINT* destFp : { destFp1, destFp2 } )
1749 BOOST_CHECK_MESSAGE( countSilkText( destFp,
F_SilkS ) == 1,
1750 wxString::Format(
"%s lost its F.Silkscreen reference text "
1751 "(issue 24583): F=%d B=%d",
1752 destFp->GetReference(), countSilkText( destFp,
F_SilkS ),
1753 countSilkText( destFp,
B_SilkS ) ) );
1765 m_board = std::make_unique<BOARD>();
1789 m_board->Add( destPlaceholder );
1792 destGroup->
SetName( wxT(
"design-block-dest" ) );
1793 destGroup->
AddItem( destPlaceholder );
1794 m_board->Add( destGroup );
1798 for(
BOARD_ITEM* item : m_board->Drawings() )
1804 BOOST_REQUIRE_EQUAL( silkBefore, 2 );
1832 toolMgr.
SetEnvironment( m_board.get(),
nullptr,
nullptr,
nullptr, toolsHolder );
1838 .m_connectedRoutingOnly =
false,
1839 .m_copyPlacement =
true,
1840 .m_copyOtherItems =
true,
1841 .m_groupItems =
false,
1842 .m_includeLockedItems =
true,
1843 .m_anchorFp =
nullptr };
1851 BOOST_REQUIRE_MESSAGE(
result >= 0, wxString::Format(
"RepeatLayout failed for a footprint-free "
1852 "(graphics-only) design block (issue 24592): %s",
1860 for(
BOARD_ITEM* item : m_board->Drawings() )
1873 BOOST_CHECK_MESSAGE( silkAfter == 2, wxString::Format(
"Expected 2 silkscreen shapes after Apply Design Block "
1874 "Layout (source + 1 copy, placeholder replaced), found "
1877 BOOST_REQUIRE_MESSAGE( copiedRect !=
nullptr,
"Footprint-free block graphic was not copied (issue 24592)" );
1879 "Copied block graphic was not added to the destination group (issue 24592)" );
1890 m_board = std::make_unique<BOARD>();
1894 m_board->Add( net );
1903 m_board->Add( srcTrack );
1905 BOOST_REQUIRE_EQUAL( srcTrack->
GetNetCode(), 1 );
1913 m_board->Add( destPlaceholder );
1916 destGroup->
SetName( wxT(
"design-block-dest" ) );
1917 destGroup->
AddItem( destPlaceholder );
1918 m_board->Add( destGroup );
1946 toolMgr.
SetEnvironment( m_board.get(),
nullptr,
nullptr,
nullptr, toolsHolder );
1952 .m_connectedRoutingOnly =
false,
1953 .m_copyPlacement =
true,
1954 .m_copyOtherItems =
true,
1955 .m_groupItems =
false,
1956 .m_includeLockedItems =
true,
1957 .m_anchorFp =
nullptr };
1965 BOOST_REQUIRE_MESSAGE(
result >= 0, wxString::Format(
"RepeatLayout failed for a footprint-free copper "
1966 "block (issue 24592): %s",
1972 for(
PCB_TRACK* track : m_board->Tracks() )
1974 if( track != srcTrack )
1975 copiedTrack = track;
1978 BOOST_REQUIRE_MESSAGE( copiedTrack !=
nullptr,
"Footprint-free block track was not copied (issue 24592)" );
1979 BOOST_CHECK_MESSAGE( copiedTrack->
GetNetCode() == 0,
1980 wxString::Format(
"Copied copper should be no-net, got net code %d "
1984 "Copied copper was not added to the destination group (issue 24592)" );
1999 m_board = std::make_unique<BOARD>();
2008 m_board->Add( srcRect );
2016 m_board->Add( destPlaceholder );
2019 destGroup->
SetName( wxT(
"design-block-dest" ) );
2020 destGroup->
AddItem( destPlaceholder );
2021 m_board->Add( destGroup );
2025 toolMgr.
SetEnvironment( m_board.get(),
nullptr,
nullptr,
nullptr, toolsHolder );
2030 auto applyOnce = [&]() ->
int
2057 .m_connectedRoutingOnly =
false,
2058 .m_copyPlacement =
true,
2059 .m_copyOtherItems =
true,
2060 .m_groupItems =
false,
2061 .m_includeLockedItems =
true,
2062 .m_anchorFp =
nullptr };
2071 BOOST_REQUIRE_MESSAGE( applyOnce() >= 0,
"First apply failed (issue 24592)" );
2072 BOOST_REQUIRE_MESSAGE( applyOnce() >= 0,
"Second apply failed (issue 24592)" );
2078 for(
BOARD_ITEM* item : m_board->Drawings() )
2087 BOOST_CHECK_MESSAGE( rects == 2, wxString::Format(
"Re-apply stacked copies: expected 2 rectangles "
2088 "(source + 1 copy), found %d (issue 24592)",
2102 m_board = std::make_unique<BOARD>();
2106 m_board->Add( net );
2115 m_board->Add( srcTrack );
2123 m_board->Add( destPlaceholder );
2126 destGroup->
SetName( wxT(
"design-block-dest" ) );
2127 destGroup->
AddItem( destPlaceholder );
2128 m_board->Add( destGroup );
2136 m_board->Add( siblingTrack );
2139 siblingGroup->
SetName( wxT(
"design-block-sibling" ) );
2140 siblingGroup->
AddItem( siblingTrack );
2141 m_board->Add( siblingGroup );
2149 m_board->Add( looseTrack );
2177 toolMgr.
SetEnvironment( m_board.get(),
nullptr,
nullptr,
nullptr, toolsHolder );
2183 .m_connectedRoutingOnly =
false,
2184 .m_copyPlacement =
true,
2185 .m_copyOtherItems =
true,
2186 .m_groupItems =
false,
2187 .m_includeLockedItems =
true,
2188 .m_anchorFp =
nullptr };
2196 BOOST_REQUIRE_MESSAGE(
result >= 0, wxString::Format(
"RepeatLayout failed: %s", err ) );
2199 int siblingTracks = 0;
2200 int looseTracks = 0;
2202 for(
PCB_TRACK* track : m_board->Tracks() )
2204 int y = track->GetStart().y;
2212 BOOST_CHECK_MESSAGE( siblingTracks == 1,
2213 wxString::Format(
"Sibling group routing was deleted (issue 24767): found %d, expected 1",
2215 BOOST_CHECK_MESSAGE( looseTracks == 1,
2216 wxString::Format(
"Loose no-net routing in the target area was deleted (issue 24944): "
2217 "found %d, expected 1",
2229 m_board = std::make_unique<BOARD>();
2234 m_board->Add( net1 );
2235 m_board->Add( net2 );
2237 auto addTrack = [&](
double aStartX,
double aStartY,
double aEndX,
double aEndY,
NETINFO_ITEM* aNet )
2245 m_board->Add( track );
2250 PCB_TRACK* srcTrack = addTrack( -2, 0, 2, 0, net1 );
2258 m_board->Add( destPlaceholder );
2260 PCB_TRACK* prevTrack = addTrack( 46, 49, 48, 49, net1 );
2263 destGroup->
SetName( wxT(
"design-block-dest" ) );
2264 destGroup->
AddItem( destPlaceholder );
2265 destGroup->
AddItem( prevTrack );
2266 m_board->Add( destGroup );
2270 addTrack( 46, 47, 48, 47, net2 );
2272 PCB_TRACK* lockedTrack = addTrack( 46, 51.5, 48, 51.5, net1 );
2276 addTrack( 46, 53, 48, 53, net1 );
2304 toolMgr.
SetEnvironment( m_board.get(),
nullptr,
nullptr,
nullptr, toolsHolder );
2310 .m_connectedRoutingOnly =
false,
2311 .m_copyPlacement =
true,
2312 .m_copyOtherItems =
true,
2313 .m_groupItems =
false,
2314 .m_includeLockedItems =
true,
2315 .m_anchorFp =
nullptr };
2323 BOOST_REQUIRE_MESSAGE(
result >= 0, wxString::Format(
"RepeatLayout failed: %s", err ) );
2326 int unrelatedTracks = 0;
2328 int copiedTracks = 0;
2329 int lockedTracks = 0;
2330 int staleTweakTracks = 0;
2332 for(
PCB_TRACK* track : m_board->Tracks() )
2334 int y = track->GetStart().y;
2348 BOOST_CHECK_MESSAGE( unrelatedTracks == 1,
2349 wxString::Format(
"Unrelated net routing in the target area was deleted (issue 24944): "
2350 "found %d, expected 1",
2351 unrelatedTracks ) );
2352 BOOST_CHECK_MESSAGE( lockedTracks == 1,
2353 wxString::Format(
"Locked bystander routing in the target area was deleted (issue 24944): "
2354 "found %d, expected 1",
2356 BOOST_CHECK_MESSAGE(
2358 wxString::Format(
"The group's own routing should be replaced: found %d, expected 0", prevTracks ) );
2359 BOOST_CHECK_MESSAGE( staleTweakTracks == 0,
2360 wxString::Format(
"Loose routing on the group's nets should be replaced: found %d, "
2362 staleTweakTracks ) );
2363 BOOST_CHECK_MESSAGE( copiedTracks == 1,
2364 wxString::Format(
"Block routing was not copied into the target area: found %d, expected 1",
2382 toolMgr.
SetEnvironment( m_board.get(),
nullptr,
nullptr,
nullptr, toolsHolder );
2389 auto ruleData = mtTool->
GetData();
2392 static_cast<int>( ruleData->m_areas.size() ) ) );
2394 BOOST_REQUIRE_EQUAL( ruleData->m_areas.size(), 4 );
2397 const size_t sharedCount = ruleData->m_areas.front().m_components.size();
2401 for(
const RULE_AREA& ra : ruleData->m_areas )
2404 RULE_AREA* refArea = &ruleData->m_areas.front();
2408 BOOST_REQUIRE_EQUAL( ruleData->m_compatMap.size(), ruleData->m_areas.size() - 1 );
2410 for(
const auto& [targetArea, compatData] : ruleData->m_compatMap )
2412 BOOST_CHECK_MESSAGE( !compatData.m_isOk,
2413 "Duplicate placement area was wrongly reported as a valid copy target "
2415 BOOST_CHECK( !compatData.m_mismatchReasons.empty() );
2419 RULE_AREA* targetArea = &ruleData->m_areas[1];
2426 BOOST_CHECK_MESSAGE(
result < 0,
2427 "RepeatLayout copied a Rule Area onto an identical-component area "
2429 BOOST_CHECK( !err.IsEmpty() );
2451 toolMgr.
SetEnvironment( m_board.get(),
nullptr,
nullptr,
nullptr, toolsHolder );
2458 auto ruleData = mtTool->
GetData();
2461 static_cast<int>( ruleData->m_areas.size() ) ) );
2463 BOOST_REQUIRE_EQUAL( ruleData->m_areas.size(), 4 );
2467 for(
RULE_AREA& ra : ruleData->m_areas )
2475 std::set<KIID> groupUuidsBefore;
2476 std::set<wxString> userGroupNamesBefore;
2480 groupUuidsBefore.insert(
group->m_Uuid );
2482 if( !
group->GetName().IsEmpty() )
2483 userGroupNamesBefore.insert(
group->GetName() );
2488 for(
auto& [targetArea, compatData] : ruleData->m_compatMap )
2489 compatData.m_doCopy =
true;
2491 ruleData->m_options.m_copyPlacement =
true;
2492 ruleData->m_options.m_copyRouting =
true;
2493 ruleData->m_options.m_copyOtherItems =
true;
2494 ruleData->m_options.m_groupItems =
true;
2495 ruleData->m_options.m_includeLockedItems =
true;
2501 int clonedUserGroups = 0;
2505 bool isNew = !groupUuidsBefore.contains(
group->m_Uuid );
2507 if( isNew && userGroupNamesBefore.contains(
group->GetName() ) )
2511 BOOST_CHECK_MESSAGE( clonedUserGroups == 0,
2512 wxString::Format(
"Repeat layout cloned %d unrelated user groups "
2514 clonedUserGroups ) );
2533 toolMgr.
SetEnvironment( m_board.get(),
nullptr,
nullptr,
nullptr, toolsHolder );
2540 auto ruleData = mtTool->
GetData();
2542 BOOST_REQUIRE_EQUAL( ruleData->m_areas.size(), 2 );
2547 for(
RULE_AREA& ra : ruleData->m_areas )
2549 if( ra.
m_ruleName.Contains( wxT(
"Untitled Sheet/" ) ) )
2551 else if( ra.
m_ruleName.Contains( wxT(
"Untitled Sheet1/" ) ) )
2561 auto cgRef = CONNECTION_GRAPH::BuildFromFootprintSet( refArea->
m_components,
2563 auto cgTarget = CONNECTION_GRAPH::BuildFromFootprintSet( targetArea->
m_components,
2567 std::vector<TMATCH::TOPOLOGY_MISMATCH_REASON> details;
2568 bool status = cgRef->FindIsomorphism( cgTarget.get(),
result, details );
2571 BOOST_CHECK( !status );
2576 std::set<wxString> refRefDes;
2577 std::set<wxString> targetRefDes;
2585 bool sawConnectivityReason =
false;
2587 for(
const auto& reason : details )
2590 reason.m_candidate, reason.m_reason ) );
2594 BOOST_CHECK( !reason.m_reason.Contains( wxT(
"No compatible component found" ) ) );
2596 if( reason.m_reason.Contains( wxT(
"connects to" ) )
2597 || reason.m_reason.Contains( wxT(
"connectivity" ) ) )
2599 sawConnectivityReason =
true;
2603 BOOST_CHECK_MESSAGE( refRefDes.count( reason.m_reference ) > 0,
2604 wxString::Format(
"Reason reference '%s' should be a reference-area "
2605 "component", reason.m_reference ) );
2606 BOOST_CHECK_MESSAGE( targetRefDes.count( reason.m_candidate ) > 0,
2607 wxString::Format(
"Reason candidate '%s' should be a target-area "
2608 "component", reason.m_candidate ) );
2612 BOOST_CHECK_MESSAGE( sawConnectivityReason,
2613 "Topology mismatch message should explain the connectivity difference "
2614 "rather than report a generic missing-component error (issue 24192)" );
2628 std::unique_ptr<BOARD> board = std::make_unique<BOARD>();
2630 auto addNet = [&](
const wxString& aName ) ->
int
2637 const int netGnd = addNet( wxT(
"GND" ) );
2638 const int netP3V3 = addNet( wxT(
"+3V3" ) );
2639 const int netCollide = addNet( wxT(
"Net-(D3-A)" ) );
2640 const int netRefY = addNet( wxT(
"Net-(D4-A)" ) );
2641 const int netTgtX = addNet( wxT(
"Net-(D2-A)" ) );
2643 LIB_ID ledAId( wxT(
"TestLib" ), wxT(
"LED_A" ) );
2644 LIB_ID ledBId( wxT(
"TestLib" ), wxT(
"LED_B" ) );
2645 LIB_ID resId( wxT(
"TestLib" ), wxT(
"R" ) );
2647 auto addPad = [&](
FOOTPRINT* fp,
const wxString& aNum,
int aNet )
2650 pad->SetNumber( aNum );
2654 pad->SetNetCode( aNet );
2658 auto addFp = [&](
const LIB_ID& aId,
const wxString& aRef ) ->
FOOTPRINT*
2668 auto addLedAndRes = [&](
const LIB_ID& aLedId,
const wxString& aLedRef,
const wxString& aResRef,
int aAnodeNet,
2669 std::set<FOOTPRINT*>& aSet )
2671 FOOTPRINT* led = addFp( aLedId, aLedRef );
2672 addPad( led, wxT(
"1" ), netGnd );
2673 addPad( led, wxT(
"2" ), aAnodeNet );
2677 addPad(
res, wxT(
"1" ), aAnodeNet );
2678 addPad(
res, wxT(
"2" ), netP3V3 );
2683 std::set<FOOTPRINT*> refFps;
2684 addLedAndRes( ledAId, wxT(
"D3" ), wxT(
"R27" ), netCollide, refFps );
2685 addLedAndRes( ledBId, wxT(
"D4" ), wxT(
"R30" ), netRefY, refFps );
2688 std::set<FOOTPRINT*> tgtFps;
2689 addLedAndRes( ledAId, wxT(
"D2" ), wxT(
"R1" ), netTgtX, tgtFps );
2690 addLedAndRes( ledBId, wxT(
"D3" ), wxT(
"R2" ), netCollide, tgtFps );
2693 std::unordered_set<EDA_ITEM*> refItems( refFps.begin(), refFps.end() );
2696 auto cgRef = CONNECTION_GRAPH::BuildFromFootprintSet( refFps, tgtFps );
2697 auto cgTarget = CONNECTION_GRAPH::BuildFromFootprintSet( tgtFps, refFps );
2700 std::vector<TMATCH::TOPOLOGY_MISMATCH_REASON> details;
2701 bool status = cgRef->FindIsomorphism( cgTarget.get(),
result, details );
2705 for(
const auto& reason : details )
2707 BOOST_TEST_MESSAGE( wxString::Format(
"Mismatch: %s to %s: %s", reason.m_reference, reason.m_candidate,
2708 reason.m_reason ) );
2712 BOOST_CHECK_MESSAGE( status,
"Topology match failed even after isolating the block's auto nets "
2727 std::unique_ptr<BOARD> board = std::make_unique<BOARD>();
2729 const LIB_ID receptacleId( wxT(
"Don-Con" ), wxT(
"Mill-Max-Pin_Receptacle" ) );
2732 auto makeReceptacle = [&](
const wxString& aRef,
const wxString& aValue,
bool aWithSymbolPath ) ->
FOOTPRINT*
2739 if( aWithSymbolPath )
2750 pad->SetNumber( wxT(
"1" ) );
2762 FOOTPRINT* refNC0 = makeReceptacle( wxT(
"J3" ), wxT(
"NC_0" ),
false );
2763 FOOTPRINT* refNC1 = makeReceptacle( wxT(
"J5" ), wxT(
"NC_1" ),
false );
2764 FOOTPRINT* refNO0 = makeReceptacle( wxT(
"J6" ), wxT(
"NO_0" ),
false );
2765 FOOTPRINT* refNO1 = makeReceptacle( wxT(
"J7" ), wxT(
"NO_1" ),
false );
2768 FOOTPRINT* tgtNO1 = makeReceptacle( wxT(
"J20" ), wxT(
"NO_1" ),
true );
2769 FOOTPRINT* tgtNO0 = makeReceptacle( wxT(
"J21" ), wxT(
"NO_0" ),
true );
2770 FOOTPRINT* tgtNC1 = makeReceptacle( wxT(
"J22" ), wxT(
"NC_1" ),
true );
2771 FOOTPRINT* tgtNC0 = makeReceptacle( wxT(
"J23" ), wxT(
"NC_0" ),
true );
2773 auto cgRef = std::make_unique<CONNECTION_GRAPH>();
2774 cgRef->AddFootprint( refNC0,
VECTOR2I( 0, 0 ) );
2775 cgRef->AddFootprint( refNC1,
VECTOR2I( 0, 0 ) );
2776 cgRef->AddFootprint( refNO0,
VECTOR2I( 0, 0 ) );
2777 cgRef->AddFootprint( refNO1,
VECTOR2I( 0, 0 ) );
2778 cgRef->BuildConnectivity();
2780 auto cgTarget = std::make_unique<CONNECTION_GRAPH>();
2781 cgTarget->AddFootprint( tgtNO1,
VECTOR2I( 0, 0 ) );
2782 cgTarget->AddFootprint( tgtNO0,
VECTOR2I( 0, 0 ) );
2783 cgTarget->AddFootprint( tgtNC1,
VECTOR2I( 0, 0 ) );
2784 cgTarget->AddFootprint( tgtNC0,
VECTOR2I( 0, 0 ) );
2785 cgTarget->BuildConnectivity();
2788 std::vector<TMATCH::TOPOLOGY_MISMATCH_REASON> details;
2789 bool status = cgRef->FindIsomorphism( cgTarget.get(),
result, details );
2791 BOOST_CHECK( status );
2795 for(
const auto& [refFp, targetFp] :
result )
2797 BOOST_TEST_MESSAGE( wxString::Format(
"%s (%s) -> %s (%s)", refFp->GetReference(), refFp->GetValue(),
2798 targetFp->GetReference(), targetFp->GetValue() ) );
2811 m_board = std::make_unique<BOARD>();
2814 auto makeReceptacle = [&](
const wxString& aRef,
const wxString& aValue,
const VECTOR2I& aPos,
2818 fp->
SetFPID(
LIB_ID( wxT(
"Don-Con" ), wxT(
"Mill-Max-Pin_Receptacle" ) ) );
2823 if( aWithSymbolPath )
2831 m_board->Add( net );
2834 pad->SetNumber( wxT(
"1" ) );
2839 pad->SetPosition( aPos );
2846 auto mm = [](
double a,
double b )
2854 return (
double) ( aNC1.x - aNC0.
x ) * ( aNO0.y - aNC0.
y ) - (double) ( aNC1.y - aNC0.
y ) * ( aNO0.x - aNC0.
x );
2858 FOOTPRINT* srcNC0 = makeReceptacle( wxT(
"J3" ), wxT(
"NC_0" ), mm( 0, 0 ),
false );
2859 FOOTPRINT* srcNC1 = makeReceptacle( wxT(
"J5" ), wxT(
"NC_1" ), mm( 0, 7.9 ),
false );
2860 FOOTPRINT* srcNO0 = makeReceptacle( wxT(
"J6" ), wxT(
"NO_0" ), mm( -3.58, 2.28 ),
false );
2861 FOOTPRINT* srcNO1 = makeReceptacle( wxT(
"J7" ), wxT(
"NO_1" ), mm( 3.58, 5.62 ),
false );
2866 FOOTPRINT* dstNC0 = makeReceptacle( wxT(
"J8" ), wxT(
"NC_0" ), mm( 40, 40 ),
true );
2867 FOOTPRINT* dstNC1 = makeReceptacle( wxT(
"J9" ), wxT(
"NC_1" ), mm( 40, 47.9 ),
true );
2868 FOOTPRINT* dstNO0 = makeReceptacle( wxT(
"J10" ), wxT(
"NO_0" ), mm( 43.58, 42.28 ),
true );
2869 FOOTPRINT* dstNO1 = makeReceptacle( wxT(
"J11" ), wxT(
"NO_1" ), mm( 36.42, 45.62 ),
true );
2874 destGroup->
SetName( wxT(
"Pin receptacles for safety switch" ) );
2876 for(
FOOTPRINT* fp : { dstNC0, dstNC1, dstNO0, dstNO1 } )
2879 m_board->Add( destGroup );
2884 for(
FOOTPRINT* fp : { srcNC0, srcNC1, srcNO0, srcNO1 } )
2898 for(
FOOTPRINT* fp : { dstNC0, dstNC1, dstNO0, dstNO1 } )
2908 toolMgr.
SetEnvironment( m_board.get(),
nullptr,
nullptr,
nullptr, toolsHolder );
2914 .m_connectedRoutingOnly =
false,
2915 .m_copyPlacement =
true,
2916 .m_copyOtherItems =
false,
2917 .m_groupItems =
false,
2918 .m_includeLockedItems =
true,
2919 .m_anchorFp =
nullptr };
2922 BOOST_REQUIRE_MESSAGE(
result >= 0,
"RepeatLayout failed" );
2930 wxString::Format(
"block chirality %.0f, destination chirality after apply %.0f", srcChir, dstChir ) );
2932 BOOST_CHECK_MESSAGE( srcChir * dstChir > 0.0,
"Applied layout left the receptacles mirrored (NC/NO swapped)" );
2944 m_board = std::make_unique<BOARD>();
2947 auto addNet = [&](
const wxString& aName ) ->
int
2950 m_board->Add( net );
2954 const int netGnd = addNet( wxT(
"GND" ) );
2955 const int netVcc = addNet( wxT(
"+3V3" ) );
2957 const LIB_ID icId( wxT(
"TestLib" ), wxT(
"SOT-23-6" ) );
2958 const LIB_ID rId( wxT(
"TestLib" ), wxT(
"R_0402" ) );
2962 auto makeChannel = [&](
int aIdx,
int aStrapNet ) ->
ZONE*
2964 const int netOut = addNet( wxString::Format( wxT(
"Net-(U%d-OUT)" ), aIdx ) );
2965 const int netCfg = addNet( wxString::Format( wxT(
"Net-(U%d-CFG)" ), aIdx ) );
2967 auto addPad = [&](
FOOTPRINT* aFp,
const wxString& aNumber,
int aNetCode )
2970 pad->SetNumber( aNumber );
2975 pad->SetNetCode( aNetCode );
2981 ic->
SetReference( wxString::Format( wxT(
"U%d" ), aIdx ) );
2983 addPad( ic, wxT(
"1" ), netOut );
2984 addPad( ic, wxT(
"2" ), netGnd );
2988 r->
SetReference( wxString::Format( wxT(
"R%d" ), aIdx ) );
2990 addPad( r, wxT(
"1" ), netCfg );
2991 addPad( r, wxT(
"2" ), aStrapNet );
2993 ZONE* zone =
new ZONE( m_board.get() );
2994 m_board->Add( zone );
2999 std::vector<std::pair<int, int>> channelStraps = { { 1, netGnd }, { 2, netGnd }, { 3, netVcc } };
3000 std::vector<ZONE*> zones;
3004 toolMgr.
SetEnvironment( m_board.get(),
nullptr,
nullptr,
nullptr, toolsHolder );
3010 ruleData->
m_areas.reserve( channelStraps.size() );
3012 for(
const auto& [idx, strap] : channelStraps )
3014 ZONE* zone = makeChannel( idx, strap );
3015 zones.push_back( zone );
3019 ra.
m_ruleName = wxString::Format( wxT(
"Channel %d" ), idx );
3021 for(
FOOTPRINT* fp : m_board->Footprints() )
3023 if( fp->GetReference() == wxString::Format( wxT(
"U%d" ), idx )
3024 || fp->GetReference() == wxString::Format( wxT(
"R%d" ), idx ) )
3030 ruleData->
m_areas.push_back( ra );
3035 BOOST_REQUIRE_EQUAL( status, 0 );
3039 if( ra.
m_zone == zones[1] )
3045 for(
const wxString& reason : it->second.m_mismatchReasons )
3048 BOOST_CHECK_MESSAGE( it->second.m_isOk,
3049 wxString::Format(
"Channel 2 must match %s", ra.
m_ruleName ) );
3061 m_board = std::make_unique<BOARD>();
3064 auto addNet = [&](
const wxString& aName ) ->
int
3067 m_board->Add( net );
3071 const int netGnd = addNet( wxT(
"GND" ) );
3073 const LIB_ID icId( wxT(
"TestLib" ), wxT(
"SOT-23-6" ) );
3074 const LIB_ID rId( wxT(
"TestLib" ), wxT(
"R_0402" ) );
3076 auto addPad = [&](
FOOTPRINT* aFp,
const wxString& aNumber,
int aNetCode )
3079 pad->SetNumber( aNumber );
3083 pad->SetNetCode( aNetCode );
3087 auto makeFp = [&](
const LIB_ID& aFpId,
const wxString& aRef ) ->
FOOTPRINT*
3097 const int netLocal1 = addNet( wxT(
"Net-(U1-SIG)" ) );
3098 FOOTPRINT* u1 = makeFp( icId, wxT(
"U1" ) );
3099 addPad( u1, wxT(
"1" ), netLocal1 );
3100 addPad( u1, wxT(
"2" ), netGnd );
3101 FOOTPRINT* r1 = makeFp( rId, wxT(
"R1" ) );
3102 addPad( r1, wxT(
"1" ), netLocal1 );
3103 addPad( r1, wxT(
"2" ), netGnd );
3107 const int netLocal2a = addNet( wxT(
"Net-(U2-SIG)" ) );
3108 const int netLocal2b = addNet( wxT(
"Net-(R2-SIG)" ) );
3109 FOOTPRINT* u2 = makeFp( icId, wxT(
"U2" ) );
3110 addPad( u2, wxT(
"1" ), netLocal2a );
3111 addPad( u2, wxT(
"2" ), netGnd );
3112 FOOTPRINT* r2 = makeFp( rId, wxT(
"R2" ) );
3113 addPad( r2, wxT(
"1" ), netLocal2b );
3114 addPad( r2, wxT(
"2" ), netGnd );
3116 ZONE* zone1 =
new ZONE( m_board.get() );
3117 m_board->Add( zone1 );
3118 ZONE* zone2 =
new ZONE( m_board.get() );
3119 m_board->Add( zone2 );
3120 ZONE* zoneDup =
new ZONE( m_board.get() );
3121 m_board->Add( zoneDup );
3125 toolMgr.
SetEnvironment( m_board.get(),
nullptr,
nullptr,
nullptr, toolsHolder );
3131 ruleData->
m_areas.reserve( 3 );
3137 ruleData->
m_areas.push_back( ra1 );
3143 ruleData->
m_areas.push_back( ra2 );
3149 raDup.
m_ruleName = wxT(
"Channel 1 duplicate" );
3151 ruleData->
m_areas.push_back( raDup );
3154 BOOST_REQUIRE_EQUAL( status, 0 );
3169 BOOST_CHECK_MESSAGE( !it->second.m_isOk,
3170 "Channel 1 and channel 2 differ on a local net and must not match; an "
3171 "overlapping rule area must not hide that net by marking it global" );
3183 toolMgr.
SetEnvironment( m_board.get(),
nullptr,
nullptr,
nullptr, toolsHolder );
3218 toolMgr.
SetEnvironment( m_board.get(),
nullptr,
nullptr,
nullptr, toolsHolder );
3249 toolMgr.
SetEnvironment( m_board.get(),
nullptr,
nullptr,
nullptr, toolsHolder );
3256 auto ruleData = mtTool->
GetData();
3259 for(
RULE_AREA& ra : ruleData->m_areas )
3277 std::vector<PCB_GENERATOR*> strip;
3281 if( gen->GetGeneratorType() != wxT(
"tuning_pattern" ) )
3285 strip.push_back( gen );
3290 for(
EDA_ITEM* member : gen->GetItems() )
3292 if( member->IsBOARD_ITEM() )
3293 static_cast<BOARD_ITEM*
>( member )->SetParentGroup(
nullptr );
3296 m_board->Remove( gen );
3303 ruleData->m_options.m_groupItems =
true;
3306 ruleData->m_compatMap[target].m_doCopy =
true;
3323 toolMgr.
SetEnvironment( m_board.get(),
nullptr,
nullptr,
nullptr, toolsHolder );
3330 auto ruleData = mtTool->
GetData();
3333 for(
RULE_AREA& ra : ruleData->m_areas )
3347 std::set<EDA_GROUP*> groups;
3359 for(
EDA_ITEM* member : g->GetItems() )
3383 toolMgr.
SetEnvironment( m_board.get(),
nullptr,
nullptr,
nullptr, toolsHolder );
3390 auto ruleData = mtTool->
GetData();
3393 for(
RULE_AREA& ra : ruleData->m_areas )
3415 std::vector<ZONE*> stripZones;
3417 for(
ZONE* zone : m_board->Zones() )
3419 if( zone->GetIsRuleArea() )
3423 stripZones.push_back( zone );
3426 for(
ZONE* zone : stripZones )
3429 group->RemoveItem( zone );
3431 m_board->Remove( zone );
3437 ruleData->m_compatMap[target].m_doCopy =
true;
3455 toolMgr.
SetEnvironment( m_board.get(),
nullptr,
nullptr,
nullptr, toolsHolder );
3472 std::vector<TMATCH::TOPOLOGY_MISMATCH_REASON> details;
3473 bool status = cgRef->FindIsomorphism( cgTarget.get(),
result, details );
3475 BOOST_CHECK( status );
3476 BOOST_CHECK( details.empty() );
3488 m_board = std::make_unique<BOARD>();
3489 BOARD* board = m_board.get();
3493 auto addFootprint = [&](
const wxString& aRef,
const VECTOR2I& aPos )
3502 pad->SetNumber( wxT(
"1" ) );
3511 pad->SetNetCode( 1 );
3514 addFootprint( wxT(
"R1" ),
VECTOR2I( 0, 0 ) );
3523 board->
Add( track );
3528 toolMgr.
SetEnvironment( board,
nullptr,
nullptr,
nullptr, toolsHolder );
3535 auto ruleData = mtTool->
GetData();
3545 ZONE* raZone =
nullptr;
3549 if( zone->GetIsRuleArea() && zone->GetZoneName() == wxT(
"auto-placement-area-/ChannelA/" ) )
3562 "Sheet rule area outline must enclose loose inter-block routing (issue 24983)" );
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...
void SetLocked(bool aLocked) override
virtual void SetLayer(PCB_LAYER_ID aLayer)
Set the layer this item is on.
Information pertinent to a Pcbnew printed circuit board.
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT, bool aSkipConnectivity=false) override
Removes an item from the container.
const GENERATORS & Generators() const
const ZONES & Zones() const
static constexpr BOX2< VECTOR2I > ByCorners(const VECTOR2I &aCorner1, const VECTOR2I &aCorner2)
constexpr const Vec GetEnd() const
constexpr const Vec & GetOrigin() const
Store all of the related component information found in a netlist.
A set of EDA_ITEMs (i.e., without duplicates).
void AddItem(EDA_ITEM *aItem)
Add item to group.
virtual EDA_ITEM * AsEdaItem()=0
void SetName(const wxString &aName)
A base class for most all the KiCad significant classes used in schematics and boards.
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
EDA_ANGLE GetTextAngle() const 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)
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
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.
bool GetIsRuleArea() const
Accessors to parameters used in Rule Area zones:
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
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
@ SMD
Smd pad, appears on the solder paste layer (default)
Class to handle a set of BOARD_ITEMs.
Utility functions for working with shapes.
std::vector< FAB_LAYER_COLOR > dummy
MULTICHANNEL_TEST_FIXTURE()
SETTINGS_MANAGER m_settingsManager
std::unique_ptr< BOARD > m_board
bool m_includeLockedItems
std::unordered_map< RULE_AREA *, RULE_AREA_COMPAT_DATA > m_compatMap
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()
VECTOR3I expected(15, 30, 45)
int countCopperZonesInArea(BOARD *aBoard, ZONE *aRAZone)
RULE_AREA * findGroupRuleAreaByName(MULTICHANNEL_TOOL *aTool, const wxString &aGroupName)
RULE_AREA * findRuleAreaByPartialName(MULTICHANNEL_TOOL *aTool, const wxString &aName)
int countZonesByNamePrefixInRuleArea(BOARD *aBoard, const wxString &aBaseName, const RULE_AREA &aRuleArea)
RULE_AREA * findSheetRuleAreaByPath(MULTICHANNEL_TOOL *aTool, wxString aPath)
BOOST_FIXTURE_TEST_CASE(MultichannelToolRegressions, MULTICHANNEL_TEST_FIXTURE)
int countZonesByNameInRuleArea(BOARD *aBoard, const wxString &aZoneName, const RULE_AREA &aRuleArea)
int countTuningGensInArea(BOARD *aBoard, ZONE *aZone)
RULE_AREA * findRuleAreaByPlacementGroup(MULTICHANNEL_TOOL *aTool, const wxString &aGroupName)
BOOST_TEST_MESSAGE("Polyline has "<< chain.PointCount()<< " points")
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_GENERATOR_T
class PCB_GENERATOR, generator on a layer
@ 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