62 wxFileName tempDir = MakeTempDir();
63 wxFileName boardFile( tempDir.GetFullPath(), wxT(
"backdrill_board.kicad_pcb" ) );
75 via->SetSecondaryDrillStartLayer(
F_Cu );
80 via->SetFrontPostMachiningAngle( 900 );
88 wxFileName excellonFile( tempDir.GetFullPath(), wxT(
"backdrill_board_Backdrills_Drill_1_4.drl" ) );
91 wxFFile excellonStream( excellonFile.GetFullPath(), wxT(
"rb" ) );
92 wxString excellonContents;
94 BOOST_CHECK( excellonContents.Contains( wxT(
"TF.FileFunction,NonPlated,1,4,Blind" ) ) );
95 BOOST_CHECK( excellonContents.Contains( wxT(
"; Backdrill" ) ) );
96 BOOST_CHECK( excellonContents.Contains( wxT(
"post-machining" ) ) );
98 wxFileName layerPairFile( tempDir.GetFullPath(), wxT(
"backdrill_board-front-in3-backdrill.drl" ) );
101 wxFFile layerPairStream( layerPairFile.GetFullPath(), wxT(
"rb" ) );
102 wxString layerPairContents;
103 BOOST_REQUIRE( layerPairStream.ReadAll( &layerPairContents ) );
104 BOOST_CHECK( layerPairContents.Contains( wxT(
"; backdrill" ) ) );
106 wxFileName pthFile( tempDir.GetFullPath(), wxT(
"backdrill_board-PTH.drl" ) );
109 wxFFile pthStream( pthFile.GetFullPath(), wxT(
"rb" ) );
110 wxString pthContents;
112 BOOST_CHECK( pthContents.Contains( wxT(
"; Post-machining front countersink dia 0.600mm depth 0.150mm angle 90deg" ) ) );
119 wxFileName gerberFile( tempDir.GetFullPath(), wxT(
"backdrill_board_Backdrills_Drill_1_4-drl.gbr" ) );
122 wxFFile gerberStream( gerberFile.GetFullPath(), wxT(
"rb" ) );
123 wxString gerberContents;
125 BOOST_CHECK( gerberContents.Contains( wxT(
"%TA.AperFunction,BackDrill*%" ) ) );
126 BOOST_CHECK( gerberContents.Contains( wxT(
"%TF.FileFunction,NonPlated,1,4,Blind,Drill*%" ) ) );
128 wxFileName gerberLayerPairFile( tempDir.GetFullPath(),
129 wxT(
"backdrill_board-front-in3-backdrill-drl.gbr" ) );
132 wxFFile gerberLayerPairStream( gerberLayerPairFile.GetFullPath(), wxT(
"rb" ) );
133 wxString gerberLayerPairContents;
134 BOOST_REQUIRE( gerberLayerPairStream.ReadAll( &gerberLayerPairContents ) );
135 BOOST_CHECK( gerberLayerPairContents.Contains( wxT(
"%TF.FileFunction,NonPlated,1,4,Blind,Drill*%" ) ) );
136 BOOST_CHECK( gerberLayerPairContents.Contains( wxT(
"%TA.AperFunction,BackDrill*%" ) ) );
138 wxFileName odbRoot( tempDir.GetFullPath(), wxEmptyString );
139 odbRoot.AppendDir( wxT(
"odb_out" ) );
140 BOOST_REQUIRE( odbRoot.Mkdir( wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL ) );
143 std::map<std::string, UTF8> props;
144 props[
"units"] =
"mm";
145 props[
"sigfig"] =
"4";
146 BOOST_REQUIRE_NO_THROW( odbExporter.
SaveBoard( odbRoot.GetFullPath(), &board, &props ) );
148 wxFileName drill1Dir( odbRoot.GetFullPath(), wxEmptyString );
149 drill1Dir.AppendDir( wxT(
"steps" ) );
150 drill1Dir.AppendDir( wxT(
"pcb" ) );
151 drill1Dir.AppendDir( wxT(
"layers" ) );
152 drill1Dir.AppendDir( wxT(
"drill1" ) );
155 wxFileName toolsFile( drill1Dir.GetFullPath(), wxT(
"tools" ) );
158 wxFFile toolsStream( toolsFile.GetFullPath(), wxT(
"rb" ) );
159 wxString toolsContents;
161 BOOST_CHECK( toolsContents.Contains( wxT(
"TYPE=NON_PLATED" ) ) );
162 BOOST_CHECK( toolsContents.Contains( wxT(
"TYPE2=BLIND" ) ) );
164 wxFileName matrixFile( odbRoot.GetFullPath(), wxEmptyString );
165 matrixFile.AppendDir( wxT(
"matrix" ) );
166 matrixFile.SetFullName( wxT(
"matrix" ) );
169 wxFFile matrixStream( matrixFile.GetFullPath(), wxT(
"rb" ) );
170 wxString matrixContents;
172 BOOST_CHECK( matrixContents.Contains( wxT(
"ADD_TYPE=BACKDRILL" ) ) );
174 matrixStream.Close();
176 gerberStream.Close();
177 gerberLayerPairStream.Close();
178 excellonStream.Close();
179 layerPairStream.Close();
182 wxFileName::Rmdir( odbRoot.GetFullPath(), wxPATH_RMDIR_RECURSIVE );
183 wxFileName::Rmdir( tempDir.GetFullPath(), wxPATH_RMDIR_RECURSIVE );
192 wxFileName tempDir = MakeTempDir();
193 wxFileName boardFile( tempDir.GetFullPath(), wxT(
"backdrill_pair_board.kicad_pcb" ) );
199 auto topVia =
new PCB_VIA( &board );
200 topVia->SetPosition(
VECTOR2I( 0, 0 ) );
202 topVia->SetDrill(
pcbIUScale.mmToIU( 0.30 ) );
203 topVia->SetWidth(
pcbIUScale.mmToIU( 0.60 ) );
204 topVia->SetSecondaryDrillSize(
pcbIUScale.mmToIU( 0.40 ) );
205 topVia->SetSecondaryDrillStartLayer(
F_Cu );
206 topVia->SetSecondaryDrillEndLayer(
In1_Cu );
209 auto bottomVia =
new PCB_VIA( &board );
211 bottomVia->SetLayerPair(
F_Cu,
B_Cu );
212 bottomVia->SetDrill(
pcbIUScale.mmToIU( 0.30 ) );
213 bottomVia->SetWidth(
pcbIUScale.mmToIU( 0.60 ) );
214 bottomVia->SetTertiaryDrillSize(
pcbIUScale.mmToIU( 0.40 ) );
215 bottomVia->SetTertiaryDrillStartLayer(
B_Cu );
216 bottomVia->SetTertiaryDrillEndLayer(
In3_Cu );
217 board.
Add( bottomVia );
225 wxFileName topBackdrillFile( tempDir.GetFullPath(),
226 wxT(
"backdrill_pair_board_Backdrills_Drill_1_2.drl" ) );
227 BOOST_CHECK_MESSAGE( topBackdrillFile.FileExists(),
228 "Front-side backdrill drill file should be produced" );
231 wxFileName bottomBackdrillFile( tempDir.GetFullPath(),
232 wxT(
"backdrill_pair_board_Backdrills_Drill_6_4.drl" ) );
233 BOOST_CHECK_MESSAGE( bottomBackdrillFile.FileExists(),
234 "Back-side (tertiary) backdrill drill file should be produced" );
236 if( bottomBackdrillFile.FileExists() )
238 wxFFile stream( bottomBackdrillFile.GetFullPath(), wxT(
"rb" ) );
241 BOOST_CHECK( contents.Contains( wxT(
"; Backdrill" ) ) );
245 wxFileName::Rmdir( tempDir.GetFullPath(), wxPATH_RMDIR_RECURSIVE );
254 wxFileName tempDir = MakeTempDir();
255 wxFileName boardFile( tempDir.GetFullPath(), wxT(
"dual_backdrill_board.kicad_pcb" ) );
267 via->SetSecondaryDrillStartLayer(
F_Cu );
268 via->SetSecondaryDrillEndLayer(
In1_Cu );
270 via->SetTertiaryDrillStartLayer(
B_Cu );
280 wxFileName topBackdrillFile( tempDir.GetFullPath(),
281 wxT(
"dual_backdrill_board_Backdrills_Drill_1_2.drl" ) );
282 BOOST_CHECK( topBackdrillFile.FileExists() );
284 wxFileName bottomBackdrillFile( tempDir.GetFullPath(),
285 wxT(
"dual_backdrill_board_Backdrills_Drill_6_4.drl" ) );
286 BOOST_CHECK( bottomBackdrillFile.FileExists() );
288 wxFileName::Rmdir( tempDir.GetFullPath(), wxPATH_RMDIR_RECURSIVE );
296 wxFileName tempDir = MakeTempDir();
297 wxFileName boardFile( tempDir.GetFullPath(), wxT(
"precision_board.kicad_pcb" ) );
316 wxFileName gerberFile5( tempDir.GetFullPath(), wxT(
"precision_board-PTH-drl.gbr" ) );
319 wxFFile gerberStream5( gerberFile5.GetFullPath(), wxT(
"rb" ) );
320 wxString gerberContents5;
322 BOOST_CHECK_MESSAGE( gerberContents5.Contains( wxT(
"Fmt 4.5" ) ),
323 "Expected 'Fmt 4.5' in gerber header with precision=5" );
324 BOOST_CHECK( !gerberContents5.Contains( wxT(
"Fmt 4.6" ) ) );
325 gerberStream5.Close();
333 wxFFile gerberStream6( gerberFile5.GetFullPath(), wxT(
"rb" ) );
334 wxString gerberContents6;
336 BOOST_CHECK_MESSAGE( gerberContents6.Contains( wxT(
"Fmt 4.6" ) ),
337 "Expected 'Fmt 4.6' in gerber header with precision=6" );
338 BOOST_CHECK( !gerberContents6.Contains( wxT(
"Fmt 4.5" ) ) );
339 gerberStream6.Close();
341 wxFileName::Rmdir( tempDir.GetFullPath(), wxPATH_RMDIR_RECURSIVE );
418 wxFileName tempDir = MakeTempDir();
419 wxFileName boardFile( tempDir.GetFullPath(), wxT(
"test_board_with_drills.kicad_pcb" ) );
432 wxFileName reportFile( tempDir.GetFullPath(), wxT(
"test_board_with_drills-drl.rpt" ) );
438 BOOST_CHECK( reportFile.FileExists() );
440 wxFFile reportStream( reportFile.GetFullPath(), wxT(
"rb" ) );
441 wxString reportContents;
443 BOOST_CHECK( reportContents.Contains( wxT(
"T1" ) ) );
444 BOOST_CHECK( reportContents.Contains( wxT(
"0.300mm" ) ) );
445 reportStream.Close();
447 wxFileName::Rmdir( tempDir.GetFullPath(), wxPATH_RMDIR_RECURSIVE );
459 wxFileName tempDir = MakeTempDir();
460 wxFileName boardFile( tempDir.GetFullPath(), wxT(
"silk_rect_board.kicad_pcb" ) );
475 wxFileName odbRoot( tempDir.GetFullPath(), wxEmptyString );
476 odbRoot.AppendDir( wxT(
"odb_out" ) );
477 BOOST_REQUIRE( odbRoot.Mkdir( wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL ) );
480 std::map<std::string, UTF8> props;
481 props[
"units"] =
"mm";
482 props[
"sigfig"] =
"4";
483 BOOST_REQUIRE_NO_THROW( odbExporter.
SaveBoard( odbRoot.GetFullPath(), &board, &props ) );
485 wxFileName silkFeatures( odbRoot.GetFullPath(), wxT(
"features" ) );
486 silkFeatures.AppendDir( wxT(
"steps" ) );
487 silkFeatures.AppendDir( wxT(
"pcb" ) );
488 silkFeatures.AppendDir( wxT(
"layers" ) );
489 silkFeatures.AppendDir( wxT(
"f.silkscreen" ) );
492 wxFFile silkStream( silkFeatures.GetFullPath(), wxT(
"rb" ) );
493 wxString silkContents;
499 wxStringTokenizer lines( silkContents, wxT(
"\n" ) );
501 while( lines.HasMoreTokens() )
503 if( lines.GetNextToken().StartsWith( wxT(
"L " ) ) )
510 BOOST_CHECK( !silkContents.Contains( wxT(
"donut_rc" ) ) );
512 wxFileName::Rmdir( odbRoot.GetFullPath(), wxPATH_RMDIR_RECURSIVE );
513 wxFileName::Rmdir( tempDir.GetFullPath(), wxPATH_RMDIR_RECURSIVE );
591 std::unique_ptr<BOARD> board;
593 KI_TEST::LoadBoard( settingsManager, wxT(
"issue25089/odb_circle_edge_cases" ), board );
596 wxFileName tempDir = MakeTempDir();
601 BOOST_CHECK_MESSAGE( frontContents.Contains( wxT(
"r400.0" ) )
602 && !frontContents.Contains( wxT(
"donut" ) ),
603 "Circle with a hole-closing stroke should export as a solid pad, "
604 "features file holds:\n"
609 wxStringTokenizer backLines( backContents, wxT(
"\n" ) );
611 while( backLines.HasMoreTokens() )
613 wxString line = backLines.GetNextToken();
615 if( line.StartsWith( wxT(
"$" ) ) )
617 BOOST_CHECK_MESSAGE( !line.Contains( wxT(
"-" ) ),
618 "Oversized circle overflowed to a negative symbol dimension: "
623 wxFileName::Rmdir( odbRoot.GetFullPath(), wxPATH_RMDIR_RECURSIVE );
624 wxFileName::Rmdir( tempDir.GetFullPath(), wxPATH_RMDIR_RECURSIVE );
656 wxFileName tempDir = MakeTempDir();
657 wxFileName boardFile( tempDir.GetFullPath(), wxT(
"plated_slot_board.kicad_pcb" ) );
673 wxFileName odbRoot( tempDir.GetFullPath(), wxEmptyString );
674 odbRoot.AppendDir( wxT(
"odb_out" ) );
675 BOOST_REQUIRE( odbRoot.Mkdir( wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL ) );
678 std::map<std::string, UTF8> props;
679 props[
"units"] =
"mm";
680 props[
"sigfig"] =
"4";
681 BOOST_REQUIRE_NO_THROW( odbExporter.
SaveBoard( odbRoot.GetFullPath(), &board, &props ) );
683 auto layerDir = [&](
const wxString& aLayer )
685 wxFileName dir( odbRoot.GetFullPath(), wxEmptyString );
686 dir.AppendDir( wxT(
"steps" ) );
687 dir.AppendDir( wxT(
"pcb" ) );
688 dir.AppendDir( wxT(
"layers" ) );
689 dir.AppendDir( aLayer );
694 auto readFile = [](
const wxFileName& aFile )
696 wxFFile stream( aFile.GetFullPath(), wxT(
"rb" ) );
705 auto countLinesStartingWith = [](
const wxString& aContents,
const wxString& aPrefix )
708 wxStringTokenizer lines( aContents, wxT(
"\n" ) );
710 while( lines.HasMoreTokens() )
712 wxString line = lines.GetNextToken();
715 if( line.StartsWith( aPrefix ) )
722 auto containsOvalSymbol = [](
const wxString& aContents )
724 wxStringTokenizer lines( aContents, wxT(
"\n" ) );
726 while( lines.HasMoreTokens() )
728 wxString line = lines.GetNextToken();
731 if( line.StartsWith( wxT(
"$" ) ) && line.Contains( wxT(
"oval" ) ) )
740 wxFileName platedDir = layerDir( wxT(
"drill_plated_f.cu-b.cu" ) );
741 BOOST_REQUIRE_MESSAGE( platedDir.DirExists(),
"Plated drill layer should exist" );
743 wxFileName platedFeatures( platedDir.GetFullPath(), wxT(
"features" ) );
745 wxString platedContents =
readFile( platedFeatures );
748 BOOST_CHECK_MESSAGE( containsOvalSymbol( platedContents ),
749 "Plated slot should use an oval symbol" );
751 wxFileName platedTools( platedDir.GetFullPath(), wxT(
"tools" ) );
753 wxString platedToolsContents =
readFile( platedTools );
754 BOOST_CHECK_EQUAL( countLinesStartingWith( platedToolsContents, wxT(
"TYPE=PLATED" ) ), 1 );
755 BOOST_CHECK_EQUAL( countLinesStartingWith( platedToolsContents, wxT(
"TYPE=NON_PLATED" ) ), 0 );
759 wxFileName nonPlatedDir = layerDir( wxT(
"drill_non-plated_f.cu-b.cu" ) );
760 BOOST_REQUIRE_MESSAGE( nonPlatedDir.DirExists(),
"Non-plated drill layer should exist" );
762 wxFileName nonPlatedFeatures( nonPlatedDir.GetFullPath(), wxT(
"features" ) );
764 wxString nonPlatedContents =
readFile( nonPlatedFeatures );
766 BOOST_CHECK_EQUAL( countLinesStartingWith( nonPlatedContents, wxT(
"P " ) ), 1 );
767 BOOST_CHECK_MESSAGE( containsOvalSymbol( nonPlatedContents ),
768 "Non-plated slot should still export an oval symbol" );
770 wxFileName nonPlatedTools( nonPlatedDir.GetFullPath(), wxT(
"tools" ) );
772 wxString nonPlatedToolsContents =
readFile( nonPlatedTools );
773 BOOST_CHECK_EQUAL( countLinesStartingWith( nonPlatedToolsContents, wxT(
"TYPE=NON_PLATED" ) ),
775 BOOST_CHECK_EQUAL( countLinesStartingWith( nonPlatedToolsContents, wxT(
"TYPE=PLATED" ) ), 0 );
777 wxFileName::Rmdir( odbRoot.GetFullPath(), wxPATH_RMDIR_RECURSIVE );
778 wxFileName::Rmdir( tempDir.GetFullPath(), wxPATH_RMDIR_RECURSIVE );
789 std::unique_ptr<BOARD> board;
794 wxFileName tempDir = MakeTempDir();
795 wxFileName boardFile( tempDir.GetFullPath(), wxT(
"backdrill.kicad_pcb" ) );
798 board->SetFileName( boardFile.GetFullPath() );
800 wxFileName reportFile( tempDir.GetFullPath(), wxT(
"backdrill-drl.rpt" ) );
807 wxFFile reportStream( reportFile.GetFullPath(), wxT(
"rb" ) );
808 wxString reportContents;
810 reportStream.Close();
812 struct BACKDRILL_SECTION
818 std::map<wxString, BACKDRILL_SECTION> sections;
819 wxString currentFile;
820 wxStringTokenizer lines( reportContents, wxT(
"\n" ) );
822 while( lines.HasMoreTokens() )
824 wxString line = lines.GetNextToken();
825 line.Trim(
true ).Trim(
false );
827 if( line.StartsWith( wxT(
"Drill file '" ) ) )
828 currentFile = line.AfterFirst(
'\'' ).BeforeFirst(
'\'' );
830 if( line.StartsWith( wxT(
"T" ) ) && line.Contains( wxT(
"0.330mm" ) ) )
831 sections[currentFile].toolLines++;
835 if( line.StartsWith( wxT(
"Total backdrilled holes count " ), &count ) )
839 sections[currentFile].holes = value;
845 const std::map<wxString, long>
expected = {
846 { wxT(
"backdrill_Backdrills_Drill_1_4.drl" ), 2 },
847 { wxT(
"backdrill_Backdrills_Drill_10_4.drl" ), 1 },
848 { wxT(
"backdrill_Backdrills_Drill_10_7.drl" ), 1 }
854 for(
const auto& [file, holes] :
expected )
856 auto section = sections.find( file );
858 if( section == sections.end() )
860 BOOST_ERROR(
"No backdrill section for " << file );
871 BOOST_CHECK( reportContents.Contains( wxT(
"Total plated holes count 4" ) ) );
872 BOOST_CHECK( reportContents.Contains( wxT(
"Total unplated holes count 0" ) ) );
874 wxFileName::Rmdir( tempDir.GetFullPath(), wxPATH_RMDIR_RECURSIVE );
BOOST_CHECK_EQUAL(result, "25.4")