KiCad PCB EDA Suite
test_drc_courtyard_overlap.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 #include "drc_test_utils.h"
37 
42 {
46 
47  // On front or back layer (the exact layer is context-dependent)
48  bool m_front;
49 };
50 
51 
52 /*
53  * A simple mock module with a set of courtyard rectangles and some other
54  * information
55  */
57 {
58  std::string m_refdes;
59  std::vector<RECT_DEFINITION> m_rects;
61 };
62 
63 
64 /*
65  * Struct holding information about a courtyard collision
66  */
68 {
69  // The two colliding parts
70  std::string m_refdes_a;
71  std::string m_refdes_b;
72 };
73 
74 
75 std::ostream& operator<<( std::ostream& os, const COURTYARD_COLLISION& aColl )
76 {
77  os << "COURTYARD_COLLISION[ " << aColl.m_refdes_a << " -> " << aColl.m_refdes_b << "]";
78  return os;
79 }
80 
81 
87 {
88  std::string m_case_name;
89 
90  // The modules in the test case
91  std::vector<COURTYARD_TEST_MODULE> m_mods;
92 
93  // The expected number of collisions
94  std::vector<COURTYARD_COLLISION> m_collisions;
95 };
96 
97 
101 void AddRectCourtyard( MODULE& aMod, const RECT_DEFINITION& aRect )
102 {
103  const PCB_LAYER_ID layer = aRect.m_front ? F_CrtYd : B_CrtYd;
104 
105  const int width = Millimeter2iu( 0.1 );
106 
107  KI_TEST::DrawRect( aMod, aRect.m_centre, aRect.m_size, aRect.m_corner_rad, width, layer );
108 }
109 
110 
115 std::unique_ptr<MODULE> MakeCourtyardTestModule( BOARD& aBoard, const COURTYARD_TEST_MODULE& aMod )
116 {
117  auto module = std::make_unique<MODULE>( &aBoard );
118 
119  for( const auto& rect : aMod.m_rects )
120  {
121  AddRectCourtyard( *module, rect );
122  }
123 
124  module->SetReference( aMod.m_refdes );
125 
126  // As of 2019-01-17, this has to go after adding the courtyards,
127  // or all the poly sets are empty when DRC'd
128  module->SetPosition( (wxPoint) aMod.m_pos );
129 
130  return module;
131 }
132 
138 std::unique_ptr<BOARD> MakeBoard( const std::vector<COURTYARD_TEST_MODULE>& aMods )
139 {
140  auto board = std::make_unique<BOARD>();
141 
142  for( const auto& mod : aMods )
143  {
144  auto module = MakeCourtyardTestModule( *board, mod );
145 
146  board->Add( module.release() );
147  }
148 
149  return board;
150 }
151 
152 
154 {
156 };
157 
158 
159 BOOST_FIXTURE_TEST_SUITE( DrcCourtyardOverlap, COURTYARD_TEST_FIXTURE )
160 
161 // clang-format off
163  {
164  "empty board",
165  {}, // no modules
166  {}, // no collisions
167  },
168  {
169  "single empty mod",
170  {
171  {
172  "U1",
173  {}, // no courtyard
174  { 0, 0 }, // at origin
175  },
176  },
177  {}, // no collisions
178  },
179  {
180  // A single module can't overlap itself
181  "single mod, single courtyard",
182  {
183  {
184  "U1",
185  {
186  {
187  { 0, 0 },
188  { Millimeter2iu( 1 ), Millimeter2iu( 1 ) },
189  0,
190  true,
191  },
192  },
193  { 0, 0 },
194  },
195  },
196  {}, // no collisions
197  },
198  {
199  "two modules, no overlap",
200  {
201  {
202  "U1",
203  {
204  {
205  { 0, 0 },
206  { Millimeter2iu( 1 ), Millimeter2iu( 1 ) },
207  0,
208  true,
209  },
210  },
211  { 0, 0 },
212  },
213  {
214  "U2",
215  {
216  {
217  { 0, 0 },
218  { Millimeter2iu( 1 ), Millimeter2iu( 1 ) },
219  0,
220  true,
221  },
222  },
223  { Millimeter2iu( 3 ), Millimeter2iu( 1 ) }, // One module is far from the other
224  },
225  },
226  {}, // no collisions
227  },
228  {
229  "two modules, touching, no overlap",
230  {
231  {
232  "U1",
233  {
234  {
235  { 0, 0 },
236  { Millimeter2iu( 1 ), Millimeter2iu( 1 ) },
237  0,
238  true,
239  },
240  },
241  { 0, 0 },
242  },
243  {
244  "U2",
245  {
246  {
247  { 0, 0 },
248  { Millimeter2iu( 1 ), Millimeter2iu( 1 ) },
249  0,
250  true,
251  },
252  },
253  { Millimeter2iu( 1 ), Millimeter2iu( 0 ) }, // Just touching
254  },
255  },
256  {}, // Touching means not colliding
257  },
258  {
259  "two modules, overlap",
260  {
261  {
262  "U1",
263  {
264  {
265  { 0, 0 },
266  { Millimeter2iu( 1 ), Millimeter2iu( 1 ) },
267  0,
268  true,
269  },
270  },
271  { 0, 0 },
272  },
273  {
274  "U2",
275  {
276  {
277  { 0, 0 },
278  { Millimeter2iu( 1 ), Millimeter2iu( 1 ) },
279  0,
280  true,
281  },
282  },
283  { Millimeter2iu( 0.5 ), Millimeter2iu( 0 ) }, // Partial overlap
284  },
285  },
286  {
287  { "U1", "U2" }, // These two collide
288  },
289  },
290  {
291  "two modules, overlap, different sides",
292  {
293  {
294  "U1",
295  {
296  {
297  { 0, 0 },
298  { Millimeter2iu( 1 ), Millimeter2iu( 1 ) },
299  0,
300  true,
301  },
302  },
303  { 0, 0 },
304  },
305  {
306  "U2",
307  {
308  {
309  { 0, 0 },
310  { Millimeter2iu( 1 ), Millimeter2iu( 1 ) },
311  0,
312  false,
313  },
314  },
315  { 0, 0 }, // complete overlap
316  },
317  },
318  {}, // but on different sides
319  },
320  {
321  "two modules, multiple courtyards, overlap",
322  {
323  {
324  "U1",
325  {
326  {
327  { 0, 0 },
328  { Millimeter2iu( 1 ), Millimeter2iu( 1 ) },
329  0,
330  true,
331  },
332  {
333  { Millimeter2iu( 2 ), Millimeter2iu( 0 ) },
334  { Millimeter2iu( 1 ), Millimeter2iu( 1 ) },
335  0,
336  true,
337  },
338  },
339  { 0, 0 },
340  },
341  {
342  "U2",
343  {
344  {
345  { 0, 0 },
346  { Millimeter2iu( 1 ), Millimeter2iu( 1 ) },
347  0,
348  true,
349  },
350  },
351  { 0, 0 }, // complete overlap with one of the others
352  },
353  },
354  {
355  { "U1", "U2" },
356  },
357  },
358  {
359  // The courtyards do not overlap, but their bounding boxes do
360  "two modules, no overlap, bbox overlap",
361  {
362  {
363  "U1",
364  {
365  {
366  { 0, 0 },
367  { Millimeter2iu( 1 ), Millimeter2iu( 1 ) },
368  Millimeter2iu( 0.5 ),
369  true,
370  },
371  },
372  { 0, 0 },
373  },
374  {
375  "U2",
376  {
377  {
378  { Millimeter2iu( 0.9 ), Millimeter2iu( 0.9 ) },
379  { Millimeter2iu( 1 ), Millimeter2iu( 1 ) },
380  Millimeter2iu( 0.5 ),
381  true,
382  },
383  },
384  { 0, 0 },
385  },
386  },
387  {},
388  },
389 };
390 // clang-format on
391 
392 
396 static bool CollisionMatchesExpected( BOARD& aBoard, const MARKER_PCB& aMarker,
397  const COURTYARD_COLLISION& aCollision )
398 {
399  const DRC_ITEM* reporter = static_cast<const DRC_ITEM*>( aMarker.GetRCItem() );
400 
401  const MODULE* item_a = dynamic_cast<MODULE*>( aBoard.GetItem( reporter->GetMainItemID() ) );
402  const MODULE* item_b = dynamic_cast<MODULE*>( aBoard.GetItem( reporter->GetAuxItemID() ) );
403 
404  // cant' find the items!
405  if( !item_a || !item_b )
406  return false;
407 
408  const bool ref_match_aa_bb = ( item_a->GetReference() == aCollision.m_refdes_a )
409  && ( item_b->GetReference() == aCollision.m_refdes_b );
410 
411  const bool ref_match_ab_ba = ( item_a->GetReference() == aCollision.m_refdes_b )
412  && ( item_b->GetReference() == aCollision.m_refdes_a );
413 
414  // Doesn't matter which way around it is, but both have to match somehow
415  return ref_match_aa_bb || ref_match_ab_ba;
416 }
417 
418 
426 static void CheckCollisionsMatchExpected( BOARD& aBoard,
427  const std::vector<std::unique_ptr<MARKER_PCB>>& aMarkers,
428  const std::vector<COURTYARD_COLLISION>& aExpCollisions )
429 {
430  for( const auto& marker : aMarkers )
431  {
432  BOOST_CHECK_PREDICATE(
434  }
435 
436  KI_TEST::CheckUnorderedMatches( aExpCollisions, aMarkers,
437  [&]( const COURTYARD_COLLISION& aColl, const std::unique_ptr<MARKER_PCB>& aMarker )
438  {
439  return CollisionMatchesExpected( aBoard, *aMarker, aColl );
440  } );
441 }
442 
443 
449  const KI_TEST::BOARD_DUMPER& aDumper )
450 {
451  auto board = MakeBoard( aCase.m_mods );
452 
453  // Dump if env var set
454  aDumper.DumpBoardToFile( *board, aCase.m_case_name );
455 
456  BOARD_DESIGN_SETTINGS& bds = board->GetDesignSettings();
457 
459 
460  // we might not always have courtyards - that's a separate test
462 
463  // list of markers to collect
464  std::vector<std::unique_ptr<MARKER_PCB>> markers;
465 
466  DRC_COURTYARD_TESTER drc_overlap(
467  [&]( MARKER_PCB* aMarker )
468  {
469  markers.push_back( std::unique_ptr<MARKER_PCB>( aMarker ) );
470  } );
471 
472  drc_overlap.RunDRC( EDA_UNITS::MILLIMETRES, *board );
473 
474  CheckCollisionsMatchExpected( *board, markers, aCase.m_collisions );
475 }
476 
477 
478 BOOST_AUTO_TEST_CASE( OverlapCases )
479 {
480  for( const auto& c : courtyard_cases )
481  {
482  BOOST_TEST_CONTEXT( c.m_case_name )
483  {
484  DoCourtyardOverlapTest( c, m_dumper );
485  }
486  }
487 }
488 
489 BOOST_AUTO_TEST_SUITE_END()
BOOST_AUTO_TEST_CASE(OverlapCases)
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::map< int, int > m_DRCSeverities
std::vector< COURTYARD_COLLISION > m_collisions
Template specialization to enable wxStrings for certain containers (e.g. unordered_map)
Definition: bitmap.cpp:56
std::vector< RECT_DEFINITION > m_rects
bool IsDrcMarkerOfType(const MARKER_PCB &aMarker, int aErrorCode)
Predicate for testing the type of a DRC marker.
static std::vector< COURTYARD_OVERLAP_TEST_CASE > courtyard_cases
void DumpBoardToFile(BOARD &aBoard, const std::string &aName) const
Construction utilities for PCB tests.
std::ostream & operator<<(std::ostream &os, const COURTYARD_COLLISION &aColl)
BOARD_ITEM * GetItem(const KIID &aID)
std::unique_ptr< MODULE > MakeCourtyardTestModule(BOARD &aBoard, const COURTYARD_TEST_MODULE &aMod)
Construct a MODULE to use in a courtyard test from a COURTYARD_TEST_MODULE definition.
const KI_TEST::BOARD_DUMPER m_dumper
const wxString GetReference() const
Function GetReference.
Definition: class_module.h:444
void AddRectCourtyard(MODULE &aMod, const RECT_DEFINITION &aRect)
Add a rectangular courtyard outline to a module.
PCB_LAYER_ID
A quick note on layer IDs:
std::vector< COURTYARD_TEST_MODULE > m_mods
footprint courtyards overlap
footprint has no courtyard defined
KIID GetAuxItemID() const
Definition: rc_item.h:140
void DrawRect(MODULE &aMod, const VECTOR2I &aPos, const VECTOR2I &aSize, int aRadius, int aWidth, PCB_LAYER_ID aLayer)
Draw a rectangle on a module.
Functions to provide common constants and other functions to assist in making a consistent UI.
static void CheckCollisionsMatchExpected(BOARD &aBoard, const std::vector< std::unique_ptr< MARKER_PCB >> &aMarkers, const std::vector< COURTYARD_COLLISION > &aExpCollisions)
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)
static void DoCourtyardOverlapTest(const COURTYARD_OVERLAP_TEST_CASE &aCase, const KI_TEST::BOARD_DUMPER &aDumper)
Run a single courtyard overlap testcase.
Simple definition of a rectangle, can be rounded.
static bool CollisionMatchesExpected(BOARD &aBoard, const MARKER_PCB &aMarker, const COURTYARD_COLLISION &aCollision)
Check if a MARKER_PCB is described by a particular COURTYARD_COLLISION object.
A complete courtyard overlap test case: a name, the board modules list and the expected collisions.
BOARD holds information pertinent to a Pcbnew printed circuit board.
Definition: class_board.h:184
KIID GetMainItemID() const
Definition: rc_item.h:139
General utilities for PCB file IO for QA programs.
General utilities for DRC-related PCB tests.
static constexpr int Millimeter2iu(double mm)
A helper that contains logic to assist in dumping boards to disk depending on some environment variab...
std::unique_ptr< BOARD > MakeBoard(const std::vector< COURTYARD_TEST_MODULE > &aMods)
Make a board for courtyard testing.
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.