KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_stacked_pin_conversion.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 (C) 2024 KiCad Developers
5 *
6 * This program is free software: you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation, either version 3 of the License, or (at your
9 * option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
21#include <sch_pin.h>
22#include <lib_symbol.h>
23#include <eeschema_test_utils.h>
24#include <units_provider.h>
25#include <base_units.h>
26
27extern void CheckDuplicatePins( LIB_SYMBOL* aSymbol, std::vector<wxString>& aMessages,
28 UNITS_PROVIDER* aUnitsProvider );
29
31{
33 {
34 m_settingsManager = std::make_unique<SETTINGS_MANAGER>( true );
35 m_symbol = std::make_unique<LIB_SYMBOL>( "TestSymbol" );
36 }
37
38 std::unique_ptr<SETTINGS_MANAGER> m_settingsManager;
39 std::unique_ptr<LIB_SYMBOL> m_symbol;
40};
41
42BOOST_FIXTURE_TEST_SUITE( StackedPinConversion, STACKED_PIN_CONVERSION_FIXTURE )
43
44
45
48BOOST_AUTO_TEST_CASE( TestStackedPinExpansion )
49{
50 // Test simple list notation
51 SCH_PIN* pin = new SCH_PIN( m_symbol.get() );
52 pin->SetNumber( wxT("[1,2,3]") );
53
54 bool isValid;
55 std::vector<wxString> expanded = pin->GetStackedPinNumbers( &isValid );
56
57 BOOST_CHECK( isValid );
58 BOOST_REQUIRE_EQUAL( expanded.size(), 3 );
59 BOOST_CHECK_EQUAL( expanded[0], "1" );
60 BOOST_CHECK_EQUAL( expanded[1], "2" );
61 BOOST_CHECK_EQUAL( expanded[2], "3" );
62
63 delete pin;
64
65 // Test range notation
66 pin = new SCH_PIN( m_symbol.get() );
67 pin->SetNumber( wxT("[5-7]") );
68
69 expanded = pin->GetStackedPinNumbers( &isValid );
70
71 BOOST_CHECK( isValid );
72 BOOST_REQUIRE_EQUAL( expanded.size(), 3 );
73 BOOST_CHECK_EQUAL( expanded[0], "5" );
74 BOOST_CHECK_EQUAL( expanded[1], "6" );
75 BOOST_CHECK_EQUAL( expanded[2], "7" );
76
77 delete pin;
78
79 // Test mixed notation
80 pin = new SCH_PIN( m_symbol.get() );
81 pin->SetNumber( wxT("[1,3,5-7]") );
82
83 expanded = pin->GetStackedPinNumbers( &isValid );
84
85 BOOST_CHECK( isValid );
86 BOOST_REQUIRE_EQUAL( expanded.size(), 5 );
87 BOOST_CHECK_EQUAL( expanded[0], "1" );
88 BOOST_CHECK_EQUAL( expanded[1], "3" );
89 BOOST_CHECK_EQUAL( expanded[2], "5" );
90 BOOST_CHECK_EQUAL( expanded[3], "6" );
91 BOOST_CHECK_EQUAL( expanded[4], "7" );
92
93 delete pin;
94}
95
96
100BOOST_AUTO_TEST_CASE( TestStackedPinValidity )
101{
102 SCH_PIN* pin = new SCH_PIN( m_symbol.get() );
103
104 // Test valid single pin (should not be considered stacked)
105 pin->SetNumber( wxT("1") );
106 bool isValid;
107 std::vector<wxString> expanded = pin->GetStackedPinNumbers( &isValid );
108 BOOST_CHECK( isValid );
109 BOOST_CHECK_EQUAL( expanded.size(), 1 );
110 BOOST_CHECK_EQUAL( expanded[0], "1" );
111
112 // Test invalid notation (malformed brackets)
113 pin->SetNumber( wxT("[1,2") );
114 expanded = pin->GetStackedPinNumbers( &isValid );
115 BOOST_CHECK( !isValid );
116
117 // Test invalid range
118 pin->SetNumber( wxT("[5-3]") ); // backwards range
119 expanded = pin->GetStackedPinNumbers( &isValid );
120 BOOST_CHECK( !isValid );
121
122 // Test empty brackets
123 pin->SetNumber( wxT("[]") );
124 expanded = pin->GetStackedPinNumbers( &isValid );
125 BOOST_CHECK( !isValid );
126
127 delete pin;
128}
129
130
134BOOST_AUTO_TEST_CASE( TestPinCreation )
135{
136 // Create multiple pins at the same location
137 VECTOR2I position( 0, 0 );
138
139 SCH_PIN* pin1 = new SCH_PIN( m_symbol.get() );
140 pin1->SetNumber( wxT("1") );
141 pin1->SetPosition( position );
143
144 SCH_PIN* pin2 = new SCH_PIN( m_symbol.get() );
145 pin2->SetNumber( wxT("2") );
146 pin2->SetPosition( position );
148
149 SCH_PIN* pin3 = new SCH_PIN( m_symbol.get() );
150 pin3->SetNumber( wxT("3") );
151 pin3->SetPosition( position );
153
154 // Verify pins are at same location
155 BOOST_CHECK_EQUAL( pin1->GetPosition(), pin2->GetPosition() );
156 BOOST_CHECK_EQUAL( pin2->GetPosition(), pin3->GetPosition() );
157
158 // Test IsStacked functionality
159 BOOST_CHECK( pin1->IsStacked( pin2 ) );
160 BOOST_CHECK( pin2->IsStacked( pin3 ) );
161
162 delete pin1;
163 delete pin2;
164 delete pin3;
165}
166
167
171BOOST_AUTO_TEST_CASE( TestStackedPinNetNaming )
172{
173 SCH_PIN* pin = new SCH_PIN( m_symbol.get() );
174 pin->SetNumber( wxT("[8,9,10]") );
175
176 // This test would require a full SCH_SYMBOL context to test GetDefaultNetName
177 // For now just verify the pin number expansion works
178 bool isValid;
179 std::vector<wxString> expanded = pin->GetStackedPinNumbers( &isValid );
180
181 BOOST_CHECK( isValid );
182 BOOST_REQUIRE_EQUAL( expanded.size(), 3 );
183 // The smallest number should be first for deterministic net naming
184 BOOST_CHECK_EQUAL( expanded[0], "8" );
185
186 delete pin;
187}
188
189
193BOOST_AUTO_TEST_CASE( TestConvertMultiplePinsToStacked )
194{
195 // Create multiple pins at the same location
196 VECTOR2I position( 0, 0 );
197
198 SCH_PIN* pin1 = new SCH_PIN( m_symbol.get() );
199 pin1->SetNumber( wxT("1") );
200 pin1->SetPosition( position );
202 pin1->SetVisible( true );
203
204 SCH_PIN* pin2 = new SCH_PIN( m_symbol.get() );
205 pin2->SetNumber( wxT("2") );
206 pin2->SetPosition( position );
208 pin2->SetVisible( true );
209
210 SCH_PIN* pin3 = new SCH_PIN( m_symbol.get() );
211 pin3->SetNumber( wxT("3") );
212 pin3->SetPosition( position );
214 pin3->SetVisible( true );
215
216 // Test basic property access before adding to symbol
217 BOOST_CHECK_EQUAL( pin1->GetNumber(), "1" );
218 BOOST_CHECK_EQUAL( pin2->GetNumber(), "2" );
219 BOOST_CHECK_EQUAL( pin3->GetNumber(), "3" );
220
221 // Just test the basic conversion logic without symbol management
222 // Build the stacked notation string
223 wxString stackedNotation = wxT("[");
224 stackedNotation += pin1->GetNumber();
225 stackedNotation += wxT(",");
226 stackedNotation += pin2->GetNumber();
227 stackedNotation += wxT(",");
228 stackedNotation += pin3->GetNumber();
229 stackedNotation += wxT("]");
230
231 // Test stacked notation creation
232 BOOST_CHECK_EQUAL( stackedNotation, "[1,2,3]" );
233
234 // Set stacked notation on one pin
235 pin1->SetNumber( stackedNotation );
236 BOOST_CHECK_EQUAL( pin1->GetNumber(), "[1,2,3]" );
237
238 // Verify the stacked pin expansion works
239 bool isValid;
240 std::vector<wxString> expanded = pin1->GetStackedPinNumbers( &isValid );
241 BOOST_CHECK( isValid );
242 BOOST_REQUIRE_EQUAL( expanded.size(), 3 );
243 BOOST_CHECK_EQUAL( expanded[0], "1" );
244 BOOST_CHECK_EQUAL( expanded[1], "2" );
245 BOOST_CHECK_EQUAL( expanded[2], "3" );
246
247 // Clean up - delete pins manually since they're not in symbol
248 delete pin1;
249 delete pin2;
250 delete pin3;
251}
252
253
257BOOST_AUTO_TEST_CASE( TestRangeCollapsingConversion )
258{
259 // Test range collapsing logic directly without symbol management
260
261 // Test consecutive pins that should collapse to a range
262 std::vector<long> numbers = { 1, 2, 3, 4 };
263
264 // Build collapsed ranges
265 wxString result;
266 size_t i = 0;
267 while( i < numbers.size() )
268 {
269 if( !result.IsEmpty() )
270 result += wxT(",");
271
272 long start = numbers[i];
273 long end = start;
274
275 // Find the end of consecutive sequence
276 while( i + 1 < numbers.size() && numbers[i + 1] == numbers[i] + 1 )
277 {
278 i++;
279 end = numbers[i];
280 }
281
282 // Add range or single number
283 if( end > start + 1 ) // Range of 3+ numbers
284 result += wxString::Format( wxT("%ld-%ld"), start, end );
285 else if( end == start + 1 ) // Two consecutive numbers
286 result += wxString::Format( wxT("%ld,%ld"), start, end );
287 else // Single number
288 result += wxString::Format( wxT("%ld"), start );
289
290 i++;
291 }
292
293 // Verify range collapsing: 1,2,3,4 should become "1-4"
294 BOOST_CHECK_EQUAL( result, "1-4" );
295
296 // Test with mixed consecutive and non-consecutive: 1,2,3,4,7,8,9
297 numbers = { 1, 2, 3, 4, 7, 8, 9 };
298 result.Clear();
299 i = 0;
300
301 while( i < numbers.size() )
302 {
303 if( !result.IsEmpty() )
304 result += wxT(",");
305
306 long start = numbers[i];
307 long end = start;
308
309 // Find the end of consecutive sequence
310 while( i + 1 < numbers.size() && numbers[i + 1] == numbers[i] + 1 )
311 {
312 i++;
313 end = numbers[i];
314 }
315
316 // Add range or single number
317 if( end > start + 1 ) // Range of 3+ numbers
318 result += wxString::Format( wxT("%ld-%ld"), start, end );
319 else if( end == start + 1 ) // Two consecutive numbers
320 result += wxString::Format( wxT("%ld,%ld"), start, end );
321 else // Single number
322 result += wxString::Format( wxT("%ld"), start );
323
324 i++;
325 }
326
327 // Verify mixed ranges: 1,2,3,4,7,8,9 should become "1-4,7-9"
328 BOOST_CHECK_EQUAL( result, "1-4,7-9" );
329
330 // Test edge cases
331 numbers = { 1, 3, 5 }; // Non-consecutive
332 result.Clear();
333 i = 0;
334
335 while( i < numbers.size() )
336 {
337 if( !result.IsEmpty() )
338 result += wxT(",");
339
340 long start = numbers[i];
341 long end = start;
342
343 // Find the end of consecutive sequence
344 while( i + 1 < numbers.size() && numbers[i + 1] == numbers[i] + 1 )
345 {
346 i++;
347 end = numbers[i];
348 }
349
350 // Add range or single number
351 if( end > start + 1 ) // Range of 3+ numbers
352 result += wxString::Format( wxT("%ld-%ld"), start, end );
353 else if( end == start + 1 ) // Two consecutive numbers
354 result += wxString::Format( wxT("%ld,%ld"), start, end );
355 else // Single number
356 result += wxString::Format( wxT("%ld"), start );
357
358 i++;
359 }
360
361 // Verify non-consecutive: 1,3,5 should remain "1,3,5"
362 BOOST_CHECK_EQUAL( result, "1,3,5" );
363
364 // Test two consecutive numbers
365 numbers = { 5, 6 };
366 result.Clear();
367 i = 0;
368
369 while( i < numbers.size() )
370 {
371 if( !result.IsEmpty() )
372 result += wxT(",");
373
374 long start = numbers[i];
375 long end = start;
376
377 // Find the end of consecutive sequence
378 while( i + 1 < numbers.size() && numbers[i + 1] == numbers[i] + 1 )
379 {
380 i++;
381 end = numbers[i];
382 }
383
384 // Add range or single number
385 if( end > start + 1 ) // Range of 3+ numbers
386 result += wxString::Format( wxT("%ld-%ld"), start, end );
387 else if( end == start + 1 ) // Two consecutive numbers
388 result += wxString::Format( wxT("%ld,%ld"), start, end );
389 else // Single number
390 result += wxString::Format( wxT("%ld"), start );
391
392 i++;
393 }
394
395 // Verify two consecutive: 5,6 should remain "5,6" (not convert to range)
396 BOOST_CHECK_EQUAL( result, "5,6" );
397
398 // Test complex mixed case: 1,2,4,5,6,8,9,10,11
399 numbers = { 1, 2, 4, 5, 6, 8, 9, 10, 11 };
400 result.Clear();
401 i = 0;
402
403 while( i < numbers.size() )
404 {
405 if( !result.IsEmpty() )
406 result += wxT(",");
407
408 long start = numbers[i];
409 long end = start;
410
411 // Find the end of consecutive sequence
412 while( i + 1 < numbers.size() && numbers[i + 1] == numbers[i] + 1 )
413 {
414 i++;
415 end = numbers[i];
416 }
417
418 // Add range or single number
419 if( end > start + 1 ) // Range of 3+ numbers
420 result += wxString::Format( wxT("%ld-%ld"), start, end );
421 else if( end == start + 1 ) // Two consecutive numbers
422 result += wxString::Format( wxT("%ld,%ld"), start, end );
423 else // Single number
424 result += wxString::Format( wxT("%ld"), start );
425
426 i++;
427 }
428
429 // Verify complex case: 1,2,4,5,6,8,9,10,11 should become "1,2,4-6,8-11"
430 BOOST_CHECK_EQUAL( result, "1,2,4-6,8-11" );
431
432 // Test that our range notation can be expanded back correctly
433 SCH_PIN* rangePin = new SCH_PIN( m_symbol.get() );
434 rangePin->SetNumber( wxT("[1-4,7-9]") );
435
436 bool isValid;
437 std::vector<wxString> expanded = rangePin->GetStackedPinNumbers( &isValid );
438 BOOST_CHECK( isValid );
439 BOOST_REQUIRE_EQUAL( expanded.size(), 7 );
440
441 // Should expand to: 1,2,3,4,7,8,9
442 BOOST_CHECK_EQUAL( expanded[0], "1" );
443 BOOST_CHECK_EQUAL( expanded[1], "2" );
444 BOOST_CHECK_EQUAL( expanded[2], "3" );
445 BOOST_CHECK_EQUAL( expanded[3], "4" );
446 BOOST_CHECK_EQUAL( expanded[4], "7" );
447 BOOST_CHECK_EQUAL( expanded[5], "8" );
448 BOOST_CHECK_EQUAL( expanded[6], "9" );
449
450 delete rangePin;
451}
452
453
457BOOST_AUTO_TEST_CASE( TestRoundTripConversion )
458{
459 // Create multiple pins at the same location
460 VECTOR2I position( 100, 200 );
461
462 SCH_PIN* pin5 = new SCH_PIN( m_symbol.get() );
463 pin5->SetNumber( wxT("5") );
464 pin5->SetPosition( position );
467 pin5->SetName( wxT("TestPin") );
468 pin5->SetVisible( true );
469
470 SCH_PIN* pin7 = new SCH_PIN( m_symbol.get() );
471 pin7->SetNumber( wxT("7") );
472 pin7->SetPosition( position );
475 pin7->SetName( wxT("TestPin") );
476 pin7->SetVisible( true );
477
478 SCH_PIN* pin9 = new SCH_PIN( m_symbol.get() );
479 pin9->SetNumber( wxT("9") );
480 pin9->SetPosition( position );
483 pin9->SetName( wxT("TestPin") );
484 pin9->SetVisible( true );
485
486 // Store original properties for comparison
487 PIN_ORIENTATION originalOrientation = pin5->GetOrientation();
488 ELECTRICAL_PINTYPE originalType = pin5->GetType();
489 wxString originalName = pin5->GetName();
490 int originalLength = pin5->GetLength();
491
492 // Step 1: Convert to stacked notation (simulating ConvertStackedPins)
493 std::vector<SCH_PIN*> pinsToConvert = { pin5, pin7, pin9 };
494
495 // Sort pins numerically
496 std::sort( pinsToConvert.begin(), pinsToConvert.end(),
497 []( SCH_PIN* a, SCH_PIN* b )
498 {
499 long numA, numB;
500 if( a->GetNumber().ToLong( &numA ) && b->GetNumber().ToLong( &numB ) )
501 return numA < numB;
502 return a->GetNumber() < b->GetNumber();
503 });
504
505 // Build stacked notation
506 wxString stackedNotation = wxT("[5,7,9]");
507 pinsToConvert[0]->SetNumber( stackedNotation );
508
509 // Remove other pins (don't delete them yet for testing)
510 SCH_PIN* stackedPin = pinsToConvert[0];
511
512 // Step 2: Verify stacked notation
513 bool isValid;
514 std::vector<wxString> expanded = stackedPin->GetStackedPinNumbers( &isValid );
515 BOOST_CHECK( isValid );
516 BOOST_REQUIRE_EQUAL( expanded.size(), 3 );
517 BOOST_CHECK_EQUAL( expanded[0], "5" );
518 BOOST_CHECK_EQUAL( expanded[1], "7" );
519 BOOST_CHECK_EQUAL( expanded[2], "9" );
520
521 // Step 3: Convert back to individual pins (simulating ExplodeStackedPin)
522 // Sort the stacked numbers (should already be sorted in our case)
523 std::sort( expanded.begin(), expanded.end(),
524 []( const wxString& a, const wxString& b )
525 {
526 long numA, numB;
527 if( a.ToLong( &numA ) && b.ToLong( &numB ) )
528 return numA < numB;
529 return a < b;
530 });
531
532 // Change the original pin to use the first (smallest) number and make it visible
533 stackedPin->SetNumber( expanded[0] );
534 stackedPin->SetVisible( true );
535
536 // Create additional pins for the remaining numbers and make them invisible
537 std::vector<SCH_PIN*> explodedPins;
538 explodedPins.push_back( stackedPin );
539
540 for( size_t i = 1; i < expanded.size(); ++i )
541 {
542 SCH_PIN* newPin = new SCH_PIN( m_symbol.get() );
543
544 // Copy all properties from the original pin
545 newPin->SetPosition( stackedPin->GetPosition() );
546 newPin->SetOrientation( stackedPin->GetOrientation() );
547 newPin->SetShape( stackedPin->GetShape() );
548 newPin->SetLength( stackedPin->GetLength() );
549 newPin->SetType( stackedPin->GetType() );
550 newPin->SetName( stackedPin->GetName() );
551 newPin->SetNumber( expanded[i] );
552 newPin->SetNameTextSize( stackedPin->GetNameTextSize() );
553 newPin->SetNumberTextSize( stackedPin->GetNumberTextSize() );
554 newPin->SetUnit( stackedPin->GetUnit() );
555 newPin->SetBodyStyle( stackedPin->GetBodyStyle() );
556 newPin->SetVisible( false ); // Make all other pins invisible
557
558 explodedPins.push_back( newPin );
559 }
560
561 // Step 4: Verify the round-trip conversion
562 BOOST_REQUIRE_EQUAL( explodedPins.size(), 3 );
563
564 // Check pin numbers
565 BOOST_CHECK_EQUAL( explodedPins[0]->GetNumber(), "5" );
566 BOOST_CHECK_EQUAL( explodedPins[1]->GetNumber(), "7" );
567 BOOST_CHECK_EQUAL( explodedPins[2]->GetNumber(), "9" );
568
569 // Check visibility (only first pin should be visible)
570 BOOST_CHECK( explodedPins[0]->IsVisible() );
571 BOOST_CHECK( !explodedPins[1]->IsVisible() );
572 BOOST_CHECK( !explodedPins[2]->IsVisible() );
573
574 // Check that properties were preserved
575 for( SCH_PIN* pin : explodedPins )
576 {
577 BOOST_CHECK_EQUAL( pin->GetPosition(), position );
578 BOOST_CHECK( pin->GetOrientation() == originalOrientation );
579 BOOST_CHECK( pin->GetType() == originalType );
580 BOOST_CHECK_EQUAL( pin->GetName(), originalName );
581 BOOST_CHECK_EQUAL( pin->GetLength(), originalLength );
582 }
583
584 // Clean up
585 for( size_t i = 1; i < explodedPins.size(); ++i )
586 delete explodedPins[i];
587 // Note: explodedPins[0] is the original stackedPin, don't delete twice
588}
589
590
594BOOST_AUTO_TEST_CASE( TestVisibilityHandling )
595{
596 // Create a single pin to test visibility handling
597 SCH_PIN* pin = new SCH_PIN( m_symbol.get() );
598 pin->SetNumber( wxT("[8,10,12]") );
599 pin->SetVisible( false ); // Start invisible
600
601 // Test expansion of stacked notation
602 bool isValid;
603 std::vector<wxString> expanded = pin->GetStackedPinNumbers( &isValid );
604 BOOST_CHECK( isValid );
605 BOOST_REQUIRE_EQUAL( expanded.size(), 3 );
606
607 // Sort expanded numbers
608 std::sort( expanded.begin(), expanded.end(),
609 []( const wxString& a, const wxString& b )
610 {
611 long numA, numB;
612 if( a.ToLong( &numA ) && b.ToLong( &numB ) )
613 return numA < numB;
614 return a < b;
615 });
616
617 // Verify sorted order is correct
618 BOOST_CHECK_EQUAL( expanded[0], "8" );
619 BOOST_CHECK_EQUAL( expanded[1], "10" );
620 BOOST_CHECK_EQUAL( expanded[2], "12" );
621
622 // Set the smallest pin number and make it visible
623 pin->SetNumber( expanded[0] ); // "8"
624 pin->SetVisible( true ); // Make visible
625
626 // Verify the smallest pin is visible and has correct number
627 BOOST_CHECK_EQUAL( pin->GetNumber(), "8" );
628 BOOST_CHECK( pin->IsVisible() );
629
630 // Clean up
631 delete pin;
632}
633
634
638BOOST_AUTO_TEST_CASE( TestAlphanumericRangeCollapsing )
639{
640 // Test the new alphanumeric prefix parsing logic
641
642 // Helper function to test prefix parsing
643 auto testPrefixParsing = []( const wxString& pinNumber ) -> std::pair<wxString, long>
644 {
645 wxString prefix;
646 long numValue = -1;
647
648 // Find where numeric part starts (scan from end)
649 size_t numStart = pinNumber.length();
650 for( int i = pinNumber.length() - 1; i >= 0; i-- )
651 {
652 if( !wxIsdigit( pinNumber[i] ) )
653 {
654 numStart = i + 1;
655 break;
656 }
657 if( i == 0 ) // All digits
658 numStart = 0;
659 }
660
661 if( numStart < pinNumber.length() ) // Has numeric suffix
662 {
663 prefix = pinNumber.Left( numStart );
664 wxString numericPart = pinNumber.Mid( numStart );
665 numericPart.ToLong( &numValue );
666 }
667
668 return std::make_pair( prefix, numValue );
669 };
670
671 // Test basic prefix parsing
672 auto [prefix1, num1] = testPrefixParsing( wxT("A1") );
673 BOOST_CHECK_EQUAL( prefix1, "A" );
674 BOOST_CHECK_EQUAL( num1, 1 );
675
676 auto [prefix2, num2] = testPrefixParsing( wxT("AB12") );
677 BOOST_CHECK_EQUAL( prefix2, "AB" );
678 BOOST_CHECK_EQUAL( num2, 12 );
679
680 auto [prefix3, num3] = testPrefixParsing( wxT("123") );
681 BOOST_CHECK_EQUAL( prefix3, "" );
682 BOOST_CHECK_EQUAL( num3, 123 );
683
684 auto [prefix4, num4] = testPrefixParsing( wxT("XYZ") );
685 BOOST_CHECK_EQUAL( prefix4, "" ); // No numeric suffix
686 BOOST_CHECK_EQUAL( num4, -1 );
687
688 // Test grouping logic with example: AA1,AA2,AA3,AB4,CD12,CD13,CD14
689 std::map<wxString, std::vector<long>> prefixGroups;
690 std::vector<wxString> testPins = { wxT("AA1"), wxT("AA2"), wxT("AA3"), wxT("AB4"), wxT("CD12"), wxT("CD13"), wxT("CD14") };
691
692 for( const wxString& pinNumber : testPins )
693 {
694 auto [prefix, numValue] = testPrefixParsing( pinNumber );
695 if( numValue != -1 )
696 prefixGroups[prefix].push_back( numValue );
697 }
698
699 // Verify grouping
700 BOOST_CHECK_EQUAL( prefixGroups.size(), 3 );
701 BOOST_CHECK_EQUAL( prefixGroups[wxT("AA")].size(), 3 );
702 BOOST_CHECK_EQUAL( prefixGroups[wxT("AB")].size(), 1 );
703 BOOST_CHECK_EQUAL( prefixGroups[wxT("CD")].size(), 3 );
704
705 // Build expected result: AA1-AA3,AB4,CD12-CD14
706 wxString expectedResult;
707 for( auto& [prefix, numbers] : prefixGroups )
708 {
709 if( !expectedResult.IsEmpty() )
710 expectedResult += wxT(",");
711
712 std::sort( numbers.begin(), numbers.end() );
713
714 size_t i = 0;
715 while( i < numbers.size() )
716 {
717 if( i > 0 )
718 expectedResult += wxT(",");
719
720 long start = numbers[i];
721 long end = start;
722
723 // Find consecutive sequence
724 while( i + 1 < numbers.size() && numbers[i + 1] == numbers[i] + 1 )
725 {
726 i++;
727 end = numbers[i];
728 }
729
730 // Format with prefix
731 if( end > start + 1 ) // Range of 3+ numbers
732 expectedResult += wxString::Format( wxT("%s%ld-%s%ld"), prefix, start, prefix, end );
733 else if( end == start + 1 ) // Two consecutive numbers
734 expectedResult += wxString::Format( wxT("%s%ld,%s%ld"), prefix, start, prefix, end );
735 else // Single number
736 expectedResult += wxString::Format( wxT("%s%ld"), prefix, start );
737
738 i++;
739 }
740 }
741
742 // Should result in: AA1-AA3,AB4,CD12-CD14
743 BOOST_CHECK_EQUAL( expectedResult, "AA1-AA3,AB4,CD12-CD14" );
744}
745
746
747BOOST_AUTO_TEST_CASE( TestDuplicatePinDetectionWithStackedNotation )
748{
749 UNITS_PROVIDER unitsProvider( schIUScale, EDA_UNITS::MILS );
750
751 SCH_PIN* stacked = new SCH_PIN( m_symbol.get() );
752 stacked->SetNumber( wxT( "[1-3]" ) );
753 stacked->SetPosition( VECTOR2I( 0, 0 ) );
754 m_symbol->AddDrawItem( stacked );
755
756 SCH_PIN* single = new SCH_PIN( m_symbol.get() );
757 single->SetNumber( wxT( "2" ) );
758 single->SetPosition( VECTOR2I( schIUScale.MilsToIU( 100 ), 0 ) );
759 m_symbol->AddDrawItem( single );
760
761 std::vector<wxString> messages;
762 CheckDuplicatePins( m_symbol.get(), messages, &unitsProvider );
763
764 BOOST_REQUIRE_EQUAL( messages.size(), 1 );
765 BOOST_CHECK( messages.front().Contains( wxT( "Duplicate pin 2" ) ) );
766 BOOST_CHECK( messages.front().Contains( wxT( "[1-3]" ) ) );
767
768 single->SetNumber( wxT( "5" ) );
769 messages.clear();
770
771 CheckDuplicatePins( m_symbol.get(), messages, &unitsProvider );
772 BOOST_CHECK( messages.empty() );
773}
774
constexpr EDA_IU_SCALE schIUScale
Definition base_units.h:114
Define a library symbol object.
Definition lib_symbol.h:87
virtual void SetBodyStyle(int aBodyStyle)
Definition sch_item.h:246
int GetBodyStyle() const
Definition sch_item.h:247
int GetUnit() const
Definition sch_item.h:238
virtual void SetUnit(int aUnit)
Definition sch_item.h:237
int GetNumberTextSize() const
Definition sch_pin.cpp:670
int GetLength() const
Definition sch_pin.cpp:298
void SetNumber(const wxString &aNumber)
Definition sch_pin.cpp:633
void SetVisible(bool aVisible)
Definition sch_pin.h:114
void SetOrientation(PIN_ORIENTATION aOrientation)
Definition sch_pin.h:93
void SetName(const wxString &aName)
Definition sch_pin.cpp:418
void SetPosition(const VECTOR2I &aPos) override
Definition sch_pin.h:238
std::vector< wxString > GetStackedPinNumbers(bool *aValid=nullptr) const
Definition sch_pin.cpp:593
const wxString & GetName() const
Definition sch_pin.cpp:400
void SetLength(int aLength)
Definition sch_pin.h:99
PIN_ORIENTATION GetOrientation() const
Definition sch_pin.cpp:263
void SetNumberTextSize(int aSize)
Definition sch_pin.cpp:684
void SetShape(GRAPHIC_PINSHAPE aShape)
Definition sch_pin.h:96
VECTOR2I GetPosition() const override
Definition sch_pin.cpp:255
int GetNameTextSize() const
Definition sch_pin.cpp:646
void SetType(ELECTRICAL_PINTYPE aType)
Definition sch_pin.cpp:332
bool IsStacked(const SCH_PIN *aPin) const
Definition sch_pin.cpp:475
const wxString & GetNumber() const
Definition sch_pin.h:124
GRAPHIC_PINSHAPE GetShape() const
Definition sch_pin.cpp:277
ELECTRICAL_PINTYPE GetType() const
Definition sch_pin.cpp:312
void SetNameTextSize(int aSize)
Definition sch_pin.cpp:660
ELECTRICAL_PINTYPE
The symbol library pin object electrical types used in ERC tests.
Definition pin_type.h:36
@ PT_INPUT
usual pin input: must be connected
Definition pin_type.h:37
PIN_ORIENTATION
The symbol library pin object orientations.
Definition pin_type.h:105
@ PIN_RIGHT
The pin extends rightwards from the connection point.
Definition pin_type.h:111
@ PIN_LEFT
The pin extends leftwards from the connection point: Probably on the right side of the symbol.
Definition pin_type.h:118
std::unique_ptr< SETTINGS_MANAGER > m_settingsManager
BOOST_AUTO_TEST_CASE(HorizontalAlignment)
BOOST_AUTO_TEST_SUITE_END()
VECTOR2I end
void CheckDuplicatePins(LIB_SYMBOL *aSymbol, std::vector< wxString > &aMessages, UNITS_PROVIDER *aUnitsProvider)
BOOST_AUTO_TEST_CASE(TestStackedPinExpansion)
Test basic stacked pin number expansion functionality.
wxString result
Test unit parsing edge cases and error handling.
BOOST_CHECK_EQUAL(result, "25.4")
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:695