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 CHANGELOG.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>
29 #include <board_design_settings.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 
36 #include "../board_test_utils.h"
37 
38 
40 {
42 };
43 
44 
45 BOOST_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 
66 std::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
83 static 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, Millimeter2iu( 10 ) } },
114  { { 0, Millimeter2iu( 10 ) }, { Millimeter2iu( 10 ), Millimeter2iu( 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, Millimeter2iu( 10 ) } },
133  { { Millimeter2iu( 10 ), 0 }, { Millimeter2iu( 10 ), Millimeter2iu( 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, Millimeter2iu( 10 ) },
154  },
155  {
156  { 0, Millimeter2iu( 10 ) },
157  { Millimeter2iu( 10 ), Millimeter2iu( 10 ) }
158  },
159  {
160  { Millimeter2iu( 10 ), Millimeter2iu( 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, Millimeter2iu( 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 
192 std::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 = Millimeter2iu( 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( (wxPoint) aFPDef.m_pos );
210 
211  return footprint;
212 }
213 
214 
215 std::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 
233 static 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 
269 static void CheckInvalidsMatchExpected( BOARD& aBoard,
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, wxPoint aPos )
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 
323 BOOST_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 
334 BOOST_AUTO_TEST_SUITE_END()
BOARD_ITEM * GetItem(const KIID &aID) const
Definition: board.cpp:868
Design Rule Checker object that performs all the DRC tests.
Definition: drc_engine.h:81
std::vector< SEG > m_segs
Footprint Ref-Des (for identifying DRC errors)
std::vector< COURTYARD_INVALID_TEST_FP > m_mods
void DrawSegment(FOOTPRINT &aFootprint, const SEG &aSeg, int aWidth, PCB_LAYER_ID aLayer)
Draw a segment in the given footprint.
KIID niluuid(0)
static const std::vector< COURTYARD_INVALID_CASE > courtyard_invalid_cases
void DumpBoardToFile(BOARD &aBoard, const std::string &aName) const
Construction utilities for PCB tests.
const KI_TEST::BOARD_DUMPER m_dumper
std::vector< COURTYARD_INVALID_INFO > m_exp_errors
std::unique_ptr< BOARD > MakeBoard(const std::vector< COURTYARD_INVALID_TEST_FP > &aTestFPDefs)
Functions to provide common constants and other functions to assist in making a consistent UI.
VECTOR2I m_pos
List of segments that will be placed on the courtyard.
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.
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.
#define BOOST_TEST_CONTEXT(A)
void DoCourtyardInvalidTest(const COURTYARD_INVALID_CASE &aCase, const KI_TEST::BOARD_DUMPER &aDumper)
Definition: seg.h:40
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.
BOOST_AUTO_TEST_CASE(InvalidCases)
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:190
PCB_LAYER_ID
A quick note on layer IDs:
Definition: layer_ids.h:64
std::map< int, SEVERITY > m_DRCSeverities
General utilities for PCB file IO for QA programs.
static constexpr int Millimeter2iu(double mm)
A helper that contains logic to assist in dumping boards to disk depending on some environment variab...
std::shared_ptr< RC_ITEM > GetRCItem() const
Definition: marker_base.h:100
void InitEngine(const wxFileName &aRulePath)
Initialize the DRC engine.
Definition: drc_engine.cpp:626
Container for design settings for a BOARD object.
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)