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