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>
27#include <pcb_shape.h>
31#include <pcbnew/pcb_track.h>
32#include <base_units.h>
33
34#include <map>
35
36#include <core/utf8.h>
37
38#include <wx/dir.h>
39#include <wx/ffile.h>
40#include <wx/filename.h>
41#include <wx/tokenzr.h>
42#include <wx/utils.h>
43
44
45namespace
46{
47wxFileName MakeTempDir()
48{
49 wxFileName tempDir( wxFileName::GetTempDir(), wxEmptyString );
50 tempDir.AppendDir( wxString::Format( "kicad-backdrill-%llu",
51 static_cast<unsigned long long>( wxGetUTCTime() ) ) );
52 BOOST_REQUIRE( tempDir.Mkdir( wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL ) );
53
54 return tempDir;
55}
56} // anonymous namespace
57
58
59BOOST_AUTO_TEST_CASE( BackdrillCamOutputs )
60{
61 wxFileName tempDir = MakeTempDir();
62 wxFileName boardFile( tempDir.GetFullPath(), wxT( "backdrill_board.kicad_pcb" ) );
63
64 BOARD board;
65 board.SetCopperLayerCount( 6 );
66 board.SetFileName( boardFile.GetFullPath() );
67
68 auto via = new PCB_VIA( &board );
69 via->SetPosition( VECTOR2I( 0, 0 ) );
70 via->SetLayerPair( F_Cu, B_Cu );
71 via->SetDrill( pcbIUScale.mmToIU( 0.30 ) );
72 via->SetWidth( pcbIUScale.mmToIU( 0.60 ) );
73 via->SetSecondaryDrillSize( pcbIUScale.mmToIU( 0.20 ) );
74 via->SetSecondaryDrillStartLayer( F_Cu );
75 via->SetSecondaryDrillEndLayer( In3_Cu );
76 via->SetFrontPostMachiningMode( PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK );
77 via->SetFrontPostMachiningSize( pcbIUScale.mmToIU( 0.60 ) );
78 via->SetFrontPostMachiningDepth( pcbIUScale.mmToIU( 0.15 ) );
79 via->SetFrontPostMachiningAngle( 900 );
80 board.Add( via );
81
82 EXCELLON_WRITER excellon( &board );
83 excellon.SetOptions( false, false, VECTOR2I( 0, 0 ), false );
84 excellon.SetFormat( true );
85 BOOST_REQUIRE( excellon.CreateDrillandMapFilesSet( tempDir.GetFullPath(), true, false, nullptr ) );
86
87 wxFileName excellonFile( tempDir.GetFullPath(), wxT( "backdrill_board_Backdrills_Drill_1_4.drl" ) );
88 BOOST_REQUIRE( excellonFile.FileExists() );
89
90 wxFFile excellonStream( excellonFile.GetFullPath(), wxT( "rb" ) );
91 wxString excellonContents;
92 BOOST_REQUIRE( excellonStream.ReadAll( &excellonContents ) );
93 BOOST_CHECK( excellonContents.Contains( wxT( "TF.FileFunction,NonPlated,1,4,Blind" ) ) );
94 BOOST_CHECK( excellonContents.Contains( wxT( "; Backdrill" ) ) );
95 BOOST_CHECK( excellonContents.Contains( wxT( "post-machining" ) ) );
96
97 wxFileName layerPairFile( tempDir.GetFullPath(), wxT( "backdrill_board-front-in3-backdrill.drl" ) );
98 BOOST_REQUIRE( layerPairFile.FileExists() );
99
100 wxFFile layerPairStream( layerPairFile.GetFullPath(), wxT( "rb" ) );
101 wxString layerPairContents;
102 BOOST_REQUIRE( layerPairStream.ReadAll( &layerPairContents ) );
103 BOOST_CHECK( layerPairContents.Contains( wxT( "; backdrill" ) ) );
104
105 wxFileName pthFile( tempDir.GetFullPath(), wxT( "backdrill_board-PTH.drl" ) );
106 BOOST_REQUIRE( pthFile.FileExists() );
107
108 wxFFile pthStream( pthFile.GetFullPath(), wxT( "rb" ) );
109 wxString pthContents;
110 BOOST_REQUIRE( pthStream.ReadAll( &pthContents ) );
111 BOOST_CHECK( pthContents.Contains( wxT( "; Post-machining front countersink dia 0.600mm depth 0.150mm angle 90deg" ) ) );
112
113 GERBER_WRITER gerber( &board );
114 gerber.SetOptions( VECTOR2I( 0, 0 ) );
115 gerber.SetFormat( 6 );
116 BOOST_REQUIRE( gerber.CreateDrillandMapFilesSet( tempDir.GetFullPath(), true, false, false, nullptr ) );
117
118 wxFileName gerberFile( tempDir.GetFullPath(), wxT( "backdrill_board_Backdrills_Drill_1_4-drl.gbr" ) );
119 BOOST_REQUIRE( gerberFile.FileExists() );
120
121 wxFFile gerberStream( gerberFile.GetFullPath(), wxT( "rb" ) );
122 wxString gerberContents;
123 BOOST_REQUIRE( gerberStream.ReadAll( &gerberContents ) );
124 BOOST_CHECK( gerberContents.Contains( wxT( "%TA.AperFunction,BackDrill*%" ) ) );
125 BOOST_CHECK( gerberContents.Contains( wxT( "%TF.FileFunction,NonPlated,1,4,Blind,Drill*%" ) ) );
126
127 wxFileName gerberLayerPairFile( tempDir.GetFullPath(),
128 wxT( "backdrill_board-front-in3-backdrill-drl.gbr" ) );
129 BOOST_REQUIRE( gerberLayerPairFile.FileExists() );
130
131 wxFFile gerberLayerPairStream( gerberLayerPairFile.GetFullPath(), wxT( "rb" ) );
132 wxString gerberLayerPairContents;
133 BOOST_REQUIRE( gerberLayerPairStream.ReadAll( &gerberLayerPairContents ) );
134 BOOST_CHECK( gerberLayerPairContents.Contains( wxT( "%TF.FileFunction,NonPlated,1,4,Blind,Drill*%" ) ) );
135 BOOST_CHECK( gerberLayerPairContents.Contains( wxT( "%TA.AperFunction,BackDrill*%" ) ) );
136
137 wxFileName odbRoot( tempDir.GetFullPath(), wxEmptyString );
138 odbRoot.AppendDir( wxT( "odb_out" ) );
139 BOOST_REQUIRE( odbRoot.Mkdir( wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL ) );
140
141 PCB_IO_ODBPP odbExporter;
142 std::map<std::string, UTF8> props;
143 props["units"] = "mm";
144 props["sigfig"] = "4";
145 BOOST_REQUIRE_NO_THROW( odbExporter.SaveBoard( odbRoot.GetFullPath(), &board, &props ) );
146
147 wxFileName drill1Dir( odbRoot.GetFullPath(), wxEmptyString );
148 drill1Dir.AppendDir( wxT( "steps" ) );
149 drill1Dir.AppendDir( wxT( "pcb" ) );
150 drill1Dir.AppendDir( wxT( "layers" ) );
151 drill1Dir.AppendDir( wxT( "drill1" ) );
152 BOOST_REQUIRE( drill1Dir.DirExists() );
153
154 wxFileName toolsFile( drill1Dir.GetFullPath(), wxT( "tools" ) );
155 BOOST_REQUIRE( toolsFile.FileExists() );
156
157 wxFFile toolsStream( toolsFile.GetFullPath(), wxT( "rb" ) );
158 wxString toolsContents;
159 BOOST_REQUIRE( toolsStream.ReadAll( &toolsContents ) );
160 BOOST_CHECK( toolsContents.Contains( wxT( "TYPE=NON_PLATED" ) ) );
161 BOOST_CHECK( toolsContents.Contains( wxT( "TYPE2=BLIND" ) ) );
162
163 wxFileName matrixFile( odbRoot.GetFullPath(), wxEmptyString );
164 matrixFile.AppendDir( wxT( "matrix" ) );
165 matrixFile.SetFullName( wxT( "matrix" ) );
166 BOOST_REQUIRE( matrixFile.FileExists() );
167
168 wxFFile matrixStream( matrixFile.GetFullPath(), wxT( "rb" ) );
169 wxString matrixContents;
170 BOOST_REQUIRE( matrixStream.ReadAll( &matrixContents ) );
171 BOOST_CHECK( matrixContents.Contains( wxT( "ADD_TYPE=BACKDRILL" ) ) );
172
173 matrixStream.Close();
174 toolsStream.Close();
175 gerberStream.Close();
176 gerberLayerPairStream.Close();
177 excellonStream.Close();
178 layerPairStream.Close();
179 pthStream.Close();
180
181 wxFileName::Rmdir( odbRoot.GetFullPath(), wxPATH_RMDIR_RECURSIVE );
182 wxFileName::Rmdir( tempDir.GetFullPath(), wxPATH_RMDIR_RECURSIVE );
183}
184
185
186// Regression test for https://gitlab.com/kicad/code/kicad/-/issues/23914
187// Only front-side (secondary) backdrill holes were exported; back-side (tertiary)
188// backdrill operations never produced a drill file.
189BOOST_AUTO_TEST_CASE( FrontAndBackBackdrillCamOutputs )
190{
191 wxFileName tempDir = MakeTempDir();
192 wxFileName boardFile( tempDir.GetFullPath(), wxT( "backdrill_pair_board.kicad_pcb" ) );
193
194 BOARD board;
195 board.SetCopperLayerCount( 6 );
196 board.SetFileName( boardFile.GetFullPath() );
197
198 auto topVia = new PCB_VIA( &board );
199 topVia->SetPosition( VECTOR2I( 0, 0 ) );
200 topVia->SetLayerPair( F_Cu, B_Cu );
201 topVia->SetDrill( pcbIUScale.mmToIU( 0.30 ) );
202 topVia->SetWidth( pcbIUScale.mmToIU( 0.60 ) );
203 topVia->SetSecondaryDrillSize( pcbIUScale.mmToIU( 0.40 ) );
204 topVia->SetSecondaryDrillStartLayer( F_Cu );
205 topVia->SetSecondaryDrillEndLayer( In1_Cu );
206 board.Add( topVia );
207
208 auto bottomVia = new PCB_VIA( &board );
209 bottomVia->SetPosition( VECTOR2I( pcbIUScale.mmToIU( 5.0 ), 0 ) );
210 bottomVia->SetLayerPair( F_Cu, B_Cu );
211 bottomVia->SetDrill( pcbIUScale.mmToIU( 0.30 ) );
212 bottomVia->SetWidth( pcbIUScale.mmToIU( 0.60 ) );
213 bottomVia->SetTertiaryDrillSize( pcbIUScale.mmToIU( 0.40 ) );
214 bottomVia->SetTertiaryDrillStartLayer( B_Cu );
215 bottomVia->SetTertiaryDrillEndLayer( In3_Cu );
216 board.Add( bottomVia );
217
218 EXCELLON_WRITER excellon( &board );
219 excellon.SetOptions( false, false, VECTOR2I( 0, 0 ), false );
220 excellon.SetFormat( true );
221 BOOST_REQUIRE( excellon.CreateDrillandMapFilesSet( tempDir.GetFullPath(), true, false,
222 nullptr ) );
223
224 wxFileName topBackdrillFile( tempDir.GetFullPath(),
225 wxT( "backdrill_pair_board_Backdrills_Drill_1_2.drl" ) );
226 BOOST_CHECK_MESSAGE( topBackdrillFile.FileExists(),
227 "Front-side backdrill drill file should be produced" );
228
229 // Start=B_Cu (UI index 6) drilled toward In3_Cu (UI index 4) in a 6-layer board
230 wxFileName bottomBackdrillFile( tempDir.GetFullPath(),
231 wxT( "backdrill_pair_board_Backdrills_Drill_6_4.drl" ) );
232 BOOST_CHECK_MESSAGE( bottomBackdrillFile.FileExists(),
233 "Back-side (tertiary) backdrill drill file should be produced" );
234
235 if( bottomBackdrillFile.FileExists() )
236 {
237 wxFFile stream( bottomBackdrillFile.GetFullPath(), wxT( "rb" ) );
238 wxString contents;
239 BOOST_REQUIRE( stream.ReadAll( &contents ) );
240 BOOST_CHECK( contents.Contains( wxT( "; Backdrill" ) ) );
241 stream.Close();
242 }
243
244 wxFileName::Rmdir( tempDir.GetFullPath(), wxPATH_RMDIR_RECURSIVE );
245}
246
247
248// Stronger coverage for https://gitlab.com/kicad/code/kicad/-/issues/23914
249// A single via can carry both a front-side (secondary) and a back-side
250// (tertiary) backdrill. Both drill files must be produced.
251BOOST_AUTO_TEST_CASE( DualBackdrillSameViaCamOutputs )
252{
253 wxFileName tempDir = MakeTempDir();
254 wxFileName boardFile( tempDir.GetFullPath(), wxT( "dual_backdrill_board.kicad_pcb" ) );
255
256 BOARD board;
257 board.SetCopperLayerCount( 6 );
258 board.SetFileName( boardFile.GetFullPath() );
259
260 auto via = new PCB_VIA( &board );
261 via->SetPosition( VECTOR2I( 0, 0 ) );
262 via->SetLayerPair( F_Cu, B_Cu );
263 via->SetDrill( pcbIUScale.mmToIU( 0.30 ) );
264 via->SetWidth( pcbIUScale.mmToIU( 0.60 ) );
265 via->SetSecondaryDrillSize( pcbIUScale.mmToIU( 0.40 ) );
266 via->SetSecondaryDrillStartLayer( F_Cu );
267 via->SetSecondaryDrillEndLayer( In1_Cu );
268 via->SetTertiaryDrillSize( pcbIUScale.mmToIU( 0.40 ) );
269 via->SetTertiaryDrillStartLayer( B_Cu );
270 via->SetTertiaryDrillEndLayer( In3_Cu );
271 board.Add( via );
272
273 EXCELLON_WRITER excellon( &board );
274 excellon.SetOptions( false, false, VECTOR2I( 0, 0 ), false );
275 excellon.SetFormat( true );
276 BOOST_REQUIRE( excellon.CreateDrillandMapFilesSet( tempDir.GetFullPath(), true, false,
277 nullptr ) );
278
279 wxFileName topBackdrillFile( tempDir.GetFullPath(),
280 wxT( "dual_backdrill_board_Backdrills_Drill_1_2.drl" ) );
281 BOOST_CHECK( topBackdrillFile.FileExists() );
282
283 wxFileName bottomBackdrillFile( tempDir.GetFullPath(),
284 wxT( "dual_backdrill_board_Backdrills_Drill_6_4.drl" ) );
285 BOOST_CHECK( bottomBackdrillFile.FileExists() );
286
287 wxFileName::Rmdir( tempDir.GetFullPath(), wxPATH_RMDIR_RECURSIVE );
288}
289
290
291// Regression test for https://gitlab.com/kicad/code/kicad/-/issues/23451
292// GERBER_WRITER::SetFormat() precision was not passed to the plotter; output always used 4.6.
293BOOST_AUTO_TEST_CASE( GerberDrillPrecision )
294{
295 wxFileName tempDir = MakeTempDir();
296 wxFileName boardFile( tempDir.GetFullPath(), wxT( "precision_board.kicad_pcb" ) );
297
298 BOARD board;
299 board.SetCopperLayerCount( 2 );
300 board.SetFileName( boardFile.GetFullPath() );
301
302 auto via = new PCB_VIA( &board );
303 via->SetPosition( VECTOR2I( 0, 0 ) );
304 via->SetLayerPair( F_Cu, B_Cu );
305 via->SetDrill( pcbIUScale.mmToIU( 0.30 ) );
306 via->SetWidth( pcbIUScale.mmToIU( 0.60 ) );
307 board.Add( via );
308
309 // Verify precision 5 produces "Fmt 4.5" in the file header
310 GERBER_WRITER gerber5( &board );
311 gerber5.SetOptions( VECTOR2I( 0, 0 ) );
312 gerber5.SetFormat( 5 );
313 BOOST_REQUIRE( gerber5.CreateDrillandMapFilesSet( tempDir.GetFullPath(), true, false, false, nullptr ) );
314
315 wxFileName gerberFile5( tempDir.GetFullPath(), wxT( "precision_board-PTH-drl.gbr" ) );
316 BOOST_REQUIRE( gerberFile5.FileExists() );
317
318 wxFFile gerberStream5( gerberFile5.GetFullPath(), wxT( "rb" ) );
319 wxString gerberContents5;
320 BOOST_REQUIRE( gerberStream5.ReadAll( &gerberContents5 ) );
321 BOOST_CHECK_MESSAGE( gerberContents5.Contains( wxT( "Fmt 4.5" ) ),
322 "Expected 'Fmt 4.5' in gerber header with precision=5" );
323 BOOST_CHECK( !gerberContents5.Contains( wxT( "Fmt 4.6" ) ) );
324 gerberStream5.Close();
325
326 // Verify precision 6 produces "Fmt 4.6" in the file header
327 GERBER_WRITER gerber6( &board );
328 gerber6.SetOptions( VECTOR2I( 0, 0 ) );
329 gerber6.SetFormat( 6 );
330 BOOST_REQUIRE( gerber6.CreateDrillandMapFilesSet( tempDir.GetFullPath(), true, false, false, nullptr ) );
331
332 wxFFile gerberStream6( gerberFile5.GetFullPath(), wxT( "rb" ) );
333 wxString gerberContents6;
334 BOOST_REQUIRE( gerberStream6.ReadAll( &gerberContents6 ) );
335 BOOST_CHECK_MESSAGE( gerberContents6.Contains( wxT( "Fmt 4.6" ) ),
336 "Expected 'Fmt 4.6' in gerber header with precision=6" );
337 BOOST_CHECK( !gerberContents6.Contains( wxT( "Fmt 4.5" ) ) );
338 gerberStream6.Close();
339
340 wxFileName::Rmdir( tempDir.GetFullPath(), wxPATH_RMDIR_RECURSIVE );
341}
342
343
344// Regression test for https://gitlab.com/kicad/code/kicad/-/issues/23005
345// GenDrillReportFile crashed when aReporter was null (the default)
346BOOST_AUTO_TEST_CASE( DrillReportNullReporter )
347{
348 wxFileName tempDir = MakeTempDir();
349 wxFileName boardFile( tempDir.GetFullPath(), wxT( "test_board.kicad_pcb" ) );
350
351 BOARD board;
352 board.SetCopperLayerCount( 2 );
353 board.SetFileName( boardFile.GetFullPath() );
354
355 wxFileName reportFile( tempDir.GetFullPath(), wxT( "test_board-drl.rpt" ) );
356
357 // Valid path with null reporter should succeed without crashing
358 EXCELLON_WRITER excellon( &board );
359 BOOST_CHECK( excellon.GenDrillReportFile( reportFile.GetFullPath() ) );
360 BOOST_CHECK( reportFile.FileExists() );
361
362 GERBER_WRITER gerber( &board );
363 BOOST_CHECK( gerber.GenDrillReportFile( reportFile.GetFullPath() ) );
364
365 // Invalid path with null reporter should return false without crashing
366 EXCELLON_WRITER excellon2( &board );
367 BOOST_CHECK( !excellon2.GenDrillReportFile( wxT( "/nonexistent/path/report.rpt" ) ) );
368
369 GERBER_WRITER gerber2( &board );
370 BOOST_CHECK( !gerber2.GenDrillReportFile( wxT( "/nonexistent/path/report.rpt" ) ) );
371
372 wxFileName::Rmdir( tempDir.GetFullPath(), wxPATH_RMDIR_RECURSIVE );
373}
374
375
376// Regression test for https://gitlab.com/kicad/code/kicad/-/issues/23289
377// GenDrillReportFile crashed with SIGSEGV when the board had drills because
378// printToolSummary() passed integer literal 0 instead of the FILE* to fmt::print()
379BOOST_AUTO_TEST_CASE( DrillReportWithTools )
380{
381 wxFileName tempDir = MakeTempDir();
382 wxFileName boardFile( tempDir.GetFullPath(), wxT( "test_board_with_drills.kicad_pcb" ) );
383
384 BOARD board;
385 board.SetCopperLayerCount( 2 );
386 board.SetFileName( boardFile.GetFullPath() );
387
388 auto via = new PCB_VIA( &board );
389 via->SetPosition( VECTOR2I( 0, 0 ) );
390 via->SetLayerPair( F_Cu, B_Cu );
391 via->SetDrill( pcbIUScale.mmToIU( 0.30 ) );
392 via->SetWidth( pcbIUScale.mmToIU( 0.60 ) );
393 board.Add( via );
394
395 wxFileName reportFile( tempDir.GetFullPath(), wxT( "test_board_with_drills-drl.rpt" ) );
396
397 EXCELLON_WRITER excellon( &board );
398 excellon.SetOptions( false, false, VECTOR2I( 0, 0 ), false );
399 excellon.SetFormat( true );
400 BOOST_CHECK_NO_THROW( excellon.GenDrillReportFile( reportFile.GetFullPath() ) );
401 BOOST_CHECK( reportFile.FileExists() );
402
403 wxFFile reportStream( reportFile.GetFullPath(), wxT( "rb" ) );
404 wxString reportContents;
405 BOOST_REQUIRE( reportStream.ReadAll( &reportContents ) );
406 BOOST_CHECK( reportContents.Contains( wxT( "T1" ) ) );
407 BOOST_CHECK( reportContents.Contains( wxT( "0.300mm" ) ) );
408 reportStream.Close();
409
410 wxFileName::Rmdir( tempDir.GetFullPath(), wxPATH_RMDIR_RECURSIVE );
411}
412
413
414// Regression test for https://gitlab.com/kicad/code/kicad/-/issues/24014
415// A non-filled PCB_SHAPE rectangle on F.SilkS was emitted as a donut_rc symbol with a
416// corner radius smaller than half the line width. Many ODB++ viewers reject the
417// resulting degenerate symbol, so the outline appeared to be missing. The exporter now
418// emits the four outline edges as line features, matching how a rectangle built from
419// individual line segments is exported.
420BOOST_AUTO_TEST_CASE( OdbPpUnfilledRectangleOnSilk )
421{
422 wxFileName tempDir = MakeTempDir();
423 wxFileName boardFile( tempDir.GetFullPath(), wxT( "silk_rect_board.kicad_pcb" ) );
424
425 BOARD board;
426 board.SetCopperLayerCount( 2 );
427 board.SetFileName( boardFile.GetFullPath() );
428
429 // Non-filled rectangle on F.SilkS
430 PCB_SHAPE* rect = new PCB_SHAPE( &board, SHAPE_T::RECTANGLE );
431 rect->SetStart( VECTOR2I( pcbIUScale.mmToIU( 10.0 ), pcbIUScale.mmToIU( 10.0 ) ) );
432 rect->SetEnd( VECTOR2I( pcbIUScale.mmToIU( 30.0 ), pcbIUScale.mmToIU( 20.0 ) ) );
433 rect->SetLayer( F_SilkS );
434 rect->SetFilled( false );
435 rect->SetWidth( pcbIUScale.mmToIU( 0.15 ) );
436 board.Add( rect );
437
438 wxFileName odbRoot( tempDir.GetFullPath(), wxEmptyString );
439 odbRoot.AppendDir( wxT( "odb_out" ) );
440 BOOST_REQUIRE( odbRoot.Mkdir( wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL ) );
441
442 PCB_IO_ODBPP odbExporter;
443 std::map<std::string, UTF8> props;
444 props["units"] = "mm";
445 props["sigfig"] = "4";
446 BOOST_REQUIRE_NO_THROW( odbExporter.SaveBoard( odbRoot.GetFullPath(), &board, &props ) );
447
448 wxFileName silkFeatures( odbRoot.GetFullPath(), wxT( "features" ) );
449 silkFeatures.AppendDir( wxT( "steps" ) );
450 silkFeatures.AppendDir( wxT( "pcb" ) );
451 silkFeatures.AppendDir( wxT( "layers" ) );
452 silkFeatures.AppendDir( wxT( "f.silkscreen" ) );
453 BOOST_REQUIRE( silkFeatures.FileExists() );
454
455 wxFFile silkStream( silkFeatures.GetFullPath(), wxT( "rb" ) );
456 wxString silkContents;
457 BOOST_REQUIRE( silkStream.ReadAll( &silkContents ) );
458 silkStream.Close();
459
460 // Four ODB++ line ("L ...") features describe the rectangle outline.
461 int lineCount = 0;
462 wxStringTokenizer lines( silkContents, wxT( "\n" ) );
463
464 while( lines.HasMoreTokens() )
465 {
466 if( lines.GetNextToken().StartsWith( wxT( "L " ) ) )
467 lineCount++;
468 }
469
470 BOOST_CHECK_EQUAL( lineCount, 4 );
471
472 // The degenerate donut_rc symbol should not appear anymore.
473 BOOST_CHECK( !silkContents.Contains( wxT( "donut_rc" ) ) );
474
475 wxFileName::Rmdir( odbRoot.GetFullPath(), wxPATH_RMDIR_RECURSIVE );
476 wxFileName::Rmdir( tempDir.GetFullPath(), wxPATH_RMDIR_RECURSIVE );
477}
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:125
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:1247
void SetFileName(const wxString &aFileName)
Definition board.h:358
void SetCopperLayerCount(int aCount)
Definition board.cpp:943
virtual void SetFilled(bool aFlag)
Definition eda_shape.h:152
void SetStart(const VECTOR2I &aStart)
Definition eda_shape.h:194
void SetEnd(const VECTOR2I &aEnd)
Definition eda_shape.h:236
void SetWidth(int aWidth)
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...
void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
@ RECTANGLE
Use RECTANGLE instead of RECT to avoid collision in a Windows header.
Definition eda_shape.h:49
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
@ F_SilkS
Definition layer_ids.h:100
@ In1_Cu
Definition layer_ids.h:66
@ 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")
BOOST_CHECK_EQUAL(result, "25.4")
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:687