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>
60 if( wxFileName::Exists( f_path ) )
62 if( !wxRemoveFile( f_path ) )
86 wxString& aTempBoardFullPath, wxString& aTempProjectFullPath,
87 wxString& aTempBoardStemName,
bool aCopyBoardVerbatim =
false );
109 m_board = std::make_unique<BOARD>();
115 boost::uuids::uuid uuid = boost::uuids::random_generator()();
116 return boost::uuids::to_string( uuid );
120 int aExpectedInitialExclusions )
123 BOOST_REQUIRE_MESSAGE(
m_board,
124 "Could not load board " + aBoardNameStem );
126 BOOST_REQUIRE_MESSAGE( pcb_project,
"Get project pointer after initial loading." );
132 BOOST_CHECK_EQUAL( initialExclusionsCount, (
size_t) aExpectedInitialExclusions );
138 std::vector<PCB_MARKER*> markers;
147 markers.push_back( marker );
152 size_t initialExclusionsCount = markers.size();
154 BOOST_TEST_MESSAGE( std::string(
"Actual DRC exclusions count: " ) + std::to_string( actualExclusionsCount )
155 +
" after adding initial markers." );
159 int aInitialExclusions )
161 for(
int i = 0; i < aAdditionalExclusions; ++i )
166 drcItem->SetItems(
KIID( id1 ),
KIID( id2 ) );
174 marker->
SetExcluded(
true, wxString::Format(
"Exclusion %d", i ) );
179 m_board->RecordDRCExclusions();
183 const int expectedExclusions =
184 aInitialExclusions + aAdditionalExclusions / 2;
186 BOOST_TEST_MESSAGE( std::string(
"New actual DRC exclusions count: " ) + std::to_string( newActualExclusionsCount )
187 +
" after adding unconnected items." );
189 return expectedExclusions;
197 m_board->RecordDRCExclusions();
199 bool runDRCOnAllLayers =
true;
201 m_board->ResolveDRCExclusions(
false );
206 wxString& aTempBoardFullPath, wxString& aTempProjectFullPath,
207 wxString& aTempBoardStemName,
bool aCopyBoardVerbatim )
209 wxString tempPrefix =
"tmp_test_drc_";
210 aTempBoardStemName = tempPrefix + aBoardNameStem.ToStdString();
212 aCleaner.
AddFile( aTempBoardFullPath );
213 wxString tempProjectStemName = tempPrefix + aBoardNameStem.ToStdString();
215 aCleaner.
AddFile( aTempProjectFullPath );
219 if( aCopyBoardVerbatim )
229 BOOST_REQUIRE_MESSAGE( boardSaved,
"Save board to temporary file: " << aTempBoardFullPath );
232 BOOST_REQUIRE_MESSAGE( wxFileName::Exists( aTempProjectFullPath ),
233 "Save project to temporary file: " << aTempProjectFullPath );
242 BOOST_REQUIRE_MESSAGE(
m_board,
"Could not load board from tempfile:"
243 + aTempBoardStemName );
245 BOOST_REQUIRE_MESSAGE( pcb_project,
"Get project pointer after initial loading." );
258 pi->SaveBoard( filename, *board,
nullptr );
272 std::vector<std::pair<wxString, int>> tests = {
273 {
"issue17429", 10 },
276 const int NUM_DRC_RUNS = 2;
278 for(
const std::pair<wxString, int>& test_params : tests )
280 wxString boardNameStem = test_params.first;
281 int expectedInitialExclusions = test_params.second;
283 loadBoardAndVerifyInitialExclusions( boardNameStem, expectedInitialExclusions );
284 createAndVerifyInitialExclusionMarkers();
285 const int additionalExclusions = 5;
286 int expectedExclusions =
287 createAndVerifyAdditionalUnconnectedExclusions( additionalExclusions, expectedInitialExclusions );
292 BOOST_TEST_MESSAGE( std::string(
"DRC exclusions count after DRC run: " ) + std::to_string( expectedExclusions )
293 +
" after adding unconnected items." );
300 namespace fs = std::filesystem;
305 std::vector<std::pair<wxString, int>> tests = {
306 {
"issue17429", 10 },
310 for(
const std::pair<wxString, int>& test_params : tests )
313 wxString boardNameStem = test_params.first;
314 int expectedInitialExclusions = test_params.second;
316 loadBoardAndVerifyInitialExclusions( boardNameStem, expectedInitialExclusions );
318 wxString tempBoardFullPath, tempProjectFullPath, tempBoardStemName;
319 saveBoardAndProjectToTempFiles( boardNameStem, tempFileCleaner, tempBoardFullPath, tempProjectFullPath,
322 createAndVerifyInitialExclusionMarkers();
324 const int additionalExclusions = 5;
325 int expectedExclusions =
326 createAndVerifyAdditionalUnconnectedExclusions( additionalExclusions, expectedInitialExclusions );
328 bool boardSaved = SaveBoardToFile( m_board->GetBoard(), tempBoardFullPath );
329 BOOST_REQUIRE_MESSAGE( boardSaved,
"Save board to temporary file: " << tempBoardFullPath );
331 m_settingsManager.SaveProjectAs( tempProjectFullPath, m_board->GetProject() );
332 BOOST_REQUIRE_MESSAGE( wxFileName::Exists( tempProjectFullPath ),
333 "Save project to temporary file: " << tempProjectFullPath );
335 reloadBoardAndVerifyExclusions( tempBoardStemName, expectedExclusions );
348 [aBoard](
const std::shared_ptr<DRC_ITEM>& aItem,
const VECTOR2I& aPos,
int aLayer,
349 const std::function<
void(
PCB_MARKER* )>& aPathGenerator )
353 aPathGenerator( marker );
354 aBoard->
Add( marker );
378 wxString tempBoardPath, tempProjectPath, tempStem;
386 [](
const PCB_MARKER* aMarker ) -> std::string
391 std::map<std::string, int> keyCounts;
392 std::map<std::string, int> expectedKeys;
394 for(
PCB_MARKER* marker : m_board->Markers() )
396 std::string serialized = markerKey( marker );
398 keyCounts[serialized]++;
401 expectedKeys[serialized]++;
406 bool sharedKeys = std::any_of( keyCounts.begin(), keyCounts.end(),
407 [](
const std::pair<const std::string, int>& aEntry )
409 return aEntry.second > 1;
412 BOOST_REQUIRE_MESSAGE( sharedKeys,
"board no longer produces multi-layer violations" );
413 BOOST_TEST_MESSAGE(
"Markers: " << m_board->Markers().size() <<
" distinct keys: " << keyCounts.size() );
415 for(
PCB_MARKER* marker : m_board->Markers() )
416 marker->SetExcluded(
true );
418 m_board->RecordDRCExclusions();
419 BOOST_CHECK_EQUAL( m_board->GetDesignSettings().m_DrcExclusions.size(), keyCounts.size() );
421 saveBoardAndProjectToTempFiles(
"issue25101", tempFileCleaner, tempBoardPath, tempProjectPath, tempStem,
true );
427 BOOST_REQUIRE_EQUAL( reloadedBds.
m_DrcExclusions.size(), keyCounts.size() );
433 std::map<std::string, int> reloadedKeys;
435 for(
PCB_MARKER* marker : m_board->Markers() )
438 reloadedKeys[markerKey( marker )]++;
441 for(
const std::pair<const std::string, int>& entry : expectedKeys )
442 BOOST_CHECK_MESSAGE( reloadedKeys[entry.first] == entry.second,
"key not reproduced: " << entry.first );
444 for(
const std::pair<const std::string, int>& entry : reloadedKeys )
445 BOOST_CHECK_MESSAGE( expectedKeys.count( entry.first ),
"key appeared only after reload: " << entry.first );
447 m_board->ResolveDRCExclusions(
false );
449 for(
PCB_MARKER* marker : m_board->Markers() )
452 "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.
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
void DeleteMARKERs()
Delete all MARKERS from the board.
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 #PLUGIN 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()
std::unique_ptr< BOARD > m_board
int createAndVerifyAdditionalUnconnectedExclusions(int aAdditionalExclusions, int aInitialExclusions)
void createAndVerifyInitialExclusionMarkers()
void reloadBoardAndVerifyExclusions(const wxString &aTempBoardStemName, int aExpectedExclusions)
void loadBoardAndVerifyInitialExclusions(const wxString &aBoardNameStem, int aExpectedInitialExclusions)
void saveBoardAndProjectToTempFiles(const wxString &aBoardNameStem, FileCleaner &aCleaner, wxString &aTempBoardFullPath, wxString &aTempProjectFullPath, wxString &aTempBoardStemName, bool aCopyBoardVerbatim=false)
SETTINGS_MANAGER m_settingsManager
bool SaveBoardToFile(BOARD *board, const wxString &filename)
DRC_REGRESSION_TEST_FIXTURE()
std::unique_ptr< BOARD > m_board
DRC_UNCONNECTED_SAVE_FIXTURE()
void AddFile(const wxString &f_path)
std::vector< wxString > m_files_to_delete
BOOST_FIXTURE_TEST_CASE(DRCUnconnectedExclusionsLoss, DRC_UNCONNECTED_SAVE_FIXTURE)
static void runDrcAndCreateMarkers(BOARD *aBoard)
static bool isCourtyardOverlap(PCB_MARKER *aMarker)
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