KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_odb_drill_tools.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 <cmath>
21#include <filesystem>
22#include <fstream>
23#include <memory>
24#include <set>
25#include <string>
26
28#include <qa_utils/file_utils.h>
30#include <boost/test/unit_test.hpp>
31
32#include <board.h>
33#include <pad.h>
35
37
38namespace fs = std::filesystem;
39
44{
45 int m_num = -1;
46 double m_finishSize = 0.0;
47 double m_drillSize = 0.0;
48};
49
50
58static std::vector<ODB_TOOL_RECORD> parseOdbTools( const fs::path& aToolsFile )
59{
60 std::vector<ODB_TOOL_RECORD> tools;
61 std::ifstream stream( aToolsFile );
62 std::string line;
63
64 auto value = []( const std::string& aLine ) -> std::string
65 {
66 return aLine.substr( aLine.find( '=' ) + 1 );
67 };
68
69 while( std::getline( stream, line ) )
70 {
71 const size_t start = line.find_first_not_of( " \t" );
72
73 if( start == std::string::npos )
74 continue;
75
76 line = line.substr( start );
77
78 if( line.rfind( "TOOLS", 0 ) == 0 )
79 tools.emplace_back();
80 else if( tools.empty() )
81 continue;
82 else if( line.rfind( "NUM=", 0 ) == 0 )
83 tools.back().m_num = std::stoi( value( line ) );
84 else if( line.rfind( "FINISH_SIZE=", 0 ) == 0 )
85 tools.back().m_finishSize = std::stod( value( line ) );
86 else if( line.rfind( "DRILL_SIZE=", 0 ) == 0 )
87 tools.back().m_drillSize = std::stod( value( line ) );
88 }
89
90 return tools;
91}
92
93
94// The Tag-Connect footprint has repeated non-plated holes at three sizes
95// This checks size conversion and duplicate bit numbering
96static void checkNonPlatedDrillTools( const char* aUnits, double aOutputScale )
97{
98 SETTINGS_MANAGER settingsManager;
99 std::unique_ptr<BOARD> board = KI_TEST::RequireBoardFromPcbnewQAData( "issue14130.kicad_pcb" );
100
101 std::set<double> expectedSizes;
102 int npthHoleCount = 0;
103
104 for( const PAD* pad : board->GetPads() )
105 {
106 if( pad->GetAttribute() != PAD_ATTRIB::NPTH || !pad->HasHole() )
107 continue;
108
109 expectedSizes.insert( pcbIUScale.IUTomm( std::min( pad->GetDrillSize().x, pad->GetDrillSize().y ) )
110 * aOutputScale );
111 npthHoleCount++;
112 }
113
114 // A fixture whose holes were all one size would not show a mis-numbered tool table
115 BOOST_REQUIRE_GT( npthHoleCount, (int) expectedSizes.size() );
116 BOOST_REQUIRE_GT( expectedSizes.size(), 1 );
117
118 KI_TEST::SCOPED_TEMP_DIR tempDir( wxT( "kicad_qa_odb_drill_tools" ) );
119
120 BOOST_REQUIRE_NO_THROW( ExportOdb( *board, tempDir.Path(), aUnits ) );
121
122 fs::path toolsFile;
123
124 for( const fs::directory_entry& entry : fs::recursive_directory_iterator( tempDir.Path() ) )
125 {
126 if( entry.is_regular_file() && entry.path().filename() == "tools"
127 && entry.path().parent_path().filename().string().find( "non-plated" )
128 != std::string::npos )
129 {
130 toolsFile = entry.path();
131 break;
132 }
133 }
134
135 BOOST_REQUIRE_MESSAGE( !toolsFile.empty(),
136 "ODB++ export produced no non-plated drill tools file" );
137
138 std::ifstream unitsStream( toolsFile );
139 std::string unitsLine;
140 BOOST_REQUIRE( static_cast<bool>( std::getline( unitsStream, unitsLine ) ) );
141 BOOST_CHECK_EQUAL( unitsLine, std::string( "UNITS=" ) + ( std::string( aUnits ) == "mm" ? "MM" : "INCH" ) );
142
143 const std::vector<ODB_TOOL_RECORD> tools = parseOdbTools( toolsFile );
144
145 std::set<int> seenNums;
146 std::set<double> seenSizes;
147
148 for( const ODB_TOOL_RECORD& tool : tools )
149 {
150 BOOST_CHECK_MESSAGE( seenNums.insert( tool.m_num ).second,
151 "Drill tool number " << tool.m_num << " is used twice" );
152
153 BOOST_CHECK_EQUAL( tool.m_finishSize, tool.m_drillSize );
154 seenSizes.insert( tool.m_drillSize );
155 }
156
157 BOOST_CHECK_EQUAL( tools.size(), expectedSizes.size() );
158 BOOST_REQUIRE_EQUAL( seenSizes.size(), expectedSizes.size() );
159
160 auto actual = seenSizes.begin();
161 auto expected = expectedSizes.begin();
162
163 while( actual != seenSizes.end() )
164 {
165 BOOST_CHECK_SMALL( std::abs( *actual - *expected ), 0.00001 );
166 ++actual;
167 ++expected;
168 }
169}
170
171
172BOOST_AUTO_TEST_CASE( OdbNonPlatedDrillTools )
173{
174 checkNonPlatedDrillTools( "mm", 1000.0 );
175}
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:128
General utilities for PCB file IO for QA programs.
const std::filesystem::path & Path() const
Get the path to the temporary directory as a std::filesystem::path.
Definition file_utils.h:61
Definition pad.h:61
std::unique_ptr< BOARD > RequireBoardFromPcbnewQAData(const std::filesystem::path &aFilename)
Require a board from the path relative to the pcbnew QA data directory.
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition eda_angle.h:437
fs::path ExportOdb(const BOARD &aBoard, const fs::path &aDir, const std::string &aUnits, const std::string &aSigfig, REPORTER *aReporter)
@ NPTH
like PAD_PTH, but not plated mechanical use only, no connection allowed
Definition padstack.h:102
One TOOLS record from an ODB++ drill layer "tools" file.
BOOST_REQUIRE(intersection.has_value()==c.ExpectedIntersection.has_value())
VECTOR3I expected(15, 30, 45)
static std::vector< ODB_TOOL_RECORD > parseOdbTools(const fs::path &aToolsFile)
Parse the TOOLS records out of an ODB++ "tools" file.
BOOST_AUTO_TEST_CASE(OdbNonPlatedDrillTools)
static void checkNonPlatedDrillTools(const char *aUnits, double aOutputScale)
int actual
BOOST_CHECK_EQUAL(result, "25.4")