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 =
96std::unique_ptr<BOARD> loadComplexPadstackBoard()
98 return loadBoard(
"padstacks_complex.kicad_pcb" );
103std::set<std::string> captureAll(
const std::string& aText,
const std::string& aPattern )
105 std::set<std::string> found;
106 std::regex re( aPattern );
108 for(
auto it = std::sregex_iterator( aText.begin(), aText.end(), re );
109 it != std::sregex_iterator(); ++it )
111 found.insert( ( *it )[1].str() );
126 std::unique_ptr<BOARD> board = loadComplexPadstackBoard();
128 TEMP_FILE out( wxT(
"kicad_gencad_padstack_" ) );
133 const std::string cad = slurp( out.Path() );
136 std::set<std::string> shapeLayers = captureAll( cad, R
"(\nPAD P[0-9]+_([A-Za-z0-9.]+) )" );
138 BOOST_CHECK_MESSAGE( shapeLayers.count( "F.Cu" ),
"expected an F.Cu-specific PAD shape" );
139 BOOST_CHECK_MESSAGE( shapeLayers.count(
"In1.Cu" ),
"expected an In1.Cu-specific PAD shape" );
140 BOOST_CHECK_MESSAGE( shapeLayers.count(
"In2.Cu" ),
"expected an In2.Cu-specific PAD shape" );
141 BOOST_CHECK_MESSAGE( shapeLayers.count(
"B.Cu" ),
"expected a B.Cu-specific PAD shape" );
144 std::set<std::string> rectSizes = captureAll( cad, R
"(\nRECTANGLE [-0-9.]+ [-0-9.]+ ([0-9.]+))" );
146 BOOST_CHECK_MESSAGE( rectSizes.size() >= 4,
147 "flattened padstack: expected at least 4 distinct RECTANGLE sizes, got "
148 << rectSizes.size() );
151 std::regex padstackBlock( R
"(PADSTACK PAD[0-9]+ [^\n]*\n((?:PAD [^\n]*\n)+))" );
152 bool sawVaryingStack =
false;
154 for(
auto it = std::sregex_iterator( cad.begin(), cad.end(), padstackBlock );
155 it != std::sregex_iterator(); ++it )
157 if( captureAll( ( *it )[1].str(), R
"(PAD (P[0-9_A-Za-z.]+) )" ).size() > 1 )
158 sawVaryingStack = true;
161 BOOST_CHECK_MESSAGE( sawVaryingStack,
162 "no PADSTACK entry referenced more than one shape across its layers" );
166 std::set<std::string> stackLayers = captureAll( cad, R
"(\nPAD P[0-9_A-Za-z.]+ ([A-Za-z0-9-]+) )" );
168 BOOST_CHECK_MESSAGE( stackLayers.count( "INNER1" ),
"expected an INNER1 reference" );
169 BOOST_CHECK_MESSAGE( stackLayers.count(
"INNER2" ),
"expected an INNER2 reference" );
171 for(
const std::string& layer : stackLayers )
173 BOOST_CHECK_MESSAGE( layer.find(
'-' ) == std::string::npos,
174 "padstack references a layer GenCAD cannot name: " << layer );
184 std::unique_ptr<BOARD> board = loadComplexPadstackBoard();
186 TEMP_FILE out( wxT(
"kicad_specctra_padstack_" ) );
189 const std::string dsn = slurp( out.Path() );
192 BOOST_CHECK_MESSAGE( dsn.find(
"Complex[" ) != std::string::npos,
193 "flattened padstack: no layer-by-layer padstack id was emitted" );
196 std::set<std::string> rectCorners = captureAll( dsn, R
"(\(rect "[^"]+" ([-0-9]+) )" );
198 BOOST_CHECK_MESSAGE( rectCorners.size() >= 4,
199 "flattened padstack: expected at least 4 distinct rect extents, got "
200 << rectCorners.size() );
209 std::unique_ptr<BOARD> board = loadComplexPadstackBoard();
211 TEMP_FILE out( wxT(
"kicad_hyperlynx_padstack_" ) );
214 const std::string hyp = slurp( out.Path() );
216 std::set<std::string> padSizes = captureAll( hyp, R
"RX(\("[^"]+", [0-9]+, ([0-9.]+),)RX" );
218 BOOST_CHECK_MESSAGE( padSizes.size() >= 4,
219 "flattened padstack: expected at least 4 distinct pad sizes, got "
220 << padSizes.size() );
223 std::set<std::string> layersWithSizes = captureAll( hyp, R
"RX(\("([^"]+)", [0-9]+, [0-9.]+,)RX" );
225 BOOST_CHECK_MESSAGE( layersWithSizes.size() >= 4,
226 "expected per-layer entries rather than a single MDEF default" );
236 std::unique_ptr<BOARD> board = loadBoard(
"api_kitchen_sink.kicad_pcb" );
238 TEMP_FILE out( wxT(
"kicad_hyperlynx_smd_" ) );
241 const std::string hyp = slurp( out.Path() );
243 BOOST_CHECK_MESSAGE( hyp.find(
"{PADSTACK=" ) != std::string::npos,
244 "board produced no padstacks to check" );
246 std::set<std::string> drills = captureAll( hyp, R
"(\{PADSTACK=[0-9]+, ([0-9.]+))" );
248 BOOST_CHECK_MESSAGE( drills.count( "0.000000000" ) == 0,
249 "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)
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 > ReadBoardFromFileOrStream(const std::string &aFilename, std::istream &aFallback)
Read a board from a file, or another stream, as appropriate.
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()