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 <schematic.h>
27#include <sch_netchain.h>
28#include <sch_sheet.h>
29#include <sch_screen.h>
30#include <sch_pin.h>
31#include <sch_symbol.h>
32#include <lib_symbol.h>
33#include <fstream>
34#include <wx/filename.h>
36#include <locale_io.h>
37
38
39// Regression for [H-1]. CONNECTION_GRAPH::Reset() clears every committed chain's
40// non-owning symbol pointer set to drop stale SCH_SYMBOL references before the rest
41// of the graph is rebuilt. RebuildNetChains() then iterates the persisted override
42// maps and used to skip any name that was already in m_committedNetChains, leaving
43// the chain with an empty m_symbols and stale derived state. Downstream consumers
44// (netlist export, the setup panel, the tuner cache) trusted those caches.
45//
46// The fix refreshes the committed chain in place during the rebuild restore pass
47// rather than skipping it. This test exercises the full Recalculate(unconditional)
48// cycle and asserts the committed chain still has populated m_symbols and m_nets
49// afterwards.
57
58
59BOOST_FIXTURE_TEST_CASE( NetChain_RefreshCommittedChainAcrossUnconditionalRecalc,
61{
63 KI_TEST::LoadSchematic( m_settingsManager, wxString( "net_chains_four_nets" ), m_schematic );
64
65 CONNECTION_GRAPH* graph = m_schematic->ConnectionGraph();
66 BOOST_REQUIRE( graph );
67
68 SCH_SHEET_LIST sheets = m_schematic->BuildSheetListSortedByPageNumbers();
69 graph->Recalculate( sheets, /*aUnconditional=*/true );
70
71 const auto& potentials = graph->GetPotentialNetChains();
72 BOOST_REQUIRE( !potentials.empty() );
73
74 SCH_NETCHAIN* potential = potentials.front().get();
75 BOOST_REQUIRE( potential );
76
77 const std::set<wxString> originalNets = potential->GetNets();
78 const std::size_t originalNetCnt = originalNets.size();
79 const std::size_t originalSymCnt = potential->GetSymbols().size();
80
81 BOOST_REQUIRE_GT( originalNetCnt, 0u );
82 BOOST_REQUIRE_GT( originalSymCnt, 0u );
83
84 SCH_NETCHAIN* committed = graph->CreateNetChainFromPotential( potential, wxT( "REFRESH_TEST" ) );
85 BOOST_REQUIRE( committed );
86 BOOST_REQUIRE_EQUAL( committed->GetNets().size(), originalNetCnt );
87 BOOST_REQUIRE_EQUAL( committed->GetSymbols().size(), originalSymCnt );
88
89 // The hazard. Recalculate(true) -> Reset() clears m_symbols on every committed chain,
90 // and the rebuild restore pass used to skip names already present in
91 // m_committedNetChains, leaving the chain permanently empty.
92 graph->Recalculate( sheets, /*aUnconditional=*/true );
93
94 SCH_NETCHAIN* refreshed = graph->GetNetChainByName( wxT( "REFRESH_TEST" ) );
95 BOOST_REQUIRE_MESSAGE( refreshed,
96 "Committed chain disappeared across unconditional Recalculate" );
97
98 BOOST_CHECK_MESSAGE( !refreshed->GetSymbols().empty(),
99 "Committed chain has empty m_symbols after unconditional Recalculate; "
100 "Reset() cleared the cache and RebuildNetChains() failed to refresh it" );
101
102 BOOST_CHECK_MESSAGE( !refreshed->GetNets().empty(),
103 "Committed chain has empty m_nets after unconditional Recalculate" );
104
105 BOOST_CHECK_EQUAL( refreshed->GetNets().size(), originalNetCnt );
106 BOOST_CHECK_EQUAL( refreshed->GetSymbols().size(), originalSymCnt );
107
108 // Terminal pin/ref data must also survive the round trip; the setup panel and the PCB
109 // tuner walk these to find the bookend pads.
110 BOOST_CHECK( !refreshed->GetTerminalRef( 0 ).IsEmpty() );
111 BOOST_CHECK( !refreshed->GetTerminalRef( 1 ).IsEmpty() );
112
113 // A second round trip must remain stable (no slow leak of derived state).
114 graph->Recalculate( sheets, /*aUnconditional=*/true );
115
116 SCH_NETCHAIN* twice = graph->GetNetChainByName( wxT( "REFRESH_TEST" ) );
117 BOOST_REQUIRE( twice );
118 BOOST_CHECK( !twice->GetSymbols().empty() );
119 BOOST_CHECK( !twice->GetNets().empty() );
120 BOOST_CHECK_EQUAL( twice->GetNets().size(), originalNetCnt );
121 BOOST_CHECK_EQUAL( twice->GetSymbols().size(), originalSymCnt );
122}
123
124
125// Companion check. User-set netclass and color overrides live on the SCH_NETCHAIN itself
126// (not in the override map) once the chain is committed. The in-place refresh must NOT
127// reset them.
128BOOST_FIXTURE_TEST_CASE( NetChain_RefreshPreservesOverridesOnCommittedChain,
130{
132 KI_TEST::LoadSchematic( m_settingsManager, wxString( "net_chains_four_nets" ), m_schematic );
133
134 CONNECTION_GRAPH* graph = m_schematic->ConnectionGraph();
135 BOOST_REQUIRE( graph );
136
137 SCH_SHEET_LIST sheets = m_schematic->BuildSheetListSortedByPageNumbers();
138 graph->Recalculate( sheets, /*aUnconditional=*/true );
139
140 const auto& potentials = graph->GetPotentialNetChains();
141 BOOST_REQUIRE( !potentials.empty() );
142
143 SCH_NETCHAIN* committed = graph->CreateNetChainFromPotential( potentials.front().get(),
144 wxT( "OVERRIDE_TEST" ) );
145 BOOST_REQUIRE( committed );
146
147 committed->SetNetClass( wxT( "DDR_DATA" ) );
148 committed->SetColor( KIGFX::COLOR4D( 1.0, 0.5, 0.25, 1.0 ) );
149
150 graph->Recalculate( sheets, /*aUnconditional=*/true );
151
152 SCH_NETCHAIN* refreshed = graph->GetNetChainByName( wxT( "OVERRIDE_TEST" ) );
153 BOOST_REQUIRE( refreshed );
154
155 BOOST_CHECK_EQUAL( refreshed->GetNetClass(), wxT( "DDR_DATA" ) );
156 BOOST_CHECK( refreshed->GetColor() != KIGFX::COLOR4D::UNSPECIFIED );
157 BOOST_CHECK_CLOSE( refreshed->GetColor().r, 1.0, 1e-6 );
158 BOOST_CHECK_CLOSE( refreshed->GetColor().g, 0.5, 1e-6 );
159 BOOST_CHECK_CLOSE( refreshed->GetColor().b, 0.25, 1e-6 );
160}
161
162
163BOOST_FIXTURE_TEST_CASE( NetChain_RefreshPreservesTerminalPinOverride, NETCHAIN_RECALC_REFRESH_FIXTURE )
164{
165 LOCALE_IO locale;
166 KI_TEST::LoadSchematic( m_settingsManager, "net_chains_four_nets", m_schematic );
167 m_schematic->ConnectionGraph()->Recalculate( m_schematic->Hierarchy(), true );
168 auto& chains = m_schematic->NetChains();
169 const auto sheets = m_schematic->BuildSheetListSortedByPageNumbers();
170 BOOST_REQUIRE_EQUAL( sheets.size(), 1u );
171 const auto& path = sheets.front();
172 BOOST_REQUIRE( !chains.GetPotentialNetChains().empty() );
173 auto* chain = chains.CreateNetChainFromPotential( chains.GetPotentialNetChains().front().get(),
174 "TERM_OVERRIDE" );
176 const KIID originalA = chain->GetTerminalPinA();
177 const KIID originalB = chain->GetTerminalPinB();
178 auto* original = dynamic_cast<SCH_PIN*>( m_schematic->ResolveItem( originalA, nullptr, true ) );
179 BOOST_REQUIRE( original );
180 const auto connection = original->GetConnectionName( &path );
181 BOOST_REQUIRE( connection );
182 SCH_PIN* replacement = nullptr;
183
184 for( SCH_ITEM* item : path.LastScreen()->Items().OfType( SCH_SYMBOL_T ) )
185 {
186 for( SCH_PIN* pin : static_cast<SCH_SYMBOL*>( item )->GetPins( &path ) )
187 {
188 const auto net = pin->GetConnectionName( &path );
189
190 if( pin->m_Uuid != originalA && pin->m_Uuid != originalB
191 && net && *net == *connection )
192 {
193 replacement = pin;
194 break;
195 }
196 }
197
198 if( replacement )
199 break;
200 }
201
202 BOOST_REQUIRE( replacement );
203 const KIID replacementId = replacement->m_Uuid;
204 wxString replacementRef = replacement->GetParentSymbol()->GetRef( &path );
205 const wxString replacementNumber = replacement->GetNumber();
206 BOOST_CHECK( !chains.ReplaceNetChainTerminalPin( { "TERM_OVERRIDE", 1, originalA, path.Path() } ) );
207 BOOST_REQUIRE( chains.ReplaceNetChainTerminalPin( { "TERM_OVERRIDE", 1, replacementId, path.Path() } ) );
208 BOOST_CHECK( chain->GetTerminalPinA() == originalA );
209 BOOST_CHECK( chain->GetTerminalPinB() == replacementId );
210 BOOST_REQUIRE( chains.ReplaceNetChainTerminalPin( { "TERM_OVERRIDE", 1, originalB, path.Path() } ) );
211 BOOST_REQUIRE( chains.ReplaceNetChainTerminalPin( { "TERM_OVERRIDE", 0, replacementId, path.Path() } ) );
212 BOOST_CHECK_EQUAL( chain->GetTerminalRef( 0 ), replacementRef );
213 BOOST_CHECK( chain->GetTerminalPath( 0 ) == path.Path() );
214 BOOST_CHECK( !chains.ReplaceNetChainTerminalPin( { "TERM_OVERRIDE", 2, originalA, path.Path() } ) );
215
216 for( int pass = 0; pass < 2; ++pass )
217 {
218 m_schematic->ConnectionGraph()->Recalculate( m_schematic->Hierarchy(), true );
219 BOOST_CHECK( chains.GetNetChainByName( "TERM_OVERRIDE" ) == chain );
220 BOOST_CHECK( chain->GetTerminalPinA() == replacementId );
221 BOOST_CHECK( chain->GetTerminalPinB() == originalB );
222 BOOST_CHECK( chain->GetTerminalPath( 0 ) == path.Path() );
223 BOOST_CHECK_EQUAL( chain->GetTerminalRef( 0 ), replacementRef );
224 BOOST_CHECK_EQUAL( chain->GetTerminalPinNum( 0 ), replacementNumber );
225 }
226
227 auto* replacementSymbol = static_cast<SCH_SYMBOL*>( replacement->GetParentSymbol() );
228 auto library = replacementSymbol->GetLibSymbolRef()->Flatten();
229 replacementSymbol->SetLibSymbol( library.release() );
230 m_schematic->ConnectionGraph()->Recalculate( m_schematic->Hierarchy(), true );
231 BOOST_CHECK( chain->GetTerminalPinA() == replacementId );
232 BOOST_CHECK( chain->GetTerminalPath( 0 ) == path.Path() );
233 BOOST_CHECK_EQUAL( chain->GetTerminalRef( 0 ), replacementRef );
234 BOOST_CHECK_EQUAL( chain->GetNets().size(), 4u );
235
236 replacementRef = "R900";
237 static_cast<SCH_SYMBOL*>( replacement->GetParentSymbol() )->SetRef( &path, replacementRef );
238 m_schematic->ConnectionGraph()->Recalculate( m_schematic->Hierarchy(), true );
239 BOOST_CHECK( chains.GetNetChainByName( "TERM_OVERRIDE" ) == chain );
240 BOOST_CHECK( chain->GetTerminalPinA() == replacementId );
241 BOOST_CHECK_EQUAL( chain->GetTerminalRef( 0 ), replacementRef );
242 BOOST_CHECK_EQUAL( chain->GetTerminalPinNum( 0 ), replacementNumber );
243
244 replacementRef = "R901";
245 static_cast<SCH_SYMBOL*>( replacement->GetParentSymbol() )->SetRef( &path, replacementRef );
246
247 const wxString file = wxFileName::CreateTempFileName( "netchain-terminal-" );
248 KI_TEST::DumpSchematicToFile( *m_schematic, *m_schematic->GetTopLevelSheet(), file.ToStdString() );
249 std::ifstream stream( file.ToStdString() );
250 BOOST_REQUIRE( stream.good() );
251 auto reloaded = KI_TEST::ReadSchematicFromStream( stream, &m_schematic->Project() );
252 BOOST_REQUIRE( reloaded );
253 reloaded->ConnectionGraph()->Recalculate( reloaded->Hierarchy(), true );
254 auto* restored = reloaded->NetChains().GetNetChainByName( "TERM_OVERRIDE" );
255 BOOST_REQUIRE( restored );
256 BOOST_CHECK( restored->GetTerminalPinA() == replacementId );
257 BOOST_CHECK( restored->GetTerminalPinB() == originalB );
258 BOOST_CHECK_EQUAL( restored->GetTerminalRef( 0 ), replacementRef );
259 BOOST_CHECK_EQUAL( restored->GetTerminalPinNum( 0 ), replacementNumber );
260 stream.close();
261 wxRemoveFile( file );
262
263 auto* removedSymbol = static_cast<SCH_SYMBOL*>( replacement->GetParentSymbol() );
264 path.LastScreen()->Remove( removedSymbol );
265 std::unique_ptr<SCH_SYMBOL> removed( removedSymbol );
266 static_cast<SCH_SYMBOL*>( original->GetParentSymbol() )->SetRef( &path, replacementRef );
267 m_schematic->ConnectionGraph()->Recalculate( m_schematic->Hierarchy(), true );
268 BOOST_CHECK( chain->GetTerminalPinA() == replacementId );
269 BOOST_CHECK( chain->GetTerminalPath( 0 ) == path.Path() );
270 BOOST_CHECK( !chain->GetSymbols().contains( removed.get() ) );
271 BOOST_CHECK( chain->GetNets().empty() );
272}
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:597
A color representation with 4 components: red, green, blue, alpha.
Definition color4d.h:101
double r
Red component.
Definition color4d.h:390
double g
Green component.
Definition color4d.h:391
static const COLOR4D UNSPECIFIED
For legacy support; used as a value to indicate color hasn't been set yet.
Definition color4d.h:399
double b
Blue component.
Definition color4d.h:392
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:165
const SYMBOL * GetParentSymbol() const
Definition sch_item.cpp:287
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:142
A container for handling SCH_SHEET_PATH objects in a flattened hierarchy.
Schematic symbol object.
Definition sch_symbol.h:75
void SetRef(const SCH_SHEET_PATH *aSheet, const wxString &aReference)
Set the reference for the given sheet path for this symbol.
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_CHECK_EQUAL(result, "25.4")
@ SCH_SYMBOL_T
Definition typeinfo.h:168