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::ReadBoardFromFileOrStream(
100 KI_TEST::GetPcbnewTestDataDir() + "issue14130.kicad_pcb" );
101
102 BOOST_REQUIRE( board );
103
104 std::set<double> expectedSizes;
105 int npthHoleCount = 0;
106
107 for( const PAD* pad : board->GetPads() )
108 {
109 if( pad->GetAttribute() != PAD_ATTRIB::NPTH || !pad->HasHole() )
110 continue;
111
112 expectedSizes.insert( pcbIUScale.IUTomm( std::min( pad->GetDrillSizeX(), pad->GetDrillSizeY() ) )
113 * aOutputScale );
114 npthHoleCount++;
115 }
116
117 // A fixture whose holes were all one size would not show a mis-numbered tool table
118 BOOST_REQUIRE_GT( npthHoleCount, (int) expectedSizes.size() );
119 BOOST_REQUIRE_GT( expectedSizes.size(), 1 );
120
121 KI_TEST::SCOPED_TEMP_DIR tempDir( wxT( "kicad_qa_odb_drill_tools" ) );
122
123 BOOST_REQUIRE_NO_THROW( ExportOdb( *board, tempDir.Path(), aUnits ) );
124
125 fs::path toolsFile;
126
127 for( const fs::directory_entry& entry : fs::recursive_directory_iterator( tempDir.Path() ) )
128 {
129 if( entry.is_regular_file() && entry.path().filename() == "tools"
130 && entry.path().parent_path().filename().string().find( "non-plated" )
131 != std::string::npos )
132 {
133 toolsFile = entry.path();
134 break;
135 }
136 }
137
138 BOOST_REQUIRE_MESSAGE( !toolsFile.empty(),
139 "ODB++ export produced no non-plated drill tools file" );
140
141 std::ifstream unitsStream( toolsFile );
142 std::string unitsLine;
143 BOOST_REQUIRE( static_cast<bool>( std::getline( unitsStream, unitsLine ) ) );
144 BOOST_CHECK_EQUAL( unitsLine, std::string( "UNITS=" ) + ( std::string( aUnits ) == "mm" ? "MM" : "INCH" ) );
145
146 const std::vector<ODB_TOOL_RECORD> tools = parseOdbTools( toolsFile );
147
148 std::set<int> seenNums;
149 std::set<double> seenSizes;
150
151 for( const ODB_TOOL_RECORD& tool : tools )
152 {
153 BOOST_CHECK_MESSAGE( seenNums.insert( tool.m_num ).second,
154 "Drill tool number " << tool.m_num << " is used twice" );
155
156 BOOST_CHECK_EQUAL( tool.m_finishSize, tool.m_drillSize );
157 seenSizes.insert( tool.m_drillSize );
158 }
159
160 BOOST_CHECK_EQUAL( tools.size(), expectedSizes.size() );
161 BOOST_REQUIRE_EQUAL( seenSizes.size(), expectedSizes.size() );
162
163 auto actual = seenSizes.begin();
164 auto expected = expectedSizes.begin();
165
166 while( actual != seenSizes.end() )
167 {
168 BOOST_CHECK_SMALL( std::abs( *actual - *expected ), 0.00001 );
169 ++actual;
170 ++expected;
171 }
172}
173
174
175BOOST_AUTO_TEST_CASE( OdbNonPlatedDrillTools )
176{
177 checkNonPlatedDrillTools( "mm", 1000.0 );
178}
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:59
Definition pad.h:61
std::string GetPcbnewTestDataDir()
Utility which returns a path to the data directory where the test board files are stored.
std::unique_ptr< BOARD > ReadBoardFromFileOrStream(const std::string &aFilename, std::istream &aFallback)
Read a board from a file, or another stream, as appropriate.
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")