KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_pads_keepout.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 (C) 2025 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
20#include <boost/test/unit_test.hpp>
25#include <board.h>
26#include <zone.h>
27
28using namespace PADS_IO;
29
30
31BOOST_AUTO_TEST_SUITE( PadsKeepoutParsing )
32
33
34BOOST_AUTO_TEST_CASE( KeepoutStruct_DefaultConstruction )
35{
36 KEEPOUT keepout;
37 BOOST_CHECK( keepout.outline.empty() );
38 BOOST_CHECK( keepout.layers.empty() );
39 BOOST_CHECK_EQUAL( static_cast<int>( keepout.type ), static_cast<int>( KEEPOUT_TYPE::ALL ) );
40 BOOST_CHECK( keepout.no_traces );
41 BOOST_CHECK( keepout.no_vias );
42 BOOST_CHECK( keepout.no_copper );
43 BOOST_CHECK( !keepout.no_components );
44}
45
46
47BOOST_AUTO_TEST_CASE( KeepoutType_AllKeepout )
48{
49 KEEPOUT keepout;
50 keepout.type = KEEPOUT_TYPE::ALL;
51 keepout.no_traces = true;
52 keepout.no_vias = true;
53 keepout.no_copper = true;
54
55 BOOST_CHECK_EQUAL( static_cast<int>( keepout.type ), static_cast<int>( KEEPOUT_TYPE::ALL ) );
56 BOOST_CHECK( keepout.no_traces );
57 BOOST_CHECK( keepout.no_vias );
58 BOOST_CHECK( keepout.no_copper );
59}
60
61
62BOOST_AUTO_TEST_CASE( KeepoutType_ViaOnly )
63{
64 KEEPOUT keepout;
65 keepout.type = KEEPOUT_TYPE::VIA;
66 keepout.no_traces = false;
67 keepout.no_vias = true;
68 keepout.no_copper = false;
69
70 BOOST_CHECK_EQUAL( static_cast<int>( keepout.type ), static_cast<int>( KEEPOUT_TYPE::VIA ) );
71 BOOST_CHECK( !keepout.no_traces );
72 BOOST_CHECK( keepout.no_vias );
73 BOOST_CHECK( !keepout.no_copper );
74}
75
76
77BOOST_AUTO_TEST_CASE( KeepoutType_RouteOnly )
78{
79 KEEPOUT keepout;
80 keepout.type = KEEPOUT_TYPE::ROUTE;
81 keepout.no_traces = true;
82 keepout.no_vias = false;
83 keepout.no_copper = false;
84
85 BOOST_CHECK_EQUAL( static_cast<int>( keepout.type ), static_cast<int>( KEEPOUT_TYPE::ROUTE ) );
86 BOOST_CHECK( keepout.no_traces );
87 BOOST_CHECK( !keepout.no_vias );
88 BOOST_CHECK( !keepout.no_copper );
89}
90
91
92BOOST_AUTO_TEST_CASE( KeepoutType_PlacementOnly )
93{
94 KEEPOUT keepout;
96 keepout.no_traces = false;
97 keepout.no_vias = false;
98 keepout.no_copper = false;
99 keepout.no_components = true;
100
101 BOOST_CHECK_EQUAL( static_cast<int>( keepout.type ), static_cast<int>( KEEPOUT_TYPE::PLACEMENT ) );
102 BOOST_CHECK( !keepout.no_traces );
103 BOOST_CHECK( !keepout.no_vias );
104 BOOST_CHECK( !keepout.no_copper );
105 BOOST_CHECK( keepout.no_components );
106}
107
108
109BOOST_AUTO_TEST_CASE( KeepoutOutline_RectanglePoints )
110{
111 KEEPOUT keepout;
112 keepout.outline.emplace_back( 0, 0 );
113 keepout.outline.emplace_back( 100, 0 );
114 keepout.outline.emplace_back( 100, 100 );
115 keepout.outline.emplace_back( 0, 100 );
116
117 BOOST_REQUIRE_EQUAL( keepout.outline.size(), 4 );
118 BOOST_CHECK_EQUAL( keepout.outline[0].x, 0 );
119 BOOST_CHECK_EQUAL( keepout.outline[0].y, 0 );
120 BOOST_CHECK_EQUAL( keepout.outline[2].x, 100 );
121 BOOST_CHECK_EQUAL( keepout.outline[2].y, 100 );
122}
123
124
125BOOST_AUTO_TEST_CASE( KeepoutLayers_SingleLayer )
126{
127 KEEPOUT keepout;
128 keepout.layers.push_back( 1 );
129
130 BOOST_REQUIRE_EQUAL( keepout.layers.size(), 1 );
131 BOOST_CHECK_EQUAL( keepout.layers[0], 1 );
132}
133
134
135BOOST_AUTO_TEST_CASE( KeepoutLayers_MultipleLayers )
136{
137 KEEPOUT keepout;
138 keepout.layers.push_back( 1 );
139 keepout.layers.push_back( 2 );
140 keepout.layers.push_back( 4 );
141
142 BOOST_REQUIRE_EQUAL( keepout.layers.size(), 3 );
143 BOOST_CHECK_EQUAL( keepout.layers[0], 1 );
144 BOOST_CHECK_EQUAL( keepout.layers[1], 2 );
145 BOOST_CHECK_EQUAL( keepout.layers[2], 4 );
146}
147
148
149BOOST_AUTO_TEST_CASE( KeepoutLayers_AllLayers )
150{
151 // Empty layers vector means all layers
152 KEEPOUT keepout;
153
154 BOOST_CHECK( keepout.layers.empty() );
155}
156
157
158BOOST_AUTO_TEST_CASE( ParseKeepoutFile )
159{
160 wxString filename = KI_TEST::GetPcbnewTestDataDir() + "plugins/pads/keepout_test.asc";
161
162 PARSER parser;
163 parser.Parse( filename );
164
165 const auto& keepouts = parser.GetKeepouts();
166
167 BOOST_REQUIRE_EQUAL( keepouts.size(), 5 );
168
169 // Verify KEEPOUT (all restrictions)
170 bool found_keepout = false;
171
172 for( const auto& ko : keepouts )
173 {
174 if( ko.type == KEEPOUT_TYPE::ALL && ko.outline.size() == 4 )
175 {
176 found_keepout = true;
177 BOOST_CHECK( ko.no_traces );
178 BOOST_CHECK( ko.no_vias );
179 BOOST_CHECK( ko.no_copper );
180 break;
181 }
182 }
183
184 BOOST_CHECK( found_keepout );
185
186 // Verify RESTRICTVIA (via only)
187 bool found_via_restrict = false;
188
189 for( const auto& ko : keepouts )
190 {
191 if( ko.type == KEEPOUT_TYPE::VIA )
192 {
193 found_via_restrict = true;
194 BOOST_CHECK( !ko.no_traces );
195 BOOST_CHECK( ko.no_vias );
196 BOOST_CHECK( !ko.no_copper );
197 break;
198 }
199 }
200
201 BOOST_CHECK( found_via_restrict );
202
203 // Verify RESTRICTROUTE (routing only)
204 bool found_route_restrict = false;
205
206 for( const auto& ko : keepouts )
207 {
208 if( ko.type == KEEPOUT_TYPE::ROUTE )
209 {
210 found_route_restrict = true;
211 BOOST_CHECK( ko.no_traces );
212 BOOST_CHECK( !ko.no_vias );
213 BOOST_CHECK( !ko.no_copper );
214 break;
215 }
216 }
217
218 BOOST_CHECK( found_route_restrict );
219
220 // Verify RESTRICTAREA (all copper)
221 bool found_area_restrict = false;
222
223 for( const auto& ko : keepouts )
224 {
225 if( ko.type == KEEPOUT_TYPE::ALL && ko.outline.size() > 0 )
226 {
227 // This could be the general KEEPOUT or the RESTRICTAREA
228 // We need to verify we have at least one
229 found_area_restrict = true;
230 }
231 }
232
233 BOOST_CHECK( found_area_restrict );
234
235 // Verify PLACEMENT_KEEPOUT
236 bool found_placement = false;
237
238 for( const auto& ko : keepouts )
239 {
240 if( ko.type == KEEPOUT_TYPE::PLACEMENT )
241 {
242 found_placement = true;
243 BOOST_CHECK( !ko.no_traces );
244 BOOST_CHECK( !ko.no_vias );
245 BOOST_CHECK( !ko.no_copper );
246 BOOST_CHECK( ko.no_components );
247 break;
248 }
249 }
250
251 BOOST_CHECK( found_placement );
252}
253
254
255BOOST_AUTO_TEST_CASE( ParseKeepoutOutlineGeometry )
256{
257 wxString filename = KI_TEST::GetPcbnewTestDataDir() + "plugins/pads/keepout_test.asc";
258
259 PARSER parser;
260 parser.Parse( filename );
261
262 const auto& keepouts = parser.GetKeepouts();
263
264 // Find the first keepout with 4 points (the rectangular KEEPOUT)
265 const KEEPOUT* rect_keepout = nullptr;
266
267 for( const auto& ko : keepouts )
268 {
269 if( ko.type == KEEPOUT_TYPE::ALL && ko.outline.size() == 4 )
270 {
271 rect_keepout = &ko;
272 break;
273 }
274 }
275
276 BOOST_REQUIRE( rect_keepout != nullptr );
277
278 // File defines KEEPOUT_RECT at 100,100 with 4 corners relative to that:
279 // 0,0 / 200,0 / 200,200 / 0,200
280 // So absolute coordinates should be: 100,100 / 300,100 / 300,300 / 100,300
281 BOOST_REQUIRE_EQUAL( rect_keepout->outline.size(), 4 );
282
283 // The first point should be at (100, 100) in PADS coordinates
284 BOOST_CHECK_CLOSE( rect_keepout->outline[0].x, 100.0, 0.1 );
285 BOOST_CHECK_CLOSE( rect_keepout->outline[0].y, 100.0, 0.1 );
286
287 // The third point should be at (300, 300)
288 BOOST_CHECK_CLOSE( rect_keepout->outline[2].x, 300.0, 0.1 );
289 BOOST_CHECK_CLOSE( rect_keepout->outline[2].y, 300.0, 0.1 );
290}
291
292
293BOOST_AUTO_TEST_CASE( ImportKeepoutAsRuleArea )
294{
295 PCB_IO_PADS plugin;
296 wxString filename = KI_TEST::GetPcbnewTestDataDir() + "plugins/pads/keepout_test.asc";
297
298 std::unique_ptr<BOARD> board( plugin.LoadBoard( filename, nullptr, nullptr, nullptr ) );
299 BOOST_REQUIRE( board != nullptr );
300
301 // Count rule areas (keepout zones)
302 int ruleAreaCount = 0;
303 int allKeepoutCount = 0;
304 int viaKeepoutCount = 0;
305 int routeKeepoutCount = 0;
306 int placementKeepoutCount = 0;
307
308 for( ZONE* zone : board->Zones() )
309 {
310 if( zone->GetIsRuleArea() )
311 {
312 ruleAreaCount++;
313
314 if( zone->GetZoneName().StartsWith( wxT( "Keepout_" ) ) )
315 allKeepoutCount++;
316 else if( zone->GetZoneName().StartsWith( wxT( "ViaKeepout_" ) ) )
317 viaKeepoutCount++;
318 else if( zone->GetZoneName().StartsWith( wxT( "RouteKeepout_" ) ) )
319 routeKeepoutCount++;
320 else if( zone->GetZoneName().StartsWith( wxT( "PlacementKeepout_" ) ) )
321 placementKeepoutCount++;
322 }
323 }
324
325 // The test file should have 5 keepouts (KEEPOUT, RESTRICTVIA, RESTRICTROUTE, RESTRICTAREA, PLACEMENT_KEEPOUT)
326 BOOST_CHECK_EQUAL( ruleAreaCount, 5 );
327 BOOST_CHECK_EQUAL( allKeepoutCount, 2 ); // KEEPOUT and RESTRICTAREA both become Keepout_*
328 BOOST_CHECK_EQUAL( viaKeepoutCount, 1 );
329 BOOST_CHECK_EQUAL( routeKeepoutCount, 1 );
330 BOOST_CHECK_EQUAL( placementKeepoutCount, 1 );
331}
332
333
334BOOST_AUTO_TEST_CASE( KeepoutConstraintFlags )
335{
336 PCB_IO_PADS plugin;
337 wxString filename = KI_TEST::GetPcbnewTestDataDir() + "plugins/pads/keepout_test.asc";
338
339 std::unique_ptr<BOARD> board( plugin.LoadBoard( filename, nullptr, nullptr, nullptr ) );
340 BOOST_REQUIRE( board != nullptr );
341
342 // Find ViaKeepout and verify only vias are prohibited
343 ZONE* viaKeepout = nullptr;
344
345 for( ZONE* zone : board->Zones() )
346 {
347 if( zone->GetZoneName().StartsWith( wxT( "ViaKeepout_" ) ) )
348 {
349 viaKeepout = zone;
350 break;
351 }
352 }
353
354 BOOST_REQUIRE( viaKeepout != nullptr );
355 BOOST_CHECK( viaKeepout->GetDoNotAllowVias() );
356 BOOST_CHECK( !viaKeepout->GetDoNotAllowTracks() );
357 BOOST_CHECK( !viaKeepout->GetDoNotAllowZoneFills() );
358 BOOST_CHECK( !viaKeepout->GetDoNotAllowFootprints() );
359
360 // Find RouteKeepout and verify only tracks are prohibited
361 ZONE* routeKeepout = nullptr;
362
363 for( ZONE* zone : board->Zones() )
364 {
365 if( zone->GetZoneName().StartsWith( wxT( "RouteKeepout_" ) ) )
366 {
367 routeKeepout = zone;
368 break;
369 }
370 }
371
372 BOOST_REQUIRE( routeKeepout != nullptr );
373 BOOST_CHECK( routeKeepout->GetDoNotAllowTracks() );
374 BOOST_CHECK( !routeKeepout->GetDoNotAllowVias() );
375 BOOST_CHECK( !routeKeepout->GetDoNotAllowZoneFills() );
376 BOOST_CHECK( !routeKeepout->GetDoNotAllowFootprints() );
377
378 // Find PlacementKeepout and verify only footprints are prohibited
379 ZONE* placementKeepout = nullptr;
380
381 for( ZONE* zone : board->Zones() )
382 {
383 if( zone->GetZoneName().StartsWith( wxT( "PlacementKeepout_" ) ) )
384 {
385 placementKeepout = zone;
386 break;
387 }
388 }
389
390 BOOST_REQUIRE( placementKeepout != nullptr );
391 BOOST_CHECK( placementKeepout->GetDoNotAllowFootprints() );
392 BOOST_CHECK( !placementKeepout->GetDoNotAllowTracks() );
393 BOOST_CHECK( !placementKeepout->GetDoNotAllowVias() );
394 BOOST_CHECK( !placementKeepout->GetDoNotAllowZoneFills() );
395}
396
397
General utilities for PCB file IO for QA programs.
const std::vector< KEEPOUT > & GetKeepouts() const
void Parse(const wxString &aFileName)
BOARD * LoadBoard(const wxString &aFileName, BOARD *aAppendToMe, const std::map< std::string, UTF8 > *aProperties, PROJECT *aProject) override
Load information from some input file format that this PCB_IO implementation knows about into either ...
Handle a list of polygons defining a copper zone.
Definition zone.h:70
bool GetDoNotAllowVias() const
Definition zone.h:822
bool GetDoNotAllowTracks() const
Definition zone.h:823
bool GetDoNotAllowFootprints() const
Definition zone.h:825
bool GetDoNotAllowZoneFills() const
Definition zone.h:821
std::string GetPcbnewTestDataDir()
Utility which returns a path to the data directory where the test board files are stored.
@ ROUTE
Routing keepout (traces)
@ PLACEMENT
Component placement keepout.
A keepout area definition.
std::vector< ARC_POINT > outline
Keepout boundary.
bool no_vias
Prohibit vias (V restriction)
bool no_components
Prohibit component placement (P restriction)
bool no_copper
Prohibit copper pours (C restriction)
KEEPOUT_TYPE type
Type of keepout.
bool no_traces
Prohibit traces (R restriction)
std::vector< int > layers
Affected layers (empty = all)
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(KeepoutStruct_DefaultConstruction)
BOOST_CHECK_EQUAL(result, "25.4")