KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_drc_stub_length.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
20#include <boost/test/unit_test.hpp>
21
22#include <filesystem>
23#include <fstream>
24
25#include <base_units.h>
26#include <board.h>
28#include <drc/drc_engine.h>
29#include <drc/drc_item.h>
30#include <footprint.h>
31#include <netinfo.h>
32#include <pad.h>
33#include <pcb_marker.h>
34#include <pcb_track.h>
36#include <drc/drc_rule_parser.h>
37#include <reporter.h>
38
39
40// Classifier-only fixture: the stub-length check today picks trunk/stub by
41// terminal-pad ownership, not by routed topology, so the three nets need only
42// be tagged into the same chain and given individually routed traces. Pads
43// UA-pad1 and UC-pad1 are assigned as the chain's terminal pads, leaving
44// MID_B as the only chain member without an endpoint assignment. Once real
45// topological stub detection lands the segments will need to actually meet at
46// series components.
47static const char* BOARD_TEXT = R"KICAD(
48(kicad_pcb
49 (version 20250904)
50 (generator "pcbnew")
51 (generator_version "9.99")
52 (layers
53 (0 "F.Cu" signal)
54 (2 "B.Cu" signal)
55 )
56 (net 0 "")
57 (net 1 "/TRUNK_A")
58 (net 2 "/MID_B")
59 (net 3 "/TRUNK_C")
60 (footprint "lib:UA"
61 (layer "F.Cu")
62 (uuid "11111111-1111-1111-1111-111111111111")
63 (at 0 0)
64 (pad "1" smd rect
65 (at 0 0)
66 (size 1 1)
67 (layers "F.Cu")
68 (uuid "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa01")
69 (net 1 "/TRUNK_A")
70 )
71 )
72 (footprint "lib:UC"
73 (layer "F.Cu")
74 (uuid "33333333-3333-3333-3333-333333333333")
75 (at 40 0)
76 (pad "1" smd rect
77 (at 0 0)
78 (size 1 1)
79 (layers "F.Cu")
80 (uuid "cccccccc-cccc-cccc-cccc-cccccccccc01")
81 (net 3 "/TRUNK_C")
82 )
83 )
84 (segment (start 0 0) (end 2 0) (width 0.2) (layer "F.Cu") (net 1))
85 (segment (start 5 0) (end 35 0) (width 0.2) (layer "F.Cu") (net 2))
86 (segment (start 38 0) (end 40 0) (width 0.2) (layer "F.Cu") (net 3))
87)
88)KICAD";
89
90
91// stub_length 0..5 mm. The 30 mm middle net is well over budget; the 2 mm
92// endpoints are within budget *and* are protected by trunk membership anyway.
93static const char* DRU_TEXT = R"KICAD((version 1)
94
95(rule "StubBudget"
96 (condition "A.NetClass == 'Default'")
97 (constraint stub_length (min 0mm) (max 5mm))
98)
99)KICAD";
100
101
102BOOST_AUTO_TEST_SUITE( DRCStubLength )
103
104
105BOOST_AUTO_TEST_CASE( StubLengthFiresOnIntermediateNetOnly )
106{
107 namespace fs = std::filesystem;
108
109 fs::path tmpDir = fs::temp_directory_path() / "kicad_drc_stub_length";
110 fs::create_directories( tmpDir );
111
112 fs::path pcbPath = tmpDir / "stub_length.kicad_pcb";
113 fs::path druPath = tmpDir / "stub_length.kicad_dru";
114
115 {
116 std::ofstream pcbOut( pcbPath );
117 pcbOut << BOARD_TEXT;
118 }
119
120 {
121 std::ofstream druOut( druPath );
122 druOut << DRU_TEXT;
123 }
124
125 PCB_IO_KICAD_SEXPR plugin;
126 std::unique_ptr<BOARD> board = std::make_unique<BOARD>();
127 plugin.LoadBoard( pcbPath.string(), board.get() );
128 board->BuildConnectivity();
129
130 NETINFO_ITEM* netA = board->FindNet( wxS( "/TRUNK_A" ) );
131 NETINFO_ITEM* netB = board->FindNet( wxS( "/MID_B" ) );
132 NETINFO_ITEM* netC = board->FindNet( wxS( "/TRUNK_C" ) );
133
134 BOOST_REQUIRE( netA );
135 BOOST_REQUIRE( netB );
136 BOOST_REQUIRE( netC );
137
138 netA->SetNetChain( wxS( "SIG" ) );
139 netB->SetNetChain( wxS( "SIG" ) );
140 netC->SetNetChain( wxS( "SIG" ) );
141
142 PAD* padA = nullptr;
143 PAD* padC = nullptr;
144
145 for( FOOTPRINT* fp : board->Footprints() )
146 {
147 for( PAD* pad : fp->Pads() )
148 {
149 if( pad->GetNetCode() == netA->GetNetCode() )
150 padA = pad;
151
152 if( pad->GetNetCode() == netC->GetNetCode() )
153 padC = pad;
154 }
155 }
156
157 BOOST_REQUIRE( padA );
158 BOOST_REQUIRE( padC );
159
160 netA->SetTerminalPad( 0, padA );
161 netC->SetTerminalPad( 1, padC );
162
163 BOARD_DESIGN_SETTINGS& bds = board->GetDesignSettings();
164
165 auto drcEngine = std::make_shared<DRC_ENGINE>( board.get(), &bds );
166 wxFileName ruleFile( druPath.string() );
167 drcEngine->InitEngine( ruleFile );
168 bds.m_DRCEngine = drcEngine;
169
177
178 std::vector<wxString> stubMessages;
179
180 drcEngine->SetViolationHandler(
181 [&]( const std::shared_ptr<DRC_ITEM>& aItem, const VECTOR2I&, int,
182 const std::function<void( PCB_MARKER* )>& )
183 {
184 if( aItem->GetErrorCode() == DRCE_NET_CHAIN_STUB_TOO_LONG )
185 stubMessages.push_back( aItem->GetErrorMessage( false ) );
186 } );
187
188 drcEngine->RunTests( EDA_UNITS::MM, true, false );
189
190 auto matchesNet = [&]( const wxString& netName )
191 {
192 for( const wxString& msg : stubMessages )
193 {
194 if( msg.Contains( wxString::Format( wxS( "'%s'" ), netName ) ) )
195 return true;
196 }
197
198 return false;
199 };
200
201 bool aFlagged = matchesNet( netA->GetNetname() );
202 bool bFlagged = matchesNet( netB->GetNetname() );
203 bool cFlagged = matchesNet( netC->GetNetname() );
204
205 BOOST_CHECK_MESSAGE( bFlagged,
206 "Intermediate stub net MID_B should fire stub_length violation" );
207 BOOST_CHECK_MESSAGE( !aFlagged,
208 "Trunk endpoint net TRUNK_A (owns terminal_pad_0) must not fire" );
209 BOOST_CHECK_MESSAGE( !cFlagged,
210 "Trunk endpoint net TRUNK_C (owns terminal_pad_1) must not fire" );
211
212 std::error_code ec;
213 fs::remove( pcbPath, ec );
214 fs::remove( druPath, ec );
215}
216
217
218BOOST_AUTO_TEST_CASE( StubLengthQuietOnTwoNetChain )
219{
220 namespace fs = std::filesystem;
221
222 fs::path tmpDir = fs::temp_directory_path() / "kicad_drc_stub_length";
223 fs::create_directories( tmpDir );
224
225 fs::path pcbPath = tmpDir / "stub_length_2net.kicad_pcb";
226 fs::path druPath = tmpDir / "stub_length_2net.kicad_dru";
227
228 // Two-net chain: every member owns a terminal pad assignment, so no member
229 // is a stub. With the 30 mm trace on each net, a per-net stub_length would
230 // fire if the trunk classifier wrongly excluded one of them.
231 static const char* TWO_NET_BOARD = R"KICAD(
232(kicad_pcb
233 (version 20250904)
234 (generator "pcbnew")
235 (generator_version "9.99")
236 (layers
237 (0 "F.Cu" signal)
238 (2 "B.Cu" signal)
239 )
240 (net 0 "")
241 (net 1 "/A")
242 (net 2 "/B")
243 (footprint "lib:UA"
244 (layer "F.Cu")
245 (uuid "44444444-4444-4444-4444-444444444444")
246 (at 0 0)
247 (pad "1" smd rect
248 (at 0 0)
249 (size 1 1)
250 (layers "F.Cu")
251 (uuid "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa02")
252 (net 1 "/A")
253 )
254 )
255 (footprint "lib:UB"
256 (layer "F.Cu")
257 (uuid "55555555-5555-5555-5555-555555555555")
258 (at 60 0)
259 (pad "1" smd rect
260 (at 0 0)
261 (size 1 1)
262 (layers "F.Cu")
263 (uuid "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbb02")
264 (net 2 "/B")
265 )
266 )
267 (segment (start 0 0) (end 30 0) (width 0.2) (layer "F.Cu") (net 1))
268 (segment (start 30 0) (end 60 0) (width 0.2) (layer "F.Cu") (net 2))
269)
270)KICAD";
271
272 static const char* TWO_NET_DRU = R"KICAD((version 1)
273
274(rule "StubBudget"
275 (condition "A.NetClass == 'Default'")
276 (constraint stub_length (min 0mm) (max 5mm))
277)
278)KICAD";
279
280 {
281 std::ofstream pcbOut( pcbPath );
282 pcbOut << TWO_NET_BOARD;
283 }
284
285 {
286 std::ofstream druOut( druPath );
287 druOut << TWO_NET_DRU;
288 }
289
290 PCB_IO_KICAD_SEXPR plugin;
291 std::unique_ptr<BOARD> board = std::make_unique<BOARD>();
292 plugin.LoadBoard( pcbPath.string(), board.get() );
293 board->BuildConnectivity();
294
295 NETINFO_ITEM* netA = board->FindNet( wxS( "/A" ) );
296 NETINFO_ITEM* netB = board->FindNet( wxS( "/B" ) );
297
298 BOOST_REQUIRE( netA );
299 BOOST_REQUIRE( netB );
300
301 netA->SetNetChain( wxS( "SIG" ) );
302 netB->SetNetChain( wxS( "SIG" ) );
303
304 PAD* padA = nullptr;
305 PAD* padB = nullptr;
306
307 for( FOOTPRINT* fp : board->Footprints() )
308 {
309 for( PAD* pad : fp->Pads() )
310 {
311 if( pad->GetNetCode() == netA->GetNetCode() )
312 padA = pad;
313
314 if( pad->GetNetCode() == netB->GetNetCode() )
315 padB = pad;
316 }
317 }
318
319 BOOST_REQUIRE( padA );
320 BOOST_REQUIRE( padB );
321
322 netA->SetTerminalPad( 0, padA );
323 netB->SetTerminalPad( 1, padB );
324
325 BOARD_DESIGN_SETTINGS& bds = board->GetDesignSettings();
326
327 auto drcEngine = std::make_shared<DRC_ENGINE>( board.get(), &bds );
328 wxFileName ruleFile( druPath.string() );
329 drcEngine->InitEngine( ruleFile );
330 bds.m_DRCEngine = drcEngine;
331
339
340 int stubCount = 0;
341
342 drcEngine->SetViolationHandler(
343 [&]( const std::shared_ptr<DRC_ITEM>& aItem, const VECTOR2I&, int,
344 const std::function<void( PCB_MARKER* )>& )
345 {
346 if( aItem->GetErrorCode() == DRCE_NET_CHAIN_STUB_TOO_LONG )
347 ++stubCount;
348 } );
349
350 drcEngine->RunTests( EDA_UNITS::MM, true, false );
351
352 BOOST_CHECK_MESSAGE( stubCount == 0,
353 "Two-net chain with both ends owning terminal pads must not "
354 "fire any stub_length violation, got "
355 << stubCount );
356
357 std::error_code ec;
358 fs::remove( pcbPath, ec );
359 fs::remove( druPath, ec );
360}
361
362
363BOOST_AUTO_TEST_CASE( StubLengthIncludesPadToDie )
364{
365 namespace fs = std::filesystem;
366
367 fs::path tmpDir = fs::temp_directory_path() / "kicad_drc_stub_length";
368 fs::create_directories( tmpDir );
369
370 fs::path pcbPath = tmpDir / "stub_length_pad_to_die.kicad_pcb";
371 fs::path druPath = tmpDir / "stub_length_pad_to_die.kicad_dru";
372
373 // Three-net chain whose middle net (MID_B) is a stub. The stub's routed
374 // copper is a short 2 mm trace, well under the 5 mm budget. A 10 mm
375 // pad-to-die length is attached to the MID_B pad, so the *total* stub
376 // length (route + pad-to-die) is 12 mm and must violate the constraint.
377 static const char* PAD_TO_DIE_BOARD = R"KICAD(
378(kicad_pcb
379 (version 20250904)
380 (generator "pcbnew")
381 (generator_version "9.99")
382 (layers
383 (0 "F.Cu" signal)
384 (2 "B.Cu" signal)
385 )
386 (net 0 "")
387 (net 1 "/TRUNK_A")
388 (net 2 "/MID_B")
389 (net 3 "/TRUNK_C")
390 (footprint "lib:UA"
391 (layer "F.Cu")
392 (uuid "66666666-6666-6666-6666-666666666666")
393 (at 0 0)
394 (pad "1" smd rect
395 (at 0 0)
396 (size 1 1)
397 (layers "F.Cu")
398 (uuid "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa03")
399 (net 1 "/TRUNK_A")
400 )
401 )
402 (footprint "lib:UB"
403 (layer "F.Cu")
404 (uuid "77777777-7777-7777-7777-777777777777")
405 (at 5 0)
406 (pad "1" smd rect
407 (at 0 0)
408 (size 1 1)
409 (layers "F.Cu")
410 (uuid "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbb03")
411 (net 2 "/MID_B")
412 )
413 )
414 (footprint "lib:UC"
415 (layer "F.Cu")
416 (uuid "88888888-8888-8888-8888-888888888888")
417 (at 10 0)
418 (pad "1" smd rect
419 (at 0 0)
420 (size 1 1)
421 (layers "F.Cu")
422 (uuid "cccccccc-cccc-cccc-cccc-cccccccccc03")
423 (net 3 "/TRUNK_C")
424 )
425 )
426 (segment (start 0 0) (end 2 0) (width 0.2) (layer "F.Cu") (net 1))
427 (segment (start 3 0) (end 5 0) (width 0.2) (layer "F.Cu") (net 2))
428 (segment (start 8 0) (end 10 0) (width 0.2) (layer "F.Cu") (net 3))
429)
430)KICAD";
431
432 static const char* PAD_TO_DIE_DRU = R"KICAD((version 1)
433
434(rule "StubBudget"
435 (condition "A.NetClass == 'Default'")
436 (constraint stub_length (min 0mm) (max 5mm))
437)
438)KICAD";
439
440 {
441 std::ofstream pcbOut( pcbPath );
442 pcbOut << PAD_TO_DIE_BOARD;
443 }
444
445 {
446 std::ofstream druOut( druPath );
447 druOut << PAD_TO_DIE_DRU;
448 }
449
450 PCB_IO_KICAD_SEXPR plugin;
451 std::unique_ptr<BOARD> board = std::make_unique<BOARD>();
452 plugin.LoadBoard( pcbPath.string(), board.get() );
453
454 NETINFO_ITEM* netA = board->FindNet( wxS( "/TRUNK_A" ) );
455 NETINFO_ITEM* netB = board->FindNet( wxS( "/MID_B" ) );
456 NETINFO_ITEM* netC = board->FindNet( wxS( "/TRUNK_C" ) );
457
458 BOOST_REQUIRE( netA );
459 BOOST_REQUIRE( netB );
460 BOOST_REQUIRE( netC );
461
462 netA->SetNetChain( wxS( "SIG" ) );
463 netB->SetNetChain( wxS( "SIG" ) );
464 netC->SetNetChain( wxS( "SIG" ) );
465
466 PAD* padA = nullptr;
467 PAD* padB = nullptr;
468 PAD* padC = nullptr;
469
470 for( FOOTPRINT* fp : board->Footprints() )
471 {
472 for( PAD* pad : fp->Pads() )
473 {
474 if( pad->GetNetCode() == netA->GetNetCode() )
475 padA = pad;
476
477 if( pad->GetNetCode() == netB->GetNetCode() )
478 padB = pad;
479
480 if( pad->GetNetCode() == netC->GetNetCode() )
481 padC = pad;
482 }
483 }
484
485 BOOST_REQUIRE( padA );
486 BOOST_REQUIRE( padB );
487 BOOST_REQUIRE( padC );
488
489 // 10 mm pad-to-die on the stub net pushes the total stub length above the
490 // 5 mm budget even though the routed copper is only 2 mm.
491 padB->SetPadToDieLength( pcbIUScale.mmToIU( 10 ) );
492
493 netA->SetTerminalPad( 0, padA );
494 netC->SetTerminalPad( 1, padC );
495
496 board->BuildConnectivity();
497
498 BOARD_DESIGN_SETTINGS& bds = board->GetDesignSettings();
499
500 auto drcEngine = std::make_shared<DRC_ENGINE>( board.get(), &bds );
501 wxFileName ruleFile( druPath.string() );
502 drcEngine->InitEngine( ruleFile );
503 bds.m_DRCEngine = drcEngine;
504
512
513 std::vector<wxString> stubMessages;
514
515 drcEngine->SetViolationHandler(
516 [&]( const std::shared_ptr<DRC_ITEM>& aItem, const VECTOR2I&, int,
517 const std::function<void( PCB_MARKER* )>& )
518 {
519 if( aItem->GetErrorCode() == DRCE_NET_CHAIN_STUB_TOO_LONG )
520 stubMessages.push_back( aItem->GetErrorMessage( false ) );
521 } );
522
523 drcEngine->RunTests( EDA_UNITS::MM, true, false );
524
525 auto matchesNet = [&]( const wxString& netName )
526 {
527 for( const wxString& msg : stubMessages )
528 {
529 if( msg.Contains( wxString::Format( wxS( "'%s'" ), netName ) ) )
530 return true;
531 }
532
533 return false;
534 };
535
536 BOOST_CHECK_MESSAGE( matchesNet( netB->GetNetname() ),
537 "Stub net MID_B with 2 mm route + 10 mm pad-to-die must violate "
538 "the 5 mm stub_length budget" );
539
540 std::error_code ec;
541 fs::remove( pcbPath, ec );
542 fs::remove( druPath, ec );
543}
544
545
546BOOST_AUTO_TEST_CASE( StubLengthAcceptsTimeDomainUnits )
547{
548 // Regression for H-5: stub_length used to be missing from the
549 // allowsTimeDomain allow-list in DRC_RULES_PARSER, so a (max 100ps)
550 // value would be rejected with "Time based units not allowed for
551 // constraint type." The rule must parse cleanly and the resulting
552 // constraint must be flagged as TIME_DOMAIN.
553 const wxString DRU_TIME = wxS(
554 "(version 1)\n"
555 "(rule \"StubBudgetTime\"\n"
556 " (condition \"A.NetClass == 'Default'\")\n"
557 " (constraint stub_length (max 100ps))\n"
558 ")\n" );
559
560 DRC_RULES_PARSER parser( DRU_TIME, wxS( "stub_length_time_test" ) );
561
563 std::vector<std::shared_ptr<DRC_RULE>> rules;
564
565 parser.Parse( rules, &reporter );
566
567 BOOST_CHECK_MESSAGE( !reporter.HasMessageOfSeverity( RPT_SEVERITY_ERROR ),
568 "stub_length with ps units must parse without error, got: "
569 << reporter.GetMessages().ToStdString() );
570
571 BOOST_REQUIRE_EQUAL( rules.size(), 1u );
572
573 std::optional<DRC_CONSTRAINT> stubConstraint =
574 rules.front()->FindConstraint( NET_CHAIN_STUB_LENGTH_CONSTRAINT );
575
576 BOOST_REQUIRE( stubConstraint.has_value() );
577 BOOST_CHECK( stubConstraint->GetOption( DRC_CONSTRAINT::OPTIONS::TIME_DOMAIN ) );
578 BOOST_CHECK( !stubConstraint->GetOption( DRC_CONSTRAINT::OPTIONS::SPACE_DOMAIN ) );
579 BOOST_REQUIRE( stubConstraint->GetValue().HasMax() );
580 // 1 ps == 1e6 IU (internal time unit is the attosecond).
581 BOOST_CHECK_EQUAL( stubConstraint->GetValue().Max(), 100 * 1000 * 1000 );
582}
583
584
585BOOST_AUTO_TEST_CASE( ReturnPathDoesNotCarryTimeDomainOption )
586{
587 // return_path takes a layer name, not a delay, so the TIME_DOMAIN option
588 // must never be set on a return_path constraint regardless of stray
589 // (max ...) subtrees. The return_path branch in the parser ignores any
590 // unknown sub-options (it only consumes (layer ...)), so we verify the
591 // resulting constraint stays in space-domain configuration.
592 const wxString DRU_LAYER = wxS(
593 "(version 1)\n"
594 "(rule \"GoodReturnPath\"\n"
595 " (condition \"A.NetClass == 'Default'\")\n"
596 " (constraint return_path (layer \"B.Cu\"))\n"
597 ")\n" );
598
599 DRC_RULES_PARSER parser( DRU_LAYER, wxS( "return_path_layer_test" ) );
600
602 std::vector<std::shared_ptr<DRC_RULE>> rules;
603
604 parser.Parse( rules, &reporter );
605
606 BOOST_CHECK_MESSAGE( !reporter.HasMessageOfSeverity( RPT_SEVERITY_ERROR ),
607 "return_path with a layer must parse without error, got: "
608 << reporter.GetMessages().ToStdString() );
609
610 BOOST_REQUIRE_EQUAL( rules.size(), 1u );
611
612 std::optional<DRC_CONSTRAINT> rpConstraint =
613 rules.front()->FindConstraint( NET_CHAIN_RETURN_PATH_CONSTRAINT );
614
615 BOOST_REQUIRE( rpConstraint.has_value() );
616 BOOST_CHECK( !rpConstraint->GetOption( DRC_CONSTRAINT::OPTIONS::TIME_DOMAIN ) );
617}
618
619
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:121
Container for design settings for a BOARD object.
std::map< int, SEVERITY > m_DRCSeverities
std::shared_ptr< DRC_ENGINE > m_DRCEngine
void Parse(std::vector< std::shared_ptr< DRC_RULE > > &aRules, REPORTER *aReporter)
Handle the data for a net.
Definition netinfo.h:46
const wxString & GetNetname() const
Definition netinfo.h:100
int GetNetCode() const
Definition netinfo.h:94
void SetNetChain(const wxString &aNetChain)
Definition netinfo.h:113
void SetTerminalPad(int aIndex, PAD *aPad)
Definition netinfo.h:116
Definition pad.h:61
void SetPadToDieLength(int aLength)
Definition pad.h:572
A #PLUGIN derivation for saving and loading Pcbnew s-expression formatted files.
BOARD * LoadBoard(const wxString &aFileName, BOARD *aAppendToMe, const std::map< std::string, UTF8 > *aProperties=nullptr, PROJECT *aProject=nullptr) override
Load information from some input file format that this PCB_IO implementation knows about into either ...
A wrapper for reporting to a wxString object.
Definition reporter.h:189
@ DRCE_UNCONNECTED_ITEMS
Definition drc_item.h:36
@ DRCE_LIB_FOOTPRINT_ISSUES
Definition drc_item.h:79
@ DRCE_INVALID_OUTLINE
Definition drc_item.h:69
@ DRCE_NET_CHAIN_STUB_TOO_LONG
Definition drc_item.h:102
@ DRCE_DRILL_OUT_OF_RANGE
Definition drc_item.h:57
@ DRCE_DANGLING_VIA
Definition drc_item.h:47
@ DRCE_LIB_FOOTPRINT_MISMATCH
Definition drc_item.h:80
@ NET_CHAIN_STUB_LENGTH_CONSTRAINT
Definition drc_rule.h:75
@ NET_CHAIN_RETURN_PATH_CONSTRAINT
Definition drc_rule.h:76
@ RPT_SEVERITY_ERROR
@ RPT_SEVERITY_IGNORE
BOOST_AUTO_TEST_CASE(HorizontalAlignment)
BOOST_AUTO_TEST_SUITE(CadstarPartParser)
static const char * DRU_TEXT
static const char * BOARD_TEXT
BOOST_AUTO_TEST_CASE(StubLengthFiresOnIntermediateNetOnly)
BOOST_REQUIRE(intersection.has_value()==c.ExpectedIntersection.has_value())
BOOST_AUTO_TEST_SUITE_END()
IbisParser parser & reporter
BOOST_CHECK_MESSAGE(totalMismatches==0, std::to_string(totalMismatches)+" board(s) with strategy disagreements")
BOOST_CHECK_EQUAL(result, "25.4")
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683