KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_incremental_netlister.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 3
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, see <https://www.gnu.org/licenses/>.
18 */
19
20
23
24#include <connection_graph.h>
25#include <schematic.h>
26#include <sch_sheet.h>
27#include <sch_screen.h>
28#include <sch_symbol.h>
29#include <sch_pin.h>
30#include <lib_symbol.h>
32#include <locale_io.h>
33
35{
38
40 std::unique_ptr<SCHEMATIC> m_schematic;
41};
42
43BOOST_FIXTURE_TEST_CASE( DestroyingAnotherSchematicPreservesPinCleanup, CONNECTIVITY_TEST_FIXTURE )
44{
45 KI_TEST::LoadSchematic( m_settingsManager, "issue7203", m_schematic );
46 auto* graph = m_schematic->ConnectionGraph();
47 graph->Recalculate( m_schematic->Hierarchy(), true );
48 SCH_SCREEN* screen = m_schematic->Hierarchy().front().LastScreen();
49 auto symbols = screen->Items().OfType( SCH_SYMBOL_T );
50 BOOST_REQUIRE( symbols.begin() != symbols.end() );
51 auto* symbol = static_cast<SCH_SYMBOL*>( *symbols.begin() );
52 std::vector<SCH_PIN*> pins = symbol->GetGraphicalPins( ALL_UNITS, ALL_BODY_STYLES );
53 BOOST_REQUIRE( !pins.empty() );
54 BOOST_REQUIRE( graph->GetSubgraphForItem( pins.front() ) );
55
56 {
57 SCHEMATIC other( nullptr );
58 }
59
60 screen->Remove( symbol );
61 delete symbol;
62
63 for( SCH_PIN* pin : pins )
64 BOOST_CHECK( !graph->GetSubgraphForItem( pin ) );
65}
66
67
68BOOST_FIXTURE_TEST_CASE( PinCleanupHandlesMultipleGraphsAndExpiredOwners, CONNECTIVITY_TEST_FIXTURE )
69{
70 KI_TEST::LoadSchematic( m_settingsManager, "issue7203", m_schematic );
71
72 CONNECTION_GRAPH* mainGraph = m_schematic->ConnectionGraph();
73 const SCH_SHEET_LIST paths = m_schematic->Hierarchy();
74 mainGraph->Recalculate( paths, true );
75 auto expired = std::make_unique<CONNECTION_GRAPH>( m_schematic.get() );
76 expired->Recalculate( paths, true );
77 CONNECTION_GRAPH otherGraph( m_schematic.get() );
78 otherGraph.Recalculate( paths, true );
79 SCH_SCREEN* screen = paths.front().LastScreen();
80 auto symbols = screen->Items().OfType( SCH_SYMBOL_T );
81 BOOST_REQUIRE( symbols.begin() != symbols.end() );
82
83 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( *symbols.begin() );
84 const std::vector<SCH_PIN*> pins = symbol->GetGraphicalPins( ALL_UNITS, ALL_BODY_STYLES );
85 BOOST_REQUIRE( !pins.empty() );
86
87 for( SCH_PIN* pin : pins )
88 {
89 BOOST_REQUIRE( mainGraph->GetSubgraphForItem( pin ) );
90 BOOST_REQUIRE( otherGraph.GetSubgraphForItem( pin ) );
91 }
92
93 // ASAN detects stale owner access during symbol destruction below.
94 expired.reset();
95 screen->Remove( symbol );
96 delete symbol;
97
98 for( SCH_PIN* pin : pins )
99 {
100 BOOST_CHECK( !mainGraph->GetSubgraphForItem( pin ) );
101 BOOST_CHECK( !otherGraph.GetSubgraphForItem( pin ) );
102 }
103}
104
105
107{
109
110 // Check for Errors when using global labels
111 std::vector<wxString> tests = {// "incremental_test",
112 // "issue10430",
113 // "issue10926_1",
114 // "issue11926",
115 // "issue12505",
116 // "issue12814",
117 // "issue13112",
118 // "issue13162",
119 // "issue13212",
120 // "issue13431",
121 // "issue13591",
122 // "issue16223",
123 // "issue6588",
124 "issue7203"};//,
125 // "issue9367"};
126
127 for( const wxString& test : tests )
128 {
129 KI_TEST::LoadSchematic( m_settingsManager, test, m_schematic );
130
131 SCH_SHEET_LIST sheets = m_schematic->BuildSheetListSortedByPageNumbers();
132
133 for( const SCH_SHEET_PATH& path : sheets )
134 {
135 for( size_t ii = 0; ii < path.size(); ++ii )
136 {
137 const SCH_SHEET* sheet = path.GetSheet( ii );
138 const SCH_SCREEN* screen = sheet->GetScreen();
139 std::vector<SCH_ITEM*> items;
140
141 for( SCH_ITEM* item : screen->Items() )
142 {
143 if( !item->IsConnectable() )
144 {
145 continue;
146 }
147
148 if( item->Type() == SCH_SYMBOL_T )
149 {
150 for( SCH_PIN* pin : static_cast<SCH_SYMBOL*>( item )->GetGraphicalPins( ALL_UNITS, ALL_BODY_STYLES ) )
151 {
152 items.push_back( pin );
153 }
154 }
155 else
156 {
157 items.push_back( item );
158 }
159 }
160
161 for( SCH_ITEM* item : items )
162 {
163 const std::vector<SCH_ITEM*>& conn_items = item->ConnectedItems( path );
164 SCH_CONNECTION* conn = item->Connection();
165 wxString netname = conn ? conn->GetNetName() : wxString( "NoNet" );
166 int subgraph = conn ? conn->SubgraphCode() : -1;
167
168 BOOST_TEST_MESSAGE( test.ToStdString()
169 << ": Item " << item->GetFriendlyName().ToStdString()
170 << " in net " << netname.ToStdString() << " subgraph " << subgraph
171 << " has " << conn_items.size() << " connections" );
172
173 if( !conn )
174 continue;
175
176 if( !item->IsConnectable() )
177 continue;
178
179 std::vector<SCH_ITEM*> prev_items = item->ConnectedItems( path );
180 std::sort( prev_items.begin(), prev_items.end() );
181 alg::remove_duplicates( prev_items );
182
183 std::set<std::pair<SCH_SHEET_PATH, SCH_ITEM*>> all_items =
184 m_schematic->ConnectionGraph()->ExtractAffectedItems( { item } );
185 all_items.insert( { path, item } );
186 BOOST_TEST_MESSAGE( test.ToStdString()
187 << ": Item " << item->GetFriendlyName().ToStdString()
188 << " in net " << netname.ToStdString()
189 << " has " << all_items.size() << " affected items" );
190
191 CONNECTION_GRAPH new_graph( m_schematic.get() );
192
193 new_graph.SetLastCodes( m_schematic->ConnectionGraph() );
194
195 for( auto&[ apath, aitem ] : all_items )
196 {
197 wxCHECK2( aitem, continue );
198 aitem->SetConnectivityDirty();
199 }
200
201 new_graph.Recalculate( sheets, false );
202 m_schematic->ConnectionGraph()->Merge( new_graph );
203
204 std::vector<SCH_ITEM*> curr_items = item->ConnectedItems( path );
205 std::sort( curr_items.begin(), curr_items.end() );
206 alg::remove_duplicates( curr_items );
207
208 BOOST_CHECK_MESSAGE( prev_items == curr_items,
209 test.ToStdString()
210 << ": Item " << item->GetFriendlyName().ToStdString()
211 << " in net " << netname.ToStdString()
212 << " changed from " << prev_items.size() << " to " << curr_items.size()
213 << " Location:" << item->GetPosition().x << "," << item->GetPosition().y );
214 }
215
216 }
217 }
218 }
219}
220
221
222// Reproducer for Sentry KICAD-4SJ / KICAD-10HY. A pin on a shared (multi-instantiated) sheet is
223// registered in one subgraph per sheet path, but the connection graph's item-to-subgraph map only
224// remembers one of them. When SCH_SYMBOL::UpdatePins() frees the pin after a library update that
225// dropped it, ~SCH_ITEM only cleans the mapped subgraph and the other instance keeps a dangling
226// driver, which a later recalculation hands to CONNECTION_SUBGRAPH::ResolveDrivers().
227BOOST_FIXTURE_TEST_CASE( SharedSheetUpdatePinsNoDanglingDriver, CONNECTIVITY_TEST_FIXTURE )
228{
230
231 KI_TEST::LoadSchematic( m_settingsManager, wxS( "netlists/complex_hierarchy_shared/complex_hierarchy" ),
232 m_schematic );
233
234 CONNECTION_GRAPH* graph = m_schematic->ConnectionGraph();
235 SCH_SHEET_LIST sheets = m_schematic->BuildSheetListSortedByPageNumbers();
236
237 std::map<SCH_SCREEN*, int> instanceCount;
238
239 for( const SCH_SHEET_PATH& path : sheets )
240 instanceCount[path.LastScreen()]++;
241
242 // Count how many retained subgraphs still reference an item. Pointer identity only; the
243 // pointer may be freed by the time this runs.
244 auto countRefs =
245 [&]( SCH_ITEM* aItem ) -> int
246 {
247 int refs = 0;
248
249 for( const auto& [key, subgraphs] : graph->GetNetMap() )
250 {
251 for( CONNECTION_SUBGRAPH* sg : subgraphs )
252 {
253 if( sg->GetItems().count( aItem ) )
254 refs++;
255 }
256 }
257
258 return refs;
259 };
260
261 // Find a pin on a shared screen that the initial full rebuild registered once per sheet path
262 SCH_SYMBOL* symbol = nullptr;
263 SCH_PIN* victim = nullptr;
264
265 for( auto& [screen, count] : instanceCount )
266 {
267 if( count < 2 )
268 continue;
269
270 for( SCH_ITEM* item : screen->Items().OfType( SCH_SYMBOL_T ) )
271 {
272 SCH_SYMBOL* candidate = static_cast<SCH_SYMBOL*>( item );
273
274 if( !candidate->GetLibSymbolRef() || candidate->GetGraphicalPins( ALL_UNITS, ALL_BODY_STYLES ).size() < 2 )
275 continue;
276
277 for( SCH_PIN* pin : candidate->GetGraphicalPins( ALL_UNITS, ALL_BODY_STYLES ) )
278 {
279 if( countRefs( pin ) >= 2 )
280 {
281 symbol = candidate;
282 victim = pin;
283 break;
284 }
285 }
286
287 if( symbol )
288 break;
289 }
290
291 if( symbol )
292 break;
293 }
294
295 BOOST_REQUIRE_MESSAGE( symbol && victim, "No shared-sheet pin registered on multiple paths" );
296
297 wxString pinNumber = victim->GetNumber();
298 std::vector<SCH_PIN*> doomedPins = symbol->GetPinsByNumber( pinNumber );
299
300 BOOST_REQUIRE( !doomedPins.empty() );
301
302 std::vector<SCH_ITEM*> danglingCandidates( doomedPins.begin(), doomedPins.end() );
303
304 // Update the symbol from a library version that no longer has this pin. This mirrors
305 // Update Symbol from Library; SetLibSymbol() -> UpdatePins() frees the surplus SCH_PINs.
306 std::unique_ptr<LIB_SYMBOL> updated = symbol->GetLibSymbolRef()->Flatten();
307
308 for( SCH_PIN* libPin : updated->GetPinsByNumber( pinNumber ) )
309 updated->RemoveDrawItem( libPin );
310
311 symbol->SetLibSymbol( updated.release() );
312
313 BOOST_REQUIRE( symbol->GetPinsByNumber( pinNumber ).empty() );
314
315 // The freed pins must be gone from every retained subgraph, or the next incremental
316 // recalculation will dereference them from a thread pool worker
317 for( SCH_ITEM* freedPin : danglingCandidates )
318 {
319 BOOST_CHECK_MESSAGE( countRefs( freedPin ) == 0,
320 "Freed pin " << pinNumber.ToStdString()
321 << " still referenced by a retained subgraph" );
322 }
323}
324
325
326// Reproducer for issue 16836. A global label joining a local net to an existing multi-sheet net
327// used to rebuild only its own sheet, and the merge then dropped the rest of the net
328BOOST_FIXTURE_TEST_CASE( IncrementalMergeKeepsOtherSheetsOnNet, CONNECTIVITY_TEST_FIXTURE )
329{
331
332 KI_TEST::LoadSchematic( m_settingsManager, wxT( "issue16836/issue16836" ), m_schematic );
333
334 CONNECTION_GRAPH* graph = m_schematic->ConnectionGraph();
335 SCH_SHEET_LIST sheets = m_schematic->BuildSheetListSortedByPageNumbers();
336
337 const wxString globalNet = wxT( "+12V" );
338 const wxString localNet = wxT( "local_label_on_page7" );
339
340 auto sheetsOnNet =
341 [&]( const wxString& aNetName )
342 {
343 std::set<wxString> paths;
344
345 for( const CONNECTION_SUBGRAPH* sg : graph->GetAllSubgraphs( aNetName ) )
346 paths.insert( sg->GetSheet().PathAsString() );
347
348 return paths;
349 };
350
351 const std::set<wxString> before = sheetsOnNet( globalNet );
352
353 BOOST_REQUIRE_MESSAGE( before.size() > 1,
354 "Fixture net " << globalNet.ToStdString() << " must span several sheets" );
355
356 // The reporter left a free-standing local label on the page they edited, so dropping a global
357 // label onto it reproduces their step without guessing at geometry
358 SCH_LABEL* localLabel = nullptr;
359 SCH_SHEET_PATH localPath;
360
361 for( const SCH_SHEET_PATH& path : sheets )
362 {
363 for( SCH_ITEM* item : path.LastScreen()->Items().OfType( SCH_LABEL_T ) )
364 {
365 if( static_cast<SCH_LABEL*>( item )->GetText() == localNet )
366 {
367 localLabel = static_cast<SCH_LABEL*>( item );
368 localPath = path;
369 }
370 }
371 }
372
373 BOOST_REQUIRE_MESSAGE( localLabel, "Fixture must carry the local label from the issue report" );
374
375 SCH_GLOBALLABEL* newLabel = new SCH_GLOBALLABEL( localLabel->GetPosition(), globalNet );
376 localPath.LastScreen()->Append( newLabel );
377
378 // Mirror the incremental branch of SCHEMATIC::RecalculateConnections()
379 std::set<SCH_ITEM*> changed = { newLabel, localLabel };
380
381 std::set<std::pair<SCH_SHEET_PATH, SCH_ITEM*>> affected = graph->ExtractAffectedItems( changed );
382
383 affected.insert( { localPath, newLabel } );
384 affected.insert( { localPath, localLabel } );
385
386 for( const auto& [path, item] : affected )
387 item->SetConnectivityDirty();
388
389 CONNECTION_GRAPH new_graph( m_schematic.get() );
390 new_graph.SetLastCodes( graph );
391 new_graph.Recalculate( sheets, false );
392 graph->Merge( new_graph );
393
394 const std::set<wxString> after = sheetsOnNet( globalNet );
395
396 for( const wxString& path : before )
397 {
398 BOOST_CHECK_MESSAGE( after.count( path ),
399 "Sheet " << path.ToStdString() << " dropped from net "
400 << globalNet.ToStdString() << " (" << before.size() << " -> "
401 << after.size() << " sheets)" );
402 }
403
404 // Rebuilding only part of the net would also leave the survivors on their old net code, which
405 // splits the net for every consumer of GetNetMap()
406 std::set<int> codes;
407
408 for( const auto& [key, subgraphs] : graph->GetNetMap() )
409 {
410 if( key.Name == globalNet )
411 codes.insert( key.Netcode );
412 }
413
414 BOOST_CHECK_MESSAGE( codes.size() == 1, "Net " << globalNet.ToStdString() << " split across "
415 << codes.size() << " net codes" );
416}
Calculate the connectivity of a schematic and generate netlists.
const NET_MAP & GetNetMap() const
const std::vector< CONNECTION_SUBGRAPH * > & GetAllSubgraphs(const wxString &aNetName) const
void Recalculate(const SCH_SHEET_LIST &aSheetList, bool aUnconditional=false, std::function< void(SCH_ITEM *)> *aChangedItemHandler=nullptr, PROGRESS_REPORTER *aProgressReporter=nullptr)
Update the connection graph for the given list of sheets.
std::set< std::pair< SCH_SHEET_PATH, SCH_ITEM * > > ExtractAffectedItems(const std::set< SCH_ITEM * > &aItems)
For a set of items, this will remove the connected items and their associated data including subgraph...
void SetLastCodes(const CONNECTION_GRAPH *aOther)
void Merge(CONNECTION_GRAPH &aGraph)
Combine the input graph contents into the current graph.
CONNECTION_SUBGRAPH * GetSubgraphForItem(SCH_ITEM *aItem) const
A subgraph is a set of items that are electrically connected on a single sheet.
EE_TYPE OfType(KICAD_T aType) const
Definition sch_rtree.h:248
std::unique_ptr< LIB_SYMBOL > Flatten() const
Return a flattened symbol inheritance to the caller.
Instantiate the current locale within a scope in which you are expecting exceptions to be thrown.
Definition locale_io.h:37
Holds all the data relating to one schematic.
Definition schematic.h:149
Each graphical item can have a SCH_CONNECTION describing its logical connection (to a bus or net).
wxString GetNetName() const
int SubgraphCode() const
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition sch_item.h:170
const wxString & GetNumber() const
Definition sch_pin.h:144
void Append(SCH_ITEM *aItem, bool aUpdateLibSymbol=true)
EE_RTREE & Items()
Get the full RTree, usually for iterating.
Definition sch_screen.h:122
bool Remove(SCH_ITEM *aItem, bool aUpdateLibSymbol=true)
Remove aItem from the schematic associated with this screen.
A container for handling SCH_SHEET_PATH objects in a flattened hierarchy.
Handle access to a stack of flattened SCH_SHEET objects by way of a path for creating a flattened sch...
SCH_SCREEN * LastScreen()
Sheet symbol placed in a schematic, and is the entry point for a sub schematic.
Definition sch_sheet.h:48
SCH_SCREEN * GetScreen() const
Definition sch_sheet.h:145
Schematic symbol object.
Definition sch_symbol.h:73
std::vector< SCH_PIN * > GetGraphicalPins(int aUnit, int aBodyStyle) const override
std::vector< SCH_PIN * > GetPinsByNumber(const wxString &aNumber) const
Find all symbol pins with the given number.
std::unique_ptr< LIB_SYMBOL > & GetLibSymbolRef()
Definition sch_symbol.h:182
void SetLibSymbol(LIB_SYMBOL *aLibSymbol)
Set this schematic symbol library symbol reference to aLibSymbol.
VECTOR2I GetPosition() const override
Definition sch_text.h:143
void LoadSchematic(SETTINGS_MANAGER &aSettingsManager, const wxString &aRelPath, std::unique_ptr< SCHEMATIC > &aSchematic)
void remove_duplicates(_Container &__c)
Deletes all duplicate values from __c.
Definition kicad_algo.h:157
std::vector< FAB_LAYER_COLOR > dummy
std::unique_ptr< SCHEMATIC > m_schematic
#define ALL_UNITS
Definition symbol.h:150
#define ALL_BODY_STYLES
Definition symbol.h:151
BOOST_REQUIRE(intersection.has_value()==c.ExpectedIntersection.has_value())
BOOST_FIXTURE_TEST_CASE(DestroyingAnotherSchematicPreservesPinCleanup, CONNECTIVITY_TEST_FIXTURE)
std::string path
KIBIS_PIN * pin
BOOST_TEST_MESSAGE("Polyline has "<< chain.PointCount()<< " points")
@ SCH_SYMBOL_T
Definition typeinfo.h:168
@ SCH_LABEL_T
Definition typeinfo.h:163