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 <pcb_track.h>
43#include <project.h>
47#include <tool/tool_manager.h>
48#include <wx/string.h>
49
51{
52 std::vector<wxString> m_files_to_delete;
53
54 FileCleaner() = default;
56 {
57 for( const auto& f_path : m_files_to_delete )
58 {
59 if( wxFileName::Exists( f_path ) )
60 {
61 if( !wxRemoveFile( f_path ) )
62 {
63 BOOST_TEST_MESSAGE( "Warning: Failed to delete temporary file " << f_path );
64 }
65 }
66 }
67 }
68
69 void AddFile( const wxString& f_path ) { m_files_to_delete.push_back( f_path ); }
70};
71
73{
75 {
76 }
77
78 std::string generate_uuid();
79 bool SaveBoardToFile( BOARD* board, const wxString& filename );
80 void loadBoardAndVerifyInitialExclusions( const wxString& aBoardNameStem, int aExpectedInitialExclusions );
82 int createAndVerifyAdditionalUnconnectedExclusions( int aAdditionalExclusions, int aInitialExclusions );
83 void runDrcOnBoard();
84 void saveBoardAndProjectToTempFiles( const wxString& aBoardNameStem, FileCleaner& aCleaner,
85 wxString& aTempBoardFullPath, wxString& aTempProjectFullPath,
86 wxString& aTempBoardStemName );
87 void reloadBoardAndVerifyExclusions( const wxString& aTempBoardStemName, int aExpectedExclusions );
88
89
91 std::unique_ptr<BOARD> m_board;
92};
93
95{
100};
101
102
104{
107 {
108 m_board = std::make_unique<BOARD>();
109 }
110};
111
113{
114 boost::uuids::uuid uuid = boost::uuids::random_generator()();
115 return boost::uuids::to_string( uuid );
116}
117
118void DRC_BASE_FIXTURE::loadBoardAndVerifyInitialExclusions( const wxString& aBoardNameStem,
119 int aExpectedInitialExclusions )
120{
121 KI_TEST::LoadBoard( m_settingsManager, aBoardNameStem, m_board );
122 BOOST_REQUIRE_MESSAGE( m_board,
123 "Could not load board " + aBoardNameStem ); // Ensure board loaded from test data directory
124 PROJECT* pcb_project = m_board->GetProject();
125 BOOST_REQUIRE_MESSAGE( pcb_project, "Get project pointer after initial loading." );
126
127 // Board test file comes with initial exclusions, check if they are preserved after loading
128 const BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
129 size_t initialExclusionsCount = bds.m_DrcExclusions.size();
130 size_t initialExclusionsCommentsCount = bds.m_DrcExclusionComments.size();
131 BOOST_TEST_MESSAGE( "Initial DRC exclusions count: " << initialExclusionsCount );
132 BOOST_CHECK_EQUAL( initialExclusionsCount, (size_t) aExpectedInitialExclusions );
133 BOOST_TEST_MESSAGE( "Initial DRC exclusion comments count: " << initialExclusionsCommentsCount );
134 BOOST_CHECK_EQUAL( initialExclusionsCommentsCount, (size_t) aExpectedInitialExclusions );
135}
136
138{
139 const BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
140 std::vector<PCB_MARKER*> markers;
141 for( const wxString exclusion : bds.m_DrcExclusions )
142 {
143 PCB_MARKER* marker = PCB_MARKER::DeserializeFromString( exclusion );
144 if( marker )
145 {
146 wxString comment = bds.m_DrcExclusionComments.at( exclusion );
147 marker->SetExcluded( true, comment );
148 markers.push_back( marker );
149 m_board->Add( marker );
150 }
151 }
152 size_t actualExclusionsCount = bds.m_DrcExclusions.size();
153 size_t initialExclusionsCount = markers.size();
154 BOOST_CHECK_EQUAL( actualExclusionsCount, initialExclusionsCount );
155 BOOST_TEST_MESSAGE( std::string( "Actual DRC exclusions count: " ) + std::to_string( actualExclusionsCount )
156 + " after adding initial markers." );
157}
158
160 int aInitialExclusions )
161{
162 for( int i = 0; i < aAdditionalExclusions; ++i )
163 {
164 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_UNCONNECTED_ITEMS );
165 wxString id1 = wxString( generate_uuid() );
166 wxString id2 = wxString( generate_uuid() );
167 drcItem->SetItems( KIID( id1 ), KIID( id2 ) );
168
169 PCB_MARKER* marker = new PCB_MARKER( drcItem, VECTOR2I( 1000 * i, 1000 * i ) );
170 m_board->Add( marker );
171
172 // Exclude odd-numbered markers
173 if( i % 2 == 1 )
174 {
175 marker->SetExcluded( true, wxString::Format( "Exclusion %d", i ) );
176 }
177 }
178
179 // Store the new exclusion markers in the board
180 m_board->RecordDRCExclusions();
181
182 // Verify the number of exclusions after adding unconnected items
183 const BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
184 const int expectedExclusions =
185 aInitialExclusions + aAdditionalExclusions / 2; // Only odd-numbered markers are excluded
186 size_t newActualExclusionsCount = bds.m_DrcExclusions.size();
187 BOOST_TEST_MESSAGE( std::string( "New actual DRC exclusions count: " ) + std::to_string( newActualExclusionsCount )
188 + " after adding unconnected items." );
189 BOOST_CHECK_EQUAL( newActualExclusionsCount, (size_t) expectedExclusions );
190 return expectedExclusions;
191}
192
194{
195 BOOST_TEST_MESSAGE( "Running DRC on board." );
196 BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
197 bds.m_DRCEngine->InitEngine( wxFileName() );
198 m_board->RecordDRCExclusions();
199 bool runDRC = true;
200 bool runDRCOnAllLayers = true;
201 bds.m_DRCEngine->RunTests( EDA_UNITS::MM, runDRC, runDRCOnAllLayers );
202 m_board->ResolveDRCExclusions( false );
203 BOOST_TEST_MESSAGE( "DRC done." );
204}
205
206void DRC_BASE_FIXTURE::saveBoardAndProjectToTempFiles( const wxString& aBoardNameStem, FileCleaner& aCleaner,
207 wxString& aTempBoardFullPath, wxString& aTempProjectFullPath,
208 wxString& aTempBoardStemName )
209{
210 wxString tempPrefix = "tmp_test_drc_";
211 aTempBoardStemName = tempPrefix + aBoardNameStem.ToStdString();
212 aTempBoardFullPath = KI_TEST::GetPcbnewTestDataDir() + aTempBoardStemName + ".kicad_pcb";
213 aCleaner.AddFile( aTempBoardFullPath );
214 wxString tempProjectStemName = tempPrefix + aBoardNameStem.ToStdString();
215 aTempProjectFullPath = KI_TEST::GetPcbnewTestDataDir() + aTempBoardStemName + ".kicad_pro";
216 aCleaner.AddFile( aTempProjectFullPath );
217
218 bool boardSaved = SaveBoardToFile( m_board->GetBoard(), aTempBoardFullPath );
219 BOOST_REQUIRE_MESSAGE( boardSaved, "Save board to temporary file: " << aTempBoardFullPath );
220
221 m_settingsManager.SaveProjectAs( aTempProjectFullPath, m_board->GetProject() );
222 BOOST_REQUIRE_MESSAGE( wxFileName::Exists( aTempProjectFullPath ),
223 "Save project to temporary file: " << aTempProjectFullPath );
224}
225
226void DRC_BASE_FIXTURE::reloadBoardAndVerifyExclusions( const wxString& aTempBoardStemName, int aExpectedExclusions )
227{
228 // clear the current board to ensure a fresh load
229 m_board.reset();
230
231 KI_TEST::LoadBoard( m_settingsManager, aTempBoardStemName, m_board );
232 BOOST_REQUIRE_MESSAGE( m_board, "Could not load board from tempfile:"
233 + aTempBoardStemName ); // Ensure board loaded from test data directory
234 PROJECT* pcb_project = m_board->GetProject();
235 BOOST_REQUIRE_MESSAGE( pcb_project, "Get project pointer after initial loading." );
236
237 BOARD_DESIGN_SETTINGS& reloaded_bds = m_board->GetDesignSettings();
238 size_t reloadedExclusionsCount = reloaded_bds.m_DrcExclusions.size();
239 BOOST_TEST_MESSAGE( "Reloaded DRC exclusions count: " << reloadedExclusionsCount );
240 BOOST_CHECK_EQUAL( reloadedExclusionsCount, aExpectedExclusions );
241}
242
243bool DRC_BASE_FIXTURE::SaveBoardToFile( BOARD* board, const wxString& filename )
244{
245 try
246 {
248 pi->SaveBoard( filename, board, nullptr );
249 return true;
250 }
251 catch( const IO_ERROR& error )
252 {
253 BOOST_TEST_MESSAGE( wxString::Format( "Save board to %s: %s", filename, error.What() ) );
254 return false;
255 }
256}
258{
259 // Test that unconnected item exclusions are not lost after multiple DRC runs.
260 // This test is expected to fail if the bug (issue17429) is present.
261
262 std::vector<std::pair<wxString, int>> tests = {
263 { "issue17429", 10 }, // board name stem, expected initial exclusions
264 };
265
266 const int NUM_DRC_RUNS = 2;
267
268 for( const std::pair<wxString, int>& test_params : tests )
269 {
270 wxString boardNameStem = test_params.first;
271 int expectedInitialExclusions = test_params.second;
272
273 loadBoardAndVerifyInitialExclusions( boardNameStem, expectedInitialExclusions );
274 createAndVerifyInitialExclusionMarkers();
275 const int additionalExclusions = 5;
276 int expectedExclusions =
277 createAndVerifyAdditionalUnconnectedExclusions( additionalExclusions, expectedInitialExclusions );
278
279 runDrcOnBoard();
280
281 const BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
282 BOOST_TEST_MESSAGE( std::string( "DRC exclusions count after DRC run: " ) + std::to_string( expectedExclusions )
283 + " after adding unconnected items." );
284 BOOST_CHECK_EQUAL( bds.m_DrcExclusions.size(), expectedExclusions );
285 }
286}
287
288BOOST_FIXTURE_TEST_CASE( DRCUnconnectedItemsExclusionsSaveLoad, DRC_REGRESSION_TEST_FIXTURE )
289{
290 namespace fs = std::filesystem;
291
292 // Test that unconnected item exclusions are not lost during save/load.
293 // This test is expected to fail if the bug (issue17429) is present.
294
295 std::vector<std::pair<wxString, int>> tests = {
296 { "issue17429", 10 }, // board name stem, expected initial exclusions
297 };
298
299
300 for( const std::pair<wxString, int>& test_params : tests )
301 {
302 FileCleaner tempFileCleaner;
303 wxString boardNameStem = test_params.first;
304 int expectedInitialExclusions = test_params.second;
305
306 loadBoardAndVerifyInitialExclusions( boardNameStem, expectedInitialExclusions );
307
308 wxString tempBoardFullPath, tempProjectFullPath, tempBoardStemName;
309 saveBoardAndProjectToTempFiles( boardNameStem, tempFileCleaner, tempBoardFullPath, tempProjectFullPath,
310 tempBoardStemName );
311
312 createAndVerifyInitialExclusionMarkers();
313
314 const int additionalExclusions = 5;
315 int expectedExclusions =
316 createAndVerifyAdditionalUnconnectedExclusions( additionalExclusions, expectedInitialExclusions );
317
318 bool boardSaved = SaveBoardToFile( m_board->GetBoard(), tempBoardFullPath );
319 BOOST_REQUIRE_MESSAGE( boardSaved, "Save board to temporary file: " << tempBoardFullPath );
320
321 m_settingsManager.SaveProjectAs( tempProjectFullPath, m_board->GetProject() );
322 BOOST_REQUIRE_MESSAGE( wxFileName::Exists( tempProjectFullPath ),
323 "Save project to temporary file: " << tempProjectFullPath );
324
325 reloadBoardAndVerifyExclusions( tempBoardStemName, expectedExclusions );
326 }
327}
General utilities for PCB file IO for QA programs.
Container for design settings for a BOARD object.
std::map< wxString, wxString > m_DrcExclusionComments
std::shared_ptr< DRC_ENGINE > m_DRCEngine
std::set< wxString > m_DrcExclusions
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:372
void RunTests(EDA_UNITS aUnits, bool aReportAllTrackErrors, bool aTestFootprints, BOARD_COMMIT *aCommit=nullptr)
Run the DRC tests.
void InitEngine(const wxFileName &aRulePath)
Initialize the DRC engine.
static std::shared_ptr< DRC_ITEM > Create(int aErrorCode)
Constructs a DRC_ITEM for the given error code.
Definition drc_item.cpp:417
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:44
void SetExcluded(bool aExcluded, const wxString &aComment=wxEmptyString)
Definition marker_base.h:90
@ KICAD_SEXP
S-expression Pcbnew file format.
Definition pcb_io_mgr.h:54
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 * DeserializeFromString(const wxString &data)
Container for project specific data.
Definition project.h:62
@ DRCE_UNCONNECTED_ITEMS
Definition drc_item.h:36
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 saveBoardAndProjectToTempFiles(const wxString &aBoardNameStem, FileCleaner &aCleaner, wxString &aTempBoardFullPath, wxString &aTempProjectFullPath, wxString &aTempBoardStemName)
int createAndVerifyAdditionalUnconnectedExclusions(int aAdditionalExclusions, int aInitialExclusions)
void reloadBoardAndVerifyExclusions(const wxString &aTempBoardStemName, int aExpectedExclusions)
void loadBoardAndVerifyInitialExclusions(const wxString &aBoardNameStem, int aExpectedInitialExclusions)
bool SaveBoardToFile(BOARD *board, const wxString &filename)
void AddFile(const wxString &f_path)
std::vector< wxString > m_files_to_delete
FileCleaner()=default
BOOST_FIXTURE_TEST_CASE(DRCUnconnectedExclusionsLoss, DRC_UNCONNECTED_SAVE_FIXTURE)
BOOST_TEST_MESSAGE("\n=== Real-World Polygon PIP Benchmark ===\n"<< formatTable(table))
BOOST_CHECK_EQUAL(result, "25.4")
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683