KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_net_settings.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 along
17 * with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#include <boost/test/unit_test.hpp>
21
23#include <netclass.h>
25
26
27BOOST_AUTO_TEST_SUITE( NetSettingsTests )
28
29
30// Regression guard for the dirty-check used by the project save framework.
31// Without m_netChainClasses included in operator==, edits to chain-class
32// assignments returned "no change" and were silently dropped on close.
33BOOST_AUTO_TEST_CASE( ChainClassAssignmentAffectsEquality )
34{
35 NET_SETTINGS a( nullptr, "" );
36 NET_SETTINGS b( nullptr, "" );
37
38 BOOST_CHECK( a == b );
39
40 a.SetNetChainClass( wxS( "CHAIN_A" ), wxS( "Default" ) );
41
42 BOOST_CHECK( a != b );
43
44 b.SetNetChainClass( wxS( "CHAIN_A" ), wxS( "Default" ) );
45
46 BOOST_CHECK( a == b );
47}
48
49
50// Verify that differing chain-class values (same chain key) compare unequal,
51// and that clearing an assignment via empty class string restores equality.
52BOOST_AUTO_TEST_CASE( ChainClassValueAndClearAffectEquality )
53{
54 NET_SETTINGS a( nullptr, "" );
55 NET_SETTINGS b( nullptr, "" );
56
57 a.SetNetChainClass( wxS( "CHAIN_A" ), wxS( "Default" ) );
58 b.SetNetChainClass( wxS( "CHAIN_A" ), wxS( "HighSpeed" ) );
59
60 BOOST_CHECK( a != b );
61
62 b.SetNetChainClass( wxS( "CHAIN_A" ), wxS( "Default" ) );
63
64 BOOST_CHECK( a == b );
65
66 a.SetNetChainClass( wxS( "CHAIN_A" ), wxString() );
67
68 BOOST_CHECK( a != b );
69
70 b.SetNetChainClass( wxS( "CHAIN_A" ), wxString() );
71
72 BOOST_CHECK( a == b );
73}
74
75
76// Asymmetric-size guard for the std::equal calls in operator==. A 3-iterator
77// std::equal returns true when the LHS is a prefix of the RHS, which silently
78// dropped one-sided edits.
79BOOST_AUTO_TEST_CASE( AssignmentSizeDifferencesAffectEquality )
80{
81 NET_SETTINGS a( nullptr, "" );
82 NET_SETTINGS b( nullptr, "" );
83
84 b.SetNetclassLabelAssignment( wxS( "NET_A" ), { wxS( "Default" ) } );
85
86 BOOST_CHECK( a != b );
87 BOOST_CHECK( b != a );
88}
89
90
91// Returns true if the effective netclass for aNetName resolves to a netclass named aExpected,
92// either directly or as a constituent of a composite effective netclass.
93static bool resolvesToNetclass( NET_SETTINGS& aSettings, const wxString& aNetName,
94 const wxString& aExpected )
95{
96 std::shared_ptr<NETCLASS> resolved = aSettings.GetEffectiveNetClass( aNetName );
97
98 if( !resolved )
99 return false;
100
101 if( resolved->GetName() == aExpected )
102 return true;
103
104 for( NETCLASS* constituent : resolved->GetConstituentNetclasses() )
105 {
106 if( constituent && constituent->GetName() == aExpected )
107 return true;
108 }
109
110 return false;
111}
112
113
114// Chain-derived netclass pattern assignments must contribute to effective netclass resolution
115// alongside user-authored patterns, but must be cleanable independently so stale chain entries
116// do not persist across netlist updates.
117BOOST_AUTO_TEST_CASE( ChainPatternAssignmentResolvesAndClears )
118{
119 NET_SETTINGS settings( nullptr, "" );
120
121 std::shared_ptr<NETCLASS> highSpeed = std::make_shared<NETCLASS>( wxS( "HighSpeed" ), false );
122 std::map<wxString, std::shared_ptr<NETCLASS>> classes;
123 classes[wxS( "HighSpeed" )] = highSpeed;
124 settings.SetNetclasses( classes );
125
126 settings.SetChainPatternAssignment( wxS( "DDR_DQ0" ), wxS( "HighSpeed" ) );
127
128 BOOST_CHECK( resolvesToNetclass( settings, wxS( "DDR_DQ0" ), wxS( "HighSpeed" ) ) );
129
131
132 BOOST_CHECK( !resolvesToNetclass( settings, wxS( "DDR_DQ0" ), wxS( "HighSpeed" ) ) );
133}
134
135
136// Clearing chain-derived patterns must not disturb user-authored patterns.
137BOOST_AUTO_TEST_CASE( ClearChainPatternAssignmentsLeavesUserPatterns )
138{
139 NET_SETTINGS settings( nullptr, "" );
140
141 std::shared_ptr<NETCLASS> highSpeed = std::make_shared<NETCLASS>( wxS( "HighSpeed" ), false );
142 std::shared_ptr<NETCLASS> power = std::make_shared<NETCLASS>( wxS( "Power" ), false );
143 std::map<wxString, std::shared_ptr<NETCLASS>> classes;
144 classes[wxS( "HighSpeed" )] = highSpeed;
145 classes[wxS( "Power" )] = power;
146 settings.SetNetclasses( classes );
147
148 settings.SetNetclassPatternAssignment( wxS( "VCC_*" ), wxS( "Power" ) );
149 settings.SetChainPatternAssignment( wxS( "DDR_DQ0" ), wxS( "HighSpeed" ) );
150
151 BOOST_CHECK( resolvesToNetclass( settings, wxS( "VCC_3V3" ), wxS( "Power" ) ) );
152 BOOST_CHECK( resolvesToNetclass( settings, wxS( "DDR_DQ0" ), wxS( "HighSpeed" ) ) );
153
155
156 BOOST_CHECK( resolvesToNetclass( settings, wxS( "VCC_3V3" ), wxS( "Power" ) ) );
157 BOOST_CHECK( !resolvesToNetclass( settings, wxS( "DDR_DQ0" ), wxS( "HighSpeed" ) ) );
158}
159
160
161// ClearNetChainClasses must remove all chain->class entries.
162BOOST_AUTO_TEST_CASE( ClearNetChainClassesRemovesAllEntries )
163{
164 NET_SETTINGS settings( nullptr, "" );
165
166 settings.SetNetChainClass( wxS( "CHAIN_A" ), wxS( "Default" ) );
167 settings.SetNetChainClass( wxS( "CHAIN_B" ), wxS( "HighSpeed" ) );
168
169 BOOST_CHECK_EQUAL( settings.GetNetChainClasses().size(), 2u );
170
171 settings.ClearNetChainClasses();
172
173 BOOST_CHECK( settings.GetNetChainClasses().empty() );
174 BOOST_CHECK( settings.GetNetChainClass( wxS( "CHAIN_A" ) ).IsEmpty() );
175}
176
177
178// m_netClassChainPatternAssignments is derived state rebuilt on every netlist update from
179// m_netChainClasses plus board NETINFO. It must NOT contribute to operator==, otherwise a
180// no-op netlist rebuild marks the project dirty even when the user made no edit. The
181// persisted m_netChainClasses map (covered above) is the source of truth for equality.
182BOOST_AUTO_TEST_CASE( ChainPatternAssignmentExcludedFromEquality )
183{
184 NET_SETTINGS a( nullptr, "" );
185 NET_SETTINGS b( nullptr, "" );
186
187 std::shared_ptr<NETCLASS> highSpeed = std::make_shared<NETCLASS>( wxS( "HighSpeed" ), false );
188 std::shared_ptr<NETCLASS> power = std::make_shared<NETCLASS>( wxS( "Power" ), false );
189 std::map<wxString, std::shared_ptr<NETCLASS>> classes;
190 classes[wxS( "HighSpeed" )] = highSpeed;
191 classes[wxS( "Power" )] = power;
192
193 a.SetNetclasses( classes );
194 b.SetNetclasses( classes );
195
196 BOOST_CHECK( a == b );
197
198 a.SetChainPatternAssignment( wxS( "DDR_DQ0" ), wxS( "HighSpeed" ) );
199
200 BOOST_CHECK( a == b );
201
202 a.SetChainPatternAssignment( wxS( "VCC_3V3" ), wxS( "Power" ) );
203 b.SetChainPatternAssignment( wxS( "OTHER" ), wxS( "HighSpeed" ) );
204
205 BOOST_CHECK( a == b );
206
208
209 BOOST_CHECK( a == b );
210}
211
212
213// Persisted-state regression guard for the "net_chain_classes" JSON key. In-memory
214// equality tests cannot detect a renamed/missing JSON key or a broken read lambda --
215// those failures present as silent loss of every chain->class assignment on project
216// reopen. Exercise the full Store -> serialize -> parse -> Load round trip so any
217// drift in the JSON contract fails the suite immediately.
218BOOST_AUTO_TEST_CASE( NetChainClassesJsonRoundTrip )
219{
220 NET_SETTINGS source( nullptr, "" );
221
222 source.SetNetChainClass( wxS( "CHAIN_A" ), wxS( "Default" ) );
223 source.SetNetChainClass( wxS( "CHAIN_B" ), wxS( "HighSpeed" ) );
224 source.SetNetChainClass( wxS( "CHAIN_WITH_SPACES and: punctuation!" ), wxS( "Power" ) );
225 source.SetNetChainClass( wxS( "Unicode_éèê" ), wxS( "RF_µwave" ) );
226
227 BOOST_REQUIRE( source.Store() );
228
229 // Round-trip through the text form so a renamed key, broken serializer, or broken
230 // parser all surface as a test failure. FormatAsString -> parse mirrors what
231 // SaveToFile / LoadFromFile do on disk.
232 std::string serialized = source.FormatAsString();
233 nlohmann::json reparsed = nlohmann::json::parse( serialized );
234
235 BOOST_REQUIRE( reparsed.contains( "net_chain_classes" ) );
236 BOOST_REQUIRE( reparsed["net_chain_classes"].is_object() );
237
238 NET_SETTINGS sink( nullptr, "" );
239
240 // Seed the sink with a stale entry so a no-op reader would still leave it behind;
241 // the read lambda must clear() before applying the loaded map.
242 sink.SetNetChainClass( wxS( "STALE_CHAIN" ), wxS( "Default" ) );
243
244 JSON_SETTINGS_INTERNALS reparsedInternals;
245 static_cast<nlohmann::json&>( reparsedInternals ) = reparsed;
246 sink.Internals()->CloneFrom( reparsedInternals );
247 sink.Load();
248
249 BOOST_CHECK( sink.GetNetChainClasses().size() == source.GetNetChainClasses().size() );
250 BOOST_CHECK( sink.GetNetChainClasses() == source.GetNetChainClasses() );
251 BOOST_CHECK( sink.GetNetChainClass( wxS( "CHAIN_A" ) ) == wxS( "Default" ) );
252 BOOST_CHECK( sink.GetNetChainClass( wxS( "CHAIN_B" ) ) == wxS( "HighSpeed" ) );
253 BOOST_CHECK( sink.GetNetChainClass( wxS( "CHAIN_WITH_SPACES and: punctuation!" ) )
254 == wxS( "Power" ) );
255 BOOST_CHECK( sink.GetNetChainClass( wxS( "Unicode_éèê" ) ) == wxS( "RF_µwave" ) );
256 BOOST_CHECK( sink.GetNetChainClass( wxS( "STALE_CHAIN" ) ).IsEmpty() );
257 BOOST_CHECK( source == sink );
258}
259
260
261// An empty m_netChainClasses must serialize as an empty object (not be omitted, not be
262// emitted as null) and round-trip back to an empty map without leaving stale entries.
263BOOST_AUTO_TEST_CASE( NetChainClassesJsonRoundTripEmpty )
264{
265 NET_SETTINGS source( nullptr, "" );
266
267 BOOST_REQUIRE( source.Store() );
268
269 std::string serialized = source.FormatAsString();
270 nlohmann::json reparsed = nlohmann::json::parse( serialized );
271
272 BOOST_REQUIRE( reparsed.contains( "net_chain_classes" ) );
273 BOOST_CHECK( reparsed["net_chain_classes"].is_object() );
274 BOOST_CHECK( reparsed["net_chain_classes"].empty() );
275
276 NET_SETTINGS sink( nullptr, "" );
277
278 sink.SetNetChainClass( wxS( "STALE" ), wxS( "Default" ) );
279
280 JSON_SETTINGS_INTERNALS reparsedInternals;
281 static_cast<nlohmann::json&>( reparsedInternals ) = reparsed;
282 sink.Internals()->CloneFrom( reparsedInternals );
283 sink.Load();
284
285 BOOST_CHECK( sink.GetNetChainClasses().empty() );
286}
287
288
289// Empty class strings are how chain assignments are cleared (SetNetChainClass with "")
290// and the writer must not emit them. An attacker-supplied or hand-edited JSON file
291// that contains an empty string value must also be ignored on load, matching the
292// in-memory clear semantics tested in ChainClassValueAndClearAffectEquality.
293BOOST_AUTO_TEST_CASE( NetChainClassesJsonDropsEmptyValues )
294{
295 NET_SETTINGS source( nullptr, "" );
296
297 source.SetNetChainClass( wxS( "KEEP" ), wxS( "Default" ) );
298
299 BOOST_REQUIRE( source.Store() );
300
301 nlohmann::json reparsed = nlohmann::json::parse( source.FormatAsString() );
302
303 BOOST_REQUIRE( reparsed.contains( "net_chain_classes" ) );
304 BOOST_REQUIRE_EQUAL( reparsed["net_chain_classes"].size(), 1u );
305
306 // Inject a hand-crafted empty-value entry to confirm the reader rejects it.
307 reparsed["net_chain_classes"]["EMPTY"] = "";
308
309 NET_SETTINGS sink( nullptr, "" );
310
311 JSON_SETTINGS_INTERNALS reparsedInternals;
312 static_cast<nlohmann::json&>( reparsedInternals ) = reparsed;
313 sink.Internals()->CloneFrom( reparsedInternals );
314 sink.Load();
315
316 BOOST_CHECK_EQUAL( sink.GetNetChainClasses().size(), 1u );
317 BOOST_CHECK( sink.GetNetChainClass( wxS( "KEEP" ) ) == wxS( "Default" ) );
318 BOOST_CHECK( sink.GetNetChainClass( wxS( "EMPTY" ) ).IsEmpty() );
319}
320
321
void CloneFrom(const JSON_SETTINGS_INTERNALS &aOther)
virtual void Load()
Updates the parameters of this object based on the current JSON document contents.
const std::string FormatAsString()
JSON_SETTINGS_INTERNALS * Internals()
virtual bool Store()
Stores the current parameters into the JSON document represented by this object Note: this doesn't do...
A collection of nets and the parameters used to route or test these nets.
Definition netclass.h:42
NET_SETTINGS stores various net-related settings in a project context.
void ClearChainPatternAssignments()
Clears all chain-derived pattern assignments.
void SetNetChainClass(const wxString &aChain, const wxString &aClass)
Assign a net chain to a named class (used by inNetChainClass() DRC scope).
std::shared_ptr< NETCLASS > GetEffectiveNetClass(const wxString &aNetName)
Fetches the effective (may be aggregate) netclass for the given net name.
wxString GetNetChainClass(const wxString &aChain) const
Look up the class assigned to a chain. Empty string means "no class".
void SetNetclasses(const std::map< wxString, std::shared_ptr< NETCLASS > > &netclasses)
Sets all netclass Calling this method will reset the effective netclass calculation caches.
void SetNetclassLabelAssignment(const wxString &netName, const std::set< wxString > &netclasses)
Sets a net name to netclasses assignment Calling user is responsible for resetting the effective netc...
void SetNetclassPatternAssignment(const wxString &pattern, const wxString &netclass)
Sets a netclass pattern assignment Calling this method will reset the effective netclass calculation ...
const std::map< wxString, wxString > & GetNetChainClasses() const
void SetChainPatternAssignment(const wxString &pattern, const wxString &netclass)
Sets a chain-derived netclass pattern assignment.
void ClearNetChainClasses()
Removes all chain-to-class assignments.
static bool empty(const wxTextEntryBase *aCtrl)
BOOST_AUTO_TEST_CASE(HorizontalAlignment)
BOOST_AUTO_TEST_SUITE(CadstarPartParser)
BOOST_REQUIRE(intersection.has_value()==c.ExpectedIntersection.has_value())
BOOST_AUTO_TEST_SUITE_END()
BOOST_AUTO_TEST_CASE(ChainClassAssignmentAffectsEquality)
static bool resolvesToNetclass(NET_SETTINGS &aSettings, const wxString &aNetName, const wxString &aExpected)
BOOST_CHECK_EQUAL(result, "25.4")