KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_annotation_units_conflicts.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 <sch_reference_list.h>
24#include <refdes_tracker.h>
25
27{
28protected:
29 // Helper method to create SCH_REFERENCE objects for testing
30 SCH_REFERENCE createTestReference( const wxString& aRef, const wxString& aValue, int aUnit )
31 {
32 SCH_SYMBOL dummySymbol;
33 SCH_SHEET_PATH dummyPath;
34
35 SCH_REFERENCE ref( &dummySymbol, dummyPath );
36 ref.SetRef( aRef );
37 ref.SetValue( aValue );
38 ref.SetUnit( aUnit );
39
40 return ref;
41 }
42
43 // Helper method to setup units checker for testing
45 {
46 tracker.SetReuseRefDes( false ); // Disable reuse for these tests
47 tracker.SetUnitsChecker( []( const SCH_REFERENCE& aTestRef,
48 const std::vector<SCH_REFERENCE>& aExistingRefs,
49 const std::vector<int>& aRequiredUnits )
50 {
51 // Mock implementation for unit availability check
52 for( int unit : aRequiredUnits )
53 {
54 for( const auto& ref : aExistingRefs )
55 {
56 if( ref.GetUnit() == unit
57 && ref.CompareValue( aTestRef ) == 0 )
58 {
59 return false; // Conflict found
60 }
61 }
62 }
63 return true; // All required units are available
64 } );
65 }
66};
67
68// Test cases that specifically validate the unit conflict detection logic
69// These tests focus on the areUnitsAvailable method and related functionality
70
72{
73 std::string m_caseName;
74 std::string m_refPrefix;
75 std::string m_refValue;
76 std::string m_refLibName;
77 std::vector<int> m_existingUnits; // Units already used for this reference number
78 std::string m_existingValue; // Value of existing references
79 std::string m_existingLibName; // Library name of existing references
80 std::vector<int> m_requestedUnits; // Units being requested
81 bool m_expectedAvailable; // Whether units should be available
82 std::string m_reason; // Reason for expected result
83};
84
85static const std::vector<UNIT_CONFLICT_TEST_CASE> unitConflictCases = {
86 {
87 "Units available - no conflicts",
88 "U", "LM358", "OpAmp_Dual",
89 {3, 4}, // Existing units 3, 4
90 "LM358", "OpAmp_Dual",
91 {1, 2}, // Requesting units 1, 2
92 true, // Should be available
93 "Requested units don't conflict with existing units"
94 },
95 {
96 "Units conflict - same unit requested",
97 "U", "LM358", "OpAmp_Dual",
98 {1, 2}, // Existing units 1, 2
99 "LM358", "OpAmp_Dual",
100 {2, 3}, // Requesting units 2, 3 (2 conflicts)
101 false, // Should NOT be available
102 "Unit 2 is already in use"
103 },
104 {
105 "Value mismatch - can't share reference",
106 "R", "1k", "Resistor",
107 {1}, // Existing unit 1
108 "2k", "Resistor", // Different value
109 {2}, // Requesting unit 2
110 false, // Should NOT be available
111 "Can't share reference designator with different values"
112 },
113 {
114 "Library mismatch - can't share reference",
115 "U", "LM358", "OpAmp_Dual",
116 {1}, // Existing unit 1
117 "LM358", "OpAmp_Single", // Different library
118 {2}, // Requesting unit 2
119 false, // Should NOT be available
120 "Can't share reference designator with different library parts"
121 },
122 {
123 "Empty existing units - should be available",
124 "IC", "74HC00", "Logic_Gate",
125 {}, // No existing units
126 "74HC00", "Logic_Gate",
127 {1, 2, 3, 4}, // Requesting all 4 units
128 true, // Should be available
129 "No existing units to conflict with"
130 },
131 {
132 "Negative units filtered out",
133 "U", "LM324", "OpAmp_Quad",
134 {2}, // Existing unit 2
135 "LM324", "OpAmp_Quad",
136 {-1, 1, -5, 3}, // Mix of negative and positive units
137 true, // Should be available (negatives ignored)
138 "Negative unit numbers are filtered out, only units 1,3 considered"
139 },
140 {
141 "All units conflict",
142 "U", "LM324", "OpAmp_Quad",
143 {1, 2, 3, 4}, // All units already used
144 "LM324", "OpAmp_Quad",
145 {1, 2, 3, 4}, // Requesting all units
146 false, // Should NOT be available
147 "All requested units are already in use"
148 },
149 {
150 "Partial conflict with mixed values",
151 "R", "1k", "Resistor",
152 {1}, // Existing unit 1 with value "1k"
153 "1k", "Resistor",
154 {1, 2}, // Requesting units 1,2 where 1 has same value
155 false, // Should NOT be available
156 "Unit 1 conflicts even with same value (already occupied)"
157 },
158 {
159 "Complex multi-unit scenario",
160 "U", "LM339", "Comparator_Quad",
161 {1, 3}, // Existing units 1, 3
162 "LM339", "Comparator_Quad",
163 {2, 4}, // Requesting units 2, 4
164 true, // Should be available
165 "Units 2,4 don't conflict with existing 1,3"
166 }
167};
168
169BOOST_FIXTURE_TEST_SUITE( UnitConflicts, TEST_ANNOTATION_UNITS_CONFLICTS )
170
171BOOST_AUTO_TEST_CASE( ValidateUnitConflictDetection )
172{
173 for( const UNIT_CONFLICT_TEST_CASE& testCase : unitConflictCases )
174 {
175 BOOST_TEST_INFO_SCOPE( testCase.m_caseName );
176
177 auto validateUnitConflictLogic = [&]( const UNIT_CONFLICT_TEST_CASE& aTestCase )
178 {
179
180 REFDES_TRACKER tracker;
181
182 // Create mock reference for testing (conceptual - would need real SCH_REFERENCE in practice)
183 BOOST_TEST_MESSAGE( "Testing: " + aTestCase.m_reason );
184
185 // Test the logical conditions that areUnitsAvailable should check:
186
187 // 1. Value comparison logic
188 bool valueMatches = ( aTestCase.m_refValue == aTestCase.m_existingValue );
189
190 // 2. Library comparison logic
191 bool libMatches = ( aTestCase.m_refLibName == aTestCase.m_existingLibName );
192
193 // 3. Unit conflict detection
194 bool hasUnitConflict = false;
195 for( int requestedUnit : aTestCase.m_requestedUnits )
196 {
197 if( requestedUnit < 0 ) continue; // Skip negative units
198
199 for( int existingUnit : aTestCase.m_existingUnits )
200 {
201 if( requestedUnit == existingUnit )
202 {
203 hasUnitConflict = true;
204 break;
205 }
206 }
207 if( hasUnitConflict ) break;
208 }
209
210 // The logic from areUnitsAvailable:
211 // Return false if: different value OR different library OR unit conflict
212 bool shouldBeAvailable = valueMatches && libMatches && !hasUnitConflict;
213
214 BOOST_CHECK_EQUAL( shouldBeAvailable, aTestCase.m_expectedAvailable );
215
216 if( shouldBeAvailable != aTestCase.m_expectedAvailable )
217 {
218 BOOST_TEST_MESSAGE( "Logic mismatch:" );
219 BOOST_TEST_MESSAGE( " Value match: " + std::to_string( valueMatches ) );
220 BOOST_TEST_MESSAGE( " Lib match: " + std::to_string( libMatches ) );
221 BOOST_TEST_MESSAGE( " Unit conflict: " + std::to_string( hasUnitConflict ) );
222 BOOST_TEST_MESSAGE( " Expected: " + std::to_string( aTestCase.m_expectedAvailable ) );
223 BOOST_TEST_MESSAGE( " Actual: " + std::to_string( shouldBeAvailable ) );
224 }
225 };
226
227 validateUnitConflictLogic( testCase );
228 }
229}
230
231
232BOOST_AUTO_TEST_CASE( GetNextRefDesForUnits_Integration )
233{
234 REFDES_TRACKER tracker;
235 setupRefDesTracker( tracker );
236
237 // Test the overall GetNextRefDesForUnits logic using the tracker
238
239 // Preload some references to simulate previously used ones
240 tracker.Insert( "U1" );
241 tracker.Insert( "U5" );
242
243 // Test case 1: Completely unused reference with empty units
244 // Should get U2 (first unused after U1)
245 SCH_REFERENCE testRef = createTestReference( "U", "LM358", 1 );
246 std::map<int, std::vector<SCH_REFERENCE>> emptyMap;
247 std::vector<int> emptyUnits;
248
249 int nextRef = tracker.GetNextRefDesForUnits( testRef, emptyMap, emptyUnits, 1 );
250 BOOST_CHECK_EQUAL( nextRef, 2 ); // Should skip U1, get U2
251
252 // Test case 2: Min value higher than next available
253 nextRef = tracker.GetNextRefDesForUnits( testRef, emptyMap, emptyUnits, 10 );
254 BOOST_CHECK_EQUAL( nextRef, 10 ); // Should start from min value
255
256 // Verify references were inserted
257 BOOST_CHECK( tracker.Contains( "U2" ) );
258 BOOST_CHECK( tracker.Contains( "U10" ) );
259
260 // Test case 3: New prefix
261 SCH_REFERENCE icRef = createTestReference( "IC", "74HC00", 1 );
262 nextRef = tracker.GetNextRefDesForUnits( icRef, emptyMap, emptyUnits, 1 );
263 BOOST_CHECK_EQUAL( nextRef, 1 ); // New prefix should start at 1
264 BOOST_CHECK( tracker.Contains( "IC1" ) );
265}
266
267BOOST_AUTO_TEST_CASE( RefDesTracker_StateConsistency )
268{
269 REFDES_TRACKER tracker;
270 setupRefDesTracker( tracker );
271
272 // Test that the tracker maintains consistent state across operations
273
274 // Insert some references manually
275 BOOST_CHECK( tracker.Insert( "R1" ) );
276 BOOST_CHECK( tracker.Insert( "R3" ) );
277 BOOST_CHECK( tracker.Insert( "R5" ) );
278
279 // Verify they exist
280 BOOST_CHECK( tracker.Contains( "R1" ) );
281 BOOST_CHECK( tracker.Contains( "R3" ) );
282 BOOST_CHECK( tracker.Contains( "R5" ) );
283 BOOST_CHECK( !tracker.Contains( "R2" ) );
284 BOOST_CHECK( !tracker.Contains( "R4" ) );
285
286 // Test GetNextRefDesForUnits with empty units - should fill gap at R2
287 SCH_REFERENCE testRef = createTestReference( "R", "1k", 1 );
288 std::map<int, std::vector<SCH_REFERENCE>> emptyMap;
289 std::vector<int> emptyUnits;
290
291 int next = tracker.GetNextRefDesForUnits( testRef, emptyMap, emptyUnits, 1 );
293 BOOST_CHECK( tracker.Contains( "R2" ) );
294
295 // Get next reference - should fill gap at R4
296 next = tracker.GetNextRefDesForUnits( testRef, emptyMap, emptyUnits, 1 );
298 BOOST_CHECK( tracker.Contains( "R4" ) );
299
300 // Get next reference - should go to R6
301 next = tracker.GetNextRefDesForUnits( testRef, emptyMap, emptyUnits, 1 );
303 BOOST_CHECK( tracker.Contains( "R6" ) );
304
305 // Verify total count
306 BOOST_CHECK_EQUAL( tracker.Size(), 6 );
307}
308
309BOOST_AUTO_TEST_CASE( CacheConsistency_AfterInserts )
310{
311 REFDES_TRACKER tracker;
312 setupRefDesTracker( tracker );
313
314 // Test that cache remains consistent after mixed Insert/GetNextRefDesForUnits operations
315
316 // Start with some manual inserts
317 tracker.Insert( "C1" );
318 tracker.Insert( "C5" );
319 tracker.Insert( "C10" );
320
321 SCH_REFERENCE testRef = createTestReference( "C", "100nF", 1 );
322 std::map<int, std::vector<SCH_REFERENCE>> emptyMap;
323 std::vector<int> emptyUnits;
324
325 // Get next ref - should use cached logic to find C2
326 int next = tracker.GetNextRefDesForUnits( testRef, emptyMap, emptyUnits, 1 );
328
329 // Insert C3 manually
330 tracker.Insert( "C3" );
331
332 // Get next ref - cache should be updated, should get C4
333 next = tracker.GetNextRefDesForUnits( testRef, emptyMap, emptyUnits, 1 );
335
336 // Test with minimum value - should respect cache
337 next = tracker.GetNextRefDesForUnits( testRef, emptyMap, emptyUnits, 7 );
338 BOOST_CHECK_EQUAL( next, 7 ); // C6 available but min is 7
339
340 // Verify all references exist
341 BOOST_CHECK( tracker.Contains( "C1" ) );
342 BOOST_CHECK( tracker.Contains( "C2" ) );
343 BOOST_CHECK( tracker.Contains( "C3" ) );
344 BOOST_CHECK( tracker.Contains( "C4" ) );
345 BOOST_CHECK( tracker.Contains( "C5" ) );
346 BOOST_CHECK( !tracker.Contains( "C6" ) ); // Skipped due to min value
347 BOOST_CHECK( tracker.Contains( "C7" ) );
348 BOOST_CHECK( tracker.Contains( "C10" ) );
349}
350
351BOOST_AUTO_TEST_CASE( ThreadSafety_BasicValidation )
352{
353 REFDES_TRACKER tracker( true ); // Enable thread safety
354 setupRefDesTracker( tracker );
355
356 // Basic validation that thread-safe operations work
357 BOOST_CHECK( tracker.Insert( "U1" ) );
358 BOOST_CHECK( tracker.Contains( "U1" ) );
359
360 // Test GetNextRefDesForUnits with thread safety
361 SCH_REFERENCE testRef = createTestReference( "U", "LM358", 1 );
362 std::map<int, std::vector<SCH_REFERENCE>> emptyMap;
363 std::vector<int> emptyUnits;
364
365 int next = tracker.GetNextRefDesForUnits( testRef, emptyMap, emptyUnits, 1 );
367
368 // Test serialization with thread safety
369 std::string serialized = tracker.Serialize();
370 BOOST_CHECK( !serialized.empty() );
371
372 REFDES_TRACKER tracker2( true );
373 BOOST_CHECK( tracker2.Deserialize( serialized ) );
374 BOOST_CHECK( tracker2.Contains( "U1" ) );
375 BOOST_CHECK( tracker2.Contains( "U2" ) );
376}
377
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.
size_t Size() const
Get the total count of stored reference designators.
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
SCH_REFERENCE createTestReference(const wxString &aRef, const wxString &aValue, int aUnit)
void setupRefDesTracker(REFDES_TRACKER &tracker)
CITER next(CITER it)
Definition: ptree.cpp:124
static const std::vector< UNIT_CONFLICT_TEST_CASE > unitConflictCases
BOOST_AUTO_TEST_CASE(ValidateUnitConflictDetection)
BOOST_CHECK_EQUAL(ret, c.m_exp_result)
BOOST_AUTO_TEST_SUITE_END()
BOOST_TEST_MESSAGE("Polyline has "<< chain.PointCount()<< " points")