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#include <qa_utils/file_utils.h>
42
43#include <wx/filefn.h>
44#include <wx/filename.h>
45#include <wx/stdpaths.h>
46
47
48BOOST_AUTO_TEST_SUITE( NetChainNetclassPersistence )
49
50
51namespace
52{
53
54// Chain memberships and netclass overrides as the issue's schematic declares them; the board's
55// (net_chains ...) block carries the same memberships.
56const wxString CHAIN_1 = wxS( "CHAIN_1" );
57const wxString CHAIN_2 = wxS( "CHAIN_2" );
58const wxString CHAIN_1_NETCLASS = wxS( "CHAIN_NC_1" );
59const wxString CHAIN_2_NETCLASS = wxS( "CHAIN_NC_2" );
60
61const std::vector<wxString> CHAIN_1_NETS = { wxS( "Net-(R1-Pad1)" ), wxS( "Net-(R1-Pad2)" ),
62 wxS( "Net-(R2-Pad2)" ) };
63const std::vector<wxString> CHAIN_2_NETS = { wxS( "Net-(R3-Pad1)" ), wxS( "Net-(R3-Pad2)" ),
64 wxS( "Net-(R4-Pad2)" ) };
65
66// Assigned by a schematic directive label rather than a chain, so it round-tripped even before
67// the fix. Serves as the positive control.
68const wxString LABEL_NET = wxS( "Net-(R5-Pad1)" );
69const wxString LABEL_NETCLASS = wxS( "NC_3" );
70
71
72struct FIXTURE
73{
74 FIXTURE() :
75 m_dir( wxS( "kicad-qa-issue25065" ) )
76 {
77 // The project is saved and reloaded repeatedly, so work on copies rather than the
78 // checked-in data.
79 wxString srcDir = wxString::FromUTF8( KI_TEST::GetPcbnewTestDataDir() ) + wxS( "issue25065/" );
80 wxString sep = wxFileName::GetPathSeparator();
81
82 m_boardPath = m_dir.PathStr() + sep + wxS( "issue25065.kicad_pcb" );
83 m_projectPath = m_dir.PathStr() + sep + wxS( "issue25065.kicad_pro" );
84
85 BOOST_REQUIRE( wxCopyFile( srcDir + wxS( "issue25065.kicad_pcb" ), m_boardPath ) );
86 BOOST_REQUIRE( wxCopyFile( srcDir + wxS( "issue25065.kicad_pro" ), m_projectPath ) );
87 }
88
89 ~FIXTURE()
90 {
91 closeBoard();
92 }
93
95 void OpenBoard()
96 {
97 BOOST_REQUIRE( m_manager.LoadProject( m_projectPath ) );
98
99 m_board = KI_TEST::ReadBoardFromFileOrStream( m_boardPath.ToStdString() );
100 BOOST_REQUIRE( m_board );
101
102 m_board->SetProject( &m_manager.Prj() );
103 m_board->BuildListOfNets();
104 }
105
107 void SaveAndReopenBoard()
108 {
109 BOOST_REQUIRE( m_manager.SaveProject() );
110
111 closeBoard();
112 OpenBoard();
113 }
114
116 void UpdateFromNetlist( const NETLIST& aNetlist )
117 {
118 BOARD_NETLIST_UPDATER::ApplyChainAssignments( m_board.get(), aNetlist, nullptr, false );
119 BOARD_NETLIST_UPDATER::ApplyChainNetclasses( m_board.get(), aNetlist );
120 m_board->SynchronizeNetsAndNetClasses( true );
121 }
122
123 NET_SETTINGS& NetSettings() const { return *m_board->GetDesignSettings().m_NetSettings; }
124
126 wxString ResolvedNetclass( const wxString& aNetname ) const
127 {
128 NETINFO_ITEM* net = m_board->FindNet( aNetname );
129
130 return net ? net->GetNetClass()->GetHumanReadableName() : wxString( wxS( "<no such net>" ) );
131 }
132
133 bool ResolvesTo( const wxString& aNetname, const wxString& aNetclass ) const
134 {
135 NETINFO_ITEM* net = m_board->FindNet( aNetname );
136 BOOST_REQUIRE_MESSAGE( net, "net " << aNetname << " missing from the board" );
137
138 return net->GetNetClass()->ContainsNetclassWithName( aNetclass );
139 }
140
141 void closeBoard()
142 {
143 if( m_board )
144 {
145 m_board->SetProject( nullptr );
146 m_board.reset();
147 }
148
149 m_manager.UnloadProject( &m_manager.Prj(), false );
150 }
151
152 SETTINGS_MANAGER m_manager;
153 std::unique_ptr<BOARD> m_board;
154 KI_TEST::SCOPED_TEMP_DIR m_dir;
155 wxString m_boardPath;
156 wxString m_projectPath;
157};
158
159
162void AddChain( NETLIST& aNetlist, const wxString& aChain, const wxString& aNetclass,
163 const std::vector<wxString>& aNets )
164{
165 aNetlist.SetSignalNetClass( aChain, aNetclass );
166
167 for( const wxString& net : aNets )
168 aNetlist.SetNetChainFor( net, aChain );
169}
170
171} // namespace
172
173
174// The whole point of the issue: the chain netclass must survive a save and reload with no
175// intervening Update PCB from Schematic.
176BOOST_FIXTURE_TEST_CASE( ChainNetclassSurvivesReload, FIXTURE )
177{
178 OpenBoard();
179
180 // The netclass a directive label assigns is persisted per-net and resolves on a bare load;
181 // if this control fails the board or project copy is wrong, not the chain handling.
182 BOOST_REQUIRE_MESSAGE( ResolvesTo( LABEL_NET, LABEL_NETCLASS ),
183 LABEL_NET << " resolved to " << ResolvedNetclass( LABEL_NET ) );
184
186 AddChain( netlist, CHAIN_1, CHAIN_1_NETCLASS, CHAIN_1_NETS );
187 AddChain( netlist, CHAIN_2, CHAIN_2_NETCLASS, CHAIN_2_NETS );
188
189 UpdateFromNetlist( netlist );
190
191 for( const wxString& net : CHAIN_1_NETS )
192 {
193 BOOST_REQUIRE_MESSAGE( ResolvesTo( net, CHAIN_1_NETCLASS ),
194 net << " resolved to " << ResolvedNetclass( net )
195 << " straight after the netlist update" );
196 }
197
198 SaveAndReopenBoard();
199
200 for( const wxString& net : CHAIN_1_NETS )
201 {
202 BOOST_CHECK_MESSAGE( ResolvesTo( net, CHAIN_1_NETCLASS ),
203 net << " resolved to " << ResolvedNetclass( net ) << " after reload" );
204 }
205
206 for( const wxString& net : CHAIN_2_NETS )
207 {
208 BOOST_CHECK_MESSAGE( ResolvesTo( net, CHAIN_2_NETCLASS ),
209 net << " resolved to " << ResolvedNetclass( net ) << " after reload" );
210 }
211
212 BOOST_CHECK_MESSAGE( ResolvesTo( LABEL_NET, LABEL_NETCLASS ),
213 LABEL_NET << " resolved to " << ResolvedNetclass( LABEL_NET )
214 << " after reload" );
215
216 // Nets outside any chain must not pick one up.
217 BOOST_CHECK( !ResolvesTo( wxS( "Net-(R6-Pad2)" ), CHAIN_1_NETCLASS ) );
218}
219
220
221// Persisting the override must not resurrect it: a chain dropped from the schematic stops
222// applying its netclass, both in the session and across the next reload.
223BOOST_FIXTURE_TEST_CASE( RemovedChainNetclassIsDropped, FIXTURE )
224{
225 OpenBoard();
226
228 AddChain( netlist, CHAIN_1, CHAIN_1_NETCLASS, CHAIN_1_NETS );
229 AddChain( netlist, CHAIN_2, CHAIN_2_NETCLASS, CHAIN_2_NETS );
230
231 UpdateFromNetlist( netlist );
232 SaveAndReopenBoard();
233 BOOST_REQUIRE( ResolvesTo( CHAIN_1_NETS[0], CHAIN_1_NETCLASS ) );
234
235 NETLIST withoutChain1;
236 AddChain( withoutChain1, CHAIN_2, CHAIN_2_NETCLASS, CHAIN_2_NETS );
237
238 UpdateFromNetlist( withoutChain1 );
239
240 BOOST_CHECK( NetSettings().GetNetChainNetClass( CHAIN_1 ).IsEmpty() );
241 BOOST_CHECK_MESSAGE( !ResolvesTo( CHAIN_1_NETS[0], CHAIN_1_NETCLASS ),
242 CHAIN_1_NETS[0] << " still resolved to "
243 << ResolvedNetclass( CHAIN_1_NETS[0] ) );
244 BOOST_CHECK( ResolvesTo( CHAIN_2_NETS[0], CHAIN_2_NETCLASS ) );
245
246 // The board file still lists CHAIN_1's membership, so a stale project-side override would
247 // reappear here.
248 SaveAndReopenBoard();
249
250 BOOST_CHECK( !ResolvesTo( CHAIN_1_NETS[0], CHAIN_1_NETCLASS ) );
251 BOOST_CHECK( ResolvesTo( CHAIN_2_NETS[0], CHAIN_2_NETCLASS ) );
252}
253
254
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:346
bool ContainsNetclassWithName(const wxString &netclass) const
Determines if the given netclass name is a constituent of this (maybe aggregate) netclass.
Definition netclass.cpp:336
NETCLASS * GetNetClass()
Definition netinfo.h:101
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:77
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.
void closeBoard(KICAD_API_SERVER *aServer)
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)