KiCad PCB EDA Suite
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 (C) 2018-2021 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
25
28#include <board.h>
30#include <footprint.h>
31#include <pcb_marker.h>
32#include <drc/drc_engine.h>
33#include <drc/drc_item.h>
34#include <widgets/ui_common.h>
35
37
38
40{
42};
43
44
45BOOST_FIXTURE_TEST_SUITE( DrcCourtyardInvalid, COURTYARD_TEST_FIXTURE )
46
47
48/*
49 * A simple mock footprint with a set of courtyard rectangles and some other information
50 */
52{
53 std::string m_refdes;
54 std::vector<SEG> m_segs;
56};
57
58
60{
61 std::string m_refdes;
63};
64
65
66std::ostream& operator<<( std::ostream& os, const COURTYARD_INVALID_INFO& aInvalid )
67{
68 os << "COURTYARD_INVALID_INFO[ " << aInvalid.m_refdes;
69 os << ", code: " << aInvalid.m_drc_error_code << "]";
70 return os;
71}
72
73
75{
76 std::string m_case_name;
77 std::vector<COURTYARD_INVALID_TEST_FP> m_mods;
78 std::vector<COURTYARD_INVALID_INFO> m_exp_errors;
79};
80
81
82// clang-format off
83static const std::vector<COURTYARD_INVALID_CASE> courtyard_invalid_cases =
84{
85 {
86 // Empty board has no footprints to be invalid
87 "empty board",
88 {},
89 {},
90 },
91 {
92 "single footprint, no courtyard",
93 {
94 {
95 "U1",
96 {}, // Empty courtyard layer
97 { 0, 0 },
98 },
99 },
100 { // one error: the footprint has no courtyard
101 {
102 "U1",
104 },
105 },
106 },
107 {
108 "single footprint, unclosed courtyard",
109 {
110 {
111 "U1",
112 { // Unclosed polygon
113 { { 0, 0 }, { 0, pcbIUScale.mmToIU( 10 ) } },
114 { { 0, pcbIUScale.mmToIU( 10 ) }, { pcbIUScale.mmToIU( 10 ), pcbIUScale.mmToIU( 10 ) } },
115 },
116 { 0, 0 },
117 },
118 },
119 { // one error: the footprint has malformed courtyard
120 {
121 "U1",
123 },
124 },
125 },
126 {
127 "single footprint, disjoint courtyard",
128 {
129 {
130 "U1",
131 { // Unclosed polygon - two disjoint segments
132 { { 0, 0 }, { 0, pcbIUScale.mmToIU( 10 ) } },
133 { { pcbIUScale.mmToIU( 10 ), 0 }, { pcbIUScale.mmToIU( 10 ), pcbIUScale.mmToIU( 10 ) } },
134 },
135 { 0, 0 },
136 },
137 },
138 { // one error: the footprint has malformed courtyard
139 {
140 "U1",
142 },
143 },
144 },
145 {
146 "two footprints, one OK, one malformed",
147 {
148 {
149 "U1",
150 { // Closed polygon - triangle
151 {
152 { 0, 0 },
153 { 0, pcbIUScale.mmToIU( 10 ) },
154 },
155 {
156 { 0, pcbIUScale.mmToIU( 10 ) },
157 { pcbIUScale.mmToIU( 10 ), pcbIUScale.mmToIU( 10 ) }
158 },
159 {
160 { pcbIUScale.mmToIU( 10 ), pcbIUScale.mmToIU( 10 ) },
161 { 0, 0 }
162 },
163 },
164 { 0, 0 },
165 },
166 {
167 "U2",
168 { // Un-Closed polygon - one seg
169 {
170 { 0, 0 },
171 { 0, pcbIUScale.mmToIU( 10 ) },
172 },
173 },
174 { 0, 0 },
175 },
176 },
177 { // one error: the second footprint has malformed courtyard
178 {
179 "U2",
181 },
182 },
183 },
184};
185// clang-format on
186
187
192std::unique_ptr<FOOTPRINT> MakeInvalidCourtyardTestFP( BOARD& aBoard,
193 const COURTYARD_INVALID_TEST_FP& aFPDef )
194{
195 std::unique_ptr<FOOTPRINT> footprint = std::make_unique<FOOTPRINT>( &aBoard );
196
197 for( const SEG& seg : aFPDef.m_segs )
198 {
199 const PCB_LAYER_ID layer = F_CrtYd; // aRect.m_front ? F_CrtYd : B_CrtYd;
200 const int width = pcbIUScale.mmToIU( 0.1 );
201
202 KI_TEST::DrawSegment( *footprint, seg, width, layer );
203 }
204
205 footprint->SetReference( aFPDef.m_refdes );
206
207 // As of 2019-01-17, this has to go after adding the courtyards,
208 // or all the poly sets are empty when DRC'd
209 footprint->SetPosition( aFPDef.m_pos );
210
211 return footprint;
212}
213
214
215std::unique_ptr<BOARD> MakeBoard( const std::vector<COURTYARD_INVALID_TEST_FP>& aTestFPDefs )
216{
217 auto board = std::make_unique<BOARD>();
218
219 for( const COURTYARD_INVALID_TEST_FP& fpDef : aTestFPDefs )
220 {
221 std::unique_ptr<FOOTPRINT> footprint = MakeInvalidCourtyardTestFP( *board, fpDef );
222
223 board->Add( footprint.release() );
224 }
225
226 return board;
227}
228
229
233static bool InvalidMatchesExpected( BOARD& aBoard, const PCB_MARKER& aMarker,
234 const COURTYARD_INVALID_INFO& aInvalid )
235{
236 auto reporter = std::static_pointer_cast<DRC_ITEM>( aMarker.GetRCItem() );
237 const FOOTPRINT* item_a = dynamic_cast<FOOTPRINT*>( aBoard.GetItem( reporter->GetMainItemID() ) );
238
239 // This one is more than just a mismatch!
240 if( reporter->GetAuxItemID() != niluuid )
241 {
242 BOOST_WARN_MESSAGE( false, "Expected no auxiliary item for invalid courtyard DRC." );
243 return false;
244 }
245
246 if( item_a == nullptr )
247 {
248 BOOST_ERROR( "Could not get board DRC item." );
249 return false;
250 }
251
252 if( item_a->GetReference() != aInvalid.m_refdes )
253 return false;
254
255 if( reporter->GetErrorCode() != aInvalid.m_drc_error_code )
256 return false;
257
258 return true;
259}
260
261
270 const std::vector<std::unique_ptr<PCB_MARKER>>& aMarkers,
271 const std::vector<COURTYARD_INVALID_INFO>& aExpInvalids )
272{
273 KI_TEST::CheckUnorderedMatches( aExpInvalids, aMarkers,
274 [&]( const COURTYARD_INVALID_INFO& aInvalid,
275 const std::unique_ptr<PCB_MARKER>& aMarker )
276 {
277 return InvalidMatchesExpected( aBoard, *aMarker, aInvalid );
278 } );
279}
280
281
283 const KI_TEST::BOARD_DUMPER& aDumper )
284{
285 auto board = MakeBoard( aCase.m_mods );
286
287 // Dump if env var set
288 aDumper.DumpBoardToFile( *board, aCase.m_case_name );
289
290 BOARD_DESIGN_SETTINGS& bds = board->GetDesignSettings();
291
292 // do the overlap tests - that's a different test, but if not set,
293 // the invalid courtyard checks don't run either
295
296 // we will also check for missing courtyards here
298
299 // list of markers to collect
300 std::vector<std::unique_ptr<PCB_MARKER>> markers;
301
302 DRC_ENGINE drcEngine( board.get(), &board->GetDesignSettings() );
303
304 drcEngine.InitEngine( wxFileName() );
305
306 drcEngine.SetViolationHandler(
307 [&]( const std::shared_ptr<DRC_ITEM>& aItem, VECTOR2I aPos, int aLayer )
308 {
309 if( aItem->GetErrorCode() == DRCE_OVERLAPPING_FOOTPRINTS
310 || aItem->GetErrorCode() == DRCE_MALFORMED_COURTYARD
311 || aItem->GetErrorCode() == DRCE_MISSING_COURTYARD )
312 {
313 markers.push_back( std::make_unique<PCB_MARKER>( aItem, aPos ) );
314 }
315 } );
316
317 drcEngine.RunTests( EDA_UNITS::MILLIMETRES, true, false );
318
319 CheckInvalidsMatchExpected( *board, markers, aCase.m_exp_errors );
320}
321
322
323BOOST_AUTO_TEST_CASE( InvalidCases )
324{
325 for( const auto& c : courtyard_invalid_cases )
326 {
327 BOOST_TEST_CONTEXT( c.m_case_name )
328 {
329 DoCourtyardInvalidTest( c, m_dumper );
330 }
331 }
332}
333
334BOOST_AUTO_TEST_SUITE_END()
constexpr EDA_IU_SCALE pcbIUScale
Definition: base_units.h:109
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:265
BOARD_ITEM * GetItem(const KIID &aID) const
Definition: board.cpp:997
Design Rule Checker object that performs all the DRC tests.
Definition: drc_engine.h:83
void SetViolationHandler(DRC_VIOLATION_HANDLER aHandler)
Set an optional DRC violation handler (receives DRC_ITEMs and positions).
Definition: drc_engine.h:110
void RunTests(EDA_UNITS aUnits, bool aReportAllTrackErrors, bool aTestFootprints)
Run the DRC tests.
Definition: drc_engine.cpp:567
void InitEngine(const wxFileName &aRulePath)
Initialize the DRC engine.
Definition: drc_engine.cpp:514
const wxString & GetReference() const
Definition: footprint.h:519
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: marker_base.h:105
Definition: seg.h:42
@ DRCE_OVERLAPPING_FOOTPRINTS
Definition: drc_item.h:61
@ DRCE_MISSING_COURTYARD
Definition: drc_item.h:62
@ DRCE_MALFORMED_COURTYARD
Definition: drc_item.h:63
KIID niluuid(0)
PCB_LAYER_ID
A quick note on layer IDs:
Definition: layer_ids.h:59
@ F_CrtYd
Definition: layer_ids.h:117
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
constexpr int mmToIU(double mm) const
Definition: base_units.h:89
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)
Functions to provide common constants and other functions to assist in making a consistent UI.
#define BOOST_TEST_CONTEXT(A)