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