KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_cam_backdrill.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 3
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/gpl-3.0.html
19 * or you may search the http://www.gnu.org website for the version 3 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
24#include <boost/test/unit_test.hpp>
25
26#include <board.h>
30#include <pcbnew/pcb_track.h>
31#include <base_units.h>
32
33#include <map>
34
35#include <core/utf8.h>
36
37#include <wx/dir.h>
38#include <wx/ffile.h>
39#include <wx/filename.h>
40#include <wx/utils.h>
41
42
43namespace
44{
45wxFileName MakeTempDir()
46{
47 wxFileName tempDir( wxFileName::GetTempDir(), wxEmptyString );
48 tempDir.AppendDir( wxString::Format( "kicad-backdrill-%llu",
49 static_cast<unsigned long long>( wxGetUTCTime() ) ) );
50 BOOST_REQUIRE( tempDir.Mkdir( wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL ) );
51
52 return tempDir;
53}
54} // anonymous namespace
55
56
57BOOST_AUTO_TEST_CASE( BackdrillCamOutputs )
58{
59 wxFileName tempDir = MakeTempDir();
60 wxFileName boardFile( tempDir.GetFullPath(), wxT( "backdrill_board.kicad_pcb" ) );
61
62 BOARD board;
63 board.SetCopperLayerCount( 6 );
64 board.SetFileName( boardFile.GetFullPath() );
65
66 auto via = new PCB_VIA( &board );
67 via->SetPosition( VECTOR2I( 0, 0 ) );
68 via->SetLayerPair( F_Cu, B_Cu );
69 via->SetDrill( pcbIUScale.mmToIU( 0.30 ) );
70 via->SetWidth( pcbIUScale.mmToIU( 0.60 ) );
71 via->SetSecondaryDrillSize( pcbIUScale.mmToIU( 0.20 ) );
72 via->SetSecondaryDrillStartLayer( F_Cu );
73 via->SetSecondaryDrillEndLayer( In3_Cu );
74 via->SetFrontPostMachiningMode( PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK );
75 via->SetFrontPostMachiningSize( pcbIUScale.mmToIU( 0.60 ) );
76 via->SetFrontPostMachiningDepth( pcbIUScale.mmToIU( 0.15 ) );
77 via->SetFrontPostMachiningAngle( 900 );
78 board.Add( via );
79
80 EXCELLON_WRITER excellon( &board );
81 excellon.SetOptions( false, false, VECTOR2I( 0, 0 ), false );
82 excellon.SetFormat( true );
83 BOOST_REQUIRE( excellon.CreateDrillandMapFilesSet( tempDir.GetFullPath(), true, false, nullptr ) );
84
85 wxFileName excellonFile( tempDir.GetFullPath(), wxT( "backdrill_board_Backdrills_Drill_1_4.drl" ) );
86 BOOST_REQUIRE( excellonFile.FileExists() );
87
88 wxFFile excellonStream( excellonFile.GetFullPath(), wxT( "rb" ) );
89 wxString excellonContents;
90 BOOST_REQUIRE( excellonStream.ReadAll( &excellonContents ) );
91 BOOST_CHECK( excellonContents.Contains( wxT( "TF.FileFunction,NonPlated,1,4,Blind" ) ) );
92 BOOST_CHECK( excellonContents.Contains( wxT( "; Backdrill" ) ) );
93 BOOST_CHECK( excellonContents.Contains( wxT( "post-machining" ) ) );
94
95 wxFileName layerPairFile( tempDir.GetFullPath(), wxT( "backdrill_board-front-in3-backdrill.drl" ) );
96 BOOST_REQUIRE( layerPairFile.FileExists() );
97
98 wxFFile layerPairStream( layerPairFile.GetFullPath(), wxT( "rb" ) );
99 wxString layerPairContents;
100 BOOST_REQUIRE( layerPairStream.ReadAll( &layerPairContents ) );
101 BOOST_CHECK( layerPairContents.Contains( wxT( "; backdrill" ) ) );
102
103 wxFileName pthFile( tempDir.GetFullPath(), wxT( "backdrill_board-PTH.drl" ) );
104 BOOST_REQUIRE( pthFile.FileExists() );
105
106 wxFFile pthStream( pthFile.GetFullPath(), wxT( "rb" ) );
107 wxString pthContents;
108 BOOST_REQUIRE( pthStream.ReadAll( &pthContents ) );
109 BOOST_CHECK( pthContents.Contains( wxT( "; Post-machining front countersink dia 0.600mm depth 0.150mm angle 90deg" ) ) );
110
111 GERBER_WRITER gerber( &board );
112 gerber.SetOptions( VECTOR2I( 0, 0 ) );
113 gerber.SetFormat( 6 );
114 BOOST_REQUIRE( gerber.CreateDrillandMapFilesSet( tempDir.GetFullPath(), true, false, false, nullptr ) );
115
116 wxFileName gerberFile( tempDir.GetFullPath(), wxT( "backdrill_board_Backdrills_Drill_1_4-drl.gbr" ) );
117 BOOST_REQUIRE( gerberFile.FileExists() );
118
119 wxFFile gerberStream( gerberFile.GetFullPath(), wxT( "rb" ) );
120 wxString gerberContents;
121 BOOST_REQUIRE( gerberStream.ReadAll( &gerberContents ) );
122 BOOST_CHECK( gerberContents.Contains( wxT( "%TA.AperFunction,BackDrill*%" ) ) );
123 BOOST_CHECK( gerberContents.Contains( wxT( "%TF.FileFunction,NonPlated,1,4,Blind,Drill*%" ) ) );
124
125 wxFileName gerberLayerPairFile( tempDir.GetFullPath(),
126 wxT( "backdrill_board-front-in3-backdrill-drl.gbr" ) );
127 BOOST_REQUIRE( gerberLayerPairFile.FileExists() );
128
129 wxFFile gerberLayerPairStream( gerberLayerPairFile.GetFullPath(), wxT( "rb" ) );
130 wxString gerberLayerPairContents;
131 BOOST_REQUIRE( gerberLayerPairStream.ReadAll( &gerberLayerPairContents ) );
132 BOOST_CHECK( gerberLayerPairContents.Contains( wxT( "%TF.FileFunction,NonPlated,1,4,Blind,Drill*%" ) ) );
133 BOOST_CHECK( gerberLayerPairContents.Contains( wxT( "%TA.AperFunction,BackDrill*%" ) ) );
134
135 wxFileName odbRoot( tempDir.GetFullPath(), wxEmptyString );
136 odbRoot.AppendDir( wxT( "odb_out" ) );
137 BOOST_REQUIRE( odbRoot.Mkdir( wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL ) );
138
139 PCB_IO_ODBPP odbExporter;
140 std::map<std::string, UTF8> props;
141 props["units"] = "mm";
142 props["sigfig"] = "4";
143 BOOST_REQUIRE_NO_THROW( odbExporter.SaveBoard( odbRoot.GetFullPath(), &board, &props ) );
144
145 wxFileName drill1Dir( odbRoot.GetFullPath(), wxEmptyString );
146 drill1Dir.AppendDir( wxT( "steps" ) );
147 drill1Dir.AppendDir( wxT( "pcb" ) );
148 drill1Dir.AppendDir( wxT( "layers" ) );
149 drill1Dir.AppendDir( wxT( "drill1" ) );
150 BOOST_REQUIRE( drill1Dir.DirExists() );
151
152 wxFileName toolsFile( drill1Dir.GetFullPath(), wxT( "tools" ) );
153 BOOST_REQUIRE( toolsFile.FileExists() );
154
155 wxFFile toolsStream( toolsFile.GetFullPath(), wxT( "rb" ) );
156 wxString toolsContents;
157 BOOST_REQUIRE( toolsStream.ReadAll( &toolsContents ) );
158 BOOST_CHECK( toolsContents.Contains( wxT( "TYPE=NON_PLATED" ) ) );
159 BOOST_CHECK( toolsContents.Contains( wxT( "TYPE2=BLIND" ) ) );
160
161 wxFileName matrixFile( odbRoot.GetFullPath(), wxEmptyString );
162 matrixFile.AppendDir( wxT( "matrix" ) );
163 matrixFile.SetFullName( wxT( "matrix" ) );
164 BOOST_REQUIRE( matrixFile.FileExists() );
165
166 wxFFile matrixStream( matrixFile.GetFullPath(), wxT( "rb" ) );
167 wxString matrixContents;
168 BOOST_REQUIRE( matrixStream.ReadAll( &matrixContents ) );
169 BOOST_CHECK( matrixContents.Contains( wxT( "ADD_TYPE=BACKDRILL" ) ) );
170
171 matrixStream.Close();
172 toolsStream.Close();
173 gerberStream.Close();
174 gerberLayerPairStream.Close();
175 excellonStream.Close();
176 layerPairStream.Close();
177 pthStream.Close();
178
179 wxFileName::Rmdir( odbRoot.GetFullPath(), wxPATH_RMDIR_RECURSIVE );
180 wxFileName::Rmdir( tempDir.GetFullPath(), wxPATH_RMDIR_RECURSIVE );
181}
182
183
184// Regression test for https://gitlab.com/kicad/code/kicad/-/issues/23451
185// GERBER_WRITER::SetFormat() precision was not passed to the plotter; output always used 4.6.
186BOOST_AUTO_TEST_CASE( GerberDrillPrecision )
187{
188 wxFileName tempDir = MakeTempDir();
189 wxFileName boardFile( tempDir.GetFullPath(), wxT( "precision_board.kicad_pcb" ) );
190
191 BOARD board;
192 board.SetCopperLayerCount( 2 );
193 board.SetFileName( boardFile.GetFullPath() );
194
195 auto via = new PCB_VIA( &board );
196 via->SetPosition( VECTOR2I( 0, 0 ) );
197 via->SetLayerPair( F_Cu, B_Cu );
198 via->SetDrill( pcbIUScale.mmToIU( 0.30 ) );
199 via->SetWidth( pcbIUScale.mmToIU( 0.60 ) );
200 board.Add( via );
201
202 // Verify precision 5 produces "Fmt 4.5" in the file header
203 GERBER_WRITER gerber5( &board );
204 gerber5.SetOptions( VECTOR2I( 0, 0 ) );
205 gerber5.SetFormat( 5 );
206 BOOST_REQUIRE( gerber5.CreateDrillandMapFilesSet( tempDir.GetFullPath(), true, false, false, nullptr ) );
207
208 wxFileName gerberFile5( tempDir.GetFullPath(), wxT( "precision_board-PTH-drl.gbr" ) );
209 BOOST_REQUIRE( gerberFile5.FileExists() );
210
211 wxFFile gerberStream5( gerberFile5.GetFullPath(), wxT( "rb" ) );
212 wxString gerberContents5;
213 BOOST_REQUIRE( gerberStream5.ReadAll( &gerberContents5 ) );
214 BOOST_CHECK_MESSAGE( gerberContents5.Contains( wxT( "Fmt 4.5" ) ),
215 "Expected 'Fmt 4.5' in gerber header with precision=5" );
216 BOOST_CHECK( !gerberContents5.Contains( wxT( "Fmt 4.6" ) ) );
217 gerberStream5.Close();
218
219 // Verify precision 6 produces "Fmt 4.6" in the file header
220 GERBER_WRITER gerber6( &board );
221 gerber6.SetOptions( VECTOR2I( 0, 0 ) );
222 gerber6.SetFormat( 6 );
223 BOOST_REQUIRE( gerber6.CreateDrillandMapFilesSet( tempDir.GetFullPath(), true, false, false, nullptr ) );
224
225 wxFFile gerberStream6( gerberFile5.GetFullPath(), wxT( "rb" ) );
226 wxString gerberContents6;
227 BOOST_REQUIRE( gerberStream6.ReadAll( &gerberContents6 ) );
228 BOOST_CHECK_MESSAGE( gerberContents6.Contains( wxT( "Fmt 4.6" ) ),
229 "Expected 'Fmt 4.6' in gerber header with precision=6" );
230 BOOST_CHECK( !gerberContents6.Contains( wxT( "Fmt 4.5" ) ) );
231 gerberStream6.Close();
232
233 wxFileName::Rmdir( tempDir.GetFullPath(), wxPATH_RMDIR_RECURSIVE );
234}
235
236
237// Regression test for https://gitlab.com/kicad/code/kicad/-/issues/23005
238// GenDrillReportFile crashed when aReporter was null (the default)
239BOOST_AUTO_TEST_CASE( DrillReportNullReporter )
240{
241 wxFileName tempDir = MakeTempDir();
242 wxFileName boardFile( tempDir.GetFullPath(), wxT( "test_board.kicad_pcb" ) );
243
244 BOARD board;
245 board.SetCopperLayerCount( 2 );
246 board.SetFileName( boardFile.GetFullPath() );
247
248 wxFileName reportFile( tempDir.GetFullPath(), wxT( "test_board-drl.rpt" ) );
249
250 // Valid path with null reporter should succeed without crashing
251 EXCELLON_WRITER excellon( &board );
252 BOOST_CHECK( excellon.GenDrillReportFile( reportFile.GetFullPath() ) );
253 BOOST_CHECK( reportFile.FileExists() );
254
255 GERBER_WRITER gerber( &board );
256 BOOST_CHECK( gerber.GenDrillReportFile( reportFile.GetFullPath() ) );
257
258 // Invalid path with null reporter should return false without crashing
259 EXCELLON_WRITER excellon2( &board );
260 BOOST_CHECK( !excellon2.GenDrillReportFile( wxT( "/nonexistent/path/report.rpt" ) ) );
261
262 GERBER_WRITER gerber2( &board );
263 BOOST_CHECK( !gerber2.GenDrillReportFile( wxT( "/nonexistent/path/report.rpt" ) ) );
264
265 wxFileName::Rmdir( tempDir.GetFullPath(), wxPATH_RMDIR_RECURSIVE );
266}
267
268
269// Regression test for https://gitlab.com/kicad/code/kicad/-/issues/23289
270// GenDrillReportFile crashed with SIGSEGV when the board had drills because
271// printToolSummary() passed integer literal 0 instead of the FILE* to fmt::print()
272BOOST_AUTO_TEST_CASE( DrillReportWithTools )
273{
274 wxFileName tempDir = MakeTempDir();
275 wxFileName boardFile( tempDir.GetFullPath(), wxT( "test_board_with_drills.kicad_pcb" ) );
276
277 BOARD board;
278 board.SetCopperLayerCount( 2 );
279 board.SetFileName( boardFile.GetFullPath() );
280
281 auto via = new PCB_VIA( &board );
282 via->SetPosition( VECTOR2I( 0, 0 ) );
283 via->SetLayerPair( F_Cu, B_Cu );
284 via->SetDrill( pcbIUScale.mmToIU( 0.30 ) );
285 via->SetWidth( pcbIUScale.mmToIU( 0.60 ) );
286 board.Add( via );
287
288 wxFileName reportFile( tempDir.GetFullPath(), wxT( "test_board_with_drills-drl.rpt" ) );
289
290 EXCELLON_WRITER excellon( &board );
291 excellon.SetOptions( false, false, VECTOR2I( 0, 0 ), false );
292 excellon.SetFormat( true );
293 BOOST_CHECK_NO_THROW( excellon.GenDrillReportFile( reportFile.GetFullPath() ) );
294 BOOST_CHECK( reportFile.FileExists() );
295
296 wxFFile reportStream( reportFile.GetFullPath(), wxT( "rb" ) );
297 wxString reportContents;
298 BOOST_REQUIRE( reportStream.ReadAll( &reportContents ) );
299 BOOST_CHECK( reportContents.Contains( wxT( "T1" ) ) );
300 BOOST_CHECK( reportContents.Contains( wxT( "0.300mm" ) ) );
301 reportStream.Close();
302
303 wxFileName::Rmdir( tempDir.GetFullPath(), wxPATH_RMDIR_RECURSIVE );
304}
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:112
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:323
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT, bool aSkipConnectivity=false) override
Removes an item from the container.
Definition board.cpp:1237
void SetFileName(const wxString &aFileName)
Definition board.h:358
void SetCopperLayerCount(int aCount)
Definition board.cpp:934
Create Excellon drill, drill map, and drill report files.
void SetFormat(bool aMetric, ZEROS_FMT aZerosFmt=DECIMAL_FORMAT, int aLeftDigits=0, int aRightDigits=0)
Initialize internal parameters to match the given format.
bool CreateDrillandMapFilesSet(const wxString &aPlotDirectory, bool aGenDrill, bool aGenMap, REPORTER *aReporter=nullptr)
Create the full set of Excellon drill file for the board.
void SetOptions(bool aMirror, bool aMinimalHeader, const VECTOR2I &aOffset, bool aMerge_PTH_NPTH)
Initialize internal parameters to match drill options.
bool GenDrillReportFile(const wxString &aFullFileName, REPORTER *aReporter=nullptr)
Create a plain text report file giving a list of drill values and drill count for through holes,...
Used to create Gerber drill files.
bool CreateDrillandMapFilesSet(const wxString &aPlotDirectory, bool aGenDrill, bool aGenMap, bool aGenTenting, REPORTER *aReporter=nullptr)
Create the full set of Excellon drill file for the board filenames are computed from the board name,...
void SetOptions(const VECTOR2I &aOffset)
Initialize internal parameters to match drill options.
void SetFormat(int aRightDigits=6)
Initialize internal parameters to match the given format.
void SaveBoard(const wxString &aFileName, BOARD *aBoard, const std::map< std::string, UTF8 > *aProperties=nullptr) override
Write aBoard to a storage file in a format that this PCB_IO implementation knows about or it can be u...
Classes used in drill files, map files and report files generation.
Classes used in drill files, map files and report files generation.
@ B_Cu
Definition layer_ids.h:65
@ In3_Cu
Definition layer_ids.h:68
@ F_Cu
Definition layer_ids.h:64
BOOST_AUTO_TEST_CASE(BackdrillCamOutputs)
BOOST_REQUIRE(intersection.has_value()==c.ExpectedIntersection.has_value())
BOOST_CHECK_MESSAGE(totalMismatches==0, std::to_string(totalMismatches)+" board(s) with strategy disagreements")
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:687