20#include <boost/test/unit_test.hpp>
29#include <system_error>
38#include <wx/filename.h>
54 explicit TEMP_FILE(
const wxString& aPrefix ) :
55 m_path( std::string( wxFileName::CreateTempFileName( aPrefix ).ToUTF8() ) )
62 std::filesystem::remove( m_path, ec );
65 const std::filesystem::path& Path()
const {
return m_path; }
66 wxString WxStr()
const {
return wxString::FromUTF8( m_path.string() ); }
69 std::filesystem::path m_path;
73std::string slurp(
const std::filesystem::path& aPath )
75 std::ifstream in( aPath );
82std::unique_ptr<BOARD> loadBoard(
const char* aName )
84 const std::filesystem::path boardPath =
93std::unique_ptr<BOARD> loadComplexPadstackBoard()
95 return loadBoard(
"padstacks_complex.kicad_pcb" );
100std::set<std::string> captureAll(
const std::string& aText,
const std::string& aPattern )
102 std::set<std::string> found;
103 std::regex re( aPattern );
105 for(
auto it = std::sregex_iterator( aText.begin(), aText.end(), re );
106 it != std::sregex_iterator(); ++it )
108 found.insert( ( *it )[1].str() );
123 std::unique_ptr<BOARD> board = loadComplexPadstackBoard();
125 TEMP_FILE out( wxT(
"kicad_gencad_padstack_" ) );
130 const std::string cad = slurp( out.Path() );
133 std::set<std::string> shapeLayers = captureAll( cad, R
"(\nPAD P[0-9]+_([A-Za-z0-9.]+) )" );
135 BOOST_CHECK_MESSAGE( shapeLayers.count( "F.Cu" ),
"expected an F.Cu-specific PAD shape" );
136 BOOST_CHECK_MESSAGE( shapeLayers.count(
"In1.Cu" ),
"expected an In1.Cu-specific PAD shape" );
137 BOOST_CHECK_MESSAGE( shapeLayers.count(
"In2.Cu" ),
"expected an In2.Cu-specific PAD shape" );
138 BOOST_CHECK_MESSAGE( shapeLayers.count(
"B.Cu" ),
"expected a B.Cu-specific PAD shape" );
141 std::set<std::string> rectSizes = captureAll( cad, R
"(\nRECTANGLE [-0-9.]+ [-0-9.]+ ([0-9.]+))" );
143 BOOST_CHECK_MESSAGE( rectSizes.size() >= 4,
144 "flattened padstack: expected at least 4 distinct RECTANGLE sizes, got "
145 << rectSizes.size() );
148 std::regex padstackBlock( R
"(PADSTACK PAD[0-9]+ [^\n]*\n((?:PAD [^\n]*\n)+))" );
149 bool sawVaryingStack =
false;
151 for(
auto it = std::sregex_iterator( cad.begin(), cad.end(), padstackBlock );
152 it != std::sregex_iterator(); ++it )
154 if( captureAll( ( *it )[1].str(), R
"(PAD (P[0-9_A-Za-z.]+) )" ).size() > 1 )
155 sawVaryingStack = true;
158 BOOST_CHECK_MESSAGE( sawVaryingStack,
159 "no PADSTACK entry referenced more than one shape across its layers" );
163 std::set<std::string> stackLayers = captureAll( cad, R
"(\nPAD P[0-9_A-Za-z.]+ ([A-Za-z0-9-]+) )" );
165 BOOST_CHECK_MESSAGE( stackLayers.count( "INNER1" ),
"expected an INNER1 reference" );
166 BOOST_CHECK_MESSAGE( stackLayers.count(
"INNER2" ),
"expected an INNER2 reference" );
168 for(
const std::string& layer : stackLayers )
170 BOOST_CHECK_MESSAGE( layer.find(
'-' ) == std::string::npos,
171 "padstack references a layer GenCAD cannot name: " << layer );
181 std::unique_ptr<BOARD> board = loadComplexPadstackBoard();
183 TEMP_FILE out( wxT(
"kicad_specctra_padstack_" ) );
186 const std::string dsn = slurp( out.Path() );
189 BOOST_CHECK_MESSAGE( dsn.find(
"Complex[" ) != std::string::npos,
190 "flattened padstack: no layer-by-layer padstack id was emitted" );
193 std::set<std::string> rectCorners = captureAll( dsn, R
"(\(rect "[^"]+" ([-0-9]+) )" );
195 BOOST_CHECK_MESSAGE( rectCorners.size() >= 4,
196 "flattened padstack: expected at least 4 distinct rect extents, got "
197 << rectCorners.size() );
206 std::unique_ptr<BOARD> board = loadComplexPadstackBoard();
208 TEMP_FILE out( wxT(
"kicad_hyperlynx_padstack_" ) );
211 const std::string hyp = slurp( out.Path() );
213 std::set<std::string> padSizes = captureAll( hyp, R
"RX(\("[^"]+", [0-9]+, ([0-9.]+),)RX" );
215 BOOST_CHECK_MESSAGE( padSizes.size() >= 4,
216 "flattened padstack: expected at least 4 distinct pad sizes, got "
217 << padSizes.size() );
220 std::set<std::string> layersWithSizes = captureAll( hyp, R
"RX(\("([^"]+)", [0-9]+, [0-9.]+,)RX" );
222 BOOST_CHECK_MESSAGE( layersWithSizes.size() >= 4,
223 "expected per-layer entries rather than a single MDEF default" );
233 std::unique_ptr<BOARD> board = loadBoard(
"api_kitchen_sink.kicad_pcb" );
235 TEMP_FILE out( wxT(
"kicad_hyperlynx_smd_" ) );
238 const std::string hyp = slurp( out.Path() );
240 BOOST_CHECK_MESSAGE( hyp.find(
"{PADSTACK=" ) != std::string::npos,
241 "board produced no padstacks to check" );
243 std::set<std::string> drills = captureAll( hyp, R
"(\{PADSTACK=[0-9]+, ([0-9.]+))" );
245 BOOST_CHECK_MESSAGE( drills.count( "0.000000000" ) == 0,
246 "a hole-less padstack declared a zero drill instead of omitting it" );
General utilities for PCB file IO for QA programs.
Export board to GenCAD file format.
bool WriteFile(const wxString &aFullFileName)
Export a GenCAD file.
Instantiate the current locale within a scope in which you are expecting exceptions to be thrown.
bool ExportBoardToHyperlynxFile(BOARD *aBoard, const wxString &aFullFilename)
Write aBoard to aFullFilename in Hyperlynx .hyp format.
void ExportBoardToSpecctraFile(BOARD *aBoard, const wxString &aFullFilename, REPORTER *aReporter=nullptr)
Helper method to export board to DSN file.
std::string GetPcbnewTestDataDir()
Utility which returns a path to the data directory where the test board files are stored.
std::unique_ptr< BOARD > RequireBoardFromPcbnewQAData(const std::filesystem::path &aFilename)
Require a board from the path relative to the pcbnew QA data directory.
BOOST_AUTO_TEST_CASE(HorizontalAlignment)
BOOST_AUTO_TEST_SUITE(CadstarPartParser)
BOOST_AUTO_TEST_CASE(GencadEmitsShapePerLayer)
BOOST_REQUIRE(intersection.has_value()==c.ExpectedIntersection.has_value())
BOOST_AUTO_TEST_SUITE_END()