KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_drc_solder_mask_expansion.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, you may find one here:
18 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
19 * or you may search the http://www.gnu.org website for the version 2 license,
20 * or you may write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22 */
23
26#include <board.h>
28#include <pad.h>
29#include <pcb_track.h>
30#include <pcb_shape.h>
31#include <footprint.h>
32#include <drc/drc_engine.h>
33#include <core/profile.h>
35
36
45
46
48{
49 // This test verifies that footprint-level solder mask expansion override is correctly
50 // applied to pads that don't have their own local override.
51 // Regression test for https://gitlab.com/kicad/code/kicad/-/issues/22751
52
53 KI_TEST::LoadBoard( m_settingsManager, "issue22751/issue22751", m_board );
54
55 BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
56 bds.m_DRCEngine->InitEngine( wxFileName() );
57
58 // Board default solder mask expansion is 0.05mm (50000 IU)
60
61 // Find footprints and their pads
62 PAD* padWithFootprintMargin = nullptr;
63 PAD* padWithNoMargin = nullptr;
64 PAD* padWithOwnMargin = nullptr;
65
66 for( FOOTPRINT* fp : m_board->Footprints() )
67 {
68 if( fp->GetFPIDAsString() == "TestFootprint_WithSolderMaskMargin" )
69 {
70 // Footprint has solder mask margin of 0.1mm (100000 IU)
71 BOOST_CHECK( fp->GetLocalSolderMaskMargin().has_value() );
72 BOOST_CHECK_EQUAL( fp->GetLocalSolderMaskMargin().value(), 100000 );
73
74 for( PAD* pad : fp->Pads() )
75 {
76 if( pad->GetNumber() == "1" )
77 {
78 padWithFootprintMargin = pad;
79
80 // Pad should NOT have its own local margin
81 BOOST_CHECK( !pad->GetLocalSolderMaskMargin().has_value() );
82 }
83 }
84 }
85 else if( fp->GetFPIDAsString() == "TestFootprint_NoSolderMaskMargin" )
86 {
87 // Footprint has no solder mask margin
88 BOOST_CHECK( !fp->GetLocalSolderMaskMargin().has_value() );
89
90 for( PAD* pad : fp->Pads() )
91 {
92 if( pad->GetNumber() == "1" )
93 padWithNoMargin = pad;
94 }
95 }
96 else if( fp->GetFPIDAsString() == "TestFootprint_PadOverridesSolderMaskMargin" )
97 {
98 // Footprint has solder mask margin of 0.1mm
99 BOOST_CHECK( fp->GetLocalSolderMaskMargin().has_value() );
100 BOOST_CHECK_EQUAL( fp->GetLocalSolderMaskMargin().value(), 100000 );
101
102 for( PAD* pad : fp->Pads() )
103 {
104 if( pad->GetNumber() == "1" )
105 {
106 padWithOwnMargin = pad;
107
108 // Pad has its own margin of 0.2mm (200000 IU)
109 BOOST_CHECK( pad->GetLocalSolderMaskMargin().has_value() );
110 BOOST_CHECK_EQUAL( pad->GetLocalSolderMaskMargin().value(), 200000 );
111 }
112 }
113 }
114 }
115
116 BOOST_REQUIRE( padWithFootprintMargin != nullptr );
117 BOOST_REQUIRE( padWithNoMargin != nullptr );
118 BOOST_REQUIRE( padWithOwnMargin != nullptr );
119
120 // Test GetSolderMaskExpansion which uses the DRC engine to evaluate the constraint
121
122 // Pad in footprint with solder mask margin should use footprint's margin (0.1mm = 100000 IU)
123 int expansionWithFpMargin = padWithFootprintMargin->GetSolderMaskExpansion( F_Mask );
124 BOOST_CHECK_EQUAL( expansionWithFpMargin, 100000 );
125
126 // Pad in footprint without solder mask margin should use board default (0.05mm = 50000 IU)
127 int expansionNoMargin = padWithNoMargin->GetSolderMaskExpansion( F_Mask );
128 BOOST_CHECK_EQUAL( expansionNoMargin, 50000 );
129
130 // Pad with its own solder mask margin should use pad's margin (0.2mm = 200000 IU),
131 // overriding the footprint's margin
132 int expansionWithPadMargin = padWithOwnMargin->GetSolderMaskExpansion( F_Mask );
133 BOOST_CHECK_EQUAL( expansionWithPadMargin, 200000 );
134}
135
136
138{
139 // Verify that GetSolderMaskExpansion is fast when no custom DRC rules exist.
140 // Regression test for https://gitlab.com/kicad/code/kicad/-/issues/23213
141 //
142 // Before the fix, GetSolderMaskExpansion called EvalRules for every item even when no
143 // custom rules existed, causing a 10-14x DRC slowdown vs KiCad 9.
144
145 KI_TEST::LoadBoard( m_settingsManager, "stonehenge", m_board );
146
147 BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
148 bds.m_DRCEngine->InitEngine( wxFileName() );
149
151
152 // Collect all items that would be queried during the solder mask test
153 std::vector<PAD*> pads;
154 std::vector<PCB_TRACK*> tracks;
155 std::vector<PCB_SHAPE*> shapes;
156
157 for( FOOTPRINT* fp : m_board->Footprints() )
158 {
159 for( PAD* pad : fp->Pads() )
160 pads.push_back( pad );
161
162 for( BOARD_ITEM* item : fp->GraphicalItems() )
163 {
164 if( item->Type() == PCB_SHAPE_T )
165 shapes.push_back( static_cast<PCB_SHAPE*>( item ) );
166 }
167 }
168
169 for( PCB_TRACK* track : m_board->Tracks() )
170 tracks.push_back( track );
171
172 for( BOARD_ITEM* item : m_board->Drawings() )
173 {
174 if( item->Type() == PCB_SHAPE_T )
175 shapes.push_back( static_cast<PCB_SHAPE*>( item ) );
176 }
177
178 // Simulate the access pattern from the solder mask bridge test: call GetSolderMaskExpansion
179 // many times for each item (once per potential collision pair).
180 const int iterations = 1000;
181
182 PROF_TIMER timer;
183
184 for( int i = 0; i < iterations; ++i )
185 {
186 for( PAD* pad : pads )
187 pad->GetSolderMaskExpansion( F_Mask );
188
189 for( PCB_TRACK* track : tracks )
190 track->GetSolderMaskExpansion();
191
192 for( PCB_SHAPE* shape : shapes )
193 shape->GetSolderMaskExpansion();
194 }
195
196 timer.Stop();
197
198 int totalCalls = iterations * ( pads.size() + tracks.size() + shapes.size() );
199
200 BOOST_TEST_MESSAGE( wxString::Format( "%d calls to GetSolderMaskExpansion took %0.1f ms "
201 "(%0.0f ns/call)",
202 totalCalls, timer.msecs(),
203 timer.msecs() * 1e6 / totalCalls ) );
204
205 // With the fix (direct property lookup), this should be well under 100ms.
206 // Without the fix (EvalRules for every call), this was ~500ms+ even in debug builds.
207 // Use a generous threshold to avoid flakiness on slow CI machines.
208 BOOST_CHECK_MESSAGE( timer.msecs() < 500.0,
209 wxString::Format( "GetSolderMaskExpansion too slow: %0.1f ms for %d calls",
210 timer.msecs(), totalCalls ) );
211}
Container for design settings for a BOARD object.
std::shared_ptr< DRC_ENGINE > m_DRCEngine
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition board_item.h:84
bool HasRulesForConstraintType(DRC_CONSTRAINT_T constraintID)
void InitEngine(const wxFileName &aRulePath)
Initialize the DRC engine.
Definition pad.h:55
int GetSolderMaskExpansion(PCB_LAYER_ID aLayer) const
Definition pad.cpp:1639
A small class to help profiling.
Definition profile.h:49
void Stop()
Save the time when this function was called, and set the counter stane to stop.
Definition profile.h:88
double msecs(bool aSinceLast=false)
Definition profile.h:149
@ SOLDER_MASK_EXPANSION_CONSTRAINT
Definition drc_rule.h:66
@ F_Mask
Definition layer_ids.h:97
void LoadBoard(SETTINGS_MANAGER &aSettingsManager, const wxString &aRelPath, std::unique_ptr< BOARD > &aBoard)
BOOST_FIXTURE_TEST_CASE(FootprintLevelSolderMaskExpansion, DRC_SOLDER_MASK_EXPANSION_TEST_FIXTURE)
BOOST_REQUIRE(intersection.has_value()==c.ExpectedIntersection.has_value())
BOOST_TEST_MESSAGE("Polyline has "<< chain.PointCount()<< " points")
BOOST_CHECK_EQUAL(result, "25.4")
@ PCB_SHAPE_T
class PCB_SHAPE, a segment not on copper layers
Definition typeinfo.h:88