KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_net_chain_recalc_refresh.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
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 */
19
20#include <boost/test/unit_test.hpp>
21
24
25#include <connection_graph.h>
26#include <advanced_config.h>
27#include <schematic.h>
28#include <sch_netchain.h>
29#include <sch_sheet.h>
30#include <sch_screen.h>
31#include <sch_pin.h>
32#include <sch_symbol.h>
33#include <lib_symbol.h>
34#include <fstream>
35#include <wx/filename.h>
37#include <locale_io.h>
38#include <scoped_set_reset.h>
39
40
41// Regression for [H-1]. CONNECTION_GRAPH::Reset() clears every committed chain's
42// non-owning symbol pointer set to drop stale SCH_SYMBOL references before the rest
43// of the graph is rebuilt. RebuildNetChains() then iterates the persisted override
44// maps and used to skip any name that was already in m_committedNetChains, leaving
45// the chain with an empty m_symbols and stale derived state. Downstream consumers
46// (netlist export, the setup panel, the tuner cache) trusted those caches.
47//
48// The fix refreshes the committed chain in place during the rebuild restore pass
49// rather than skipping it. This test exercises the full Recalculate(unconditional)
50// cycle and asserts the committed chain still has populated m_symbols and m_nets
51// afterwards.
59
60
61BOOST_FIXTURE_TEST_CASE( NetChain_RefreshCommittedChainAcrossUnconditionalRecalc,
63{
65 KI_TEST::LoadSchematic( m_settingsManager, wxString( "net_chains_four_nets" ), m_schematic );
66
67 CONNECTION_GRAPH* graph = m_schematic->ConnectionGraph();
68 BOOST_REQUIRE( graph );
69
70 SCH_SHEET_LIST sheets = m_schematic->BuildSheetListSortedByPageNumbers();
71 graph->Recalculate( sheets, /*aUnconditional=*/true );
72
73 const auto& potentials = graph->GetPotentialNetChains();
74 BOOST_REQUIRE( !potentials.empty() );
75
76 SCH_NETCHAIN* potential = potentials.front().get();
77 BOOST_REQUIRE( potential );
78
79 const std::set<wxString> originalNets = potential->GetNets();
80 const std::size_t originalNetCnt = originalNets.size();
81 const std::size_t originalSymCnt = potential->GetSymbols().size();
82
83 BOOST_REQUIRE_GT( originalNetCnt, 0u );
84 BOOST_REQUIRE_GT( originalSymCnt, 0u );
85
86 SCH_NETCHAIN* committed = graph->CreateNetChainFromPotential( potential, wxT( "REFRESH_TEST" ) );
87 BOOST_REQUIRE( committed );
88 BOOST_REQUIRE_EQUAL( committed->GetNets().size(), originalNetCnt );
89 BOOST_REQUIRE_EQUAL( committed->GetSymbols().size(), originalSymCnt );
90
91 // The hazard. Recalculate(true) -> Reset() clears m_symbols on every committed chain,
92 // and the rebuild restore pass used to skip names already present in
93 // m_committedNetChains, leaving the chain permanently empty.
94 graph->Recalculate( sheets, /*aUnconditional=*/true );
95
96 SCH_NETCHAIN* refreshed = graph->GetNetChainByName( wxT( "REFRESH_TEST" ) );
97 BOOST_REQUIRE_MESSAGE( refreshed,
98 "Committed chain disappeared across unconditional Recalculate" );
99
100 BOOST_CHECK_MESSAGE( !refreshed->GetSymbols().empty(),
101 "Committed chain has empty m_symbols after unconditional Recalculate; "
102 "Reset() cleared the cache and RebuildNetChains() failed to refresh it" );
103
104 BOOST_CHECK_MESSAGE( !refreshed->GetNets().empty(),
105 "Committed chain has empty m_nets after unconditional Recalculate" );
106
107 BOOST_CHECK_EQUAL( refreshed->GetNets().size(), originalNetCnt );
108 BOOST_CHECK_EQUAL( refreshed->GetSymbols().size(), originalSymCnt );
109
110 // Terminal pin/ref data must also survive the round trip; the setup panel and the PCB
111 // tuner walk these to find the bookend pads.
112 BOOST_CHECK( !refreshed->GetTerminalRef( 0 ).IsEmpty() );
113 BOOST_CHECK( !refreshed->GetTerminalRef( 1 ).IsEmpty() );
114
115 // A second round trip must remain stable (no slow leak of derived state).
116 graph->Recalculate( sheets, /*aUnconditional=*/true );
117
118 SCH_NETCHAIN* twice = graph->GetNetChainByName( wxT( "REFRESH_TEST" ) );
119 BOOST_REQUIRE( twice );
120 BOOST_CHECK( !twice->GetSymbols().empty() );
121 BOOST_CHECK( !twice->GetNets().empty() );
122 BOOST_CHECK_EQUAL( twice->GetNets().size(), originalNetCnt );
123 BOOST_CHECK_EQUAL( twice->GetSymbols().size(), originalSymCnt );
124}
125
126
127// Companion check. User-set netclass and color overrides live on the SCH_NETCHAIN itself
128// (not in the override map) once the chain is committed. The in-place refresh must NOT
129// reset them.
130BOOST_FIXTURE_TEST_CASE( NetChain_RefreshPreservesOverridesOnCommittedChain,
132{
134 KI_TEST::LoadSchematic( m_settingsManager, wxString( "net_chains_four_nets" ), m_schematic );
135
136 CONNECTION_GRAPH* graph = m_schematic->ConnectionGraph();
137 BOOST_REQUIRE( graph );
138
139 SCH_SHEET_LIST sheets = m_schematic->BuildSheetListSortedByPageNumbers();
140 graph->Recalculate( sheets, /*aUnconditional=*/true );
141
142 const auto& potentials = graph->GetPotentialNetChains();
143 BOOST_REQUIRE( !potentials.empty() );
144
145 SCH_NETCHAIN* committed = graph->CreateNetChainFromPotential( potentials.front().get(),
146 wxT( "OVERRIDE_TEST" ) );
147 BOOST_REQUIRE( committed );
148
149 committed->SetNetClass( wxT( "DDR_DATA" ) );
150 committed->SetColor( KIGFX::COLOR4D( 1.0, 0.5, 0.25, 1.0 ) );
151
152 graph->Recalculate( sheets, /*aUnconditional=*/true );
153
154 SCH_NETCHAIN* refreshed = graph->GetNetChainByName( wxT( "OVERRIDE_TEST" ) );
155 BOOST_REQUIRE( refreshed );
156
157 BOOST_CHECK_EQUAL( refreshed->GetNetClass(), wxT( "DDR_DATA" ) );
158 BOOST_CHECK( refreshed->GetColor() != KIGFX::COLOR4D::UNSPECIFIED );
159 BOOST_CHECK_CLOSE( refreshed->GetColor().r, 1.0, 1e-6 );
160 BOOST_CHECK_CLOSE( refreshed->GetColor().g, 0.5, 1e-6 );
161 BOOST_CHECK_CLOSE( refreshed->GetColor().b, 0.25, 1e-6 );
162}
163
164
165BOOST_FIXTURE_TEST_CASE( NetChain_RefreshPreservesTerminalPinOverride, NETCHAIN_RECALC_REFRESH_FIXTURE )
166{
167 auto& enabled = const_cast<ADVANCED_CFG&>( ADVANCED_CFG::GetCfg() ).m_ConnectivityEngine;
168 SCOPED_SET_RESET restore( enabled, enabled );
169
170 for( bool backend : { false, true } )
171 {
172 BOOST_TEST_CONTEXT( "ConnectivityEngine=" << backend )
173 {
174 enabled = backend;
175 SETTINGS_MANAGER settingsManager;
176 std::unique_ptr<SCHEMATIC> schematic;
177 LOCALE_IO locale;
178 KI_TEST::LoadSchematic( settingsManager, "net_chains_four_nets", schematic );
179 schematic->RebuildConnectivity();
180 auto& chains = schematic->NetChains();
181 const auto sheets = schematic->BuildSheetListSortedByPageNumbers();
182 BOOST_REQUIRE_EQUAL( sheets.size(), 1u );
183 const auto& path = sheets.front();
184 BOOST_REQUIRE( !chains.GetPotentialNetChains().empty() );
185 auto* chain = chains.CreateNetChainFromPotential( chains.GetPotentialNetChains().front().get(),
186 "TERM_OVERRIDE" );
188 const KIID originalA = chain->GetTerminalPinA();
189 const KIID originalB = chain->GetTerminalPinB();
190 auto* original = dynamic_cast<SCH_PIN*>( schematic->ResolveItem( originalA, nullptr, true ) );
191 BOOST_REQUIRE( original );
192 const auto connection = original->GetConnectionName( &path );
193 BOOST_REQUIRE( connection );
194 SCH_PIN* replacement = nullptr;
195
196 for( SCH_ITEM* item : path.LastScreen()->Items().OfType( SCH_SYMBOL_T ) )
197 {
198 for( SCH_PIN* pin : static_cast<SCH_SYMBOL*>( item )->GetPins( &path ) )
199 {
200 const auto net = pin->GetConnectionName( &path );
201
202 if( pin->m_Uuid != originalA && pin->m_Uuid != originalB
203 && net && *net == *connection )
204 {
205 replacement = pin;
206 break;
207 }
208 }
209
210 if( replacement )
211 break;
212 }
213
214 BOOST_REQUIRE( replacement );
215 const KIID replacementId = replacement->m_Uuid;
216 wxString replacementRef = replacement->GetParentSymbol()->GetRef( &path );
217 const wxString replacementNumber = replacement->GetNumber();
218 BOOST_CHECK( !chains.ReplaceNetChainTerminalPin( { "TERM_OVERRIDE", 1, originalA, path.Path() } ) );
219 BOOST_REQUIRE( chains.ReplaceNetChainTerminalPin( { "TERM_OVERRIDE", 1, replacementId, path.Path() } ) );
220 BOOST_CHECK( chain->GetTerminalPinA() == originalA );
221 BOOST_CHECK( chain->GetTerminalPinB() == replacementId );
222 BOOST_REQUIRE( chains.ReplaceNetChainTerminalPin( { "TERM_OVERRIDE", 1, originalB, path.Path() } ) );
223 BOOST_REQUIRE( chains.ReplaceNetChainTerminalPin( { "TERM_OVERRIDE", 0, replacementId, path.Path() } ) );
224 BOOST_CHECK_EQUAL( chain->GetTerminalRef( 0 ), replacementRef );
225 BOOST_CHECK( chain->GetTerminalPath( 0 ) == path.Path() );
226 BOOST_CHECK( !chains.ReplaceNetChainTerminalPin( { "TERM_OVERRIDE", 2, originalA, path.Path() } ) );
227
228 for( int pass = 0; pass < 2; ++pass )
229 {
230 schematic->RebuildConnectivity();
231 BOOST_CHECK( chains.GetNetChainByName( "TERM_OVERRIDE" ) == chain );
232 BOOST_CHECK( chain->GetTerminalPinA() == replacementId );
233 BOOST_CHECK( chain->GetTerminalPinB() == originalB );
234 BOOST_CHECK( chain->GetTerminalPath( 0 ) == path.Path() );
235 BOOST_CHECK_EQUAL( chain->GetTerminalRef( 0 ), replacementRef );
236 BOOST_CHECK_EQUAL( chain->GetTerminalPinNum( 0 ), replacementNumber );
237 }
238
239 auto* replacementSymbol = static_cast<SCH_SYMBOL*>( replacement->GetParentSymbol() );
240 auto library = replacementSymbol->GetLibSymbolRef()->Flatten();
241 replacementSymbol->SetLibSymbol( library.release() );
242 schematic->RebuildConnectivity();
243 BOOST_CHECK( chain->GetTerminalPinA() == replacementId );
244 BOOST_CHECK( chain->GetTerminalPath( 0 ) == path.Path() );
245 BOOST_CHECK_EQUAL( chain->GetTerminalRef( 0 ), replacementRef );
246 BOOST_CHECK_EQUAL( chain->GetNets().size(), 4u );
247
248 replacementRef = "R900";
249 static_cast<SCH_SYMBOL*>( replacement->GetParentSymbol() )->SetRef( &path, replacementRef );
250 schematic->RebuildConnectivity();
251 BOOST_CHECK( chains.GetNetChainByName( "TERM_OVERRIDE" ) == chain );
252 BOOST_CHECK( chain->GetTerminalPinA() == replacementId );
253 BOOST_CHECK_EQUAL( chain->GetTerminalRef( 0 ), replacementRef );
254 BOOST_CHECK_EQUAL( chain->GetTerminalPinNum( 0 ), replacementNumber );
255
256 replacementRef = "R901";
257 static_cast<SCH_SYMBOL*>( replacement->GetParentSymbol() )->SetRef( &path, replacementRef );
258
259 const wxString file = wxFileName::CreateTempFileName( "netchain-terminal-" );
260 KI_TEST::DumpSchematicToFile( *schematic, *schematic->GetTopLevelSheet(), file.ToStdString() );
261 std::ifstream stream( file.ToStdString() );
262 BOOST_REQUIRE( stream.good() );
263 auto reloaded = KI_TEST::ReadSchematicFromStream( stream, &schematic->Project() );
264 BOOST_REQUIRE( reloaded );
265 reloaded->RebuildConnectivity();
266 auto* restored = reloaded->NetChains().GetNetChainByName( "TERM_OVERRIDE" );
267 BOOST_REQUIRE( restored );
268 BOOST_CHECK( restored->GetTerminalPinA() == replacementId );
269 BOOST_CHECK( restored->GetTerminalPinB() == originalB );
270 BOOST_CHECK_EQUAL( restored->GetTerminalRef( 0 ), replacementRef );
271 BOOST_CHECK_EQUAL( restored->GetTerminalPinNum( 0 ), replacementNumber );
272 stream.close();
273 wxRemoveFile( file );
274
275 auto* removedSymbol = static_cast<SCH_SYMBOL*>( replacement->GetParentSymbol() );
276 path.LastScreen()->Remove( removedSymbol );
277 std::unique_ptr<SCH_SYMBOL> removed( removedSymbol );
278 static_cast<SCH_SYMBOL*>( original->GetParentSymbol() )->SetRef( &path, replacementRef );
279 schematic->RebuildConnectivity();
280 BOOST_CHECK( chain->GetTerminalPinA() == replacementId );
281 BOOST_CHECK( chain->GetTerminalPath( 0 ) == path.Path() );
282 BOOST_CHECK( !chain->GetSymbols().contains( removed.get() ) );
283 BOOST_CHECK( chain->GetNets().empty() );
284 }
285 }
286}
static const ADVANCED_CFG & GetCfg()
Get the singleton instance's config, which is shared by all consumers.
Calculate the connectivity of a schematic and generate netlists.
SCH_NETCHAIN * GetNetChainByName(const wxString &aName)
SCH_NETCHAIN * CreateNetChainFromPotential(SCH_NETCHAIN *aPotential, const wxString &aName)
Promote a potential net chain to an actual user net chain with the provided name.
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.
const std::vector< std::unique_ptr< SCH_NETCHAIN > > & GetPotentialNetChains() const
Potential net chains are inferred groupings produced by RebuildNetChains() but not yet user-committed...
const KIID m_Uuid
Definition eda_item.h:599
A color representation with 4 components: red, green, blue, alpha.
Definition color4d.h:101
double r
Red component.
Definition color4d.h:391
double g
Green component.
Definition color4d.h:392
static const COLOR4D UNSPECIFIED
For legacy support; used as a value to indicate color hasn't been set yet.
Definition color4d.h:400
double b
Blue component.
Definition color4d.h:393
Definition kiid.h:46
Instantiate the current locale within a scope in which you are expecting exceptions to be thrown.
Definition locale_io.h:37
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition sch_item.h:170
const SYMBOL * GetParentSymbol() const
Definition sch_item.cpp:350
A net chain is a collection of nets that are connected together through passive components.
const std::set< wxString > & GetNets() const
const wxString & GetTerminalRef(int aIdx) const
const wxString & GetNetClass() const
const KIGFX::COLOR4D & GetColor() const
const std::set< class SCH_SYMBOL * > & GetSymbols() const
void SetNetClass(const wxString &aNetClass)
Net chains may override the netclass applied to every member net.
void SetColor(const KIGFX::COLOR4D &aColor)
Optional display color for the chain.
const wxString & GetNumber() const
Definition sch_pin.h:144
A container for handling SCH_SHEET_PATH objects in a flattened hierarchy.
Schematic symbol object.
Definition sch_symbol.h:73
void SetRef(const SCH_SHEET_PATH *aSheet, const wxString &aReference)
Set the reference for the given sheet path for this symbol.
RAII class that sets an value at construction and resets it to the original value at destruction.
virtual const wxString GetRef(const SCH_SHEET_PATH *aSheet, bool aIncludeUnit=false) const =0
std::unique_ptr< SCHEMATIC > ReadSchematicFromStream(std::istream &aStream, PROJECT *aProject)
void LoadSchematic(SETTINGS_MANAGER &aSettingsManager, const wxString &aRelPath, std::unique_ptr< SCHEMATIC > &aSchematic)
void DumpSchematicToFile(SCHEMATIC &aSchematic, SCH_SHEET &aSheet, const std::string &aFilename)
std::vector< FAB_LAYER_COLOR > dummy
BOOST_REQUIRE(intersection.has_value()==c.ExpectedIntersection.has_value())
std::string path
KIBIS_PIN * pin
BOOST_FIXTURE_TEST_CASE(NetChain_RefreshCommittedChainAcrossUnconditionalRecalc, NETCHAIN_RECALC_REFRESH_FIXTURE)
const SHAPE_LINE_CHAIN chain
BOOST_TEST_CONTEXT("Test Clearance")
BOOST_CHECK_EQUAL(result, "25.4")
@ SCH_SYMBOL_T
Definition typeinfo.h:168