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>
40#include <api/board/board_rules.pb.h>
62 if( wxFileName::Exists( f_path ) )
64 if( !wxRemoveFile( f_path ) )
72 wxFileName::Rmdir( dir, wxPATH_RMDIR_RECURSIVE );
93 wxString& aTempBoardFullPath, wxString& aTempProjectFullPath,
94 wxString& aTempBoardStemName,
bool aCopyBoardVerbatim =
false );
121 m_board = std::make_unique<BOARD>();
127 boost::uuids::uuid uuid = boost::uuids::random_generator()();
128 return boost::uuids::to_string( uuid );
138 BOOST_REQUIRE_MESSAGE( dir.Mkdir( wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL ),
"Create folder " << dir.GetPath() );
139 aCleaner.
AddDir( dir.GetPath() );
141 for(
const wxString& ext : { wxS(
"kicad_pcb" ), wxS(
"kicad_pro" ), wxS(
"kicad_prl" ) } )
145 if( wxFileName::Exists(
source ) )
146 BOOST_REQUIRE( wxCopyFile(
source, dir.GetPath() + wxFileName::GetPathSeparator() + aBoardNameStem +
"."
155 int aExpectedInitialExclusions )
158 BOOST_REQUIRE_MESSAGE(
m_board,
159 "Could not load board " + aBoardNameStem );
161 BOOST_REQUIRE_MESSAGE( pcb_project,
"Get project pointer after initial loading." );
167 BOOST_CHECK_EQUAL( initialExclusionsCount, (
size_t) aExpectedInitialExclusions );
173 std::vector<PCB_MARKER*> markers;
182 markers.push_back( marker );
187 size_t initialExclusionsCount = markers.size();
189 BOOST_TEST_MESSAGE( std::string(
"Actual DRC exclusions count: " ) + std::to_string( actualExclusionsCount )
190 +
" after adding initial markers." );
194 int aInitialExclusions )
196 for(
int i = 0; i < aAdditionalExclusions; ++i )
201 drcItem->SetItems(
KIID( id1 ),
KIID( id2 ) );
209 marker->
SetExcluded(
true, wxString::Format(
"Exclusion %d", i ) );
214 m_board->RecordDRCExclusions();
218 const int expectedExclusions =
219 aInitialExclusions + aAdditionalExclusions / 2;
221 BOOST_TEST_MESSAGE( std::string(
"New actual DRC exclusions count: " ) + std::to_string( newActualExclusionsCount )
222 +
" after adding unconnected items." );
224 return expectedExclusions;
232 m_board->RecordDRCExclusions();
234 bool runDRCOnAllLayers =
true;
236 m_board->ResolveDRCExclusions(
false );
241 wxString& aTempBoardFullPath, wxString& aTempProjectFullPath,
242 wxString& aTempBoardStemName,
bool aCopyBoardVerbatim )
244 wxString tempPrefix =
m_stagedDir +
"/tmp_test_drc_";
245 aTempBoardStemName = tempPrefix + aBoardNameStem.ToStdString();
247 aCleaner.
AddFile( aTempBoardFullPath );
248 wxString tempProjectStemName = tempPrefix + aBoardNameStem.ToStdString();
250 aCleaner.
AddFile( aTempProjectFullPath );
254 if( aCopyBoardVerbatim )
258 aTempBoardFullPath );
265 BOOST_REQUIRE_MESSAGE( boardSaved,
"Save board to temporary file: " << aTempBoardFullPath );
268 BOOST_REQUIRE_MESSAGE( wxFileName::Exists( aTempProjectFullPath ),
269 "Save project to temporary file: " << aTempProjectFullPath );
278 BOOST_REQUIRE_MESSAGE(
m_board,
"Could not load board from tempfile:"
279 + aTempBoardStemName );
281 BOOST_REQUIRE_MESSAGE( pcb_project,
"Get project pointer after initial loading." );
294 pi->SaveBoard( filename, *board,
nullptr );
308 std::vector<std::pair<wxString, int>> tests = {
309 {
"issue17429", 10 },
312 const int NUM_DRC_RUNS = 2;
314 for(
const std::pair<wxString, int>& test_params : tests )
317 wxString boardNameStem = test_params.first;
318 int expectedInitialExclusions = test_params.second;
320 loadBoardAndVerifyInitialExclusions( stageBoard( boardNameStem, tempFileCleaner ), expectedInitialExclusions );
321 createAndVerifyInitialExclusionMarkers();
322 const int additionalExclusions = 5;
323 int expectedExclusions =
324 createAndVerifyAdditionalUnconnectedExclusions( additionalExclusions, expectedInitialExclusions );
329 BOOST_TEST_MESSAGE( std::string(
"DRC exclusions count after DRC run: " ) + std::to_string( expectedExclusions )
330 +
" after adding unconnected items." );
337 namespace fs = std::filesystem;
342 std::vector<std::pair<wxString, int>> tests = {
343 {
"issue17429", 10 },
347 for(
const std::pair<wxString, int>& test_params : tests )
350 wxString boardNameStem = test_params.first;
351 int expectedInitialExclusions = test_params.second;
353 loadBoardAndVerifyInitialExclusions( stageBoard( boardNameStem, tempFileCleaner ), expectedInitialExclusions );
355 wxString tempBoardFullPath, tempProjectFullPath, tempBoardStemName;
356 saveBoardAndProjectToTempFiles( boardNameStem, tempFileCleaner, tempBoardFullPath, tempProjectFullPath,
359 createAndVerifyInitialExclusionMarkers();
361 const int additionalExclusions = 5;
362 int expectedExclusions =
363 createAndVerifyAdditionalUnconnectedExclusions( additionalExclusions, expectedInitialExclusions );
365 bool boardSaved = SaveBoardToFile( m_board->GetBoard(), tempBoardFullPath );
366 BOOST_REQUIRE_MESSAGE( boardSaved,
"Save board to temporary file: " << tempBoardFullPath );
368 m_settingsManager.SaveProjectAs( tempProjectFullPath, m_board->GetProject() );
369 BOOST_REQUIRE_MESSAGE( wxFileName::Exists( tempProjectFullPath ),
370 "Save project to temporary file: " << tempProjectFullPath );
372 reloadBoardAndVerifyExclusions( tempBoardStemName, expectedExclusions );
385 [aBoard](
const std::shared_ptr<DRC_ITEM>& aItem,
const VECTOR2I& aPos,
int aLayer,
386 const std::function<
void(
PCB_MARKER* )>& aPathGenerator )
390 aPathGenerator( marker );
391 aBoard->
Add( marker );
415 wxString tempBoardPath, tempProjectPath, tempStem;
417 KI_TEST::LoadBoard( m_settingsManager, stageBoard(
"issue25101", tempFileCleaner ), m_board );
423 [](
const PCB_MARKER* aMarker ) -> std::string
428 std::map<std::string, int> keyCounts;
429 std::map<std::string, int> expectedKeys;
431 for(
PCB_MARKER* marker : m_board->Markers() )
433 std::string serialized = markerKey( marker );
435 keyCounts[serialized]++;
438 expectedKeys[serialized]++;
443 bool sharedKeys = std::any_of( keyCounts.begin(), keyCounts.end(),
444 [](
const std::pair<const std::string, int>& aEntry )
446 return aEntry.second > 1;
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() );
452 for(
PCB_MARKER* marker : m_board->Markers() )
453 marker->SetExcluded(
true );
455 m_board->RecordDRCExclusions();
456 BOOST_CHECK_EQUAL( m_board->GetDesignSettings().m_DrcExclusions.size(), keyCounts.size() );
458 saveBoardAndProjectToTempFiles(
"issue25101", tempFileCleaner, tempBoardPath, tempProjectPath, tempStem,
true );
464 BOOST_REQUIRE_EQUAL( reloadedBds.
m_DrcExclusions.size(), keyCounts.size() );
470 std::map<std::string, int> reloadedKeys;
472 for(
PCB_MARKER* marker : m_board->Markers() )
475 reloadedKeys[markerKey( marker )]++;
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 );
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 );
484 m_board->ResolveDRCExclusions(
false );
486 for(
PCB_MARKER* marker : m_board->Markers() )
489 "exclusion lost for " << markerKey( marker ) );
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.
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT, bool aSkipConnectivity=false) override
Removes an item from the container.
void DeleteMARKERs()
Delete all MARKERS from the board.
BOARD_DESIGN_SETTINGS & GetDesignSettings()
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).
void ClearViolationHandler()
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.
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()
std::shared_ptr< RC_ITEM > GetRCItem() const
void SetExcluded(bool aExcluded, const wxString &aComment=wxEmptyString)
@ KICAD_SEXP
S-expression Pcbnew file format.
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.
@ DRCE_OVERLAPPING_FOOTPRINTS
std::unique_ptr< T > IO_RELEASER
Helper to hold and release an IO_BASE object when exceptions are thrown.
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)
std::string generate_uuid()
void reloadBoardAndVerifyExclusions(const wxString &aTempBoardStemName, int aExpectedExclusions)
DRC_EXCLUSION_BASE_FIXTURE()
int createAndVerifyAdditionalUnconnectedExclusions(int aAdditionalExclusions, int aInitialExclusions)
SETTINGS_MANAGER m_settingsManager
void createAndVerifyInitialExclusionMarkers()
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)
FileCleaner m_tempFileCleaner
DRC_EXCLUSION_REGRESSION_FIXTURE()
DRC_EXCLUSION_SAVE_FIXTURE()
void AddFile(const wxString &f_path)
std::vector< wxString > m_dirs_to_delete
void AddDir(const wxString &d_path)
std::vector< wxString > m_files_to_delete
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