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 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 
29 #include <class_module.h>
30 #include <drc/drc.h>
31 #include <drc/drc_item.h>
33 #include <widgets/ui_common.h>
34 
35 #include "../board_test_utils.h"
36 
37 
39 {
41 };
42 
43 
44 BOOST_FIXTURE_TEST_SUITE( DrcCourtyardInvalid, COURTYARD_TEST_FIXTURE )
45 
46 
47 /*
48  * A simple mock module with a set of courtyard rectangles and some other
49  * information
50  */
52 {
53 
54  std::string m_refdes;
55  std::vector<SEG> m_segs;
57 };
58 
59 
61 {
62  std::string m_refdes;
64 };
65 
66 
67 std::ostream& operator<<( std::ostream& os, const COURTYARD_INVALID_INFO& aInvalid )
68 {
69  os << "COURTYARD_INVALID_INFO[ " << aInvalid.m_refdes;
70  os << ", code: " << aInvalid.m_drc_error_code << "]";
71  return os;
72 }
73 
74 
76 {
77  std::string m_case_name;
78  std::vector<COURTYARD_INVALID_TEST_MODULE> m_mods;
79  std::vector<COURTYARD_INVALID_INFO> m_exp_errors;
80 };
81 
82 
83 // clang-format off
84 static const std::vector<COURTYARD_INVALID_CASE> courtyard_invalid_cases =
85 {
86  {
87  // Empty board has no modules to be invalid
88  "empty board",
89  {},
90  {},
91  },
92  {
93  "single mod, no courtyard",
94  {
95  {
96  "U1",
97  {}, // Empty courtyard layer
98  { 0, 0 },
99  },
100  },
101  { // one error: the module has no courtyard
102  {
103  "U1",
105  },
106  },
107  },
108  {
109  "single mod, unclosed courtyard",
110  {
111  {
112  "U1",
113  { // Unclosed polygon
114  { { 0, 0 }, { 0, Millimeter2iu( 10 ) } },
115  { { 0, Millimeter2iu( 10 ) }, { Millimeter2iu( 10 ), Millimeter2iu( 10 ) } },
116  },
117  { 0, 0 },
118  },
119  },
120  { // one error: the module has malformed courtyard
121  {
122  "U1",
124  },
125  },
126  },
127  {
128  "single mod, disjoint courtyard",
129  {
130  {
131  "U1",
132  { // Unclosed polygon - two disjoint segments
133  { { 0, 0 }, { 0, Millimeter2iu( 10 ) } },
134  { { Millimeter2iu( 10 ), 0 }, { Millimeter2iu( 10 ), Millimeter2iu( 10 ) } },
135  },
136  { 0, 0 },
137  },
138  },
139  { // one error: the module has malformed courtyard
140  {
141  "U1",
143  },
144  },
145  },
146  {
147  "two mods, one OK, one malformed",
148  {
149  {
150  "U1",
151  { // Closed polygon - triangle
152  {
153  { 0, 0 },
154  { 0, Millimeter2iu( 10 ) },
155  },
156  {
157  { 0, Millimeter2iu( 10 ) },
158  { Millimeter2iu( 10 ), Millimeter2iu( 10 ) }
159  },
160  {
161  { Millimeter2iu( 10 ), Millimeter2iu( 10 ) },
162  { 0, 0 }
163  },
164  },
165  { 0, 0 },
166  },
167  {
168  "U2",
169  { // Un-Closed polygon - one seg
170  {
171  { 0, 0 },
172  { 0, Millimeter2iu( 10 ) },
173  },
174  },
175  { 0, 0 },
176  },
177  },
178  { // one error: the second module has malformed courtyard
179  {
180  "U2",
182  },
183  },
184  },
185 };
186 // clang-format on
187 
188 
193 std::unique_ptr<MODULE> MakeInvalidCourtyardTestModule( BOARD& aBoard,
194  const COURTYARD_INVALID_TEST_MODULE& aMod )
195 {
196  auto module = std::make_unique<MODULE>( &aBoard );
197 
198  for( const auto& seg : aMod.m_segs )
199  {
200  const PCB_LAYER_ID layer = F_CrtYd; // aRect.m_front ? F_CrtYd : B_CrtYd;
201  const int width = Millimeter2iu( 0.1 );
202 
203  KI_TEST::DrawSegment( *module, seg, width, layer );
204  }
205 
206  module->SetReference( aMod.m_refdes );
207 
208  // As of 2019-01-17, this has to go after adding the courtyards,
209  // or all the poly sets are empty when DRC'd
210  module->SetPosition( (wxPoint) aMod.m_pos );
211 
212  return module;
213 }
214 
215 
216 std::unique_ptr<BOARD> MakeBoard( const std::vector<COURTYARD_INVALID_TEST_MODULE>& aMods )
217 {
218  auto board = std::make_unique<BOARD>();
219 
220  for( const auto& mod : aMods )
221  {
222  auto module = MakeInvalidCourtyardTestModule( *board, mod );
223 
224  board->Add( module.release() );
225  }
226 
227  return board;
228 }
229 
230 
236 {
237  BOARD_DESIGN_SETTINGS des_settings;
238 
239  // do the overlap tests - that's a different test, but if not set,
240  // the invalid courtyard checks don't run either
242 
243  // we will also check for missing courtyards here
245 
246  return des_settings;
247 }
248 
249 
253 static bool InvalidMatchesExpected( BOARD& aBoard, const MARKER_PCB& aMarker,
254  const COURTYARD_INVALID_INFO& aInvalid )
255 {
256  const DRC_ITEM* reporter = static_cast<const DRC_ITEM*>( aMarker.GetRCItem() );
257  const MODULE* item_a = dynamic_cast<MODULE*>( aBoard.GetItem( reporter->GetMainItemID() ) );
258 
259  // This one is more than just a mis-match!
260  if( reporter->GetAuxItemID() != niluuid )
261  {
262  BOOST_WARN_MESSAGE( false, "Expected no auxiliary item for invalid courtyard DRC." );
263  return false;
264  }
265 
266  if( item_a->GetReference() != aInvalid.m_refdes )
267  return false;
268 
269  if( reporter->GetErrorCode() != aInvalid.m_drc_error_code )
270  return false;
271 
272  return true;
273 }
274 
275 
283 static void CheckInvalidsMatchExpected( BOARD& aBoard,
284  const std::vector<std::unique_ptr<MARKER_PCB>>& aMarkers,
285  const std::vector<COURTYARD_INVALID_INFO>& aExpInvalids )
286 {
287  KI_TEST::CheckUnorderedMatches( aExpInvalids, aMarkers,
288  [&]( const COURTYARD_INVALID_INFO& aInvalid,
289  const std::unique_ptr<MARKER_PCB>& aMarker )
290  {
291  return InvalidMatchesExpected( aBoard, *aMarker, aInvalid );
292  } );
293 }
294 
295 
297  const KI_TEST::BOARD_DUMPER& aDumper )
298 {
299  auto board = MakeBoard( aCase.m_mods );
300 
301  // Dump if env var set
302  aDumper.DumpBoardToFile( *board, aCase.m_case_name );
303 
304  board->SetDesignSettings( GetOverlapCheckDesignSettings() );
305 
306  // list of markers to collect
307  std::vector<std::unique_ptr<MARKER_PCB>> markers;
308 
309  DRC_COURTYARD_TESTER drc_overlap(
310  [&]( MARKER_PCB* aMarker )
311  {
312  markers.push_back( std::unique_ptr<MARKER_PCB>( aMarker ) );
313  } );
314 
315  drc_overlap.RunDRC( EDA_UNITS::MILLIMETRES, *board );
316 
317  CheckInvalidsMatchExpected( *board, markers, aCase.m_exp_errors );
318 }
319 
320 
321 BOOST_AUTO_TEST_CASE( InvalidCases )
322 {
323  for( const auto& c : courtyard_invalid_cases )
324  {
325  BOOST_TEST_CONTEXT( c.m_case_name )
326  {
327  DoCourtyardInvalidTest( c, m_dumper );
328  }
329  }
330 }
331 
332 BOOST_AUTO_TEST_SUITE_END()
std::unique_ptr< BOARD > MakeBoard(const std::vector< COURTYARD_INVALID_TEST_MODULE > &aMods)
RC_ITEM * GetRCItem()
Function GetReporter returns the DRC_ITEM held within this MARKER so that its interface may be used.
Definition: marker_base.h:117
std::vector< SEG > m_segs
Module Ref-Des (for identifying DRC errors)
static const std::vector< COURTYARD_INVALID_CASE > courtyard_invalid_cases
std::map< int, int > m_DRCSeverities
static bool InvalidMatchesExpected(BOARD &aBoard, const MARKER_PCB &aMarker, const COURTYARD_INVALID_INFO &aInvalid)
Check if a MARKER_PCB is described by a particular COURTYARD_INVALID_INFO object.
footprint has a courtyard but malformed (not convertible to a closed polygon with holes)
Definition: drc.h:97
void DumpBoardToFile(BOARD &aBoard, const std::string &aName) const
Construction utilities for PCB tests.
static void CheckInvalidsMatchExpected(BOARD &aBoard, const std::vector< std::unique_ptr< MARKER_PCB >> &aMarkers, const std::vector< COURTYARD_INVALID_INFO > &aExpInvalids)
Check that the produced markers match the expected.
BOARD_ITEM * GetItem(const KIID &aID)
const KI_TEST::BOARD_DUMPER m_dumper
std::vector< COURTYARD_INVALID_TEST_MODULE > m_mods
PCB_LAYER_ID
A quick note on layer IDs:
VECTOR2I m_pos
List of segments that will be placed on the courtyard.
std::vector< COURTYARD_INVALID_INFO > m_exp_errors
KIID niluuid(0)
KIID GetAuxItemID() const
Definition: rc_item.h:124
int GetErrorCode() const
Definition: rc_item.h:138
Functions to provide common constants and other functions to assist in making a consistent UI.
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)
std::unique_ptr< MODULE > MakeInvalidCourtyardTestModule(BOARD &aBoard, const COURTYARD_INVALID_TEST_MODULE &aMod)
Construct a MODULE to use in a courtyard test from a COURTYARD_TEST_MODULE definition.
BOOST_AUTO_TEST_CASE(InvalidCases)
BOARD holds information pertinent to a Pcbnew printed circuit board.
Definition: class_board.h:181
footprint has no courtyard defined
Definition: drc.h:96
footprint courtyards overlap
Definition: drc.h:95
KIID GetMainItemID() const
Definition: rc_item.h:123
void DrawSegment(MODULE &aMod, const SEG &aSeg, int aWidth, PCB_LAYER_ID aLayer)
Draw a segment in the given module.
General utilities for PCB file IO for QA programs.
static BOARD_DESIGN_SETTINGS GetOverlapCheckDesignSettings()
Get a BOARD_DESIGN_SETTINGS object that will cause DRC to check for courtyard invalidity.
A helper that contains logic to assist in dumping boards to disk depending on some environment variab...
bool RunDRC(EDA_UNITS aUnits, BOARD &aBoard) override
Runs this provider against the given PCB with configured options (if any).
BOARD_DESIGN_SETTINGS contains design settings for a BOARD object.
std::ostream & operator<<(std::ostream &os, const COURTYARD_INVALID_INFO &aInvalid)