KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_odbpp_rout.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, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14 * more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program. If not, see <https://www.gnu.org/licenses/>.
18 */
19
22
23#include <algorithm>
24#include <map>
25#include <memory>
26#include <set>
27#include <sstream>
28#include <string>
29
30#include <board.h>
34#include <reporter.h>
36
37#include "odb_test_utils.h"
38
39namespace fs = std::filesystem;
40
41namespace
42{
43using FIELDS = std::map<std::string, std::string>;
44
45std::vector<FIELDS> MatrixLayers( const fs::path& aRoot )
46{
47 std::vector<FIELDS> rows;
48 FIELDS fields;
49 bool inLayer = false;
50
51 for( const std::string& line : ReadLines( aRoot / "matrix" / "matrix" ) )
52 {
53 if( line == "LAYER {" )
54 {
55 fields.clear();
56 inLayer = true;
57 }
58 else if( inLayer && line == "}" )
59 {
60 rows.push_back( fields );
61 inLayer = false;
62 }
63 else if( inLayer )
64 {
65 size_t equals = line.find( '=' );
66
67 if( equals != std::string::npos )
68 {
69 size_t start = line.find_first_not_of( ' ' );
70 fields[line.substr( start, equals - start )] = line.substr( equals + 1 );
71 }
72 }
73 }
74
75 return rows;
76}
77
78
79const FIELDS* FindRow( const std::vector<FIELDS>& aRows, const std::string& aName )
80{
81 auto it = std::find_if( aRows.begin(), aRows.end(), [&]( const FIELDS& aRow )
82 { return aRow.at( "NAME" ) == aName; } );
83
84 return it == aRows.end() ? nullptr : &*it;
85}
86
87
88struct ROUT_SPAN
89{
90 std::pair<std::string, std::string> m_start;
91 std::pair<std::string, std::string> m_end;
92};
93
94
95std::map<int, std::vector<ROUT_SPAN>> RoutChains( const std::vector<std::string>& aLines )
96{
97 std::map<int, std::vector<ROUT_SPAN>> chains;
98 std::string chainAttribute;
99
100 for( const std::string& line : aLines )
101 {
102 if( line.rfind( "@", 0 ) == 0 && line.find( " .rout_chain" ) != std::string::npos )
103 chainAttribute = line.substr( 1, line.find( ' ' ) - 1 ) + "=";
104 }
105
106 if( chainAttribute.empty() )
107 return chains;
108
109 for( const std::string& line : aLines )
110 {
111 if( line.rfind( "L ", 0 ) != 0 && line.rfind( "A ", 0 ) != 0 )
112 continue;
113
114 std::istringstream fields( line );
115 std::string record;
116 ROUT_SPAN span;
117 fields >> record >> span.m_start.first >> span.m_start.second >> span.m_end.first >> span.m_end.second;
118
119 size_t semicolon = line.find( ';' );
120
121 if( semicolon == std::string::npos )
122 continue;
123
124 std::istringstream attributes( line.substr( semicolon + 1 ) );
125 std::string attribute;
126
127 while( std::getline( attributes, attribute, ',' ) )
128 {
129 if( attribute.rfind( chainAttribute, 0 ) == 0 )
130 chains[std::stoi( attribute.substr( chainAttribute.size() ) )].push_back( span );
131 }
132 }
133
134 return chains;
135}
136}
137
138
139BOOST_AUTO_TEST_CASE( OdbCopperLayerTypesFollowBoardSetup )
140{
141 std::unique_ptr<BOARD> board = KI_TEST::RequireBoardFromPcbnewQAData( "connect/connect.kicad_pcb" );
142 BOOST_REQUIRE( board->GetLayerType( In1_Cu ) == LT_POWER );
143 fs::path root = ExportOdb( *board, TempDir().Path() );
144 std::vector<FIELDS> rows = MatrixLayers( root );
145 const FIELDS* inner = FindRow( rows, "in1.cu" );
146 BOOST_REQUIRE( inner );
147 BOOST_CHECK_EQUAL( inner->at( "TYPE" ), "POWER_GROUND" );
148
149 board = KI_TEST::RequireBoardFromPcbnewQAData( "issue24089/issue24089.kicad_pcb" );
150 BOOST_REQUIRE( board->GetLayerType( F_Cu ) == LT_MIXED );
151 root = ExportOdb( *board, TempDir().Path() );
152 rows = MatrixLayers( root );
153 const FIELDS* mixed = FindRow( rows, "f.cu" );
154 BOOST_REQUIRE( mixed );
155 BOOST_CHECK_EQUAL( mixed->at( "TYPE" ), "MIXED" );
156}
157
158
159BOOST_AUTO_TEST_CASE( OdbRoutLayerCarriesEveryIsland )
160{
161 std::unique_ptr<BOARD> board = KI_TEST::RequireBoardFromPcbnewQAData( "odbpp/two_board_islands.kicad_pcb" );
162 SHAPE_POLY_SET outlines;
163 BOOST_REQUIRE( board->GetBoardPolygonOutlines( outlines, true ) );
164 BOOST_REQUIRE_EQUAL( outlines.OutlineCount(), 2 );
165 fs::path root = ExportOdb( *board, TempDir().Path() );
166 std::vector<FIELDS> rows = MatrixLayers( root );
167 const FIELDS* rout = FindRow( rows, "rout" );
168 BOOST_REQUIRE( rout );
169 BOOST_CHECK( !FindRow( rows, "edge.cuts" ) );
170 BOOST_CHECK_EQUAL( rout->at( "TYPE" ), "ROUT" );
171 BOOST_CHECK_EQUAL( rout->at( "START_NAME" ), "f.cu" );
172 BOOST_CHECK_EQUAL( rout->at( "END_NAME" ), "b.cu" );
173 ODB_PRODUCT product( root );
174 std::vector<std::string> lines = ReadLines( fs::path( product.LayerFile( "rout", "features" ).ToStdWstring() ) );
175 size_t routes = std::count_if( lines.begin(), lines.end(), []( const std::string& aLine )
176 { return aLine.rfind( "L ", 0 ) == 0 || aLine.rfind( "A ", 0 ) == 0; } );
177 BOOST_CHECK_GT( routes, 7u );
178 BOOST_CHECK( std::none_of( lines.begin(), lines.end(), []( const std::string& aLine )
179 { return aLine.rfind( "S ", 0 ) == 0; } ) );
180 BOOST_CHECK( std::any_of( lines.begin(), lines.end(), []( const std::string& aLine )
181 { return aLine.rfind( "$0 r0", 0 ) == 0; } ) );
182 OdbFeatureFile features( fs::path( product.LayerFile( "rout", "features" ).ToStdWstring() ) );
183 BOOST_CHECK_GT( features.CountRecordsWithInteger( ".rout_chain", 1 ), 0u );
184 BOOST_CHECK_GT( features.CountRecordsWithInteger( ".rout_chain", 2 ), 0u );
185
186 std::map<int, std::vector<ROUT_SPAN>> chains = RoutChains( lines );
187 BOOST_REQUIRE_EQUAL( chains.size(), 2u );
188
189 for( const auto& [number, spans] : chains )
190 {
191 BOOST_REQUIRE_GT( spans.size(), 2u );
192
193 for( size_t i = 0; i < spans.size(); ++i )
194 {
195 const ROUT_SPAN& current = spans[i];
196 const ROUT_SPAN& next = spans[( i + 1 ) % spans.size()];
197 BOOST_CHECK_MESSAGE( current.m_end == next.m_start, "open rout chain " << number << " at " << i );
198 }
199 }
200}
201
202
203BOOST_AUTO_TEST_CASE( OdbPlatedEdgesAreRoutPlated )
204{
205 std::unique_ptr<BOARD> board = KI_TEST::RequireBoardFromPcbnewQAData( "issue24089/issue24089.kicad_pcb" );
206 BOOST_REQUIRE( board->GetDesignSettings().GetStackupDescriptor().m_EdgePlating );
207 fs::path root = ExportOdb( *board, TempDir().Path() );
208 ODB_PRODUCT product( root );
209 fs::path rout( product.LayerFile( "rout", "features" ).ToStdWstring() );
210 BOOST_REQUIRE( fs::exists( rout ) );
211 OdbFeatureFile features( rout );
212 BOOST_CHECK_GT( features.CountRecordsWithFlag( ".rout_plated" ), 0u );
213 std::vector<std::string> routLines = ReadLines( rout );
214 BOOST_CHECK( std::any_of( routLines.begin(), routLines.end(), []( const std::string& aLine )
215 { return aLine.rfind( "A ", 0 ) == 0; } ) );
216
217 board = KI_TEST::RequireBoardFromPcbnewQAData( "issue19325/issue19325.kicad_pcb" );
218 root = ExportOdb( *board, TempDir().Path() );
219 ODB_PRODUCT castellated( root );
220 fs::path castRout( castellated.LayerFile( "rout", "features" ).ToStdWstring() );
221 BOOST_REQUIRE( fs::exists( castRout ) );
222 OdbFeatureFile castFeatures( castRout );
223 BOOST_CHECK_EQUAL( castFeatures.CountRecordsWithFlag( ".rout_plated" ), 0u );
224}
225
226
227BOOST_AUTO_TEST_CASE( OdbMaskPasteSilkReferenceCopper )
228{
229 std::unique_ptr<BOARD> board = KI_TEST::RequireBoardFromPcbnewQAData( "issue24089/issue24089.kicad_pcb" );
230 fs::path root = ExportOdb( *board, TempDir().Path() );
231 std::vector<FIELDS> rows = MatrixLayers( root );
232
233 for( const std::string& side : { "f", "b" } )
234 {
235 const FIELDS* copper = FindRow( rows, side + ".cu" );
236 BOOST_REQUIRE( copper );
237 BOOST_REQUIRE( copper->count( "ID" ) );
238
239 for( const std::string& suffix : { "mask", "paste", "silkscreen" } )
240 {
241 const FIELDS* row = FindRow( rows, side + "." + suffix );
242
243 if( row )
244 {
245 BOOST_REQUIRE( row->count( "REF" ) );
246 BOOST_CHECK_EQUAL( row->at( "REF" ), copper->at( "ID" ) );
247 }
248 }
249 }
250
251 std::set<std::string> copperIds;
252
253 for( const FIELDS& row : rows )
254 {
255 if( row.at( "TYPE" ) == "SIGNAL" || row.at( "TYPE" ) == "POWER_GROUND"
256 || row.at( "TYPE" ) == "MIXED" )
257 {
258 copperIds.insert( row.at( "ID" ) );
259 }
260 }
261
262 for( const FIELDS& row : rows )
263 {
264 if( row.at( "TYPE" ) != "DIELECTRIC" )
265 continue;
266
267 if( row.at( "DIELECTRIC_TYPE" ) == "CORE" )
268 {
269 BOOST_REQUIRE( row.count( "CU_TOP" ) );
270 BOOST_REQUIRE( row.count( "CU_BOTTOM" ) );
271 BOOST_REQUIRE( row.count( "DIELECTRIC_NAME" ) );
272 BOOST_CHECK( copperIds.count( row.at( "CU_TOP" ) ) );
273 BOOST_CHECK( copperIds.count( row.at( "CU_BOTTOM" ) ) );
274 BOOST_CHECK( !row.at( "DIELECTRIC_NAME" ).empty() );
275 }
276 else
277 {
278 BOOST_CHECK( !row.count( "CU_TOP" ) );
279 BOOST_CHECK( !row.count( "CU_BOTTOM" ) );
280 }
281 }
282}
283
284
285BOOST_AUTO_TEST_CASE( OdbLayerTypeOverrides )
286{
287 std::unique_ptr<BOARD> board = KI_TEST::RequireBoardFromPcbnewQAData( "issue24089/issue24089.kicad_pcb" );
288 KI_TEST::SCOPED_TEMP_DIR dir( wxT( "odb_layer_type_overrides" ) );
289 PCB_IO_ODBPP plugin;
290 ODB_EXPORT_OPTIONS options;
291 options.m_layerOverrides = { { F_SilkS, true, wxEmptyString, wxS( "DOCUMENT" ) },
292 { Dwgs_User, true, wxEmptyString, wxS( "SOLDER_MASK" ) },
293 { In1_Cu, true, wxEmptyString, wxS( "SIGNAL" ) },
294 { B_Cu, true, wxEmptyString, wxS( "SILK_SCREEN" ) } };
295 BOOST_REQUIRE_NO_THROW( plugin.Export( wxString::FromUTF8( dir.Path().string() ), *board, options ) );
296 std::vector<FIELDS> rows = MatrixLayers( dir.Path() );
297
298 const FIELDS* silk = FindRow( rows, "f.silkscreen" );
299 BOOST_REQUIRE( silk );
300 BOOST_CHECK_EQUAL( silk->at( "TYPE" ), "DOCUMENT" );
301 BOOST_CHECK( !silk->contains( "REF" ) );
302
303 const FIELDS* drawings = FindRow( rows, "user.drawings" );
304 BOOST_REQUIRE( drawings );
305 BOOST_CHECK_EQUAL( drawings->at( "TYPE" ), "SOLDER_MASK" );
306 BOOST_CHECK_EQUAL( drawings->at( "CONTEXT" ), "MISC" );
307 BOOST_CHECK( !drawings->contains( "REF" ) );
308
309 const FIELDS* inner = FindRow( rows, "in1.cu" );
310 BOOST_REQUIRE( inner );
311 BOOST_CHECK_EQUAL( inner->at( "TYPE" ), "SIGNAL" );
312
313 // Copper cannot take a drawing layer type
314 const FIELDS* bottom = FindRow( rows, "b.cu" );
315 BOOST_REQUIRE( bottom );
316 BOOST_CHECK_EQUAL( bottom->at( "TYPE" ), "MIXED" );
317}
318
319
320BOOST_AUTO_TEST_CASE( OdbMatrixIdsAreUnique )
321{
322 std::unique_ptr<BOARD> board = KI_TEST::RequireBoardFromPcbnewQAData( "issue24089/issue24089.kicad_pcb" );
323 fs::path root = ExportOdb( *board, TempDir().Path() );
324 std::vector<FIELDS> rows = MatrixLayers( root );
325 std::set<unsigned long> ids;
326 unsigned long maxId = 0;
327
328 for( const FIELDS& row : rows )
329 {
330 BOOST_REQUIRE( row.count( "ID" ) );
331 }
332
333 for( const std::string& line : ReadLines( root / "matrix" / "matrix" ) )
334 {
335 if( line.rfind( " ID=", 0 ) != 0 )
336 continue;
337
338 unsigned long id = std::stoul( line.substr( 7 ) );
339
340 BOOST_CHECK( ids.insert( id ).second );
341 maxId = std::max( maxId, id );
342 }
343
344 bool foundMax = false;
345
346 for( const std::string& line : ReadLines( root / "misc" / "info" ) )
347 {
348 if( line.rfind( "MAX_UID=", 0 ) == 0 )
349 {
350 foundMax = true;
351 BOOST_CHECK_GE( std::stoul( line.substr( 8 ) ), maxId );
352 }
353 }
354
355 BOOST_CHECK( foundMax );
356}
357
358
359BOOST_AUTO_TEST_CASE( OdbSlotsStayOnDrillAndGainRoutChains )
360{
361 std::unique_ptr<BOARD> board = KI_TEST::RequireBoardFromPcbnewQAData( "odbpp/pad_roles.kicad_pcb" );
362 fs::path root = ExportOdb( *board, TempDir().Path() );
363 ODB_PRODUCT product( root );
364 fs::path rout( product.LayerFile( "rout", "features" ).ToStdWstring() );
365 BOOST_REQUIRE( fs::exists( rout ) );
366 OdbFeatureFile routFeatures( rout );
367 BOOST_CHECK_GT( routFeatures.CountRecordsWithFlag( ".rout_plated" ), 0u );
368
369 bool drillHasSlotLine = false;
370
371 for( const ODB_MATRIX_ROW& row : product.Matrix() )
372 {
373 if( row.m_type != wxS( "DRILL" ) )
374 continue;
375
376 for( const std::string& line : ReadLines( fs::path( product.LayerFile( row.m_name,
377 wxS( "features" ) ).ToStdWstring() ) ) )
378 {
379 if( line.rfind( "L ", 0 ) == 0 )
380 drillHasSlotLine = true;
381 }
382 }
383
384 BOOST_CHECK( drillHasSlotLine );
385
386 board = KI_TEST::RequireBoardFromPcbnewQAData( "odbpp/npth_pressfit_slot.kicad_pcb" );
387 root = ExportOdb( *board, TempDir().Path() );
388 ODB_PRODUCT npthProduct( root );
389 fs::path npthRout( npthProduct.LayerFile( "rout", "features" ).ToStdWstring() );
390 BOOST_REQUIRE( fs::exists( npthRout ) );
391 OdbFeatureFile npthFeatures( npthRout );
392 BOOST_CHECK_EQUAL( npthFeatures.CountRecordsWithFlag( ".rout_plated" ), 0u );
393}
394
395
396BOOST_AUTO_TEST_CASE( OdbApiKitchenSinkMatrixReferencesResolve )
397{
398 std::unique_ptr<BOARD> board = KI_TEST::RequireBoardFromPcbnewQAData( "api_kitchen_sink.kicad_pcb" );
399 fs::path root = ExportOdb( *board, TempDir().Path() );
400 ODB_PRODUCT product( root );
401 std::set<wxString> ids;
402
403 for( const ODB_MATRIX_ROW& row : product.Matrix() )
404 ids.insert( row.m_id );
405
406 for( const ODB_MATRIX_ROW& row : product.Matrix() )
407 {
408 for( const wxString& reference : { row.m_ref, row.m_cuTop, row.m_cuBottom } )
409 BOOST_CHECK_MESSAGE( reference.IsEmpty() || ids.count( reference ), row.m_name << " references " << reference );
410 }
411}
412
413
414BOOST_AUTO_TEST_CASE( OdbRoutDoesNotInferMissingBoardOutline )
415{
416 std::unique_ptr<BOARD> board = KI_TEST::RequireBoardFromPcbnewQAData( "component_classes.kicad_pcb" );
417 SHAPE_POLY_SET outline;
418 BOOST_CHECK( !board->GetBoardPolygonOutlines( outline, false ) );
420 fs::path root = ExportOdb( *board, TempDir().Path(), "mm", "6", &reporter );
421 ODB_PRODUCT product( root );
422 std::vector<std::string> lines = ReadLines( fs::path( product.LayerFile( "rout", "features" ).ToStdWstring() ) );
423 BOOST_CHECK( std::none_of( lines.begin(), lines.end(), []( const std::string& aLine )
424 { return aLine.rfind( "L ", 0 ) == 0 || aLine.rfind( "A ", 0 ) == 0; } ) );
425 BOOST_CHECK( reporter.GetMessages().Contains( wxS( "ODB++ rout layer has no board contour" ) ) );
426}
427
428
429BOOST_AUTO_TEST_CASE( OdbCollidingLayerNamesKeepMatrixReferences )
430{
431 std::unique_ptr<BOARD> board = KI_TEST::RequireBoardFromPcbnewQAData( "odbpp/layer_name_collisions.kicad_pcb" );
432 BOOST_REQUIRE_EQUAL( board->GetLayerName( F_Cu ), wxS( "Signal" ) );
433 BOOST_REQUIRE_EQUAL( board->GetLayerName( In1_Cu ), wxS( "SiGnAl" ) );
434 BOOST_REQUIRE_EQUAL( board->GetLayerName( B_Cu ), wxS( "SIGNAL" ) );
435 BOOST_REQUIRE_EQUAL( board->GetLayerName( User_1 ), wxS( "notes" ) );
436 BOOST_REQUIRE_EQUAL( board->GetLayerName( User_2 ), wxS( "Notes" ) );
437 BOOST_REQUIRE_EQUAL( board->GetLayerName( User_3 ), wxS( "notes_1" ) );
438 BOOST_REQUIRE_EQUAL( board->GetLayerName( User_4 ), wxS( "COMP_+_BOT" ) );
439 KI_TEST::SCOPED_TEMP_DIR dir( wxT( "odb_colliding_layers" ) );
440
441 for( bool filtered : { false, true } )
442 {
443 fs::path root = dir.Path() / ( filtered ? "filtered" : "default" );
444
445 if( filtered )
446 {
447 fs::create_directories( root );
448 PCB_IO_ODBPP plugin;
449 ODB_EXPORT_OPTIONS options;
451 BOOST_REQUIRE_NO_THROW( plugin.Export( wxString::FromUTF8( root.string() ), *board, options ) );
452 }
453 else
454 {
455 ExportOdb( *board, root );
456 }
457
458 std::vector<FIELDS> rows = MatrixLayers( root );
459 BOOST_REQUIRE( !rows.empty() );
460 std::set<std::string> names;
461
462 for( const FIELDS& row : rows )
463 BOOST_CHECK( names.insert( row.at( "NAME" ) ).second );
464
465 for( const std::string& name : { "signal", "signal_2", "signal_3", "notes", "notes_2", "notes_1",
466 "comp_+_bot_2" } )
467 BOOST_CHECK_EQUAL( names.count( name ), 1u );
468
469 const FIELDS* rout = FindRow( rows, "rout" );
470 BOOST_REQUIRE( rout );
471 BOOST_CHECK_EQUAL( rout->at( "START_NAME" ), "signal" );
472 BOOST_CHECK_EQUAL( rout->at( "END_NAME" ), "signal_3" );
473
474 bool throughDrill = false;
475 bool backdrill = false;
476
477 for( const FIELDS& row : rows )
478 {
479 if( row.at( "TYPE" ) != "DRILL" )
480 continue;
481
482 if( row.contains( "ADD_TYPE" ) && row.at( "ADD_TYPE" ) == "BACKDRILL" )
483 {
484 if( row.at( "START_NAME" ) == "in2.cu" && row.at( "END_NAME" ) == "signal_3" )
485 {
486 backdrill = true;
487 fs::path attrs = root / "steps" / "pcb" / "layers" / row.at( "NAME" ) / "attrlist";
488 std::vector<std::string> lines = ReadLines( attrs );
489 BOOST_CHECK( std::find( lines.begin(), lines.end(),
490 ".backdrill_penetrate_stop_layer=signal_2" ) != lines.end() );
491 }
492 }
493 else if( row.at( "START_NAME" ) == "signal" && row.at( "END_NAME" ) == "signal_3" )
494 {
495 throughDrill = true;
496 }
497 }
498
499 BOOST_CHECK( throughDrill );
500 BOOST_CHECK( backdrill );
501
502 const FIELDS* bottomComponents = FindRow( rows, "comp_+_bot_2" );
503 BOOST_REQUIRE( bottomComponents );
504 BOOST_CHECK_EQUAL( bottomComponents->at( "TYPE" ), "COMPONENT" );
505 fs::path components = root / "steps" / "pcb" / "layers" / "comp_+_bot_2" / "components";
506 BOOST_REQUIRE( fs::exists( components ) );
507 std::vector<std::string> lines = ReadLines( components );
508 BOOST_CHECK( std::any_of( lines.begin(), lines.end(), []( const std::string& aLine )
509 { return aLine.rfind( "CMP ", 0 ) == 0; } ) );
510 }
511}
const char * name
@ LT_POWER
Definition board.h:246
@ LT_MIXED
Definition board.h:247
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
The matrix and layer directories of an exported ODB++ product model tree.
wxString LayerFile(const wxString &aLayer, const wxString &aFile) const
Path of a file in a layer directory, matched to the directory name without regard to case.
const std::vector< ODB_MATRIX_ROW > & Matrix() const
size_t CountRecordsWithInteger(const std::string &aName, int aValue) const
size_t CountRecordsWithFlag(const std::string &aName) const
void Export(const wxString &aFileName, BOARD &aBoard, const ODB_EXPORT_OPTIONS &aOptions)
Write the product model tree at aFileName Throw IO_ERROR when the write fails.
Represent a set of closed polygons.
int OutlineCount() const
Return the number of outlines in the set.
A wrapper for reporting to a wxString object.
Definition reporter.h:242
@ Dwgs_User
Definition layer_ids.h:103
@ B_Cu
Definition layer_ids.h:61
@ F_SilkS
Definition layer_ids.h:96
@ In1_Cu
Definition layer_ids.h:62
@ User_3
Definition layer_ids.h:122
@ User_1
Definition layer_ids.h:120
@ User_4
Definition layer_ids.h:123
@ User_2
Definition layer_ids.h:121
@ F_Cu
Definition layer_ids.h:60
SECTION_SET LegacySections()
Schema sections of the content that KiCad wrote before the function modes.
std::unique_ptr< BOARD > RequireBoardFromPcbnewQAData(const std::filesystem::path &aFilename)
Require a board from the path relative to the pcbnew QA data directory.
bool equals(glm::mat< L, C, T, Q > const &aFirst, glm::mat< L, C, T, Q > const &aSecond, T aEpsilon=static_cast< T >(FLT_EPSILON *10))
Template to compare two glm::mat<T> values for equality within a required epsilon.
fs::path ExportOdb(const BOARD &aBoard, const fs::path &aDir, const std::string &aUnits, const std::string &aSigfig, REPORTER *aReporter)
std::vector< std::string > ReadLines(const fs::path &aFile)
const KI_TEST::SCOPED_TEMP_DIR & TempDir()
CITER next(CITER it)
Definition ptree.cpp:120
What a caller asks of one ODB++ export.
Definition odb_util.h:50
std::optional< FAB::SECTION_SET > m_sections
Unset exports every section.
Definition odb_util.h:61
std::vector< ODB_LAYER_OVERRIDE > m_layerOverrides
Definition odb_util.h:62
One LAYER block of matrix/matrix.
BOOST_REQUIRE(intersection.has_value()==c.ExpectedIntersection.has_value())
IbisParser parser & reporter
BOOST_AUTO_TEST_CASE(OdbCopperLayerTypesFollowBoardSetup)
BOOST_CHECK_EQUAL(result, "25.4")