KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_board_design_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
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
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
21
22#include <board.h>
25#include <drc/drc_engine.h>
26#include <drc/drc_rule.h>
31
32#include <json_common.h>
33
34
35namespace
36{
37struct BDS_TEST_FIXTURE
38{
39 BDS_TEST_FIXTURE() :
40 m_board( new BOARD() )
41 { }
42
43 SETTINGS_MANAGER m_settingsManager;
44 std::unique_ptr<BOARD> m_board;
45};
46
47
48// A caller-owned parent so a BOARD_DESIGN_SETTINGS can nest under it without a full project.
49class BDS_TEST_PARENT : public JSON_SETTINGS
50{
51public:
52 BDS_TEST_PARENT() :
53 JSON_SETTINGS( "bds_test_parent", SETTINGS_LOC::NONE, 0, false, false, false )
54 {
55 }
56};
57} // namespace
58
59
60BOOST_FIXTURE_TEST_SUITE( BoardDesignSettings, BDS_TEST_FIXTURE )
61
62
63
70BOOST_AUTO_TEST_CASE( NegativeSilkClearanceRoundTrip )
71{
72 BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
73
74 int negativeValue = pcbIUScale.mmToIU( -0.1 );
75 bds.m_SilkClearance = negativeValue;
76
77 // Store all params into the JSON backing store
78 bds.Store();
79
80 // Reset to default
81 bds.m_SilkClearance = 0;
82
83 // Load back from JSON
84 bds.Load();
85
86 BOOST_CHECK_EQUAL( bds.m_SilkClearance, negativeValue );
87}
88
89
99BOOST_AUTO_TEST_CASE( BiggestClearanceIncludesPhysicalHoleClearance )
100{
101 BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
102
103 // Larger than any default clearance, so it can only come from the rule below.
104 const int ruleClearance = pcbIUScale.mmToIU( 4.0 );
105
106 auto rule = std::make_shared<DRC_RULE>( wxT( "NPTH Hole to Track Clearance" ) );
107
109 constraint.Value().SetMin( ruleClearance );
110 rule->AddConstraint( constraint );
111
112 auto engine = std::make_shared<DRC_ENGINE>( m_board.get(), &bds );
113 engine->InitEngine( rule );
114 bds.m_DRCEngine = engine;
115
116 BOOST_CHECK_GE( bds.GetBiggestClearanceValue(), ruleClearance );
117}
118
119
120// Regression test for https://gitlab.com/kicad/code/kicad/-/issues/24646
121// Cycling predefined sizes must skip the index-0 netclass placeholder on roll-over and stay
122// monotonic.
123BOOST_AUTO_TEST_CASE( PredefinedTrackWidthCyclingMonotonic )
124{
125 BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
126
127 // Index 0 is the netclass placeholder; indices 1..5 are sorted predefined sizes.
128 bds.m_TrackWidthList = { 0,
129 pcbIUScale.mmToIU( 0.5 ),
130 pcbIUScale.mmToIU( 0.6 ),
131 pcbIUScale.mmToIU( 0.8 ),
132 pcbIUScale.mmToIU( 1.0 ),
133 pcbIUScale.mmToIU( 1.2 ) };
134
135 const int lastReal = (int) bds.m_TrackWidthList.size() - 1;
136
137 // Increment: netclass(0) -> smallest(1) -> ... -> largest, then roll over to smallest, never 0.
138 BOOST_CHECK_EQUAL( bds.GetNextTrackWidthIndex( 0, true ), 1 );
139
140 int idx = 1;
141
142 for( int step = 2; step <= lastReal; ++step )
143 {
144 idx = bds.GetNextTrackWidthIndex( idx, true );
145 BOOST_CHECK_EQUAL( idx, step );
146 }
147
148 // Roll-over from the largest must skip the placeholder and land on the smallest real size.
149 BOOST_CHECK_EQUAL( bds.GetNextTrackWidthIndex( lastReal, true ), 1 );
150
151 // Decrement: smallest(1) rolls over to the largest, never to the placeholder.
152 BOOST_CHECK_EQUAL( bds.GetNextTrackWidthIndex( 1, false ), lastReal );
153
154 idx = lastReal;
155
156 for( int step = lastReal - 1; step >= 1; --step )
157 {
158 idx = bds.GetNextTrackWidthIndex( idx, false );
159 BOOST_CHECK_EQUAL( idx, step );
160 }
161
162 // A full forward cycle must never report the netclass placeholder once stepping has started.
163 idx = bds.GetNextTrackWidthIndex( 0, true );
164
165 for( int step = 0; step < lastReal * 3; ++step )
166 {
167 BOOST_CHECK_GE( idx, 1 );
168 idx = bds.GetNextTrackWidthIndex( idx, true );
169 }
170
171 // An empty list (no placeholder, no predefined sizes) must stay at index 0 and never store a
172 // negative index through SetTrackWidthIndex.
173 bds.m_TrackWidthList.clear();
174 BOOST_CHECK_EQUAL( bds.GetNextTrackWidthIndex( 0, true ), 0 );
175 BOOST_CHECK_EQUAL( bds.GetNextTrackWidthIndex( 0, false ), 0 );
176 bds.SetTrackWidthIndex( bds.GetNextTrackWidthIndex( 0, false ) );
178}
179
180
181BOOST_AUTO_TEST_CASE( PredefinedViaSizeCyclingMonotonic )
182{
183 BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
184
185 // Index 0 is the netclass placeholder; indices 1..3 are sorted predefined via sizes.
186 bds.m_ViasDimensionsList = { { 0, 0 },
187 { pcbIUScale.mmToIU( 0.6 ), pcbIUScale.mmToIU( 0.3 ) },
188 { pcbIUScale.mmToIU( 0.8 ), pcbIUScale.mmToIU( 0.4 ) },
189 { pcbIUScale.mmToIU( 1.0 ), pcbIUScale.mmToIU( 0.5 ) } };
190
191 const int lastReal = (int) bds.m_ViasDimensionsList.size() - 1;
192
193 BOOST_CHECK_EQUAL( bds.GetNextViaSizeIndex( 0, true ), 1 );
194 BOOST_CHECK_EQUAL( bds.GetNextViaSizeIndex( lastReal, true ), 1 );
195 BOOST_CHECK_EQUAL( bds.GetNextViaSizeIndex( 1, false ), lastReal );
196
197 // A list holding only the netclass placeholder must stay put in both directions.
198 bds.m_ViasDimensionsList = { { 0, 0 } };
199 BOOST_CHECK_EQUAL( bds.GetNextViaSizeIndex( 0, true ), 0 );
200 BOOST_CHECK_EQUAL( bds.GetNextViaSizeIndex( 0, false ), 0 );
201
202 // An empty list must stay at index 0 and never store a negative index.
203 bds.m_ViasDimensionsList.clear();
204 BOOST_CHECK_EQUAL( bds.GetNextViaSizeIndex( 0, true ), 0 );
205 BOOST_CHECK_EQUAL( bds.GetNextViaSizeIndex( 0, false ), 0 );
206 bds.SetViaSizeIndex( bds.GetNextViaSizeIndex( 0, false ) );
208}
209
210
211BOOST_AUTO_TEST_CASE( PredefinedDiffPairCyclingMonotonic )
212{
213 BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
214
215 // Index 0 is the netclass placeholder; indices 1..3 are sorted predefined diff-pair dimensions.
216 bds.m_DiffPairDimensionsList = { { 0, 0, 0 },
217 { pcbIUScale.mmToIU( 0.2 ), pcbIUScale.mmToIU( 0.2 ), 0 },
218 { pcbIUScale.mmToIU( 0.25 ), pcbIUScale.mmToIU( 0.25 ), 0 },
219 { pcbIUScale.mmToIU( 0.3 ), pcbIUScale.mmToIU( 0.3 ), 0 } };
220
221 const int lastReal = (int) bds.m_DiffPairDimensionsList.size() - 1;
222
223 BOOST_CHECK_EQUAL( bds.GetNextDiffPairIndex( 0, true ), 1 );
224 BOOST_CHECK_EQUAL( bds.GetNextDiffPairIndex( lastReal, true ), 1 );
225 BOOST_CHECK_EQUAL( bds.GetNextDiffPairIndex( 1, false ), lastReal );
226
227 // An empty list must stay at index 0 and never store a negative index.
228 bds.m_DiffPairDimensionsList.clear();
229 BOOST_CHECK_EQUAL( bds.GetNextDiffPairIndex( 0, true ), 0 );
230 BOOST_CHECK_EQUAL( bds.GetNextDiffPairIndex( 0, false ), 0 );
231 bds.SetDiffPairIndex( bds.GetNextDiffPairIndex( 0, false ) );
233}
234
235
236// Regression test for https://gitlab.com/kicad/code/kicad/-/issues/24644
237// Switching track width from the connected-width placeholder must advance to a real size on the
238// first press. The router gating is GUI bound, so this drives the same GetNextTrackWidthIndex
239// stepping the hotkeys call.
240BOOST_AUTO_TEST_CASE( TrackWidthSwitchAdvancesFromConnectedWidth )
241{
242 BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
243
244 const int size1 = pcbIUScale.mmToIU( 0.25 );
245 const int size2 = pcbIUScale.mmToIU( 0.50 );
246 const int size3 = pcbIUScale.mmToIU( 1.00 );
247
248 // Index 0 is the netclass/connected-width placeholder; the rest are real predefined sizes.
249 bds.m_TrackWidthList = { 0, size1, size2, size3 };
250 bds.UseCustomTrackViaSize( false );
251
252 // Start on the connected-width placeholder, as the router does before any override.
253 bds.SetTrackWidthIndex( 0 );
255
256 auto incIndex = [&]() { bds.SetTrackWidthIndex( bds.GetNextTrackWidthIndex( bds.GetTrackWidthIndex(), true ) ); };
257 auto decIndex = [&]() { bds.SetTrackWidthIndex( bds.GetNextTrackWidthIndex( bds.GetTrackWidthIndex(), false ) ); };
258
259 // First "Next" press must move to the first real predefined size, not no-op.
260 incIndex();
263
264 // Subsequent presses step through the list, then roll over to the smallest real size, skipping
265 // the netclass placeholder at index 0.
266 incIndex();
268 incIndex();
270 incIndex();
273
274 // First "Previous" press from the placeholder must roll over to the last real size.
275 bds.SetTrackWidthIndex( 0 );
276 decIndex();
279}
280
281
289BOOST_AUTO_TEST_CASE( SeededBoardValueSurvivesRoundTrip )
290{
291 BDS_TEST_PARENT parent;
292
293 ( *parent.Internals() )["/board/design_settings"_json_pointer] =
294 nlohmann::json{ { "meta", { { "version", 2 } } } };
295
296 const int negativeValue = pcbIUScale.mmToIU( -0.1 );
297
298 {
299 BOARD_DESIGN_SETTINGS bds( &parent, "board.design_settings" );
300
301 bds.m_SilkClearance = negativeValue;
302 bds.LoadFromFile();
303 BOOST_CHECK_EQUAL( bds.m_SilkClearance, negativeValue );
304
305 bds.SaveToFile();
306 }
307
308 BOARD_DESIGN_SETTINGS reloaded( &parent, "board.design_settings" );
309 reloaded.LoadFromFile();
310 BOOST_CHECK_EQUAL( reloaded.m_SilkClearance, negativeValue );
311}
312
313
314// A hand-edited preset holding the wrong type must not take the presets after it down with it.
315BOOST_AUTO_TEST_CASE( OneMalformedPresetDoesNotDropTheRest )
316{
317 BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
318
319 nlohmann::json js = nlohmann::json::array();
320
321 for( int i = 0; i < 3; ++i )
322 {
323 nlohmann::json entry = {};
324 entry["name"] = "preset" + std::to_string( i );
325 entry["start_layer"] = (int) F_Cu;
326 entry["end_layer"] = (int) In1_Cu;
327
328 // The middle one carries its size as a string, the way a hand edit would leave it.
329 entry["via_size"] = ( i == 1 ) ? nlohmann::json( "0.25" ) : nlohmann::json( 0.25 );
330 js.push_back( entry );
331 }
332
333 bds.Set( "via_stack_presets", js );
334 bds.m_ViaStackPresets.clear();
335 bds.Load();
336
337 BOOST_REQUIRE_EQUAL( bds.m_ViaStackPresets.size(), 3u );
338 BOOST_CHECK_EQUAL( bds.m_ViaStackPresets[2].m_Name, wxS( "preset2" ) );
339
340 // The unreadable size falls back rather than carrying a garbage value.
341 BOOST_CHECK_EQUAL( bds.m_ViaStackPresets[1].m_ViaSize, 0 );
342 BOOST_CHECK_EQUAL( bds.m_ViaStackPresets[2].m_ViaSize, pcbIUScale.mmToIU( 0.25 ) );
343}
344
345
346// A hand-edited pitch has to be bounded, or the hop position arithmetic overflows.
347BOOST_AUTO_TEST_CASE( AnAbsurdPresetPitchIsClampedOnLoad )
348{
349 BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
350
351 nlohmann::json entry = {};
352 entry["name"] = "silly";
353 entry["start_layer"] = (int) F_Cu;
354 entry["end_layer"] = (int) In1_Cu;
355 entry["staggered"] = true;
356 entry["pitch"] = 100000.0;
357
358 nlohmann::json js = nlohmann::json::array();
359 js.push_back( entry );
360
361 bds.Set( "via_stack_presets", js );
362 bds.m_ViaStackPresets.clear();
363 bds.Load();
364
365 BOOST_REQUIRE_EQUAL( bds.m_ViaStackPresets.size(), 1u );
367}
368
369
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:128
#define MAX_MICROVIA_STACK_PITCH_MM
Container for design settings for a BOARD object.
void UseCustomTrackViaSize(bool aEnabled)
Enables/disables custom track/via size settings.
int GetNextDiffPairIndex(int aIndex, bool aForward) const
Compute the next diff pair dimensions list index when cycling predefined sizes, skipping the index-0 ...
void SetViaSizeIndex(int aIndex)
Set the current via size list index to aIndex.
std::shared_ptr< DRC_ENGINE > m_DRCEngine
std::vector< DIFF_PAIR_DIMENSION > m_DiffPairDimensionsList
int GetNextTrackWidthIndex(int aIndex, bool aForward) const
Compute the next track width list index when cycling predefined sizes, skipping the index-0 netclass ...
bool LoadFromFile(const wxString &aDirectory="") override
Loads the backing file from disk and then calls Load()
void SetTrackWidthIndex(int aIndex)
Set the current track width list index to aIndex.
int GetNextViaSizeIndex(int aIndex, bool aForward) const
Compute the next via size list index when cycling predefined sizes, skipping the index-0 netclass pla...
std::vector< int > m_TrackWidthList
std::vector< VIA_STACK_PRESET > m_ViaStackPresets
std::vector< VIA_DIMENSION > m_ViasDimensionsList
MINOPTMAX< int > & Value()
Definition drc_rule.h:130
void Set(const std::string &aPath, ValueType aVal)
Stores a value into the JSON document Will throw an exception if ValueType isn't something that the l...
virtual void Load()
Updates the parameters of this object based on the current JSON document contents.
virtual bool Store()
Stores the current parameters into the JSON document represented by this object Note: this doesn't do...
void SetMin(T v)
Definition minoptmax.h:41
bool SaveToFile(const wxString &aDirectory="", bool aForce=false) override
Calls Store() and then saves the JSON document contents into the parent JSON_SETTINGS.
@ PHYSICAL_HOLE_CLEARANCE_CONSTRAINT
@ NONE
Definition eda_fill.h:42
SETTINGS_LOC
@ In1_Cu
Definition layer_ids.h:62
@ F_Cu
Definition layer_ids.h:60
BOOST_AUTO_TEST_CASE(HorizontalAlignment)
BOOST_AUTO_TEST_CASE(NegativeSilkClearanceRoundTrip)
Regression test for https://gitlab.com/kicad/code/kicad/-/issues/23327.
BOOST_AUTO_TEST_SUITE_END()
BOOST_CHECK_EQUAL(result, "25.4")