KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_drc_unconnected_items_exclusion_loss.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
20#include <filesystem>
21#include <iostream>
22#include <string>
23
25#include <drc/drc_engine.h>
26#include <board.h>
27#include <boost/test/unit_test.hpp>
28#include <boost/uuid/uuid_generators.hpp>
29#include <boost/uuid/uuid_io.hpp>
30#include <boost/uuid/uuid.hpp>
31#include <drc/drc_engine.h>
32#include <drc/drc_item.h>
33#include <footprint.h>
34#include <pad.h>
35#include <pcb_edit_frame.h>
37#include <pcb_io/pcb_io_mgr.h>
38#include <pcb_io/pcb_io.h>
39#include <pcb_marker.h>
40#include <api/board/board_rules.pb.h>
41#include <pcb_track.h>
44#include <project.h>
48#include <tool/tool_manager.h>
49#include <wx/string.h>
50#include <wx/utils.h>
51
53{
54 std::vector<wxString> m_files_to_delete;
55 std::vector<wxString> m_dirs_to_delete;
56
57 FileCleaner() = default;
59 {
60 for( const auto& f_path : m_files_to_delete )
61 {
62 if( wxFileName::Exists( f_path ) )
63 {
64 if( !wxRemoveFile( f_path ) )
65 {
66 BOOST_TEST_MESSAGE( "Warning: Failed to delete temporary file " << f_path );
67 }
68 }
69 }
70
71 for( const wxString& dir : m_dirs_to_delete )
72 wxFileName::Rmdir( dir, wxPATH_RMDIR_RECURSIVE );
73 }
74
75 void AddFile( const wxString& f_path ) { m_files_to_delete.push_back( f_path ); }
76 void AddDir( const wxString& d_path ) { m_dirs_to_delete.push_back( d_path ); }
77};
78
80{
84
85 std::string generate_uuid();
86 bool SaveBoardToFile( BOARD* board, const wxString& filename );
87 wxString stageBoard( const wxString& aBoardNameStem, FileCleaner& aCleaner );
88 void loadBoardAndVerifyInitialExclusions( const wxString& aBoardNameStem, int aExpectedInitialExclusions );
90 int createAndVerifyAdditionalUnconnectedExclusions( int aAdditionalExclusions, int aInitialExclusions );
91 void runDrcOnBoard();
92 void saveBoardAndProjectToTempFiles( const wxString& aBoardNameStem, FileCleaner& aCleaner,
93 wxString& aTempBoardFullPath, wxString& aTempProjectFullPath,
94 wxString& aTempBoardStemName, bool aCopyBoardVerbatim = false );
95 void reloadBoardAndVerifyExclusions( const wxString& aTempBoardStemName, int aExpectedExclusions );
96
97
98 // declared first so the staged files go after the settings manager has released their project locks
101 std::unique_ptr<BOARD> m_board;
102
104 wxString m_stagedDir;
105};
106
114
115
117{
120 {
121 m_board = std::make_unique<BOARD>();
122 }
123};
124
126{
127 boost::uuids::uuid uuid = boost::uuids::random_generator()();
128 return boost::uuids::to_string( uuid );
129}
130
131wxString DRC_EXCLUSION_BASE_FIXTURE::stageBoard( const wxString& aBoardNameStem, FileCleaner& aCleaner )
132{
133 // A project locks its file, so tests running at once must not share one
134 wxFileName dir( KI_TEST::GetPcbnewTestDataDir() + wxString::Format( "tmp_test_drc_%lu_", wxGetProcessId() )
135 + aBoardNameStem,
136 wxEmptyString );
137
138 BOOST_REQUIRE_MESSAGE( dir.Mkdir( wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL ), "Create folder " << dir.GetPath() );
139 aCleaner.AddDir( dir.GetPath() );
140
141 for( const wxString& ext : { wxS( "kicad_pcb" ), wxS( "kicad_pro" ), wxS( "kicad_prl" ) } )
142 {
143 wxString source = KI_TEST::GetPcbnewTestDataDir() + aBoardNameStem + "." + ext;
144
145 if( wxFileName::Exists( source ) )
146 BOOST_REQUIRE( wxCopyFile( source, dir.GetPath() + wxFileName::GetPathSeparator() + aBoardNameStem + "."
147 + ext ) );
148 }
149
150 m_stagedDir = dir.GetDirs().Last();
151 return m_stagedDir + "/" + aBoardNameStem;
152}
153
155 int aExpectedInitialExclusions )
156{
157 KI_TEST::LoadBoard( m_settingsManager, aBoardNameStem, m_board );
158 BOOST_REQUIRE_MESSAGE( m_board,
159 "Could not load board " + aBoardNameStem ); // Ensure board loaded from test data directory
160 PROJECT* pcb_project = m_board->GetProject();
161 BOOST_REQUIRE_MESSAGE( pcb_project, "Get project pointer after initial loading." );
162
163 // Board test file comes with initial exclusions, check if they are preserved after loading
164 const BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
165 size_t initialExclusionsCount = bds.m_DrcExclusions.size();
166 BOOST_TEST_MESSAGE( "Initial DRC exclusions count: " << initialExclusionsCount );
167 BOOST_CHECK_EQUAL( initialExclusionsCount, (size_t) aExpectedInitialExclusions );
168}
169
171{
172 const BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
173 std::vector<PCB_MARKER*> markers;
174
175 for( const DRC_EXCLUSION& exclusion : bds.m_DrcExclusions )
176 {
177 PCB_MARKER* marker = PCB_MARKER::FromProto( exclusion.ToProto().marker() );
178
179 if( marker )
180 {
181 marker->SetExcluded( true, exclusion.GetComment() );
182 markers.push_back( marker );
183 m_board->Add( marker );
184 }
185 }
186 size_t actualExclusionsCount = bds.m_DrcExclusions.size();
187 size_t initialExclusionsCount = markers.size();
188 BOOST_CHECK_EQUAL( actualExclusionsCount, initialExclusionsCount );
189 BOOST_TEST_MESSAGE( std::string( "Actual DRC exclusions count: " ) + std::to_string( actualExclusionsCount )
190 + " after adding initial markers." );
191}
192
194 int aInitialExclusions )
195{
196 for( int i = 0; i < aAdditionalExclusions; ++i )
197 {
198 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_UNCONNECTED_ITEMS );
199 wxString id1 = wxString( generate_uuid() );
200 wxString id2 = wxString( generate_uuid() );
201 drcItem->SetItems( KIID( id1 ), KIID( id2 ) );
202
203 PCB_MARKER* marker = new PCB_MARKER( drcItem, VECTOR2I( 1000 * i, 1000 * i ) );
204 m_board->Add( marker );
205
206 // Exclude odd-numbered markers
207 if( i % 2 == 1 )
208 {
209 marker->SetExcluded( true, wxString::Format( "Exclusion %d", i ) );
210 }
211 }
212
213 // Store the new exclusion markers in the board
214 m_board->RecordDRCExclusions();
215
216 // Verify the number of exclusions after adding unconnected items
217 const BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
218 const int expectedExclusions =
219 aInitialExclusions + aAdditionalExclusions / 2; // Only odd-numbered markers are excluded
220 size_t newActualExclusionsCount = bds.m_DrcExclusions.size();
221 BOOST_TEST_MESSAGE( std::string( "New actual DRC exclusions count: " ) + std::to_string( newActualExclusionsCount )
222 + " after adding unconnected items." );
223 BOOST_CHECK_EQUAL( newActualExclusionsCount, (size_t) expectedExclusions );
224 return expectedExclusions;
225}
226
228{
229 BOOST_TEST_MESSAGE( "Running DRC on board." );
230 BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
231 bds.m_DRCEngine->InitEngine( wxFileName() );
232 m_board->RecordDRCExclusions();
233 bool runDRC = true;
234 bool runDRCOnAllLayers = true;
235 bds.m_DRCEngine->RunTests( EDA_UNITS::MM, runDRC, runDRCOnAllLayers );
236 m_board->ResolveDRCExclusions( false );
237 BOOST_TEST_MESSAGE( "DRC done." );
238}
239
240void DRC_EXCLUSION_BASE_FIXTURE::saveBoardAndProjectToTempFiles( const wxString& aBoardNameStem, FileCleaner& aCleaner,
241 wxString& aTempBoardFullPath, wxString& aTempProjectFullPath,
242 wxString& aTempBoardStemName, bool aCopyBoardVerbatim )
243{
244 wxString tempPrefix = m_stagedDir + "/tmp_test_drc_";
245 aTempBoardStemName = tempPrefix + aBoardNameStem.ToStdString();
246 aTempBoardFullPath = KI_TEST::GetPcbnewTestDataDir() + aTempBoardStemName + ".kicad_pcb";
247 aCleaner.AddFile( aTempBoardFullPath );
248 wxString tempProjectStemName = tempPrefix + aBoardNameStem.ToStdString();
249 aTempProjectFullPath = KI_TEST::GetPcbnewTestDataDir() + aTempBoardStemName + ".kicad_pro";
250 aCleaner.AddFile( aTempProjectFullPath );
251
252 bool boardSaved;
253
254 if( aCopyBoardVerbatim )
255 {
256 // Re-saving perturbs geometry slightly, which moves violation positions
257 boardSaved = wxCopyFile( KI_TEST::GetPcbnewTestDataDir() + m_stagedDir + "/" + aBoardNameStem + ".kicad_pcb",
258 aTempBoardFullPath );
259 }
260 else
261 {
262 boardSaved = SaveBoardToFile( m_board->GetBoard(), aTempBoardFullPath );
263 }
264
265 BOOST_REQUIRE_MESSAGE( boardSaved, "Save board to temporary file: " << aTempBoardFullPath );
266
267 m_settingsManager.SaveProjectAs( aTempProjectFullPath, m_board->GetProject() );
268 BOOST_REQUIRE_MESSAGE( wxFileName::Exists( aTempProjectFullPath ),
269 "Save project to temporary file: " << aTempProjectFullPath );
270}
271
272void DRC_EXCLUSION_BASE_FIXTURE::reloadBoardAndVerifyExclusions( const wxString& aTempBoardStemName, int aExpectedExclusions )
273{
274 // clear the current board to ensure a fresh load
275 m_board.reset();
276
277 KI_TEST::LoadBoard( m_settingsManager, aTempBoardStemName, m_board );
278 BOOST_REQUIRE_MESSAGE( m_board, "Could not load board from tempfile:"
279 + aTempBoardStemName ); // Ensure board loaded from test data directory
280 PROJECT* pcb_project = m_board->GetProject();
281 BOOST_REQUIRE_MESSAGE( pcb_project, "Get project pointer after initial loading." );
282
283 BOARD_DESIGN_SETTINGS& reloaded_bds = m_board->GetDesignSettings();
284 size_t reloadedExclusionsCount = reloaded_bds.m_DrcExclusions.size();
285 BOOST_TEST_MESSAGE( "Reloaded DRC exclusions count: " << reloadedExclusionsCount );
286 BOOST_CHECK_EQUAL( reloadedExclusionsCount, aExpectedExclusions );
287}
288
289bool DRC_EXCLUSION_BASE_FIXTURE::SaveBoardToFile( BOARD* board, const wxString& filename )
290{
291 try
292 {
294 pi->SaveBoard( filename, *board, nullptr );
295 return true;
296 }
297 catch( const IO_ERROR& error )
298 {
299 BOOST_TEST_MESSAGE( wxString::Format( "Save board to %s: %s", filename, error.What() ) );
300 return false;
301 }
302}
304{
305 // Test that unconnected item exclusions are not lost after multiple DRC runs.
306 // This test is expected to fail if the bug (issue17429) is present.
307
308 std::vector<std::pair<wxString, int>> tests = {
309 { "issue17429", 10 }, // board name stem, expected initial exclusions
310 };
311
312 const int NUM_DRC_RUNS = 2;
313
314 for( const std::pair<wxString, int>& test_params : tests )
315 {
316 FileCleaner& tempFileCleaner = m_tempFileCleaner;
317 wxString boardNameStem = test_params.first;
318 int expectedInitialExclusions = test_params.second;
319
320 loadBoardAndVerifyInitialExclusions( stageBoard( boardNameStem, tempFileCleaner ), expectedInitialExclusions );
321 createAndVerifyInitialExclusionMarkers();
322 const int additionalExclusions = 5;
323 int expectedExclusions =
324 createAndVerifyAdditionalUnconnectedExclusions( additionalExclusions, expectedInitialExclusions );
325
326 runDrcOnBoard();
327
328 const BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
329 BOOST_TEST_MESSAGE( std::string( "DRC exclusions count after DRC run: " ) + std::to_string( expectedExclusions )
330 + " after adding unconnected items." );
331 BOOST_CHECK_EQUAL( bds.m_DrcExclusions.size(), expectedExclusions );
332 }
333}
334
335BOOST_FIXTURE_TEST_CASE( DRCUnconnectedItemsExclusionsSaveLoad, DRC_EXCLUSION_REGRESSION_FIXTURE )
336{
337 namespace fs = std::filesystem;
338
339 // Test that unconnected item exclusions are not lost during save/load.
340 // This test is expected to fail if the bug (issue17429) is present.
341
342 std::vector<std::pair<wxString, int>> tests = {
343 { "issue17429", 10 }, // board name stem, expected initial exclusions
344 };
345
346
347 for( const std::pair<wxString, int>& test_params : tests )
348 {
349 FileCleaner& tempFileCleaner = m_tempFileCleaner;
350 wxString boardNameStem = test_params.first;
351 int expectedInitialExclusions = test_params.second;
352
353 loadBoardAndVerifyInitialExclusions( stageBoard( boardNameStem, tempFileCleaner ), expectedInitialExclusions );
354
355 wxString tempBoardFullPath, tempProjectFullPath, tempBoardStemName;
356 saveBoardAndProjectToTempFiles( boardNameStem, tempFileCleaner, tempBoardFullPath, tempProjectFullPath,
357 tempBoardStemName );
358
359 createAndVerifyInitialExclusionMarkers();
360
361 const int additionalExclusions = 5;
362 int expectedExclusions =
363 createAndVerifyAdditionalUnconnectedExclusions( additionalExclusions, expectedInitialExclusions );
364
365 bool boardSaved = SaveBoardToFile( m_board->GetBoard(), tempBoardFullPath );
366 BOOST_REQUIRE_MESSAGE( boardSaved, "Save board to temporary file: " << tempBoardFullPath );
367
368 m_settingsManager.SaveProjectAs( tempProjectFullPath, m_board->GetProject() );
369 BOOST_REQUIRE_MESSAGE( wxFileName::Exists( tempProjectFullPath ),
370 "Save project to temporary file: " << tempProjectFullPath );
371
372 reloadBoardAndVerifyExclusions( tempBoardStemName, expectedExclusions );
373 }
374}
375
376
377static void runDrcAndCreateMarkers( BOARD* aBoard )
378{
379 aBoard->DeleteMARKERs();
380
382
383 bds.m_DRCEngine->InitEngine( wxFileName() );
385 [aBoard]( const std::shared_ptr<DRC_ITEM>& aItem, const VECTOR2I& aPos, int aLayer,
386 const std::function<void( PCB_MARKER* )>& aPathGenerator )
387 {
388 PCB_MARKER* marker = new PCB_MARKER( aItem, aPos, aLayer );
389
390 aPathGenerator( marker );
391 aBoard->Add( marker );
392 } );
393
394 bds.m_DRCEngine->RunTests( EDA_UNITS::MM, true, true );
396}
397
398
399// Courtyard overlaps are positioned by SHAPE_POLY_SET::Collide(), which walks a triangulation
400// whose triangle order is not reproducible between loads, so their keys move on their own. That
401// is a separate defect in the geometry layer, not in the exclusion bookkeeping under test here.
402static bool isCourtyardOverlap( PCB_MARKER* aMarker )
403{
404 return aMarker->GetRCItem()->GetErrorCode() == DRCE_OVERLAPPING_FOOTPRINTS;
405}
406
407
408BOOST_FIXTURE_TEST_CASE( DRCExclusionsSurviveProjectRoundTrip, DRC_EXCLUSION_REGRESSION_FIXTURE )
409{
410 // Exclude every violation on the board, write the exclusions to the project file, then load
411 // the project back and run DRC again. Nothing may be reported the second time round. The
412 // board file itself is copied rather than re-saved, so both runs see identical geometry.
413
414 FileCleaner& tempFileCleaner = m_tempFileCleaner;
415 wxString tempBoardPath, tempProjectPath, tempStem;
416
417 KI_TEST::LoadBoard( m_settingsManager, stageBoard( "issue25101", tempFileCleaner ), m_board );
418 BOOST_REQUIRE( m_board );
419
420 runDrcAndCreateMarkers( m_board.get() );
421
422 auto markerKey =
423 []( const PCB_MARKER* aMarker ) -> std::string
424 {
425 return nlohmann::json( DRC_EXCLUSION::FromMarker( *aMarker ) ).dump();
426 };
427
428 std::map<std::string, int> keyCounts;
429 std::map<std::string, int> expectedKeys;
430
431 for( PCB_MARKER* marker : m_board->Markers() )
432 {
433 std::string serialized = markerKey( marker );
434
435 keyCounts[serialized]++;
436
437 if( !isCourtyardOverlap( marker ) )
438 expectedKeys[serialized]++;
439 }
440
441 // A violation found on several copper layers is reported once per layer but serializes to a
442 // single key, so one stored exclusion has to cover every marker that shares it
443 bool sharedKeys = std::any_of( keyCounts.begin(), keyCounts.end(),
444 []( const std::pair<const std::string, int>& aEntry )
445 {
446 return aEntry.second > 1;
447 } );
448
449 BOOST_REQUIRE_MESSAGE( sharedKeys, "board no longer produces multi-layer violations" );
450 BOOST_TEST_MESSAGE( "Markers: " << m_board->Markers().size() << " distinct keys: " << keyCounts.size() );
451
452 for( PCB_MARKER* marker : m_board->Markers() )
453 marker->SetExcluded( true );
454
455 m_board->RecordDRCExclusions();
456 BOOST_CHECK_EQUAL( m_board->GetDesignSettings().m_DrcExclusions.size(), keyCounts.size() );
457
458 saveBoardAndProjectToTempFiles( "issue25101", tempFileCleaner, tempBoardPath, tempProjectPath, tempStem, true );
459
460 KI_TEST::LoadBoard( m_settingsManager, tempStem, m_board );
461 BOOST_REQUIRE( m_board );
462
463 BOARD_DESIGN_SETTINGS& reloadedBds = m_board->GetDesignSettings();
464 BOOST_REQUIRE_EQUAL( reloadedBds.m_DrcExclusions.size(), keyCounts.size() );
465
466 runDrcAndCreateMarkers( m_board.get() );
467
468 // Violations have to be reported against the same items, at the same place, and just as
469 // often, on a board that has just been reloaded, or no exclusion can survive the trip
470 std::map<std::string, int> reloadedKeys;
471
472 for( PCB_MARKER* marker : m_board->Markers() )
473 {
474 if( !isCourtyardOverlap( marker ) )
475 reloadedKeys[markerKey( marker )]++;
476 }
477
478 for( const std::pair<const std::string, int>& entry : expectedKeys )
479 BOOST_CHECK_MESSAGE( reloadedKeys[entry.first] == entry.second, "key not reproduced: " << entry.first );
480
481 for( const std::pair<const std::string, int>& entry : reloadedKeys )
482 BOOST_CHECK_MESSAGE( expectedKeys.count( entry.first ), "key appeared only after reload: " << entry.first );
483
484 m_board->ResolveDRCExclusions( false );
485
486 for( PCB_MARKER* marker : m_board->Markers() )
487 {
488 BOOST_CHECK_MESSAGE( marker->IsExcluded() || isCourtyardOverlap( marker ),
489 "exclusion lost for " << markerKey( marker ) );
490 }
491}
General utilities for PCB file IO for QA programs.
Container for design settings for a BOARD object.
std::shared_ptr< DRC_ENGINE > m_DRCEngine
std::set< DRC_EXCLUSION, DRC_EXCLUSION_COMPARE > m_DrcExclusions
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:411
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT, bool aSkipConnectivity=false) override
Removes an item from the container.
Definition board.cpp:1520
void DeleteMARKERs()
Delete all MARKERS from the board.
Definition board.cpp:2066
BOARD_DESIGN_SETTINGS & GetDesignSettings()
Definition board.cpp:1290
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 ClearViolationHandler()
Definition drc_engine.h:169
void InitEngine(const wxFileName &aRulePath)
Initialize the DRC engine.
Container for an DRC exclusion, which is a PCB_MARKER plus an optional comment.
wxString GetComment() const
static DRC_EXCLUSION FromMarker(const PCB_MARKER &aMarker)
const kiapi::board::DrcExclusion & ToProto() const
static std::shared_ptr< DRC_ITEM > Create(int aErrorCode)
Constructs a DRC_ITEM for the given error code.
Definition drc_item.cpp:492
Hold an error message and may be used when throwing exceptions containing meaningful error messages.
virtual const wxString What() const
A composite of Problem() and Where()
Definition kiid.h:46
std::shared_ptr< RC_ITEM > GetRCItem() const
void SetExcluded(bool aExcluded, const wxString &aComment=wxEmptyString)
Definition marker_base.h:91
@ KICAD_SEXP
S-expression Pcbnew file format.
Definition pcb_io_mgr.h:54
static PCB_IO * FindPlugin(PCB_FILE_T aFileType)
Return a PCB_IO which the caller can use to import, export, save, or load design documents.
static PCB_MARKER * FromProto(const kiapi::board::DrcMarker &aMsg)
Container for project specific data.
Definition project.h:63
int GetErrorCode() const
Definition rc_item.h:213
@ DRCE_UNCONNECTED_ITEMS
Definition drc_item.h:48
@ DRCE_OVERLAPPING_FOOTPRINTS
Definition drc_item.h:79
std::string source
std::unique_ptr< T > IO_RELEASER
Helper to hold and release an IO_BASE object when exceptions are thrown.
Definition io_mgr.h:33
std::string GetPcbnewTestDataDir()
Utility which returns a path to the data directory where the test board files are stored.
void LoadBoard(SETTINGS_MANAGER &aSettingsManager, const wxString &aRelPath, std::unique_ptr< BOARD > &aBoard)
void reloadBoardAndVerifyExclusions(const wxString &aTempBoardStemName, int aExpectedExclusions)
int createAndVerifyAdditionalUnconnectedExclusions(int aAdditionalExclusions, int aInitialExclusions)
wxString stageBoard(const wxString &aBoardNameStem, FileCleaner &aCleaner)
void loadBoardAndVerifyInitialExclusions(const wxString &aBoardNameStem, int aExpectedInitialExclusions)
std::unique_ptr< BOARD > m_board
Folder in the test data, relative to it, that holds this test's private copy of the board.
bool SaveBoardToFile(BOARD *board, const wxString &filename)
void saveBoardAndProjectToTempFiles(const wxString &aBoardNameStem, FileCleaner &aCleaner, wxString &aTempBoardFullPath, wxString &aTempProjectFullPath, wxString &aTempBoardStemName, bool aCopyBoardVerbatim=false)
void AddFile(const wxString &f_path)
void AddDir(const wxString &d_path)
std::vector< wxString > m_files_to_delete
FileCleaner()=default
static void runDrcAndCreateMarkers(BOARD *aBoard)
static bool isCourtyardOverlap(PCB_MARKER *aMarker)
BOOST_FIXTURE_TEST_CASE(DRCUnconnectedExclusionsLoss, DRC_EXCLUSION_SAVE_FIXTURE)
BOOST_REQUIRE(intersection.has_value()==c.ExpectedIntersection.has_value())
BOOST_TEST_MESSAGE("Polyline has "<< chain.PointCount()<< " points")
BOOST_CHECK_EQUAL(result, "25.4")
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:708