KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_multichannel.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, you may find one here:
18 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
19 * or you may search the http://www.gnu.org website for the version 2 license,
20 * or you may write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22 */
23
26#include <board.h>
28#include <pad.h>
29#include <pcb_track.h>
30#include <pcb_text.h>
31#include <pcb_field.h>
32#include <footprint.h>
33#include <zone.h>
34#include <drc/drc_item.h>
38#include <lib_id.h>
39#include <atomic>
40
48
50{
51public:
54
55 virtual wxWindow* GetToolCanvas() const override { return nullptr; }
56};
57
58BOOST_FIXTURE_TEST_SUITE( MultichannelTool, MULTICHANNEL_TEST_FIXTURE )
59
60RULE_AREA* findRuleAreaByPartialName( MULTICHANNEL_TOOL* aTool, const wxString& aName )
61{
62 for( RULE_AREA& ra : aTool->GetData()->m_areas )
63 {
64 if( ra.m_ruleName.Contains( ( aName ) ) )
65 return &ra;
66 }
67
68 return nullptr;
69}
70
71RULE_AREA* findRuleAreaByPlacementGroup( MULTICHANNEL_TOOL* aTool, const wxString& aGroupName )
72{
73 for( RULE_AREA& ra : aTool->GetData()->m_areas )
74 {
75 if( ra.m_zone && ra.m_zone->GetPlacementAreaSource() == aGroupName )
76 return &ra;
77 }
78
79 return nullptr;
80}
81
82int countZonesByNameInRuleArea( BOARD* aBoard, const wxString& aZoneName, const RULE_AREA& aRuleArea )
83{
84 int count = 0;
85
86 for( const ZONE* zone : aBoard->Zones() )
87 {
88 if( zone == aRuleArea.m_zone )
89 continue;
90
91 if( zone->GetZoneName() != aZoneName )
92 continue;
93
94 if( aRuleArea.m_zone->Outline()->Contains( zone->Outline()->COutline( 0 ).Centre() ) )
95 count++;
96 }
97
98 return count;
99}
100
101
103{
105
106 std::vector<wxString> tests = { "vme-wren" };
107
108 for( const wxString& relPath : tests )
109 {
110 KI_TEST::LoadBoard( m_settingsManager, relPath, m_board );
111
112 TOOL_MANAGER toolMgr;
113 MOCK_TOOLS_HOLDER* toolsHolder = new MOCK_TOOLS_HOLDER;
114
115 toolMgr.SetEnvironment( m_board.get(), nullptr, nullptr, nullptr, toolsHolder );
116
117 MULTICHANNEL_TOOL* mtTool = new MULTICHANNEL_TOOL; // TOOL_MANAGER owns the tools
118 toolMgr.RegisterTool( mtTool );
119
120 //RULE_AREAS_DATA* raData = m_parentTool->GetData();
121
123
124 auto ruleData = mtTool->GetData();
125
126 BOOST_TEST_MESSAGE( wxString::Format( "RA multichannel sheets = %d",
127 static_cast<int>( ruleData->m_areas.size() ) ) );
128
129 BOOST_CHECK_EQUAL( ruleData->m_areas.size(), 72 );
130
131 int cnt = 0;
132
133 ruleData->m_replaceExisting = true;
134
135 for( RULE_AREA& ra : ruleData->m_areas )
136 {
137 if( ra.m_sheetName == wxT( "io_driver.kicad_sch" )
138 || ra.m_sheetName == wxT( "pp_driver_2x.kicad_sch" ) )
139 {
140 ra.m_generateEnabled = true;
141 cnt++;
142 }
143 }
144
145 BOOST_TEST_MESSAGE( wxString::Format( "Autogenerating %d RAs", cnt ) );
146
147 TOOL_EVENT dummyEvent;
148
149 mtTool->AutogenerateRuleAreas( dummyEvent );
150 mtTool->FindExistingRuleAreas();
151
152 int n_areas_io = 0, n_areas_pp = 0, n_areas_other = 0;
153
154 BOOST_TEST_MESSAGE( wxString::Format( "Found %d RAs after commit",
155 static_cast<int>(ruleData->m_areas.size() ) ) );
156
157 for( const RULE_AREA& ra : ruleData->m_areas )
158 {
159 BOOST_TEST_MESSAGE( wxString::Format( "SN '%s'", ra.m_ruleName ) );
160
161 if( ra.m_ruleName.Contains( wxT( "io_drivers_fp" ) ) )
162 {
163 n_areas_io++;
164 BOOST_CHECK_EQUAL( ra.m_components.size(), 31 );
165 }
166 else if( ra.m_ruleName.Contains( wxT( "io_drivers_pp" ) ) )
167 {
168 n_areas_pp++;
169 BOOST_CHECK_EQUAL( ra.m_components.size(), 11 );
170 }
171 else
172 {
173 n_areas_other++;
174 }
175 }
176
177 BOOST_TEST_MESSAGE( wxString::Format( "IO areas=%d, PP areas=%d, others=%d",
178 n_areas_io, n_areas_pp, n_areas_other ) );
179
180 BOOST_CHECK_EQUAL( n_areas_io, 16 );
181 BOOST_CHECK_EQUAL( n_areas_pp, 16 );
182 BOOST_CHECK_EQUAL( n_areas_other, 0 );
183
184 const std::vector<wxString> rulesToTest = { wxT( "io_drivers_fp" ),
185 wxT( "io_drivers_pp" ) };
186
187 for( const wxString& ruleName : rulesToTest )
188 {
189 for( const RULE_AREA& refArea : ruleData->m_areas )
190 {
191 if( !refArea.m_ruleName.Contains( ruleName ) )
192 continue;
193
194 BOOST_TEST_MESSAGE( wxString::Format( "REF AREA: '%s'", refArea.m_ruleName ) );
195
196 for( const RULE_AREA& targetArea : ruleData->m_areas )
197 {
198 if( targetArea.m_zone == refArea.m_zone )
199 continue;
200
201 if( !targetArea.m_ruleName.Contains( ruleName ) )
202 continue;
203
204 auto cgRef = CONNECTION_GRAPH::BuildFromFootprintSet( refArea.m_components,
205 targetArea.m_components );
206 auto cgTarget =
207 CONNECTION_GRAPH::BuildFromFootprintSet( targetArea.m_components,
208 refArea.m_components );
209
211
212 std::vector<TMATCH::TOPOLOGY_MISMATCH_REASON> details;
213 bool status = cgRef->FindIsomorphism( cgTarget.get(), result, details );
214
215 BOOST_TEST_MESSAGE( wxString::Format(
216 "topo match: '%s' [%d] -> '%s' [%d] result %d", refArea.m_ruleName.c_str().AsChar(),
217 static_cast<int>( refArea.m_components.size() ), targetArea.m_ruleName.c_str().AsChar(),
218 static_cast<int>( targetArea.m_components.size() ), status ? 1 : 0 ) );
219
220 for( const auto& iter : result )
221 {
222 BOOST_TEST_MESSAGE( wxString::Format( "%s : %s",
223 iter.second->GetReference(),
224 iter.first->GetReference() ) );
225 }
226
227 BOOST_CHECK( status );
228 BOOST_CHECK( details.empty() );
229 }
230 }
231 }
232
233 auto refArea = findRuleAreaByPartialName( mtTool, wxT( "io_drivers_fp/bank3/io78/" ) );
234
235 BOOST_ASSERT( refArea );
236
237 const std::vector<wxString> targetAreaNames( { wxT( "io_drivers_fp/bank2/io78/" ),
238 wxT( "io_drivers_fp/bank1/io78/" ),
239 wxT( "io_drivers_fp/bank0/io01/" ) } );
240
241 for( const wxString& targetRaName : targetAreaNames )
242 {
243 auto targetRA = findRuleAreaByPartialName( mtTool, targetRaName );
244
245 BOOST_ASSERT( targetRA != nullptr );
246
247 BOOST_TEST_MESSAGE( wxString::Format( "Clone to: %s", targetRA->m_ruleName ) );
248
249 ruleData->m_compatMap[targetRA].m_doCopy = true;
250 }
251
252 int result = mtTool->RepeatLayout( TOOL_EVENT(), refArea->m_zone );
253
254 BOOST_ASSERT( result >= 0 );
255 }
256}
257
258
263BOOST_FIXTURE_TEST_CASE( RepeatLayoutCopiesFootprintProperties, MULTICHANNEL_TEST_FIXTURE )
264{
265 KI_TEST::LoadBoard( m_settingsManager, "issue22548/issue22548", m_board );
266
267 TOOL_MANAGER toolMgr;
268 MOCK_TOOLS_HOLDER* toolsHolder = new MOCK_TOOLS_HOLDER;
269
270 toolMgr.SetEnvironment( m_board.get(), nullptr, nullptr, nullptr, toolsHolder );
271
273 toolMgr.RegisterTool( mtTool );
274
275 mtTool->FindExistingRuleAreas();
276
277 auto ruleData = mtTool->GetData();
278
279 BOOST_TEST_MESSAGE( wxString::Format( "Found %d rule areas",
280 static_cast<int>( ruleData->m_areas.size() ) ) );
281
282 BOOST_CHECK( ruleData->m_areas.size() >= 2 );
283
284 if( ruleData->m_areas.size() < 2 )
285 return;
286
287 RULE_AREA* refArea = nullptr;
288 RULE_AREA* targetArea = nullptr;
289
290 for( RULE_AREA& ra : ruleData->m_areas )
291 {
292 if( ra.m_ruleName.Contains( wxT( "Untitled Sheet/" ) ) )
293 refArea = &ra;
294 else if( ra.m_ruleName.Contains( wxT( "Untitled Sheet1/" ) ) )
295 targetArea = &ra;
296 }
297
298 if( !refArea || !targetArea )
299 {
300 BOOST_TEST_MESSAGE( "Could not find Untitled Sheet and Untitled Sheet1 rule areas, skipping test" );
301 return;
302 }
303
304 BOOST_TEST_MESSAGE( wxString::Format( "Reference area: %s, Target area: %s",
305 refArea->m_ruleName, targetArea->m_ruleName ) );
306
307 FOOTPRINT* refFP = nullptr;
308 FOOTPRINT* targetFP = nullptr;
309
310 for( FOOTPRINT* fp : refArea->m_components )
311 {
312 if( fp->GetReference().StartsWith( wxT( "U1" ) ) )
313 {
314 refFP = fp;
315 break;
316 }
317 }
318
319 for( FOOTPRINT* fp : targetArea->m_components )
320 {
321 if( fp->GetReference().StartsWith( wxT( "U2" ) ) )
322 {
323 targetFP = fp;
324 break;
325 }
326 }
327
328 if( !refFP || !targetFP )
329 {
330 BOOST_TEST_MESSAGE( "Could not find matching footprints in the rule areas, skipping test" );
331 return;
332 }
333
334 PCB_FIELD* refValueField = refFP->GetField( FIELD_T::VALUE );
335 bool refValueVisible = refValueField ? refValueField->IsVisible() : true;
336
337 std::vector<FP_3DMODEL> refModels = refFP->Models();
338
339 mtTool->CheckRACompatibility( refArea->m_zone );
340
341 ruleData->m_compatMap[targetArea].m_doCopy = true;
342 ruleData->m_options.m_copyPlacement = true;
343
344 int result = mtTool->RepeatLayout( TOOL_EVENT(), refArea->m_zone );
345
346 BOOST_CHECK( result >= 0 );
347
348 PCB_FIELD* targetValueField = targetFP->GetField( FIELD_T::VALUE );
349
350 if( targetValueField && refValueField )
351 {
352 BOOST_CHECK_EQUAL( targetValueField->IsVisible(), refValueVisible );
353 BOOST_TEST_MESSAGE( wxString::Format( "Value field visibility: ref=%d, target=%d",
354 refValueVisible, targetValueField->IsVisible() ) );
355 }
356
357 BOOST_CHECK_EQUAL( targetFP->Models().size(), refModels.size() );
358
359 if( !refModels.empty() )
360 {
361 BOOST_TEST_MESSAGE( wxString::Format( "3D models: ref=%d, target=%d",
362 static_cast<int>( refModels.size() ),
363 static_cast<int>( targetFP->Models().size() ) ) );
364 }
365}
366
367
375BOOST_FIXTURE_TEST_CASE( RepeatLayoutDoesNotRemoveReferenceVias, MULTICHANNEL_TEST_FIXTURE )
376{
377 KI_TEST::LoadBoard( m_settingsManager, "issue21184/issue21184", m_board );
378
379 TOOL_MANAGER toolMgr;
380 MOCK_TOOLS_HOLDER* toolsHolder = new MOCK_TOOLS_HOLDER;
381
382 toolMgr.SetEnvironment( m_board.get(), nullptr, nullptr, nullptr, toolsHolder );
383
385 toolMgr.RegisterTool( mtTool );
386
387 mtTool->FindExistingRuleAreas();
388
389 auto ruleData = mtTool->GetData();
390
391 BOOST_TEST_MESSAGE( wxString::Format( "Found %d rule areas",
392 static_cast<int>( ruleData->m_areas.size() ) ) );
393
394 BOOST_CHECK_EQUAL( ruleData->m_areas.size(), 2 );
395
396 if( ruleData->m_areas.size() < 2 )
397 return;
398
399 RULE_AREA* refArea = nullptr;
400 RULE_AREA* targetArea = nullptr;
401
402 for( RULE_AREA& ra : ruleData->m_areas )
403 {
404 if( ra.m_ruleName == wxT( "Test1" ) )
405 refArea = &ra;
406 else if( ra.m_ruleName == wxT( "Test2" ) )
407 targetArea = &ra;
408 }
409
410 BOOST_REQUIRE( refArea != nullptr );
411 BOOST_REQUIRE( targetArea != nullptr );
412
413 int refViaCountBefore = 0;
414
415 for( PCB_TRACK* track : m_board->Tracks() )
416 {
417 if( track->Type() == PCB_VIA_T )
418 {
419 PCB_VIA* via = static_cast<PCB_VIA*>( track );
420 VECTOR2I viaPos = via->GetPosition();
421
422 if( refArea->m_zone->Outline()->Contains( viaPos ) )
423 refViaCountBefore++;
424 }
425 }
426
427 BOOST_TEST_MESSAGE( wxString::Format( "Reference area vias before repeat: %d", refViaCountBefore ) );
428 BOOST_CHECK( refViaCountBefore > 0 );
429
430 mtTool->CheckRACompatibility( refArea->m_zone );
431
432 ruleData->m_compatMap[targetArea].m_doCopy = true;
433 ruleData->m_options.m_copyPlacement = true;
434 ruleData->m_options.m_copyRouting = true;
435
436 int result = mtTool->RepeatLayout( TOOL_EVENT(), refArea->m_zone );
437
438 BOOST_CHECK( result >= 0 );
439
440 int refViaCountAfter = 0;
441
442 for( PCB_TRACK* track : m_board->Tracks() )
443 {
444 if( track->Type() == PCB_VIA_T )
445 {
446 PCB_VIA* via = static_cast<PCB_VIA*>( track );
447 VECTOR2I viaPos = via->GetPosition();
448
449 if( refArea->m_zone->Outline()->Contains( viaPos ) )
450 refViaCountAfter++;
451 }
452 }
453
454 BOOST_TEST_MESSAGE( wxString::Format( "Reference area vias after repeat: %d", refViaCountAfter ) );
455
456 BOOST_CHECK_EQUAL( refViaCountAfter, refViaCountBefore );
457}
458
459
464BOOST_FIXTURE_TEST_CASE( RepeatLayoutRespectsZoneLayerSetsForOtherItems, MULTICHANNEL_TEST_FIXTURE )
465{
466 KI_TEST::LoadBoard( m_settingsManager, "issue22983/issue22983", m_board );
467
468 TOOL_MANAGER toolMgr;
469 MOCK_TOOLS_HOLDER* toolsHolder = new MOCK_TOOLS_HOLDER;
470
471 toolMgr.SetEnvironment( m_board.get(), nullptr, nullptr, nullptr, toolsHolder );
472
474 toolMgr.RegisterTool( mtTool );
475
476 mtTool->FindExistingRuleAreas();
477
478 RULE_AREA* sourceA = findRuleAreaByPlacementGroup( mtTool, wxT( "SourceA" ) );
479 RULE_AREA* destA = findRuleAreaByPlacementGroup( mtTool, wxT( "DestA" ) );
480 RULE_AREA* sourceB = findRuleAreaByPlacementGroup( mtTool, wxT( "SourceB" ) );
481 RULE_AREA* destB = findRuleAreaByPlacementGroup( mtTool, wxT( "DestB" ) );
482
483 BOOST_REQUIRE( sourceA != nullptr );
484 BOOST_REQUIRE( destA != nullptr );
485 BOOST_REQUIRE( sourceB != nullptr );
486 BOOST_REQUIRE( destB != nullptr );
487
488 BOOST_CHECK_EQUAL( countZonesByNameInRuleArea( m_board.get(), wxT( "MultilayerZoneFrontAndOne" ), *sourceA ), 1 );
489 BOOST_CHECK_EQUAL( countZonesByNameInRuleArea( m_board.get(), wxT( "MultilayerZoneFrontAndOne" ), *destA ), 0 );
491 countZonesByNameInRuleArea( m_board.get(), wxT( "MultilayerZoneSourceBLayerMismatch" ), *sourceB ), 1 );
492 BOOST_CHECK_EQUAL( countZonesByNameInRuleArea( m_board.get(), wxT( "MultilayerZoneSourceBLayerMismatch" ), *destB ),
493 0 );
494 BOOST_CHECK_EQUAL( countZonesByNameInRuleArea( m_board.get(), wxT( "BottomZoneDontCopyMe" ), *sourceB ), 1 );
495 BOOST_CHECK_EQUAL( countZonesByNameInRuleArea( m_board.get(), wxT( "BottomZoneDontCopyMe" ), *destB ), 0 );
496
497 REPEAT_LAYOUT_OPTIONS options;
498 options.m_copyPlacement = false;
499 options.m_copyRouting = false;
500 options.m_copyOtherItems = true;
501 options.m_includeLockedItems = true;
502
503 int copyAStatus = mtTool->RepeatLayout( TOOL_EVENT(), *sourceA, *destA, options );
504 BOOST_REQUIRE( copyAStatus >= 0 );
505
506 int copyBStatus = mtTool->RepeatLayout( TOOL_EVENT(), *sourceB, *destB, options );
507 BOOST_REQUIRE( copyBStatus >= 0 );
508
509 // SourceA and DestA both include F.Cu+B.Cu, so this multilayer zone should copy.
510 BOOST_CHECK_EQUAL( countZonesByNameInRuleArea( m_board.get(), wxT( "MultilayerZoneFrontAndOne" ), *destA ), 1 );
511
512 // SourceB only includes F.Cu, so this F.Cu+B.Cu zone should not copy to DestB.
513 BOOST_CHECK_EQUAL( countZonesByNameInRuleArea( m_board.get(), wxT( "MultilayerZoneSourceBLayerMismatch" ), *destB ),
514 0 );
515
516 // SourceB excludes B.Cu, so this B.Cu-only zone should not copy either.
517 BOOST_CHECK_EQUAL( countZonesByNameInRuleArea( m_board.get(), wxT( "BottomZoneDontCopyMe" ), *destB ), 0 );
518}
519
520
529{
531 using TMATCH::COMPONENT;
532
533 // Create two connection graphs with components that have dotted reference designators
534 auto cgRef = std::make_unique<CONNECTION_GRAPH>();
535 auto cgTarget = std::make_unique<CONNECTION_GRAPH>();
536
537 // Create mock footprints with the same FPID
538 LIB_ID fpid( wxT( "Package_SO" ), wxT( "SOIC-8_3.9x4.9mm_P1.27mm" ) );
539
540 // Create reference footprint TRIM_1.1 and target footprint TRIM_2.1
541 FOOTPRINT fpRef( nullptr );
542 fpRef.SetFPID( fpid );
543 fpRef.SetReference( wxT( "TRIM_1.1" ) );
544
545 FOOTPRINT fpTarget( nullptr );
546 fpTarget.SetFPID( fpid );
547 fpTarget.SetReference( wxT( "TRIM_2.1" ) );
548
549 // Create matching pad structures
550 PAD padRef1( &fpRef );
551 padRef1.SetNumber( wxT( "1" ) );
552 padRef1.SetNetCode( 1 );
553 fpRef.Add( &padRef1 );
554
555 PAD padRef2( &fpRef );
556 padRef2.SetNumber( wxT( "2" ) );
557 padRef2.SetNetCode( 2 );
558 fpRef.Add( &padRef2 );
559
560 PAD padTarget1( &fpTarget );
561 padTarget1.SetNumber( wxT( "1" ) );
562 padTarget1.SetNetCode( 3 );
563 fpTarget.Add( &padTarget1 );
564
565 PAD padTarget2( &fpTarget );
566 padTarget2.SetNumber( wxT( "2" ) );
567 padTarget2.SetNetCode( 4 );
568 fpTarget.Add( &padTarget2 );
569
570 // Build connection graphs
571 cgRef->AddFootprint( &fpRef, VECTOR2I( 0, 0 ) );
572 cgTarget->AddFootprint( &fpTarget, VECTOR2I( 0, 0 ) );
573
574 cgRef->BuildConnectivity();
575 cgTarget->BuildConnectivity();
576
577 // Check that the components are considered the same kind
578 BOOST_CHECK_EQUAL( cgRef->Components().size(), 1 );
579 BOOST_CHECK_EQUAL( cgTarget->Components().size(), 1 );
580
581 COMPONENT* cmpRef = cgRef->Components()[0];
582 COMPONENT* cmpTarget = cgTarget->Components()[0];
583
584 bool sameKind = cmpRef->IsSameKind( *cmpTarget );
585
586 BOOST_TEST_MESSAGE( wxString::Format( "TRIM_1.1 and TRIM_2.1 IsSameKind: %d", sameKind ? 1 : 0 ) );
587 BOOST_CHECK( sameKind );
588
589 // Test topology matching
591 std::vector<TMATCH::TOPOLOGY_MISMATCH_REASON> details;
592 bool status = cgRef->FindIsomorphism( cgTarget.get(), result, details );
593
594 BOOST_TEST_MESSAGE( wxString::Format( "Topology match result: %d", status ? 1 : 0 ) );
595
596 if( !status && !details.empty() )
597 {
598 for( const auto& reason : details )
599 {
600 BOOST_TEST_MESSAGE( wxString::Format( "Mismatch: %s <-> %s: %s",
601 reason.m_reference, reason.m_candidate, reason.m_reason ) );
602 }
603 }
604
605 BOOST_CHECK( status );
606 BOOST_CHECK( details.empty() );
607
608 // Cleanup: remove pads before footprints go out of scope
609 fpRef.Pads().clear();
610 fpTarget.Pads().clear();
611}
612
613
622BOOST_FIXTURE_TEST_CASE( GenerateRuleAreasIncludesChildSheets, MULTICHANNEL_TEST_FIXTURE )
623{
624 KI_TEST::LoadBoard( m_settingsManager, "vme-wren", m_board );
625
626 TOOL_MANAGER toolMgr;
627 MOCK_TOOLS_HOLDER* toolsHolder = new MOCK_TOOLS_HOLDER;
628
629 toolMgr.SetEnvironment( m_board.get(), nullptr, nullptr, nullptr, toolsHolder );
630
632 toolMgr.RegisterTool( mtTool );
633
635
636 auto ruleData = mtTool->GetData();
637
638 RULE_AREA* leafArea = nullptr;
639 RULE_AREA* midArea = nullptr;
640 RULE_AREA* topArea = nullptr;
641
642 for( RULE_AREA& ra : ruleData->m_areas )
643 {
644 if( ra.m_sheetPath == wxT( "/io_drivers_fp/bank0/io01/" ) )
645 leafArea = &ra;
646 else if( ra.m_sheetPath == wxT( "/io_drivers_fp/bank0/" ) )
647 midArea = &ra;
648 else if( ra.m_sheetPath == wxT( "/io_drivers_fp/" ) )
649 topArea = &ra;
650 }
651
652 BOOST_REQUIRE( leafArea != nullptr );
653 BOOST_REQUIRE( midArea != nullptr );
654 BOOST_REQUIRE( topArea != nullptr );
655
656 BOOST_TEST_MESSAGE( wxString::Format( "Leaf /io_drivers_fp/bank0/io01/ components: %d",
657 static_cast<int>( leafArea->m_components.size() ) ) );
658 BOOST_TEST_MESSAGE( wxString::Format( "Mid /io_drivers_fp/bank0/ components: %d",
659 static_cast<int>( midArea->m_components.size() ) ) );
660 BOOST_TEST_MESSAGE( wxString::Format( "Top /io_drivers_fp/ components: %d",
661 static_cast<int>( topArea->m_components.size() ) ) );
662
663 // Leaf sheet has 31 direct components and no children
664 BOOST_CHECK_EQUAL( leafArea->m_components.size(), 31 );
665
666 // Mid-level sheet has 7 direct + 4 child sheets * 31 each = 131
667 BOOST_CHECK_EQUAL( midArea->m_components.size(), 131 );
668
669 // Top-level sheet has 3 direct + 4 banks * 131 each = 527
670 BOOST_CHECK_EQUAL( topArea->m_components.size(), 527 );
671
672 // Mid-level components must be a superset of leaf components
673 for( FOOTPRINT* fp : leafArea->m_components )
674 BOOST_CHECK( midArea->m_components.count( fp ) > 0 );
675
676 // Top-level components must be a superset of mid-level components
677 for( FOOTPRINT* fp : midArea->m_components )
678 BOOST_CHECK( topArea->m_components.count( fp ) > 0 );
679}
680
681
687{
689
690 KI_TEST::LoadBoard( m_settingsManager, "vme-wren", m_board );
691
692 TOOL_MANAGER toolMgr;
693 MOCK_TOOLS_HOLDER* toolsHolder = new MOCK_TOOLS_HOLDER;
694
695 toolMgr.SetEnvironment( m_board.get(), nullptr, nullptr, nullptr, toolsHolder );
696
698 toolMgr.RegisterTool( mtTool );
699
701
702 auto ruleData = mtTool->GetData();
703
704 ruleData->m_replaceExisting = true;
705
706 for( RULE_AREA& ra : ruleData->m_areas )
707 {
708 if( ra.m_sheetName == wxT( "io_driver.kicad_sch" ) )
709 ra.m_generateEnabled = true;
710 }
711
712 TOOL_EVENT dummyEvent;
713 mtTool->AutogenerateRuleAreas( dummyEvent );
714 mtTool->FindExistingRuleAreas();
715
716 RULE_AREA* refArea = findRuleAreaByPartialName( mtTool, wxT( "io_drivers_fp/bank3/io78/" ) );
717 RULE_AREA* targetArea = findRuleAreaByPartialName( mtTool, wxT( "io_drivers_fp/bank2/io78/" ) );
718
719 BOOST_REQUIRE( refArea != nullptr );
720 BOOST_REQUIRE( targetArea != nullptr );
721
722 auto cgRef = CONNECTION_GRAPH::BuildFromFootprintSet( refArea->m_components,
723 targetArea->m_components );
724 auto cgTarget = CONNECTION_GRAPH::BuildFromFootprintSet( targetArea->m_components,
725 refArea->m_components );
726
727 // Pre-cancelled: should return false immediately with empty result
728 {
729 std::atomic<bool> cancelled( true );
730 std::atomic<int> matched( 0 );
731 std::atomic<int> total( 0 );
732
734 params.m_cancelled = &cancelled;
735 params.m_matchedComponents = &matched;
736 params.m_totalComponents = &total;
737
739 std::vector<TMATCH::TOPOLOGY_MISMATCH_REASON> details;
740
741 bool status = cgRef->FindIsomorphism( cgTarget.get(), result, details, params );
742
743 BOOST_CHECK( !status );
744 BOOST_CHECK( result.empty() );
745
746 BOOST_TEST_MESSAGE( "Pre-cancelled FindIsomorphism correctly returned false" );
747 }
748
749 // Normal run with progress reporting
750 {
751 std::atomic<bool> cancelled( false );
752 std::atomic<int> matched( 0 );
753 std::atomic<int> total( 0 );
754
756 params.m_cancelled = &cancelled;
757 params.m_matchedComponents = &matched;
758 params.m_totalComponents = &total;
759
761 std::vector<TMATCH::TOPOLOGY_MISMATCH_REASON> details;
762
763 bool status = cgRef->FindIsomorphism( cgTarget.get(), result, details, params );
764
765 BOOST_CHECK( status );
766
767 int finalMatched = matched.load();
768 int finalTotal = total.load();
769
770 BOOST_TEST_MESSAGE( wxString::Format( "Progress: matched=%d, total=%d", finalMatched, finalTotal ) );
771
772 BOOST_CHECK( finalTotal > 0 );
773 BOOST_CHECK_EQUAL( finalMatched, finalTotal );
774 }
775
776 // Sanity check: same graphs without params still succeed
777 {
779 std::vector<TMATCH::TOPOLOGY_MISMATCH_REASON> details;
780
781 bool status = cgRef->FindIsomorphism( cgTarget.get(), result, details );
782
783 BOOST_CHECK( status );
784 BOOST_CHECK( !result.empty() );
785
786 BOOST_TEST_MESSAGE( "Default params FindIsomorphism still succeeds" );
787 }
788}
789
790
801BOOST_FIXTURE_TEST_CASE( TopoMatchGlobalNetHierarchicalPins, MULTICHANNEL_TEST_FIXTURE )
802{
804
805 KI_TEST::LoadBoard( m_settingsManager, "issue21739/topology_mismatch", m_board );
806
807 TOOL_MANAGER toolMgr;
808 MOCK_TOOLS_HOLDER* toolsHolder = new MOCK_TOOLS_HOLDER;
809
810 toolMgr.SetEnvironment( m_board.get(), nullptr, nullptr, nullptr, toolsHolder );
811
813 toolMgr.RegisterTool( mtTool );
814
815 mtTool->FindExistingRuleAreas();
816
817 auto ruleData = mtTool->GetData();
818
819 BOOST_TEST_MESSAGE( wxString::Format( "Found %d rule areas",
820 static_cast<int>( ruleData->m_areas.size() ) ) );
821
822 BOOST_REQUIRE( ruleData->m_areas.size() >= 2 );
823
824 RULE_AREA* ch0Area = nullptr;
825 RULE_AREA* ch1Area = nullptr;
826
827 for( RULE_AREA& ra : ruleData->m_areas )
828 {
829 if( !ra.m_zone )
830 continue;
831
832 wxString source = ra.m_zone->GetPlacementAreaSource();
833
834 if( source == wxT( "/i2c_thingy_ch0/" ) )
835 ch0Area = &ra;
836 else if( source == wxT( "/i2c_thingy_ch1/" ) )
837 ch1Area = &ra;
838 }
839
840 BOOST_REQUIRE_MESSAGE( ch0Area != nullptr, "Could not find i2c_thingy_ch0 rule area" );
841 BOOST_REQUIRE_MESSAGE( ch1Area != nullptr, "Could not find i2c_thingy_ch1 rule area" );
842
843 BOOST_TEST_MESSAGE( wxString::Format( "ch0 components: %d, ch1 components: %d",
844 static_cast<int>( ch0Area->m_components.size() ),
845 static_cast<int>( ch1Area->m_components.size() ) ) );
846
847 BOOST_CHECK_EQUAL( ch0Area->m_components.size(), ch1Area->m_components.size() );
848
849 auto cgRef = CONNECTION_GRAPH::BuildFromFootprintSet( ch0Area->m_components,
850 ch1Area->m_components );
851 auto cgTarget = CONNECTION_GRAPH::BuildFromFootprintSet( ch1Area->m_components,
852 ch0Area->m_components );
853
855 std::vector<TMATCH::TOPOLOGY_MISMATCH_REASON> details;
856 bool status = cgRef->FindIsomorphism( cgTarget.get(), result, details );
857
858 if( !status )
859 {
860 for( const auto& reason : details )
861 {
862 BOOST_TEST_MESSAGE( wxString::Format( "Mismatch: %s <-> %s: %s",
863 reason.m_reference, reason.m_candidate,
864 reason.m_reason ) );
865 }
866 }
867
868 BOOST_CHECK_MESSAGE( status,
869 "Topology match failed for channels with hierarchical pins "
870 "tied to global nets (issue 21739)" );
871}
872
873
bool SetNetCode(int aNetCode, bool aNoAssert)
Set net using a net code.
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:322
const ZONES & Zones() const
Definition board.h:367
Store all of the related component information found in a netlist.
virtual bool IsVisible() const
Definition eda_text.h:187
void SetFPID(const LIB_ID &aFPID)
Definition footprint.h:352
PCB_FIELD * GetField(FIELD_T aFieldType)
Return a mandatory field in this footprint.
std::deque< PAD * > & Pads()
Definition footprint.h:306
void SetReference(const wxString &aReference)
Definition footprint.h:757
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT, bool aSkipConnectivity=false) override
Removes an item from the container.
std::vector< FP_3DMODEL > & Models()
Definition footprint.h:323
const wxString & GetReference() const
Definition footprint.h:751
A logical library item identifier and consists of various portions much like a URI.
Definition lib_id.h:49
virtual wxWindow * GetToolCanvas() const override
Canvas access.
int CheckRACompatibility(ZONE *aRefZone)
RULE_AREAS_DATA * GetData()
int RepeatLayout(const TOOL_EVENT &aEvent, ZONE *aRefZone)
int AutogenerateRuleAreas(const TOOL_EVENT &aEvent)
Definition pad.h:55
void SetNumber(const wxString &aNumber)
Set the pad number (note that it can be alphanumeric, such as the array reference "AA12").
Definition pad.h:136
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.
Generic, UI-independent tool event.
Definition tool_event.h:171
Master controller class:
void RegisterTool(TOOL_BASE *aTool)
Add a tool to the manager set and sets it up.
void SetEnvironment(EDA_ITEM *aModel, KIGFX::VIEW *aView, KIGFX::VIEW_CONTROLS *aViewControls, APP_SETTINGS_BASE *aSettings, TOOLS_HOLDER *aFrame)
Set the work environment (model, view, view controls and the parent window).
Handle a list of polygons defining a copper zone.
Definition zone.h:73
wxString GetPlacementAreaSource() const
Definition zone.h:724
SHAPE_POLY_SET * Outline()
Definition zone.h:340
void LoadBoard(SETTINGS_MANAGER &aSettingsManager, const wxString &aRelPath, std::unique_ptr< BOARD > &aBoard)
std::map< FOOTPRINT *, FOOTPRINT * > COMPONENT_MATCHES
Definition topo_match.h:184
std::unique_ptr< BOARD > m_board
std::vector< RULE_AREA > m_areas
wxString m_sheetName
std::set< FOOTPRINT * > m_components
wxString m_ruleName
wxString m_sheetPath
std::atomic< bool > * m_cancelled
Definition topo_match.h:49
std::atomic< int > * m_matchedComponents
Definition topo_match.h:50
std::atomic< int > * m_totalComponents
Definition topo_match.h:51
@ 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)
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_TEST_MESSAGE("Polyline has "<< chain.PointCount()<< " points")
wxString result
Test unit parsing edge cases and error handling.
BOOST_CHECK_EQUAL(result, "25.4")
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition typeinfo.h:97
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:695