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 auto cgTarget =
206 CONNECTION_GRAPH::BuildFromFootprintSet( targetArea.m_components );
207
209
210 std::vector<TMATCH::TOPOLOGY_MISMATCH_REASON> details;
211 bool status = cgRef->FindIsomorphism( cgTarget.get(), result, details );
212
213 BOOST_TEST_MESSAGE( wxString::Format(
214 "topo match: '%s' [%d] -> '%s' [%d] result %d", refArea.m_ruleName.c_str().AsChar(),
215 static_cast<int>( refArea.m_components.size() ), targetArea.m_ruleName.c_str().AsChar(),
216 static_cast<int>( targetArea.m_components.size() ), status ? 1 : 0 ) );
217
218 for( const auto& iter : result )
219 {
220 BOOST_TEST_MESSAGE( wxString::Format( "%s : %s",
221 iter.second->GetReference(),
222 iter.first->GetReference() ) );
223 }
224
225 BOOST_CHECK( status );
226 BOOST_CHECK( details.empty() );
227 }
228 }
229 }
230
231 auto refArea = findRuleAreaByPartialName( mtTool, wxT( "io_drivers_fp/bank3/io78/" ) );
232
233 BOOST_ASSERT( refArea );
234
235 const std::vector<wxString> targetAreaNames( { wxT( "io_drivers_fp/bank2/io78/" ),
236 wxT( "io_drivers_fp/bank1/io78/" ),
237 wxT( "io_drivers_fp/bank0/io01/" ) } );
238
239 for( const wxString& targetRaName : targetAreaNames )
240 {
241 auto targetRA = findRuleAreaByPartialName( mtTool, targetRaName );
242
243 BOOST_ASSERT( targetRA != nullptr );
244
245 BOOST_TEST_MESSAGE( wxString::Format( "Clone to: %s", targetRA->m_ruleName ) );
246
247 ruleData->m_compatMap[targetRA].m_doCopy = true;
248 }
249
250 int result = mtTool->RepeatLayout( TOOL_EVENT(), refArea->m_zone );
251
252 BOOST_ASSERT( result >= 0 );
253 }
254}
255
256
261BOOST_FIXTURE_TEST_CASE( RepeatLayoutCopiesFootprintProperties, MULTICHANNEL_TEST_FIXTURE )
262{
263 KI_TEST::LoadBoard( m_settingsManager, "issue22548/issue22548", m_board );
264
265 TOOL_MANAGER toolMgr;
266 MOCK_TOOLS_HOLDER* toolsHolder = new MOCK_TOOLS_HOLDER;
267
268 toolMgr.SetEnvironment( m_board.get(), nullptr, nullptr, nullptr, toolsHolder );
269
271 toolMgr.RegisterTool( mtTool );
272
273 mtTool->FindExistingRuleAreas();
274
275 auto ruleData = mtTool->GetData();
276
277 BOOST_TEST_MESSAGE( wxString::Format( "Found %d rule areas",
278 static_cast<int>( ruleData->m_areas.size() ) ) );
279
280 BOOST_CHECK( ruleData->m_areas.size() >= 2 );
281
282 if( ruleData->m_areas.size() < 2 )
283 return;
284
285 RULE_AREA* refArea = nullptr;
286 RULE_AREA* targetArea = nullptr;
287
288 for( RULE_AREA& ra : ruleData->m_areas )
289 {
290 if( ra.m_ruleName.Contains( wxT( "Untitled Sheet/" ) ) )
291 refArea = &ra;
292 else if( ra.m_ruleName.Contains( wxT( "Untitled Sheet1/" ) ) )
293 targetArea = &ra;
294 }
295
296 if( !refArea || !targetArea )
297 {
298 BOOST_TEST_MESSAGE( "Could not find Untitled Sheet and Untitled Sheet1 rule areas, skipping test" );
299 return;
300 }
301
302 BOOST_TEST_MESSAGE( wxString::Format( "Reference area: %s, Target area: %s",
303 refArea->m_ruleName, targetArea->m_ruleName ) );
304
305 FOOTPRINT* refFP = nullptr;
306 FOOTPRINT* targetFP = nullptr;
307
308 for( FOOTPRINT* fp : refArea->m_components )
309 {
310 if( fp->GetReference().StartsWith( wxT( "U1" ) ) )
311 {
312 refFP = fp;
313 break;
314 }
315 }
316
317 for( FOOTPRINT* fp : targetArea->m_components )
318 {
319 if( fp->GetReference().StartsWith( wxT( "U2" ) ) )
320 {
321 targetFP = fp;
322 break;
323 }
324 }
325
326 if( !refFP || !targetFP )
327 {
328 BOOST_TEST_MESSAGE( "Could not find matching footprints in the rule areas, skipping test" );
329 return;
330 }
331
332 PCB_FIELD* refValueField = refFP->GetField( FIELD_T::VALUE );
333 bool refValueVisible = refValueField ? refValueField->IsVisible() : true;
334
335 std::vector<FP_3DMODEL> refModels = refFP->Models();
336
337 mtTool->CheckRACompatibility( refArea->m_zone );
338
339 ruleData->m_compatMap[targetArea].m_doCopy = true;
340 ruleData->m_options.m_copyPlacement = true;
341
342 int result = mtTool->RepeatLayout( TOOL_EVENT(), refArea->m_zone );
343
344 BOOST_CHECK( result >= 0 );
345
346 PCB_FIELD* targetValueField = targetFP->GetField( FIELD_T::VALUE );
347
348 if( targetValueField && refValueField )
349 {
350 BOOST_CHECK_EQUAL( targetValueField->IsVisible(), refValueVisible );
351 BOOST_TEST_MESSAGE( wxString::Format( "Value field visibility: ref=%d, target=%d",
352 refValueVisible, targetValueField->IsVisible() ) );
353 }
354
355 BOOST_CHECK_EQUAL( targetFP->Models().size(), refModels.size() );
356
357 if( !refModels.empty() )
358 {
359 BOOST_TEST_MESSAGE( wxString::Format( "3D models: ref=%d, target=%d",
360 static_cast<int>( refModels.size() ),
361 static_cast<int>( targetFP->Models().size() ) ) );
362 }
363}
364
365
373BOOST_FIXTURE_TEST_CASE( RepeatLayoutDoesNotRemoveReferenceVias, MULTICHANNEL_TEST_FIXTURE )
374{
375 KI_TEST::LoadBoard( m_settingsManager, "issue21184/issue21184", m_board );
376
377 TOOL_MANAGER toolMgr;
378 MOCK_TOOLS_HOLDER* toolsHolder = new MOCK_TOOLS_HOLDER;
379
380 toolMgr.SetEnvironment( m_board.get(), nullptr, nullptr, nullptr, toolsHolder );
381
383 toolMgr.RegisterTool( mtTool );
384
385 mtTool->FindExistingRuleAreas();
386
387 auto ruleData = mtTool->GetData();
388
389 BOOST_TEST_MESSAGE( wxString::Format( "Found %d rule areas",
390 static_cast<int>( ruleData->m_areas.size() ) ) );
391
392 BOOST_CHECK_EQUAL( ruleData->m_areas.size(), 2 );
393
394 if( ruleData->m_areas.size() < 2 )
395 return;
396
397 RULE_AREA* refArea = nullptr;
398 RULE_AREA* targetArea = nullptr;
399
400 for( RULE_AREA& ra : ruleData->m_areas )
401 {
402 if( ra.m_ruleName == wxT( "Test1" ) )
403 refArea = &ra;
404 else if( ra.m_ruleName == wxT( "Test2" ) )
405 targetArea = &ra;
406 }
407
408 BOOST_REQUIRE( refArea != nullptr );
409 BOOST_REQUIRE( targetArea != nullptr );
410
411 int refViaCountBefore = 0;
412
413 for( PCB_TRACK* track : m_board->Tracks() )
414 {
415 if( track->Type() == PCB_VIA_T )
416 {
417 PCB_VIA* via = static_cast<PCB_VIA*>( track );
418 VECTOR2I viaPos = via->GetPosition();
419
420 if( refArea->m_zone->Outline()->Contains( viaPos ) )
421 refViaCountBefore++;
422 }
423 }
424
425 BOOST_TEST_MESSAGE( wxString::Format( "Reference area vias before repeat: %d", refViaCountBefore ) );
426 BOOST_CHECK( refViaCountBefore > 0 );
427
428 mtTool->CheckRACompatibility( refArea->m_zone );
429
430 ruleData->m_compatMap[targetArea].m_doCopy = true;
431 ruleData->m_options.m_copyPlacement = true;
432 ruleData->m_options.m_copyRouting = true;
433
434 int result = mtTool->RepeatLayout( TOOL_EVENT(), refArea->m_zone );
435
436 BOOST_CHECK( result >= 0 );
437
438 int refViaCountAfter = 0;
439
440 for( PCB_TRACK* track : m_board->Tracks() )
441 {
442 if( track->Type() == PCB_VIA_T )
443 {
444 PCB_VIA* via = static_cast<PCB_VIA*>( track );
445 VECTOR2I viaPos = via->GetPosition();
446
447 if( refArea->m_zone->Outline()->Contains( viaPos ) )
448 refViaCountAfter++;
449 }
450 }
451
452 BOOST_TEST_MESSAGE( wxString::Format( "Reference area vias after repeat: %d", refViaCountAfter ) );
453
454 BOOST_CHECK_EQUAL( refViaCountAfter, refViaCountBefore );
455}
456
457
462BOOST_FIXTURE_TEST_CASE( RepeatLayoutRespectsZoneLayerSetsForOtherItems, MULTICHANNEL_TEST_FIXTURE )
463{
464 KI_TEST::LoadBoard( m_settingsManager, "issue22983/issue22983", m_board );
465
466 TOOL_MANAGER toolMgr;
467 MOCK_TOOLS_HOLDER* toolsHolder = new MOCK_TOOLS_HOLDER;
468
469 toolMgr.SetEnvironment( m_board.get(), nullptr, nullptr, nullptr, toolsHolder );
470
472 toolMgr.RegisterTool( mtTool );
473
474 mtTool->FindExistingRuleAreas();
475
476 RULE_AREA* sourceA = findRuleAreaByPlacementGroup( mtTool, wxT( "SourceA" ) );
477 RULE_AREA* destA = findRuleAreaByPlacementGroup( mtTool, wxT( "DestA" ) );
478 RULE_AREA* sourceB = findRuleAreaByPlacementGroup( mtTool, wxT( "SourceB" ) );
479 RULE_AREA* destB = findRuleAreaByPlacementGroup( mtTool, wxT( "DestB" ) );
480
481 BOOST_REQUIRE( sourceA != nullptr );
482 BOOST_REQUIRE( destA != nullptr );
483 BOOST_REQUIRE( sourceB != nullptr );
484 BOOST_REQUIRE( destB != nullptr );
485
486 BOOST_CHECK_EQUAL( countZonesByNameInRuleArea( m_board.get(), wxT( "MultilayerZoneFrontAndOne" ), *sourceA ), 1 );
487 BOOST_CHECK_EQUAL( countZonesByNameInRuleArea( m_board.get(), wxT( "MultilayerZoneFrontAndOne" ), *destA ), 0 );
489 countZonesByNameInRuleArea( m_board.get(), wxT( "MultilayerZoneSourceBLayerMismatch" ), *sourceB ), 1 );
490 BOOST_CHECK_EQUAL( countZonesByNameInRuleArea( m_board.get(), wxT( "MultilayerZoneSourceBLayerMismatch" ), *destB ),
491 0 );
492 BOOST_CHECK_EQUAL( countZonesByNameInRuleArea( m_board.get(), wxT( "BottomZoneDontCopyMe" ), *sourceB ), 1 );
493 BOOST_CHECK_EQUAL( countZonesByNameInRuleArea( m_board.get(), wxT( "BottomZoneDontCopyMe" ), *destB ), 0 );
494
495 REPEAT_LAYOUT_OPTIONS options;
496 options.m_copyPlacement = false;
497 options.m_copyRouting = false;
498 options.m_copyOtherItems = true;
499 options.m_includeLockedItems = true;
500
501 int copyAStatus = mtTool->RepeatLayout( TOOL_EVENT(), *sourceA, *destA, options );
502 BOOST_REQUIRE( copyAStatus >= 0 );
503
504 int copyBStatus = mtTool->RepeatLayout( TOOL_EVENT(), *sourceB, *destB, options );
505 BOOST_REQUIRE( copyBStatus >= 0 );
506
507 // SourceA and DestA both include F.Cu+B.Cu, so this multilayer zone should copy.
508 BOOST_CHECK_EQUAL( countZonesByNameInRuleArea( m_board.get(), wxT( "MultilayerZoneFrontAndOne" ), *destA ), 1 );
509
510 // SourceB only includes F.Cu, so this F.Cu+B.Cu zone should not copy to DestB.
511 BOOST_CHECK_EQUAL( countZonesByNameInRuleArea( m_board.get(), wxT( "MultilayerZoneSourceBLayerMismatch" ), *destB ),
512 0 );
513
514 // SourceB excludes B.Cu, so this B.Cu-only zone should not copy either.
515 BOOST_CHECK_EQUAL( countZonesByNameInRuleArea( m_board.get(), wxT( "BottomZoneDontCopyMe" ), *destB ), 0 );
516}
517
518
527{
529 using TMATCH::COMPONENT;
530
531 // Create two connection graphs with components that have dotted reference designators
532 auto cgRef = std::make_unique<CONNECTION_GRAPH>();
533 auto cgTarget = std::make_unique<CONNECTION_GRAPH>();
534
535 // Create mock footprints with the same FPID
536 LIB_ID fpid( wxT( "Package_SO" ), wxT( "SOIC-8_3.9x4.9mm_P1.27mm" ) );
537
538 // Create reference footprint TRIM_1.1 and target footprint TRIM_2.1
539 FOOTPRINT fpRef( nullptr );
540 fpRef.SetFPID( fpid );
541 fpRef.SetReference( wxT( "TRIM_1.1" ) );
542
543 FOOTPRINT fpTarget( nullptr );
544 fpTarget.SetFPID( fpid );
545 fpTarget.SetReference( wxT( "TRIM_2.1" ) );
546
547 // Create matching pad structures
548 PAD padRef1( &fpRef );
549 padRef1.SetNumber( wxT( "1" ) );
550 padRef1.SetNetCode( 1 );
551 fpRef.Add( &padRef1 );
552
553 PAD padRef2( &fpRef );
554 padRef2.SetNumber( wxT( "2" ) );
555 padRef2.SetNetCode( 2 );
556 fpRef.Add( &padRef2 );
557
558 PAD padTarget1( &fpTarget );
559 padTarget1.SetNumber( wxT( "1" ) );
560 padTarget1.SetNetCode( 3 );
561 fpTarget.Add( &padTarget1 );
562
563 PAD padTarget2( &fpTarget );
564 padTarget2.SetNumber( wxT( "2" ) );
565 padTarget2.SetNetCode( 4 );
566 fpTarget.Add( &padTarget2 );
567
568 // Build connection graphs
569 cgRef->AddFootprint( &fpRef, VECTOR2I( 0, 0 ) );
570 cgTarget->AddFootprint( &fpTarget, VECTOR2I( 0, 0 ) );
571
572 cgRef->BuildConnectivity();
573 cgTarget->BuildConnectivity();
574
575 // Check that the components are considered the same kind
576 BOOST_CHECK_EQUAL( cgRef->Components().size(), 1 );
577 BOOST_CHECK_EQUAL( cgTarget->Components().size(), 1 );
578
579 COMPONENT* cmpRef = cgRef->Components()[0];
580 COMPONENT* cmpTarget = cgTarget->Components()[0];
581
582 bool sameKind = cmpRef->IsSameKind( *cmpTarget );
583
584 BOOST_TEST_MESSAGE( wxString::Format( "TRIM_1.1 and TRIM_2.1 IsSameKind: %d", sameKind ? 1 : 0 ) );
585 BOOST_CHECK( sameKind );
586
587 // Test topology matching
589 std::vector<TMATCH::TOPOLOGY_MISMATCH_REASON> details;
590 bool status = cgRef->FindIsomorphism( cgTarget.get(), result, details );
591
592 BOOST_TEST_MESSAGE( wxString::Format( "Topology match result: %d", status ? 1 : 0 ) );
593
594 if( !status && !details.empty() )
595 {
596 for( const auto& reason : details )
597 {
598 BOOST_TEST_MESSAGE( wxString::Format( "Mismatch: %s <-> %s: %s",
599 reason.m_reference, reason.m_candidate, reason.m_reason ) );
600 }
601 }
602
603 BOOST_CHECK( status );
604 BOOST_CHECK( details.empty() );
605
606 // Cleanup: remove pads before footprints go out of scope
607 fpRef.Pads().clear();
608 fpTarget.Pads().clear();
609}
610
611
620BOOST_FIXTURE_TEST_CASE( GenerateRuleAreasIncludesChildSheets, MULTICHANNEL_TEST_FIXTURE )
621{
622 KI_TEST::LoadBoard( m_settingsManager, "vme-wren", m_board );
623
624 TOOL_MANAGER toolMgr;
625 MOCK_TOOLS_HOLDER* toolsHolder = new MOCK_TOOLS_HOLDER;
626
627 toolMgr.SetEnvironment( m_board.get(), nullptr, nullptr, nullptr, toolsHolder );
628
630 toolMgr.RegisterTool( mtTool );
631
633
634 auto ruleData = mtTool->GetData();
635
636 RULE_AREA* leafArea = nullptr;
637 RULE_AREA* midArea = nullptr;
638 RULE_AREA* topArea = nullptr;
639
640 for( RULE_AREA& ra : ruleData->m_areas )
641 {
642 if( ra.m_sheetPath == wxT( "/io_drivers_fp/bank0/io01/" ) )
643 leafArea = &ra;
644 else if( ra.m_sheetPath == wxT( "/io_drivers_fp/bank0/" ) )
645 midArea = &ra;
646 else if( ra.m_sheetPath == wxT( "/io_drivers_fp/" ) )
647 topArea = &ra;
648 }
649
650 BOOST_REQUIRE( leafArea != nullptr );
651 BOOST_REQUIRE( midArea != nullptr );
652 BOOST_REQUIRE( topArea != nullptr );
653
654 BOOST_TEST_MESSAGE( wxString::Format( "Leaf /io_drivers_fp/bank0/io01/ components: %d",
655 static_cast<int>( leafArea->m_components.size() ) ) );
656 BOOST_TEST_MESSAGE( wxString::Format( "Mid /io_drivers_fp/bank0/ components: %d",
657 static_cast<int>( midArea->m_components.size() ) ) );
658 BOOST_TEST_MESSAGE( wxString::Format( "Top /io_drivers_fp/ components: %d",
659 static_cast<int>( topArea->m_components.size() ) ) );
660
661 // Leaf sheet has 31 direct components and no children
662 BOOST_CHECK_EQUAL( leafArea->m_components.size(), 31 );
663
664 // Mid-level sheet has 7 direct + 4 child sheets * 31 each = 131
665 BOOST_CHECK_EQUAL( midArea->m_components.size(), 131 );
666
667 // Top-level sheet has 3 direct + 4 banks * 131 each = 527
668 BOOST_CHECK_EQUAL( topArea->m_components.size(), 527 );
669
670 // Mid-level components must be a superset of leaf components
671 for( FOOTPRINT* fp : leafArea->m_components )
672 BOOST_CHECK( midArea->m_components.count( fp ) > 0 );
673
674 // Top-level components must be a superset of mid-level components
675 for( FOOTPRINT* fp : midArea->m_components )
676 BOOST_CHECK( topArea->m_components.count( fp ) > 0 );
677}
678
679
685{
687
688 KI_TEST::LoadBoard( m_settingsManager, "vme-wren", m_board );
689
690 TOOL_MANAGER toolMgr;
691 MOCK_TOOLS_HOLDER* toolsHolder = new MOCK_TOOLS_HOLDER;
692
693 toolMgr.SetEnvironment( m_board.get(), nullptr, nullptr, nullptr, toolsHolder );
694
696 toolMgr.RegisterTool( mtTool );
697
699
700 auto ruleData = mtTool->GetData();
701
702 ruleData->m_replaceExisting = true;
703
704 for( RULE_AREA& ra : ruleData->m_areas )
705 {
706 if( ra.m_sheetName == wxT( "io_driver.kicad_sch" ) )
707 ra.m_generateEnabled = true;
708 }
709
710 TOOL_EVENT dummyEvent;
711 mtTool->AutogenerateRuleAreas( dummyEvent );
712 mtTool->FindExistingRuleAreas();
713
714 RULE_AREA* refArea = findRuleAreaByPartialName( mtTool, wxT( "io_drivers_fp/bank3/io78/" ) );
715 RULE_AREA* targetArea = findRuleAreaByPartialName( mtTool, wxT( "io_drivers_fp/bank2/io78/" ) );
716
717 BOOST_REQUIRE( refArea != nullptr );
718 BOOST_REQUIRE( targetArea != nullptr );
719
720 auto cgRef = CONNECTION_GRAPH::BuildFromFootprintSet( refArea->m_components );
721 auto cgTarget = CONNECTION_GRAPH::BuildFromFootprintSet( targetArea->m_components );
722
723 // Pre-cancelled: should return false immediately with empty result
724 {
725 std::atomic<bool> cancelled( true );
726 std::atomic<int> matched( 0 );
727 std::atomic<int> total( 0 );
728
730 params.m_cancelled = &cancelled;
731 params.m_matchedComponents = &matched;
732 params.m_totalComponents = &total;
733
735 std::vector<TMATCH::TOPOLOGY_MISMATCH_REASON> details;
736
737 bool status = cgRef->FindIsomorphism( cgTarget.get(), result, details, params );
738
739 BOOST_CHECK( !status );
740 BOOST_CHECK( result.empty() );
741
742 BOOST_TEST_MESSAGE( "Pre-cancelled FindIsomorphism correctly returned false" );
743 }
744
745 // Normal run with progress reporting
746 {
747 std::atomic<bool> cancelled( false );
748 std::atomic<int> matched( 0 );
749 std::atomic<int> total( 0 );
750
752 params.m_cancelled = &cancelled;
753 params.m_matchedComponents = &matched;
754 params.m_totalComponents = &total;
755
757 std::vector<TMATCH::TOPOLOGY_MISMATCH_REASON> details;
758
759 bool status = cgRef->FindIsomorphism( cgTarget.get(), result, details, params );
760
761 BOOST_CHECK( status );
762
763 int finalMatched = matched.load();
764 int finalTotal = total.load();
765
766 BOOST_TEST_MESSAGE( wxString::Format( "Progress: matched=%d, total=%d", finalMatched, finalTotal ) );
767
768 BOOST_CHECK( finalTotal > 0 );
769 BOOST_CHECK_EQUAL( finalMatched, finalTotal );
770 }
771
772 // Sanity check: same graphs without params still succeed
773 {
775 std::vector<TMATCH::TOPOLOGY_MISMATCH_REASON> details;
776
777 bool status = cgRef->FindIsomorphism( cgTarget.get(), result, details );
778
779 BOOST_CHECK( status );
780 BOOST_CHECK( !result.empty() );
781
782 BOOST_TEST_MESSAGE( "Default params FindIsomorphism still succeeds" );
783 }
784}
785
786
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
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