KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_annotation_refdes_tracker_units.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 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 "eeschema_test_utils.h"
22
23#include <refdes_tracker.h>
24#include <sch_reference_list.h>
25
27{
28 std::string m_caseName;
29 std::string m_testRefPrefix;
30 std::string m_testRefValue;
31 std::map<int, std::vector<std::tuple<std::string, int>>> m_refNumberMap; // Map of ref number to vector of (value, unit) tuples
32 std::vector<int> m_requiredUnits;
35 std::vector<std::string> m_trackerPreloads; // References to preload in tracker
36};
37
39{
40protected:
41 void runTestCase( const REFDES_UNITS_TEST_CASE& testCase );
42
43 // Helper method to create test references
44 SCH_REFERENCE createTestReference( const std::string& aRefPrefix, const std::string& aValue, int aUnit )
45 {
46 SCH_SYMBOL dummySymbol;
47 SCH_SHEET_PATH dummyPath;
48
49 SCH_REFERENCE ref( &dummySymbol, dummyPath );
50 ref.SetRef( aRefPrefix );
51 ref.SetValue( aValue );
52 ref.SetUnit( aUnit );
53
54 return ref;
55 }
56
57 // Helper method to setup units checker
59 {
60 tracker.SetReuseRefDes( false ); // Disable reuse for these tests
61 tracker.SetUnitsChecker( []( const SCH_REFERENCE& aTestRef,
62 const std::vector<SCH_REFERENCE>& aExistingRefs,
63 const std::vector<int>& aRequiredUnits )
64 {
65 // Check if all required units are available
66 for( int unit : aRequiredUnits )
67 {
68 for( const auto& ref : aExistingRefs )
69 {
70 if( ref.GetUnit() == unit
71 || ref.CompareValue( aTestRef ) != 0 )
72 {
73 return false; // Conflict found
74 }
75 }
76 }
77 return true; // All required units are available
78 } );
79 }
80};
81
83{
84 REFDES_TRACKER tracker;
85
86 // Preload tracker with existing references
87 for( const std::string& ref : testCase.m_trackerPreloads )
88 {
89 tracker.Insert( ref );
90 }
91
92 // Create test reference
93 SCH_REFERENCE testRef = createTestReference( testCase.m_testRefPrefix, testCase.m_testRefValue, 1 );
94
95 // Convert test case data to actual SCH_REFERENCE map
96 std::map<int, std::vector<SCH_REFERENCE>> refNumberMap;
97 for( const auto& [refNum, tupleVec] : testCase.m_refNumberMap )
98 {
99 std::vector<SCH_REFERENCE> refs;
100 for( const auto& [value, unit] : tupleVec )
101 {
102 refs.push_back( createTestReference( testCase.m_testRefPrefix, value, unit ) );
103 }
104 refNumberMap[refNum] = refs;
105 }
106
107 BOOST_TEST_INFO( "Testing case: " + testCase.m_caseName );
108
109 setupRefDesTracker( tracker );
110
111 // Test GetNextRefDesForUnits logic using the 4-parameter method
112 int result = tracker.GetNextRefDesForUnits( testRef,
113 refNumberMap,
114 testCase.m_requiredUnits,
115 testCase.m_minValue );
116
117 BOOST_CHECK_EQUAL( result, testCase.m_expectedResult );
118
119 // Additional verification: check that the result reference is in the tracker
120 // (unless it was a case where units were available in existing reference)
121 std::string resultRefDes = testCase.m_testRefPrefix + std::to_string( result );
122
123 // Check if this reference number was already in use
124 bool wasInUse = testCase.m_refNumberMap.find( result ) != testCase.m_refNumberMap.end();
125
126 if( !wasInUse && !testCase.m_requiredUnits.empty() )
127 {
128 // For completely new references, it should be added to tracker
129 BOOST_CHECK( tracker.Contains( resultRefDes ) );
130 }
131}
132
133static const std::vector<REFDES_UNITS_TEST_CASE> refdesUnitsTestCases = {
134 {
135 "Case 1: Completely unused reference - empty units",
136 "U", "LM358",
137 {}, // No currently used references
138 {}, // Empty required units (need completely unused)
139 1, // Min value
140 1, // Expected: U1
141 {} // No preloaded references
142 },
143 {
144 "Case 2: Completely unused reference - with units",
145 "U", "LM358",
146 {}, // No currently used references
147 {1, 2}, // Need units 1 and 2
148 1, // Min value
149 1, // Expected: U1
150 {} // No preloaded references
151 },
152 {
153 "Case 3: Skip currently in use reference",
154 "U", "LM358",
155 {
156 { 1, { std::make_tuple("LM358", 1) } } // U1 unit 1 in use
157 },
158 {1, 2}, // Need units 1 and 2
159 1, // Min value
160 2, // Expected: U2 (U1 conflicts with unit 1)
161 {}
162 },
163 {
164 "Case 4: Units available in currently used reference",
165 "U", "LM358",
166 {
167 { 1, { std::make_tuple("LM358", 3),
168 std::make_tuple("LM358", 4) } } // U1 units 3,4 in use
169 },
170 {1, 2}, // Need units 1 and 2 (available)
171 1, // Min value
172 1, // Expected: U1 (units 1,2 are free)
173 {}
174 },
175 {
176 "Case 5: Different value conflict",
177 "U", "LM358",
178 {
179 { 1, { std::make_tuple("LM741", 1) } } // U1 different value
180 },
181 {1}, // Need unit 1
182 1, // Min value
183 2, // Expected: U2 (can't share with different value)
184 {}
185 },
186 {
187 "Case 6: Previously used reference in tracker",
188 "U", "LM358",
189 {}, // No currently used references
190 {1}, // Need unit 1
191 1, // Min value
192 2, // Expected: U2 (U1 was previously used)
193 {"U1"} // U1 preloaded in tracker
194 },
195 {
196 "Case 7: Min value higher than available",
197 "U", "LM358",
198 {
199 { 5, { std::make_tuple("LM358", 1) } } // U5 unit 1 in use
200 },
201 {2}, // Need unit 2
202 10, // Min value = 10
203 10, // Expected: U10 (U5 has unit 2 available, but min value is 10)
204 {}
205 },
206 {
207 "Case 8: Negative units filtered out",
208 "U", "LM358",
209 {},
210 {-1, 1, -5, 2}, // Mix of negative and positive units
211 1, // Min value
212 1, // Expected: U1 (only units 1,2 considered)
213 {}
214 },
215 {
216 "Case 9: Complex scenario with gaps",
217 "IC", "74HC00",
218 {
219 { 2, { std::make_tuple("74HC00", 1) } }, // IC2 unit 1 used
220 { 4, { std::make_tuple("74HC00", 2) } } // IC4 unit 2 used
221 },
222 {1, 3}, // Need units 1 and 3
223 1, // Min value
224 3, // Expected: IC3 (IC1 unused, IC2 conflicts unit 1, IC3 available)
225 {"IC1"} // IC1 previously used
226 }
227};
228
229BOOST_FIXTURE_TEST_SUITE( RefDesTrackerUnits, TEST_REFDES_TRACKER_UNITS )
230
231BOOST_AUTO_TEST_CASE( GetNextRefDesForUnits_BasicCases )
232{
233 for( const REFDES_UNITS_TEST_CASE& testCase : refdesUnitsTestCases )
234 {
235 runTestCase( testCase );
236 }
237}
238
239BOOST_AUTO_TEST_CASE( GetNextRefDesForUnits_EdgeCases )
240{
241 REFDES_TRACKER tracker;
242
243 // Test empty required units vector - should find completely unused reference
244 SCH_REFERENCE testRef = createTestReference( "R", "1k", 1 );
245 std::map<int, std::vector<SCH_REFERENCE>> emptyMap;
246 std::vector<int> emptyUnits;
247
248 setupRefDesTracker( tracker );
249 int result = tracker.GetNextRefDesForUnits( testRef, emptyMap, emptyUnits, 1 );
250 BOOST_CHECK_EQUAL( result, 1 );
251 BOOST_CHECK( tracker.Contains( "R1" ) );
252
253 // Test with some references already in tracker
254 tracker.Insert( "R3" );
255 result = tracker.GetNextRefDesForUnits( testRef, emptyMap, emptyUnits, 1 );
256 BOOST_CHECK_EQUAL( result, 2 ); // Should skip R1 (already inserted above) and get R2
257
258 // Test with negative units (should be filtered out)
259 std::vector<int> mixedUnits = {-1, 1, -5, 2};
260 SCH_REFERENCE testRef2 = createTestReference( "C", "100nF", 1 );
261 result = tracker.GetNextRefDesForUnits( testRef2, emptyMap, mixedUnits, 1 );
262 BOOST_CHECK_EQUAL( result, 1 );
263}
264
265BOOST_AUTO_TEST_CASE( GetNextRefDesForUnits_UsagePattern )
266{
267 REFDES_TRACKER tracker;
268
269 // Demonstrate actual usage pattern for GetNextRefDesForUnits with our test helper
270 SCH_REFERENCE testRef = createTestReference( "U", "LM358", 1 );
271
272 // Create map of currently used references
273 std::map<int, std::vector<SCH_REFERENCE>> refNumberMap;
274 refNumberMap[1] = { createTestReference("U", "LM358", 1),
275 createTestReference("U", "LM358", 2) }; // U1 has units 1,2 used
276 refNumberMap[3] = { createTestReference("U", "LM358", 1) }; // U3 has unit 1 used
277
278 // Specify required units for new symbol
279 std::vector<int> requiredUnits = {1, 2};
280
281 setupRefDesTracker( tracker );
282
283 // Get next available reference number
284 int nextRefNum = tracker.GetNextRefDesForUnits( testRef, refNumberMap, requiredUnits, 1 );
285
286 // Should return 2 (U2) since U1 conflicts (units 1,2 already used) and U2 is available
287 BOOST_CHECK_EQUAL( nextRefNum, 2 );
288 BOOST_CHECK( tracker.Contains( "U2" ) );
289
290 // Test case where units are available in existing reference
291 std::vector<int> requiredUnits2 = {3, 4}; // These should be available in U1
292 refNumberMap[3] = { createTestReference("U", "LM358", 1) }; // U1 only has unit 1 and 2 used
293
294 nextRefNum = tracker.GetNextRefDesForUnits( testRef, refNumberMap, requiredUnits2, 1 );
295 BOOST_CHECK_EQUAL( nextRefNum, 1 ); // U1 should work since units 3,4 are available
296
297 // Test different value conflict
298 SCH_REFERENCE differentValueRef = createTestReference( "U", "LM741", 1 );
299 refNumberMap[4] = { createTestReference("U", "LM358", 1) }; // U4 has different value
300
301 nextRefNum = tracker.GetNextRefDesForUnits( differentValueRef, refNumberMap, {1}, 4 );
302 BOOST_CHECK_EQUAL( nextRefNum, 5 ); // Should skip U4 due to value conflict
303}
304
305BOOST_AUTO_TEST_CASE( GetNextRefDesForUnits_ThreadSafety )
306{
307 REFDES_TRACKER tracker( true ); // Enable thread safety
308
309 // Test that GetNextRefDesForUnits works with thread safety enabled
310 SCH_REFERENCE testRef = createTestReference( "U", "LM358", 1 );
311 std::map<int, std::vector<SCH_REFERENCE>> emptyMap;
312 std::vector<int> requiredUnits = {1, 2};
313
314 setupRefDesTracker( tracker );
315 int result = tracker.GetNextRefDesForUnits( testRef, emptyMap, requiredUnits, 1 );
316 BOOST_CHECK_EQUAL( result, 1 );
317 BOOST_CHECK( tracker.Contains( "U1" ) );
318
319 // Test multiple calls work correctly with thread safety
320 result = tracker.GetNextRefDesForUnits( testRef, emptyMap, requiredUnits, 1 );
321 BOOST_CHECK_EQUAL( result, 2 );
322 BOOST_CHECK( tracker.Contains( "U2" ) );
323
324 // Test with conflicts and thread safety
325 std::map<int, std::vector<SCH_REFERENCE>> conflictMap;
326 conflictMap[3] = { createTestReference("U", "LM358", 1) }; // U3 unit 1 in use
327
328 result = tracker.GetNextRefDesForUnits( testRef, conflictMap, requiredUnits, 1 );
329 BOOST_CHECK_EQUAL( result, 4 ); // Should skip U1,U2 (in tracker) and U3 (conflicted)
330}
331
332BOOST_AUTO_TEST_CASE( GetNextRefDesForUnits_Integration )
333{
334 REFDES_TRACKER tracker;
335
336 // Test that GetNextRefDesForUnits properly integrates with existing Insert/Contains
337 tracker.Insert( "U1" );
338 tracker.Insert( "U3" );
339
340 BOOST_CHECK( tracker.Contains( "U1" ) );
341 BOOST_CHECK( tracker.Contains( "U3" ) );
342 BOOST_CHECK( !tracker.Contains( "U2" ) );
343
344 // Test GetNextRefDesForUnits with preloaded tracker
345 SCH_REFERENCE testRef = createTestReference( "U", "LM358", 1 );
346 std::map<int, std::vector<SCH_REFERENCE>> emptyMap;
347 std::vector<int> requiredUnits = {1, 2};
348
349 setupRefDesTracker( tracker );
350
351 // Should get U2 since U1 is already in tracker (preloaded) and U3 is also preloaded
352 int next = tracker.GetNextRefDesForUnits( testRef, emptyMap, requiredUnits, 1 );
354 BOOST_CHECK( tracker.Contains( "U2" ) );
355
356 // Test with higher minimum value
357 next = tracker.GetNextRefDesForUnits( testRef, emptyMap, requiredUnits, 5 );
359 BOOST_CHECK( tracker.Contains( "U5" ) );
360
361 // Test integration with serialization
362 std::string serialized = tracker.Serialize();
363 REFDES_TRACKER tracker2;
364 BOOST_CHECK( tracker2.Deserialize( serialized ) );
365
366 // Verify deserialized tracker has the same state
367 BOOST_CHECK( tracker2.Contains( "U1" ) );
368 BOOST_CHECK( tracker2.Contains( "U2" ) );
369 BOOST_CHECK( tracker2.Contains( "U3" ) );
370 BOOST_CHECK( tracker2.Contains( "U5" ) );
371
372 setupRefDesTracker( tracker2 );
373
374 // GetNextRefDesForUnits should work with deserialized tracker
375 next = tracker2.GetNextRefDesForUnits( testRef, emptyMap, requiredUnits, 1 );
376 BOOST_CHECK_EQUAL( next, 4 ); // Should get U4 (first available after U1,U2,U3,U5)
377}
378
379BOOST_AUTO_TEST_CASE( Serialization_WithTrackedReferences )
380{
381 REFDES_TRACKER tracker;
382
383 // Add some references using both Insert and GetNextRefDesForUnits
384 tracker.Insert( "R1" );
385 tracker.Insert( "R3" );
386
387 setupRefDesTracker( tracker );
388
389 // Use GetNextRefDesForUnits to get next reference
390 SCH_REFERENCE testRef = createTestReference( "R", "1k", 1 );
391 std::map<int, std::vector<SCH_REFERENCE>> emptyMap;
392 std::vector<int> requiredUnits = {1};
393
394 int next = tracker.GetNextRefDesForUnits( testRef, emptyMap, requiredUnits, 1 );
395 BOOST_CHECK_EQUAL( next, 2 ); // Should get R2
396
397 // Test with different prefix
398 SCH_REFERENCE capacitorRef = createTestReference( "C", "100nF", 1 );
399 next = tracker.GetNextRefDesForUnits( capacitorRef, emptyMap, requiredUnits, 5 );
400 BOOST_CHECK_EQUAL( next, 5 ); // Should get C5
401
402 // Test serialization
403 std::string serialized = tracker.Serialize();
404 BOOST_CHECK( !serialized.empty() );
405
406 // Test deserialization
407 REFDES_TRACKER tracker2;
408 BOOST_CHECK( tracker2.Deserialize( serialized ) );
409
410 BOOST_CHECK( tracker2.Contains( "R1" ) );
411 BOOST_CHECK( tracker2.Contains( "R2" ) );
412 BOOST_CHECK( tracker2.Contains( "R3" ) );
413 BOOST_CHECK( tracker2.Contains( "C5" ) );
414
415 setupRefDesTracker( tracker2 );
416
417 // Test GetNextRefDesForUnits with deserialized tracker
418 next = tracker2.GetNextRefDesForUnits( testRef, emptyMap, requiredUnits, 1 );
419 BOOST_CHECK_EQUAL( next, 4 ); // Next reference should be R4
420
421 // Test with unit conflicts after deserialization
422 std::map<int, std::vector<SCH_REFERENCE>> conflictMap;
423 conflictMap[4] = { createTestReference("R", "2k", 1) }; // R4 different value
424
425 next = tracker2.GetNextRefDesForUnits( testRef, conflictMap, requiredUnits, 1 );
426 BOOST_CHECK_EQUAL( next, 5 ); // Should skip R4 due to value conflict
427}
428
A generic fixture for loading schematics and associated settings for qa tests.
Class to efficiently track reference designators and provide next available designators.
bool Deserialize(const std::string &aData)
Deserialize tracker data from string representation.
int GetNextRefDesForUnits(const SCH_REFERENCE &aRef, const std::map< int, std::vector< SCH_REFERENCE > > &aRefNumberMap, const std::vector< int > &aRequiredUnits, int aMinValue)
Get the next available reference designator number for multi-unit symbols.
void SetReuseRefDes(bool aReuse)
bool Insert(const std::string &aRefDes)
Insert a reference designator into the tracker.
void SetUnitsChecker(const UNITS_CHECKER_FUNC< SCH_REFERENCE > &aChecker)
Set an external units checker function for SCH_REFERENCE objects.
std::string Serialize() const
Serialize the tracker data to a compact string representation.
bool Contains(const std::string &aRefDes) const
Check if a reference designator exists in the tracker.
A helper to define a symbol's reference designator in a schematic.
void SetValue(const wxString &aValue)
void SetRef(const wxString &aReference)
void SetUnit(int aUnit)
Handle access to a stack of flattened SCH_SHEET objects by way of a path for creating a flattened sch...
Schematic symbol object.
Definition: sch_symbol.h:75
void setupRefDesTracker(REFDES_TRACKER &tracker)
void runTestCase(const REFDES_UNITS_TEST_CASE &testCase)
SCH_REFERENCE createTestReference(const std::string &aRefPrefix, const std::string &aValue, int aUnit)
CITER next(CITER it)
Definition: ptree.cpp:124
std::map< int, std::vector< std::tuple< std::string, int > > > m_refNumberMap
std::vector< std::string > m_trackerPreloads
BOOST_AUTO_TEST_CASE(GetNextRefDesForUnits_BasicCases)
static const std::vector< REFDES_UNITS_TEST_CASE > refdesUnitsTestCases
BOOST_CHECK_EQUAL(ret, c.m_exp_result)
BOOST_AUTO_TEST_SUITE_END()