KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_net_chain_netclass_persistence.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/*
21 * Regression test for https://gitlab.com/kicad/code/kicad/-/work_items/25065
22 *
23 * A netclass assigned by a net chain resolved on the board right after Update PCB from
24 * Schematic, but was gone after save + reload: the chain-to-netclass override lived only in the
25 * schematic and the transient netlist, so nothing on the board side could re-derive the per-net
26 * assignments.
27 */
28
29#include <board.h>
31#include <netinfo.h>
34#include <project.h>
38
41
42#include <wx/filefn.h>
43#include <wx/filename.h>
44#include <wx/stdpaths.h>
45
46
47BOOST_AUTO_TEST_SUITE( NetChainNetclassPersistence )
48
49
50namespace
51{
52
53// Chain memberships and netclass overrides as the issue's schematic declares them; the board's
54// (net_chains ...) block carries the same memberships.
55const wxString CHAIN_1 = wxS( "CHAIN_1" );
56const wxString CHAIN_2 = wxS( "CHAIN_2" );
57const wxString CHAIN_1_NETCLASS = wxS( "CHAIN_NC_1" );
58const wxString CHAIN_2_NETCLASS = wxS( "CHAIN_NC_2" );
59
60const std::vector<wxString> CHAIN_1_NETS = { wxS( "Net-(R1-Pad1)" ), wxS( "Net-(R1-Pad2)" ),
61 wxS( "Net-(R2-Pad2)" ) };
62const std::vector<wxString> CHAIN_2_NETS = { wxS( "Net-(R3-Pad1)" ), wxS( "Net-(R3-Pad2)" ),
63 wxS( "Net-(R4-Pad2)" ) };
64
65// Assigned by a schematic directive label rather than a chain, so it round-tripped even before
66// the fix. Serves as the positive control.
67const wxString LABEL_NET = wxS( "Net-(R5-Pad1)" );
68const wxString LABEL_NETCLASS = wxS( "NC_3" );
69
70
71struct FIXTURE
72{
73 FIXTURE()
74 {
75 // The project is saved and reloaded repeatedly, so work on copies rather than the
76 // checked-in data.
77 wxString srcDir = wxString::FromUTF8( KI_TEST::GetPcbnewTestDataDir() ) + wxS( "issue25065/" );
78
79 m_dir.AssignDir( wxStandardPaths::Get().GetTempDir() );
80 m_dir.AppendDir( wxS( "kicad-qa-issue25065" ) );
81 m_dir.Mkdir( wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL );
82 BOOST_REQUIRE( m_dir.DirExists() );
83
84 m_boardPath = m_dir.GetPathWithSep() + wxS( "issue25065.kicad_pcb" );
85 m_projectPath = m_dir.GetPathWithSep() + wxS( "issue25065.kicad_pro" );
86
87 BOOST_REQUIRE( wxCopyFile( srcDir + wxS( "issue25065.kicad_pcb" ), m_boardPath ) );
88 BOOST_REQUIRE( wxCopyFile( srcDir + wxS( "issue25065.kicad_pro" ), m_projectPath ) );
89 }
90
91 ~FIXTURE()
92 {
93 closeBoard();
94 m_dir.Rmdir( wxPATH_RMDIR_RECURSIVE );
95 }
96
98 void OpenBoard()
99 {
100 BOOST_REQUIRE( m_manager.LoadProject( m_projectPath ) );
101
102 m_board = KI_TEST::ReadBoardFromFileOrStream( m_boardPath.ToStdString() );
103 BOOST_REQUIRE( m_board );
104
105 m_board->SetProject( &m_manager.Prj() );
106 m_board->BuildListOfNets();
107 }
108
110 void SaveAndReopenBoard()
111 {
112 BOOST_REQUIRE( m_manager.SaveProject() );
113
114 closeBoard();
115 OpenBoard();
116 }
117
119 void UpdateFromNetlist( const NETLIST& aNetlist )
120 {
121 BOARD_NETLIST_UPDATER::ApplyChainAssignments( m_board.get(), aNetlist, nullptr, false );
122 BOARD_NETLIST_UPDATER::ApplyChainNetclasses( m_board.get(), aNetlist );
123 m_board->SynchronizeNetsAndNetClasses( true );
124 }
125
126 NET_SETTINGS& NetSettings() const { return *m_board->GetDesignSettings().m_NetSettings; }
127
129 wxString ResolvedNetclass( const wxString& aNetname ) const
130 {
131 NETINFO_ITEM* net = m_board->FindNet( aNetname );
132
133 return net ? net->GetNetClass()->GetHumanReadableName() : wxString( wxS( "<no such net>" ) );
134 }
135
136 bool ResolvesTo( const wxString& aNetname, const wxString& aNetclass ) const
137 {
138 NETINFO_ITEM* net = m_board->FindNet( aNetname );
139 BOOST_REQUIRE_MESSAGE( net, "net " << aNetname << " missing from the board" );
140
141 return net->GetNetClass()->ContainsNetclassWithName( aNetclass );
142 }
143
144 void closeBoard()
145 {
146 if( m_board )
147 {
148 m_board->SetProject( nullptr );
149 m_board.reset();
150 }
151
152 m_manager.UnloadProject( &m_manager.Prj(), false );
153 }
154
155 SETTINGS_MANAGER m_manager;
156 std::unique_ptr<BOARD> m_board;
157 wxFileName m_dir;
158 wxString m_boardPath;
159 wxString m_projectPath;
160};
161
162
165void AddChain( NETLIST& aNetlist, const wxString& aChain, const wxString& aNetclass,
166 const std::vector<wxString>& aNets )
167{
168 aNetlist.SetSignalNetClass( aChain, aNetclass );
169
170 for( const wxString& net : aNets )
171 aNetlist.SetNetChainFor( net, aChain );
172}
173
174} // namespace
175
176
177// The whole point of the issue: the chain netclass must survive a save and reload with no
178// intervening Update PCB from Schematic.
179BOOST_FIXTURE_TEST_CASE( ChainNetclassSurvivesReload, FIXTURE )
180{
181 OpenBoard();
182
183 // The netclass a directive label assigns is persisted per-net and resolves on a bare load;
184 // if this control fails the board or project copy is wrong, not the chain handling.
185 BOOST_REQUIRE_MESSAGE( ResolvesTo( LABEL_NET, LABEL_NETCLASS ),
186 LABEL_NET << " resolved to " << ResolvedNetclass( LABEL_NET ) );
187
189 AddChain( netlist, CHAIN_1, CHAIN_1_NETCLASS, CHAIN_1_NETS );
190 AddChain( netlist, CHAIN_2, CHAIN_2_NETCLASS, CHAIN_2_NETS );
191
192 UpdateFromNetlist( netlist );
193
194 for( const wxString& net : CHAIN_1_NETS )
195 {
196 BOOST_REQUIRE_MESSAGE( ResolvesTo( net, CHAIN_1_NETCLASS ),
197 net << " resolved to " << ResolvedNetclass( net )
198 << " straight after the netlist update" );
199 }
200
201 SaveAndReopenBoard();
202
203 for( const wxString& net : CHAIN_1_NETS )
204 {
205 BOOST_CHECK_MESSAGE( ResolvesTo( net, CHAIN_1_NETCLASS ),
206 net << " resolved to " << ResolvedNetclass( net ) << " after reload" );
207 }
208
209 for( const wxString& net : CHAIN_2_NETS )
210 {
211 BOOST_CHECK_MESSAGE( ResolvesTo( net, CHAIN_2_NETCLASS ),
212 net << " resolved to " << ResolvedNetclass( net ) << " after reload" );
213 }
214
215 BOOST_CHECK_MESSAGE( ResolvesTo( LABEL_NET, LABEL_NETCLASS ),
216 LABEL_NET << " resolved to " << ResolvedNetclass( LABEL_NET )
217 << " after reload" );
218
219 // Nets outside any chain must not pick one up.
220 BOOST_CHECK( !ResolvesTo( wxS( "Net-(R6-Pad2)" ), CHAIN_1_NETCLASS ) );
221}
222
223
224// Persisting the override must not resurrect it: a chain dropped from the schematic stops
225// applying its netclass, both in the session and across the next reload.
226BOOST_FIXTURE_TEST_CASE( RemovedChainNetclassIsDropped, FIXTURE )
227{
228 OpenBoard();
229
231 AddChain( netlist, CHAIN_1, CHAIN_1_NETCLASS, CHAIN_1_NETS );
232 AddChain( netlist, CHAIN_2, CHAIN_2_NETCLASS, CHAIN_2_NETS );
233
234 UpdateFromNetlist( netlist );
235 SaveAndReopenBoard();
236 BOOST_REQUIRE( ResolvesTo( CHAIN_1_NETS[0], CHAIN_1_NETCLASS ) );
237
238 NETLIST withoutChain1;
239 AddChain( withoutChain1, CHAIN_2, CHAIN_2_NETCLASS, CHAIN_2_NETS );
240
241 UpdateFromNetlist( withoutChain1 );
242
243 BOOST_CHECK( NetSettings().GetNetChainNetClass( CHAIN_1 ).IsEmpty() );
244 BOOST_CHECK_MESSAGE( !ResolvesTo( CHAIN_1_NETS[0], CHAIN_1_NETCLASS ),
245 CHAIN_1_NETS[0] << " still resolved to "
246 << ResolvedNetclass( CHAIN_1_NETS[0] ) );
247 BOOST_CHECK( ResolvesTo( CHAIN_2_NETS[0], CHAIN_2_NETCLASS ) );
248
249 // The board file still lists CHAIN_1's membership, so a stale project-side override would
250 // reappear here.
251 SaveAndReopenBoard();
252
253 BOOST_CHECK( !ResolvesTo( CHAIN_1_NETS[0], CHAIN_1_NETCLASS ) );
254 BOOST_CHECK( ResolvesTo( CHAIN_2_NETS[0], CHAIN_2_NETCLASS ) );
255}
256
257
General utilities for PCB file IO for QA programs.
static void ApplyChainAssignments(BOARD *aBoard, const NETLIST &aNetlist, REPORTER *aReporter, bool aDryRun)
Apply the netlist's chain assignments to every NETINFO_ITEM on the board.
static void ApplyChainNetclasses(BOARD *aBoard, const NETLIST &aNetlist)
Mirror the netlist's chain-to-class and chain-to-netclass maps into the project's NET_SETTINGS.
const wxString GetHumanReadableName() const
Gets the consolidated name of this netclass (which may be an aggregate).
Definition netclass.cpp:320
bool ContainsNetclassWithName(const wxString &netclass) const
Determines if the given netclass name is a constituent of this (maybe aggregate) netclass.
Definition netclass.cpp:310
NETCLASS * GetNetClass()
Definition netinfo.h:91
Store information read from a netlist along with the flags used to update the NETLIST in the BOARD.
void SetSignalNetClass(const wxString &aNetChain, const wxString &aNetClass)
void SetNetChainFor(const wxString &aNet, const wxString &aNetChain)
@ LABEL_NET
Definition cursors.h:76
std::string GetPcbnewTestDataDir()
Utility which returns a path to the data directory where the test board files are stored.
std::unique_ptr< BOARD > ReadBoardFromFileOrStream(const std::string &aFilename, std::istream &aFallback)
Read a board from a file, or another stream, as appropriate.
BOOST_AUTO_TEST_SUITE(CadstarPartParser)
BOOST_REQUIRE(intersection.has_value()==c.ExpectedIntersection.has_value())
BOOST_AUTO_TEST_SUITE_END()
std::string netlist
BOOST_FIXTURE_TEST_CASE(ChainNetclassSurvivesReload, FIXTURE)