KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_sch_reference_list.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) 2022 Roberto Fernandez Bautista <[email protected]>
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * This program is free software: you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation, either version 3 of the License, or (at your
10 * option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
22#include "eeschema_test_utils.h"
23
24#include <sch_reference_list.h>
25#include <sch_sheet_path.h> // SCH_MULTI_UNIT_REFERENCE_MAP
26
27
29{
30 wxString m_KIID;
31 wxString m_OriginalRef;
32 wxString m_ExpectedRef;
34};
35
36
38{
39protected:
40 void loadTestCase( wxString aSchematicRelativePath, std::vector<REANNOTATED_REFERENCE> aRefs );
41 void setupRefDesTrackerWithPreloads( const std::vector<std::string>& preloads );
43
44 SCH_SYMBOL* getSymbolByKIID( wxString aKIID, SCH_SHEET_PATH* aSymbolPath );
45
47
48 void checkAnnotation( std::vector<REANNOTATED_REFERENCE> aRefs );
49
52};
53
54
55void TEST_SCH_REFERENCE_LIST_FIXTURE::loadTestCase( wxString aSchematicRelativePath,
56 std::vector<REANNOTATED_REFERENCE> aRefs )
57{
58 m_refsToReannotate.Clear();
59 m_lockedRefs.clear();
60
61 LoadSchematic( SchematicQAPath( aSchematicRelativePath ) );
62
63 // Create list of references to reannotate
64 for( REANNOTATED_REFERENCE ref : aRefs )
65 {
66 SCH_SHEET_PATH symbolPath;
67 SCH_SYMBOL* symbol = getSymbolByKIID( ref.m_KIID, &symbolPath );
68
69 //Make sure test case is built properly
70 BOOST_REQUIRE_NE( symbol, nullptr );
71 BOOST_REQUIRE_EQUAL( symbol->GetRef( &symbolPath, true ), ref.m_OriginalRef );
72
73 if( ref.m_IncludeInReannotationList )
74 {
75 symbolPath.AppendSymbol( m_refsToReannotate, symbol );
76 symbolPath.AppendMultiUnitSymbol( m_lockedRefs, symbol );
77 }
78 }
79}
80
81
83 SCH_SHEET_PATH* aSymbolPath )
84{
85 KIID symKIID( aKIID );
86 SCH_ITEM* foundItem = m_schematic->ResolveItem( symKIID, aSymbolPath );
87 SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( foundItem );
88
89 return symbol;
90};
91
92
94{
95 // Build List of additional references to pass into Annotate()
96 SCH_REFERENCE_LIST allRefs, additionalRefs;
97
98 m_schematic->BuildSheetListSortedByPageNumbers().GetSymbols( allRefs );
99
100 for( size_t i = 0; i < allRefs.GetCount(); ++i )
101 {
102 if( !m_refsToReannotate.Contains( allRefs[i] ) )
103 additionalRefs.AddItem( allRefs[i] );
104 }
105
106 return additionalRefs;
107}
108
109
110void TEST_SCH_REFERENCE_LIST_FIXTURE::checkAnnotation( std::vector<REANNOTATED_REFERENCE> aRefs )
111{
112 for( REANNOTATED_REFERENCE ref : aRefs )
113 {
114 SCH_SHEET_PATH symbolPath;
115 SCH_SYMBOL* symbol = getSymbolByKIID( ref.m_KIID, &symbolPath );
116
117 BOOST_CHECK_EQUAL( symbol->GetRef( &symbolPath, true ), ref.m_ExpectedRef );
118 }
119}
120
121
122BOOST_FIXTURE_TEST_SUITE( SchReferenceList, TEST_SCH_REFERENCE_LIST_FIXTURE )
123
124
126{
127 std::string m_caseName;
130 std::vector<REANNOTATED_REFERENCE> m_ExpectedReannotations;
131};
132
133// Case 1: same value, same libref
134// Case 2: same value, different libref
135// Case 3: different value, same libref
136// Case 4: Not annotated unit to reannotate
137// Case 5: Duplicate references
138static const std::vector<REANNOTATION_CASE> reannotationCases = {
139 { "CASE 1. Rename only selected. All units selected",
140 "test_multiunit_reannotate",
141 1,
142 {
143 { "cf058f25-2bad-4c49-a0c4-f059825c427f", "U99A", "U3A", true },
144 { "e6c8127f-e282-4128-8744-05f7893bc3ec", "U99B", "U3B", true },
145 { "db066797-b21c-4c1c-9591-8c7c549f8087", "U99C", "U3C", true },
146 } },
147 { "CASE 1. Rename only selected. Only unit B selected (A and C should NOT be reannotated)",
148 "test_multiunit_reannotate",
149 1,
150 {
151 { "cf058f25-2bad-4c49-a0c4-f059825c427f", "U99A", "U99A", false },
152 { "e6c8127f-e282-4128-8744-05f7893bc3ec", "U99B", "U2B", true },
153 { "db066797-b21c-4c1c-9591-8c7c549f8087", "U99C", "U99C", false },
154 } },
155 { "CASE 1. Rename only selected. Only units B and C selected (A should NOT be reannotated)",
156 "test_multiunit_reannotate",
157 1,
158 {
159 { "cf058f25-2bad-4c49-a0c4-f059825c427f", "U99A", "U99A", false },
160 { "e6c8127f-e282-4128-8744-05f7893bc3ec", "U99B", "U3B", true },
161 { "db066797-b21c-4c1c-9591-8c7c549f8087", "U99C", "U3C", true },
162 } },
163 { "CASE 2. Rename only selected. All units selected",
164 "test_multiunit_reannotate_2",
165 1,
166 {
167 { "cf058f25-2bad-4c49-a0c4-f059825c427f", "U99A", "U3A", true },
168 { "e6c8127f-e282-4128-8744-05f7893bc3ec", "U99B", "U3B", true },
169 { "db066797-b21c-4c1c-9591-8c7c549f8087", "U99C", "U3C", true },
170 } },
171 { "CASE 2. Rename only selected. Only unit B selected (A and C should NOT be reannotated)",
172 "test_multiunit_reannotate_2",
173 1,
174 {
175 { "cf058f25-2bad-4c49-a0c4-f059825c427f", "U99A", "U99A", false },
176 { "e6c8127f-e282-4128-8744-05f7893bc3ec", "U99B", "U3B", true },
177 { "db066797-b21c-4c1c-9591-8c7c549f8087", "U99C", "U99C", false },
178 } },
179 { "CASE 2. Rename only selected. Only units B and C selected (A should NOT be reannotated)",
180 "test_multiunit_reannotate_2",
181 1,
182 {
183 { "cf058f25-2bad-4c49-a0c4-f059825c427f", "U99A", "U99A", false },
184 { "e6c8127f-e282-4128-8744-05f7893bc3ec", "U99B", "U3B", true },
185 { "db066797-b21c-4c1c-9591-8c7c549f8087", "U99C", "U3C", true },
186 } },
187 { "CASE 3. Rename only selected. All units selected",
188 "test_multiunit_reannotate_3",
189 1,
190 {
191 { "cf058f25-2bad-4c49-a0c4-f059825c427f", "U99A", "U3A", true },
192 { "e6c8127f-e282-4128-8744-05f7893bc3ec", "U99B", "U3B", true },
193 { "db066797-b21c-4c1c-9591-8c7c549f8087", "U99C", "U3C", true },
194 } },
195 { "CASE 3. Rename only selected. Only unit B selected (A and C should NOT be reannotated)",
196 "test_multiunit_reannotate_3",
197 1,
198 {
199 { "cf058f25-2bad-4c49-a0c4-f059825c427f", "U99A", "U99A", false },
200 { "e6c8127f-e282-4128-8744-05f7893bc3ec", "U99B", "U3B", true },
201 { "db066797-b21c-4c1c-9591-8c7c549f8087", "U99C", "U99C", false },
202 } },
203 { "CASE 3. Rename only selected. Only units B and C selected (A should NOT be reannotated)",
204 "test_multiunit_reannotate_3",
205 1,
206 {
207 { "cf058f25-2bad-4c49-a0c4-f059825c427f", "U99A", "U99A", false },
208 { "e6c8127f-e282-4128-8744-05f7893bc3ec", "U99B", "U3B", true },
209 { "db066797-b21c-4c1c-9591-8c7c549f8087", "U99C", "U3C", true },
210 } },
211 { "CASE 4 - Not previously annotated (does not get added to multi-unit locked group)",
212 "test_multiunit_reannotate_4",
213 1,
214 {
215 { "549455c3-ab6e-454e-94b0-5ca9e521ae0b", "U?B", "U2B", true },
216 } },
217 { "CASE 5 - Duplicate annotation. 1 selected",
218 "test_multiunit_reannotate_5",
219 10,
220 {
221 { "d43a1d25-d37a-467a-8b09-10cf2e2ace09", "U2A", "U2A", false },
222 { "cd562bae-2426-44e6-8196-59eee5439809", "U2B", "U2B", false },
223 { "3f20a749-efe3-4804-8fef-435caaa8dacb", "U2C", "U2C", false },
224 { "cf058f25-2bad-4c49-a0c4-f059825c427f", "U2A", "U2A", false },
225 { "e6c8127f-e282-4128-8744-05f7893bc3ec", "U2B", "U11B", true },
226 { "db066797-b21c-4c1c-9591-8c7c549f8087", "U2C", "U2C", false },
227 } },
228 { "CASE 5 - Duplicate annotation. 2 selected",
229 "test_multiunit_reannotate_5",
230 10,
231 {
232 { "d43a1d25-d37a-467a-8b09-10cf2e2ace09", "U2A", "U2A", false },
233 { "cd562bae-2426-44e6-8196-59eee5439809", "U2B", "U11B", true },
234 { "3f20a749-efe3-4804-8fef-435caaa8dacb", "U2C", "U2C", false },
235 { "cf058f25-2bad-4c49-a0c4-f059825c427f", "U2A", "U2A", false },
236 { "e6c8127f-e282-4128-8744-05f7893bc3ec", "U2B", "U12B", true },
237 { "db066797-b21c-4c1c-9591-8c7c549f8087", "U2C", "U2C", false },
238 } },
239};
240
241// @todo simplify or refactor this test case.
242// Currently it simulates part of SCH_EDIT_FRAME::AnnotateSymbols
244{
245 for( const REANNOTATION_CASE& c : reannotationCases )
246 {
247 BOOST_TEST_INFO_SCOPE( c.m_caseName );
248
249 loadTestCase( c.m_SchematicRelativePath, c.m_ExpectedReannotations );
250 m_refsToReannotate.SetRefDesTracker( m_schematic->Settings().m_refDesTracker );
251 m_refsToReannotate.RemoveAnnotation();
252 m_refsToReannotate.SplitReferences();
253 m_refsToReannotate.Annotate( false, 0, c.m_StartNumber, m_lockedRefs, getAdditionalRefs() );
254 m_refsToReannotate.UpdateAnnotation();
255
256 checkAnnotation( c.m_ExpectedReannotations );
257 }
258}
259
260
262{
263 std::string m_caseName;
265 std::vector<REANNOTATED_REFERENCE> m_ExpectedReannotations;
266};
267
268
269static const std::vector<DUPLICATE_REANNOTATION_CASE> reannotateDuplicatesCases = {
270 { "Reannotate Duplicates. Simple case",
271 "test_multiunit_reannotate_5",
272 {
273 { "d43a1d25-d37a-467a-8b09-10cf2e2ace09", "U2A", "U2A", false },
274 { "cd562bae-2426-44e6-8196-59eee5439809", "U2B", "U2B", false },
275 { "3f20a749-efe3-4804-8fef-435caaa8dacb", "U2C", "U2C", false },
276 { "cf058f25-2bad-4c49-a0c4-f059825c427f", "U2A", "U3A", true },
277 { "e6c8127f-e282-4128-8744-05f7893bc3ec", "U2B", "U3B", true },
278 { "db066797-b21c-4c1c-9591-8c7c549f8087", "U2C", "U3C", true },
279 } },
280};
281
282
283BOOST_AUTO_TEST_CASE( ReannotateDuplicates )
284{
286 {
287 BOOST_TEST_INFO_SCOPE( c.m_caseName );
288
289 loadTestCase( c.m_SchematicRelativePath, c.m_ExpectedReannotations );
290
291 m_refsToReannotate.SetRefDesTracker( m_schematic->Settings().m_refDesTracker );
292 m_refsToReannotate.ReannotateDuplicates( getAdditionalRefs(), INCREMENTAL_BY_REF );
293 m_refsToReannotate.UpdateAnnotation();
294
295 checkAnnotation( c.m_ExpectedReannotations );
296 }
297}
298
299
300BOOST_AUTO_TEST_CASE( ReferenceListDoesNotMutateEmptyValue )
301{
302 loadTestCase( "test_multiunit_reannotate", {} );
303
304 SCH_SHEET_PATH sheetPath = m_schematic->CurrentSheet();
305 SCH_SYMBOL* symbol = nullptr;
306
307 for( SCH_ITEM* item : sheetPath.LastScreen()->Items().OfType( SCH_SYMBOL_T ) )
308 {
309 symbol = static_cast<SCH_SYMBOL*>( item );
310 break;
311 }
312
313 BOOST_REQUIRE( symbol != nullptr );
314
315 symbol->SetValueFieldText( wxEmptyString );
316 BOOST_REQUIRE( symbol->GetValue( false, &sheetPath, false ).IsEmpty() );
317
319 sheetPath.AppendSymbol( refs, symbol );
320
321 BOOST_CHECK( symbol->GetValue( false, &sheetPath, false ).IsEmpty() );
322 BOOST_REQUIRE_EQUAL( refs.GetCount(), 1 );
323 BOOST_CHECK_EQUAL( refs[0].GetValue(), wxT( "~" ) );
324}
325
326
334BOOST_AUTO_TEST_CASE( ReannotateSameValueMultiUnitPreservesGrouping )
335{
336 // Load a schematic with two 3-unit LM2903 (U1, U2), same value, interleaved X positions.
337 // Sort by X gives: U1A, U2B, U1B, U2A, U1C, U2C
338 // Without locked groups this would mix units across the two symbols.
339 LoadSchematic( SchematicQAPath( "test_multiunit_reannotate_same_value" ) );
340
341 SCH_SHEET_PATH sheetPath = m_schematic->CurrentSheet();
342
343 // Build a map from UUID to symbol for verification
344 std::map<wxString, SCH_SYMBOL*> symbolsByUUID;
345
346 for( SCH_ITEM* item : sheetPath.LastScreen()->Items().OfType( SCH_SYMBOL_T ) )
347 {
348 SCH_SYMBOL* sym = static_cast<SCH_SYMBOL*>( item );
349 symbolsByUUID[ sym->m_Uuid.AsString() ] = sym;
350 }
351
352 BOOST_REQUIRE_EQUAL( symbolsByUUID.size(), 6u );
353
354 // The six symbol UUIDs (from our test schematic)
355 wxString U1_unit1 = "aa000001-0000-0000-0000-000000000001";
356 wxString U1_unit2 = "aa000001-0000-0000-0000-000000000002";
357 wxString U1_unit3 = "aa000001-0000-0000-0000-000000000003";
358 wxString U2_unit1 = "bb000002-0000-0000-0000-000000000001";
359 wxString U2_unit2 = "bb000002-0000-0000-0000-000000000002";
360 wxString U2_unit3 = "bb000002-0000-0000-0000-000000000003";
361
362 // Verify initial annotations
363 BOOST_CHECK_EQUAL( symbolsByUUID[U1_unit1]->GetRef( &sheetPath, true ), "U1A" );
364 BOOST_CHECK_EQUAL( symbolsByUUID[U1_unit2]->GetRef( &sheetPath, true ), "U1B" );
365 BOOST_CHECK_EQUAL( symbolsByUUID[U1_unit3]->GetRef( &sheetPath, true ), "U1C" );
366 BOOST_CHECK_EQUAL( symbolsByUUID[U2_unit1]->GetRef( &sheetPath, true ), "U2A" );
367 BOOST_CHECK_EQUAL( symbolsByUUID[U2_unit2]->GetRef( &sheetPath, true ), "U2B" );
368 BOOST_CHECK_EQUAL( symbolsByUUID[U2_unit3]->GetRef( &sheetPath, true ), "U2C" );
369
370 // Simulate the FIXED design block placement flow:
371 // Build locked groups BEFORE clearing, then use aResetAnnotation=true.
372 // This keeps multi-unit groups intact.
373 SCH_REFERENCE_LIST references;
374 SCH_MULTI_UNIT_REFERENCE_MAP lockedSymbols;
375
376 for( auto& [uuid, sym] : symbolsByUUID )
377 {
378 sheetPath.AppendSymbol( references, sym );
379 sheetPath.AppendMultiUnitSymbol( lockedSymbols, sym );
380 }
381
382 BOOST_REQUIRE_EQUAL( references.GetCount(), 6u );
383
384 // Locked groups should have entries for U1 and U2 (3 units each)
385 BOOST_REQUIRE_EQUAL( lockedSymbols.size(), 2u );
386 BOOST_CHECK( lockedSymbols.count( "U1" ) );
387 BOOST_CHECK( lockedSymbols.count( "U2" ) );
388 BOOST_CHECK_EQUAL( lockedSymbols["U1"].GetCount(), 3u );
389 BOOST_CHECK_EQUAL( lockedSymbols["U2"].GetCount(), 3u );
390
391 // Mark as needing reannotation (equivalent to aResetAnnotation=true path)
392 references.RemoveAnnotation();
393 references.SetRefDesTracker( m_schematic->Settings().m_refDesTracker );
394 references.SplitReferences();
395 references.Annotate( false, 0, 1, lockedSymbols, SCH_REFERENCE_LIST() );
396 references.UpdateAnnotation();
397
398 // All units of the same original symbol should share one reference designator.
399 // The exact number (U1 vs U2) depends on sort order, but the grouping must be preserved.
400 wxString u1a_ref = symbolsByUUID[U1_unit1]->GetRef( &sheetPath, false );
401 wxString u1b_ref = symbolsByUUID[U1_unit2]->GetRef( &sheetPath, false );
402 wxString u1c_ref = symbolsByUUID[U1_unit3]->GetRef( &sheetPath, false );
403 wxString u2a_ref = symbolsByUUID[U2_unit1]->GetRef( &sheetPath, false );
404 wxString u2b_ref = symbolsByUUID[U2_unit2]->GetRef( &sheetPath, false );
405 wxString u2c_ref = symbolsByUUID[U2_unit3]->GetRef( &sheetPath, false );
406
407 // All three units of symbol "A" (originally U1) must share the same ref number
408 BOOST_CHECK_EQUAL( u1a_ref, u1b_ref );
409 BOOST_CHECK_EQUAL( u1b_ref, u1c_ref );
410
411 // All three units of symbol "B" (originally U2) must share the same ref number
412 BOOST_CHECK_EQUAL( u2a_ref, u2b_ref );
413 BOOST_CHECK_EQUAL( u2b_ref, u2c_ref );
414
415 // The two symbols must have different reference designators
416 BOOST_CHECK_NE( u1a_ref, u2a_ref );
417}
418
419
const KIID m_Uuid
Definition eda_item.h:527
EE_TYPE OfType(KICAD_T aType) const
Definition sch_rtree.h:241
Definition kiid.h:49
wxString AsString() const
Definition kiid.cpp:244
A generic fixture for loading schematics and associated settings for qa tests.
std::unique_ptr< SCHEMATIC > m_schematic
virtual void LoadSchematic(const wxFileName &aFn)
virtual wxFileName SchematicQAPath(const wxString &aBaseName)
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition sch_item.h:168
Container to create a flattened list of symbols because in a complex hierarchy, a symbol can be used ...
void Annotate(bool aUseSheetNum, int aSheetIntervalId, int aStartNumber, const SCH_MULTI_UNIT_REFERENCE_MAP &aLockedUnitMap, const SCH_REFERENCE_LIST &aAdditionalRefs, bool aStartAtCurrent=false)
Set the reference designators in the list that have not been annotated.
void SetRefDesTracker(std::shared_ptr< REFDES_TRACKER > aTracker)
void SplitReferences()
Attempt to split all reference designators into a name (U) and number (1).
void RemoveAnnotation()
Treat all symbols in this list as non-annotated.
void AddItem(const SCH_REFERENCE &aItem)
void UpdateAnnotation()
Update the symbol references for the schematic project (or the current sheet).
EE_RTREE & Items()
Get the full RTree, usually for iterating.
Definition sch_screen.h:119
Handle access to a stack of flattened SCH_SHEET objects by way of a path for creating a flattened sch...
void AppendMultiUnitSymbol(SCH_MULTI_UNIT_REFERENCE_MAP &aRefList, SCH_SYMBOL *aSymbol, bool aIncludePowerSymbols=true) const
Append a SCH_REFERENCE_LIST object to aRefList based on aSymbol, storing same-reference set of multi-...
SCH_SCREEN * LastScreen()
void AppendSymbol(SCH_REFERENCE_LIST &aReferences, SCH_SYMBOL *aSymbol, bool aIncludePowerSymbols=true, bool aForceIncludeOrphanSymbols=false) const
Append a SCH_REFERENCE object to aReferences based on aSymbol.
Schematic symbol object.
Definition sch_symbol.h:76
const wxString GetValue(bool aResolve, const SCH_SHEET_PATH *aPath, bool aAllowExtraText, const wxString &aVariantName=wxEmptyString) const override
void SetValueFieldText(const wxString &aValue, const SCH_SHEET_PATH *aInstance=nullptr, const wxString &aVariantName=wxEmptyString)
const wxString GetRef(const SCH_SHEET_PATH *aSheet, bool aIncludeUnit=false) const override
void checkAnnotation(std::vector< REANNOTATED_REFERENCE > aRefs)
SCH_MULTI_UNIT_REFERENCE_MAP m_lockedRefs
void loadTestCase(wxString aSchematicRelativePath, std::vector< REANNOTATED_REFERENCE > aRefs)
void setupRefDesTrackerWithPreloads(const std::vector< std::string > &preloads)
SCH_SYMBOL * getSymbolByKIID(wxString aKIID, SCH_SHEET_PATH *aSymbolPath)
static void LoadSchematic(SCHEMATIC *aSchematic, SCH_SHEET *aRootSheet, const wxString &aFileName)
@ INCREMENTAL_BY_REF
Annotate incrementally using the first free reference number.
Definition of the SCH_SHEET_PATH and SCH_SHEET_LIST classes for Eeschema.
std::map< wxString, SCH_REFERENCE_LIST > SCH_MULTI_UNIT_REFERENCE_MAP
Container to map reference designators for multi-unit parts.
std::vector< REANNOTATED_REFERENCE > m_ExpectedReannotations
wxString m_KIID
KIID of the symbol to reannotate.
wxString m_ExpectedRef
Expected Reference Designator (after reannotating)
wxString m_OriginalRef
Original Reference Designator (prior to reannotating)
bool m_IncludeInReannotationList
True if reference is "selected" for reannotation.
std::vector< REANNOTATED_REFERENCE > m_ExpectedReannotations
BOOST_REQUIRE(intersection.has_value()==c.ExpectedIntersection.has_value())
BOOST_AUTO_TEST_SUITE_END()
static const std::vector< REANNOTATION_CASE > reannotationCases
BOOST_AUTO_TEST_CASE(Reannotate)
static const std::vector< DUPLICATE_REANNOTATION_CASE > reannotateDuplicatesCases
BOOST_CHECK_EQUAL(result, "25.4")
@ SCH_SYMBOL_T
Definition typeinfo.h:176