KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_drc_courtyard_invalid.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
24#include <board.h>
26#include <footprint.h>
27#include <pcb_marker.h>
28#include <drc/drc_engine.h>
29#include <drc/drc_item.h>
30#include <widgets/ui_common.h>
31
33
34
39
40
41BOOST_FIXTURE_TEST_SUITE( DrcCourtyardInvalid, COURTYARD_TEST_FIXTURE )
42
43
44/*
45 * A simple mock footprint with a set of courtyard rectangles and some other information
46 */
48{
49 std::string m_refdes;
50 std::vector<SEG> m_segs;
52};
53
54
56{
57 std::string m_refdes;
59};
60
61
62std::ostream& operator<<( std::ostream& os, const COURTYARD_INVALID_INFO& aInvalid )
63{
64 os << "COURTYARD_INVALID_INFO[ " << aInvalid.m_refdes;
65 os << ", code: " << aInvalid.m_drc_error_code << "]";
66 return os;
67}
68
69
71{
72 std::string m_case_name;
73 std::vector<COURTYARD_INVALID_TEST_FP> m_mods;
74 std::vector<COURTYARD_INVALID_INFO> m_exp_errors;
75};
76
77
78// clang-format off
79static const std::vector<COURTYARD_INVALID_CASE> courtyard_invalid_cases =
80{
81 {
82 // Empty board has no footprints to be invalid
83 "empty board",
84 {},
85 {},
86 },
87 {
88 "single footprint, no courtyard",
89 {
90 {
91 "U1",
92 {}, // Empty courtyard layer
93 { 0, 0 },
94 },
95 },
96 { // one error: the footprint has no courtyard
97 {
98 "U1",
100 },
101 },
102 },
103 {
104 "single footprint, unclosed courtyard",
105 {
106 {
107 "U1",
108 { // Unclosed polygon
109 { { 0, 0 }, { 0, pcbIUScale.mmToIU( 10 ) } },
110 { { 0, pcbIUScale.mmToIU( 10 ) }, { pcbIUScale.mmToIU( 10 ), pcbIUScale.mmToIU( 10 ) } },
111 },
112 { 0, 0 },
113 },
114 },
115 { // one error: the footprint has malformed courtyard
116 {
117 "U1",
119 },
120 },
121 },
122 {
123 "single footprint, disjoint courtyard",
124 {
125 {
126 "U1",
127 { // Unclosed polygon - two disjoint segments
128 { { 0, 0 }, { 0, pcbIUScale.mmToIU( 10 ) } },
129 { { pcbIUScale.mmToIU( 10 ), 0 }, { pcbIUScale.mmToIU( 10 ), pcbIUScale.mmToIU( 10 ) } },
130 },
131 { 0, 0 },
132 },
133 },
134 { // one error: the footprint has malformed courtyard
135 {
136 "U1",
138 },
139 },
140 },
141 {
142 "two footprints, one OK, one malformed",
143 {
144 {
145 "U1",
146 { // Closed polygon - triangle
147 {
148 { 0, 0 },
149 { 0, pcbIUScale.mmToIU( 10 ) },
150 },
151 {
152 { 0, pcbIUScale.mmToIU( 10 ) },
153 { pcbIUScale.mmToIU( 10 ), pcbIUScale.mmToIU( 10 ) }
154 },
155 {
156 { pcbIUScale.mmToIU( 10 ), pcbIUScale.mmToIU( 10 ) },
157 { 0, 0 }
158 },
159 },
160 { 0, 0 },
161 },
162 {
163 "U2",
164 { // Un-Closed polygon - one seg
165 {
166 { 0, 0 },
167 { 0, pcbIUScale.mmToIU( 10 ) },
168 },
169 },
170 { 0, 0 },
171 },
172 },
173 { // one error: the second footprint has malformed courtyard
174 {
175 "U2",
177 },
178 },
179 },
180};
181// clang-format on
182
183
188std::unique_ptr<FOOTPRINT> MakeInvalidCourtyardTestFP( BOARD& aBoard,
189 const COURTYARD_INVALID_TEST_FP& aFPDef )
190{
191 std::unique_ptr<FOOTPRINT> footprint = std::make_unique<FOOTPRINT>( &aBoard );
192
193 for( const SEG& seg : aFPDef.m_segs )
194 {
195 const PCB_LAYER_ID layer = F_CrtYd; // aRect.m_front ? F_CrtYd : B_CrtYd;
196 const int width = pcbIUScale.mmToIU( 0.1 );
197
198 KI_TEST::DrawSegment( *footprint, seg, width, layer );
199 }
200
201 footprint->SetReference( aFPDef.m_refdes );
202
203 // As of 2019-01-17, this has to go after adding the courtyards,
204 // or all the poly sets are empty when DRC'd
205 footprint->SetPosition( aFPDef.m_pos );
206
207 return footprint;
208}
209
210
211std::unique_ptr<BOARD> MakeBoard( const std::vector<COURTYARD_INVALID_TEST_FP>& aTestFPDefs )
212{
213 auto board = std::make_unique<BOARD>();
214
215 for( const COURTYARD_INVALID_TEST_FP& fpDef : aTestFPDefs )
216 {
217 std::unique_ptr<FOOTPRINT> footprint = MakeInvalidCourtyardTestFP( *board, fpDef );
218
219 board->Add( footprint.release() );
220 }
221
222 return board;
223}
224
225
229static bool InvalidMatchesExpected( BOARD& aBoard, const PCB_MARKER& aMarker,
230 const COURTYARD_INVALID_INFO& aInvalid )
231{
232 auto reporter = std::static_pointer_cast<DRC_ITEM>( aMarker.GetRCItem() );
233 const FOOTPRINT* item_a = dynamic_cast<FOOTPRINT*>( aBoard.ResolveItem( reporter->GetMainItemID() ) );
234
235 // This one is more than just a mismatch!
236 if( reporter->GetAuxItemID() != niluuid )
237 {
238 BOOST_WARN_MESSAGE( false, "Expected no auxiliary item for invalid courtyard DRC." );
239 return false;
240 }
241
242 if( item_a == nullptr )
243 {
244 BOOST_ERROR( "Could not get board DRC item." );
245 return false;
246 }
247
248 if( item_a->GetReference() != aInvalid.m_refdes )
249 return false;
250
251 if( reporter->GetErrorCode() != aInvalid.m_drc_error_code )
252 return false;
253
254 return true;
255}
256
257
266 const std::vector<std::unique_ptr<PCB_MARKER>>& aMarkers,
267 const std::vector<COURTYARD_INVALID_INFO>& aExpInvalids )
268{
269 KI_TEST::CheckUnorderedMatches( aExpInvalids, aMarkers,
270 [&]( const COURTYARD_INVALID_INFO& aInvalid,
271 const std::unique_ptr<PCB_MARKER>& aMarker )
272 {
273 return InvalidMatchesExpected( aBoard, *aMarker, aInvalid );
274 } );
275}
276
277
279 const KI_TEST::BOARD_DUMPER& aDumper )
280{
281 auto board = MakeBoard( aCase.m_mods );
282
283 // Dump if env var set
284 aDumper.DumpBoardToFile( *board, aCase.m_case_name );
285
286 BOARD_DESIGN_SETTINGS& bds = board->GetDesignSettings();
287
288 // do the overlap tests - that's a different test, but if not set,
289 // the invalid courtyard checks don't run either
291
292 // we will also check for missing courtyards here
294
295 // list of markers to collect
296 std::vector<std::unique_ptr<PCB_MARKER>> markers;
297
298 DRC_ENGINE drcEngine( board.get(), &board->GetDesignSettings() );
299
300 drcEngine.InitEngine( wxFileName() );
301
302 drcEngine.SetViolationHandler(
303 [&]( const std::shared_ptr<DRC_ITEM>& aItem, const VECTOR2I& aPos, int aLayer,
304 const std::function<void( PCB_MARKER* )>& aPathGenerator )
305 {
306 if( aItem->GetErrorCode() == DRCE_OVERLAPPING_FOOTPRINTS
307 || aItem->GetErrorCode() == DRCE_MALFORMED_COURTYARD
308 || aItem->GetErrorCode() == DRCE_MISSING_COURTYARD )
309 {
310 markers.push_back( std::make_unique<PCB_MARKER>( aItem, aPos ) );
311 }
312 } );
313
314 drcEngine.RunTests( EDA_UNITS::MM, true, false );
315
316 CheckInvalidsMatchExpected( *board, markers, aCase.m_exp_errors );
317}
318
319
320BOOST_AUTO_TEST_CASE( InvalidCases )
321{
322 for( const auto& c : courtyard_invalid_cases )
323 {
324 BOOST_TEST_CONTEXT( c.m_case_name )
325 {
326 DoCourtyardInvalidTest( c, m_dumper );
327 }
328 }
329}
330
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:121
Construction utilities for PCB tests.
General utilities for PCB file IO for QA programs.
Container for design settings for a BOARD object.
std::map< int, SEVERITY > m_DRCSeverities
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:372
BOARD_ITEM * ResolveItem(const KIID &aID, bool aAllowNullptrReturn=false) const
Definition board.cpp:1846
Design Rule Checker object that performs all the DRC tests.
Definition drc_engine.h:129
void RunTests(EDA_UNITS aUnits, bool aReportAllTrackErrors, bool aTestFootprints, BOARD_COMMIT *aCommit=nullptr)
Run the DRC tests.
void SetViolationHandler(DRC_VIOLATION_HANDLER aHandler)
Set an optional DRC violation handler (receives DRC_ITEMs and positions).
Definition drc_engine.h:164
void InitEngine(const wxFileName &aRulePath)
Initialize the DRC engine.
const wxString & GetReference() const
Definition footprint.h:841
A helper that contains logic to assist in dumping boards to disk depending on some environment variab...
void DumpBoardToFile(BOARD &aBoard, const std::string &aName) const
std::shared_ptr< RC_ITEM > GetRCItem() const
Definition seg.h:38
@ DRCE_OVERLAPPING_FOOTPRINTS
Definition drc_item.h:62
@ DRCE_MISSING_COURTYARD
Definition drc_item.h:63
@ DRCE_MALFORMED_COURTYARD
Definition drc_item.h:64
KIID niluuid(0)
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:56
@ F_CrtYd
Definition layer_ids.h:112
void CheckUnorderedMatches(const EXP_CONT &aExpected, const FOUND_CONT &aFound, MATCH_PRED aMatchPredicate)
Check that a container of "found" objects matches a container of "expected" objects.
void DrawSegment(FOOTPRINT &aFootprint, const SEG &aSeg, int aWidth, PCB_LAYER_ID aLayer)
Draw a segment in the given footprint.
@ RPT_SEVERITY_ERROR
std::vector< COURTYARD_INVALID_TEST_FP > m_mods
std::vector< COURTYARD_INVALID_INFO > m_exp_errors
std::vector< SEG > m_segs
Footprint Ref-Des (for identifying DRC errors)
VECTOR2I m_pos
List of segments that will be placed on the courtyard.
const KI_TEST::BOARD_DUMPER m_dumper
static const std::vector< COURTYARD_INVALID_CASE > courtyard_invalid_cases
std::unique_ptr< FOOTPRINT > MakeInvalidCourtyardTestFP(BOARD &aBoard, const COURTYARD_INVALID_TEST_FP &aFPDef)
Construct a FOOTPRINT to use in a courtyard test from a COURTYARD_TEST_FP definition.
static bool InvalidMatchesExpected(BOARD &aBoard, const PCB_MARKER &aMarker, const COURTYARD_INVALID_INFO &aInvalid)
Check if a PCB_MARKER is described by a particular COURTYARD_INVALID_INFO object.
std::ostream & operator<<(std::ostream &os, const COURTYARD_INVALID_INFO &aInvalid)
std::unique_ptr< BOARD > MakeBoard(const std::vector< COURTYARD_INVALID_TEST_FP > &aTestFPDefs)
void DoCourtyardInvalidTest(const COURTYARD_INVALID_CASE &aCase, const KI_TEST::BOARD_DUMPER &aDumper)
static void CheckInvalidsMatchExpected(BOARD &aBoard, const std::vector< std::unique_ptr< PCB_MARKER > > &aMarkers, const std::vector< COURTYARD_INVALID_INFO > &aExpInvalids)
Check that the produced markers match the expected.
BOOST_AUTO_TEST_CASE(InvalidCases)
BOOST_AUTO_TEST_SUITE_END()
IbisParser parser & reporter
BOOST_TEST_CONTEXT("Test Clearance")
Functions to provide common constants and other functions to assist in making a consistent UI.
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683