KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_odbpp_attributes.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
22
23#include <algorithm>
24#include <cmath>
25#include <cctype>
26#include <memory>
27#include <map>
28#include <sstream>
29#include <string>
30#include <vector>
31
32#include <board.h>
33#include <base_units.h>
36#include <footprint.h>
37#include <netinfo.h>
38#include <pad.h>
39#include <pcb_track.h>
40
46
47#include "odb_test_utils.h"
48
49namespace fs = std::filesystem;
50
51
53{
58
60
61 // When there is no w/h
64
65 // When there is no drill, there is w/h before epoint and exp
70};
71
72
73BOOST_AUTO_TEST_SUITE( OdbAttributesTests )
74
75BOOST_AUTO_TEST_CASE( FabComponentRowUsesSavedVariantFields )
76{
77 std::unique_ptr<BOARD> board = KI_TEST::RequireBoardFromPcbnewQAData( "variant_test/variant_test.kicad_pcb" );
78
79 const FOOTPRINT* resistor = nullptr;
80
81 for( const FOOTPRINT* footprint : board->Footprints() )
82 {
83 if( footprint->GetReference() == wxS( "R1" ) )
84 resistor = footprint;
85 }
86
87 BOOST_REQUIRE( resistor );
88 FAB_COMPONENT_ROW normal = MakeFabComponentRow( *resistor, wxString() );
89 FAB_COMPONENT_ROW variant = MakeFabComponentRow( *resistor, wxS( "Variant A" ) );
90 BOOST_CHECK( normal.Field( wxS( "Datasheet" ) ).IsEmpty() );
91 BOOST_CHECK_EQUAL( variant.Field( wxS( "Datasheet" ) ), wxString( wxS( "test" ) ) );
92 BOOST_CHECK( variant.m_mount == FAB_MOUNT::SMT );
93}
94
95
96BOOST_AUTO_TEST_CASE( FabTestPointsKeepSavedBoardViaOrder )
97{
98 std::unique_ptr<BOARD> board = KI_TEST::RequireBoardFromPcbnewQAData( "connect/connect.kicad_pcb" );
99
100 BOOST_REQUIRE( board->BuildConnectivity() );
101 std::vector<FAB_TEST_POINT> points = FabTestPoints( *board );
102 BOOST_REQUIRE( !points.empty() );
103 bool sawPad = false;
104 size_t vias = 0;
105
106 for( const FAB_TEST_POINT& point : points )
107 {
108 if( point.m_pad )
109 {
110 sawPad = true;
111 BOOST_CHECK( !point.m_via );
112 }
113 else
114 {
115 BOOST_CHECK( !sawPad );
116 BOOST_REQUIRE( point.m_via );
117 BOOST_CHECK( point.m_position == point.m_via->GetPosition() );
118 ++vias;
119 }
120 }
121
122 BOOST_CHECK_GT( vias, 0u );
123 BOOST_CHECK( sawPad );
124}
125
126
127BOOST_AUTO_TEST_CASE( FabAnonymousNetNamesFollowSavedNetCodes )
128{
129 std::unique_ptr<BOARD> board = KI_TEST::RequireBoardFromPcbnewQAData( "odbpp/net_name_collision.kicad_pcb" );
130
131 std::map<int, wxString> names = FabAnonymousNetNames( *board );
132 BOOST_REQUIRE_GT( names.size(), 1u );
133 size_t ordinal = 0;
134
135 for( const auto& [code, name] : names )
136 {
137 BOOST_CHECK_GT( code, 0 );
138 BOOST_CHECK_EQUAL( name, wxString::Format( wxS( "NET_%zu" ), ++ordinal ) );
139 }
140
141 const NETINFO_ITEM* supply = board->FindNet( wxS( "+12V" ) );
142 const NETINFO_ITEM* q2 = board->FindNet( wxS( "Net-(Q2-C)" ) );
143 const NETINFO_ITEM* q1 = board->FindNet( wxS( "Net-(Q1-C)" ) );
144 BOOST_REQUIRE( supply );
145 BOOST_REQUIRE( q2 );
146 BOOST_REQUIRE( q1 );
147 BOOST_REQUIRE_LT( q2->GetNetCode(), q1->GetNetCode() );
148 BOOST_CHECK_EQUAL( names.at( supply->GetNetCode() ), wxString( wxS( "NET_1" ) ) );
149 BOOST_CHECK_EQUAL( names.at( q2->GetNetCode() ), wxString( wxS( "NET_2" ) ) );
150 BOOST_CHECK_EQUAL( names.at( q1->GetNetCode() ), wxString( wxS( "NET_3" ) ) );
151}
152
153
154BOOST_AUTO_TEST_CASE( OdbPadUsageOptionOrderMatchesSpec )
155{
156 BOOST_CHECK_EQUAL( static_cast<int>( ODB_ATTR::PAD_USAGE::TOEPRINT ), 0 );
157 BOOST_CHECK_EQUAL( static_cast<int>( ODB_ATTR::PAD_USAGE::VIA ), 1 );
158 BOOST_CHECK_EQUAL( static_cast<int>( ODB_ATTR::PAD_USAGE::G_FIDUCIAL ), 2 );
159 BOOST_CHECK_EQUAL( static_cast<int>( ODB_ATTR::PAD_USAGE::L_FIDUCIAL ), 3 );
161 BOOST_CHECK_EQUAL( static_cast<int>( ODB_ATTR::PAD_USAGE::BOND_FINGER ), 5 );
162}
163
164
165BOOST_AUTO_TEST_CASE( OdbFiducialPadUsage )
166{
167 std::unique_ptr<BOARD> board = KI_TEST::RequireBoardFromPcbnewQAData( "odbpp/pad_roles.kicad_pcb" );
168
169 size_t globals = CountPads( *board, PAD_PROP::FIDUCIAL_GLBL, F_Cu );
170 size_t locals = CountPads( *board, PAD_PROP::FIDUCIAL_LOCAL, F_Cu );
171 BOOST_REQUIRE_GT( globals, 0 );
172 BOOST_REQUIRE_EQUAL( locals, 1 );
173
174 ODB_PRODUCT product( ExportOdb( *board, TempDir().Path() ) );
175 OdbFeatureFile top( fs::path( product.LayerFile( ODB::GenLegalEntityName( board->GetLayerName( F_Cu ) ),
176 wxS( "features" ) ).ToStdWstring() ) );
177
178 BOOST_CHECK_EQUAL( top.CountRecordsWithOption( ".pad_usage", "g_fiducial" ), globals );
179 BOOST_CHECK_EQUAL( top.CountRecordsWithOption( ".pad_usage", "l_fiducial" ), locals );
180}
181
182
183BOOST_AUTO_TEST_CASE( OdbTestPointFeatureAndCadnetFlag )
184{
185 std::unique_ptr<BOARD> board = KI_TEST::RequireBoardFromPcbnewQAData( "odbpp/pad_roles.kicad_pcb" );
186
187 BOOST_REQUIRE_EQUAL( CountPads( *board, PAD_PROP::TESTPOINT, F_Cu ), 1 );
188
189 const PAD* testPoint = nullptr;
190
191 for( const FOOTPRINT* footprint : board->Footprints() )
192 {
193 for( const PAD* pad : footprint->Pads() )
194 {
195 if( pad->GetProperty() == PAD_PROP::TESTPOINT )
196 testPoint = pad;
197 }
198 }
199
200 BOOST_REQUIRE( testPoint );
201
202 fs::path root = ExportOdb( *board, TempDir().Path() );
203 ODB_PRODUCT product( root );
204 OdbFeatureFile top( fs::path( product.LayerFile( ODB::GenLegalEntityName( board->GetLayerName( F_Cu ) ),
205 wxS( "features" ) ).ToStdWstring() ) );
206 BOOST_CHECK_EQUAL( top.CountRecordsWithFlag( ".test_point" ), 1 );
207
208 size_t taggedPoints = 0;
209
210 for( const std::string& line : ReadLines( root / "steps" / "pcb" / "netlists" / "cadnet" / "netlist" ) )
211 {
212 std::istringstream stream( line );
213 std::vector<std::string> fields;
214 std::string token;
215
216 while( stream >> token )
217 fields.push_back( token );
218
219 if( fields.size() <= NP_IDX_NODRILL_EXP )
220 continue;
221
222 const std::string& netNum = fields[NP_IDX_NET_NUM];
223
224 if( netNum.empty() || !std::isdigit( static_cast<unsigned char>( netNum[0] ) ) )
225 continue;
226
227 const double x = std::stod( fields[NP_IDX_X_COORD] );
228 const double y = std::stod( fields[NP_IDX_Y_COORD] );
229 const double padX = pcbIUScale.IUTomm( testPoint->GetPosition().x );
230 const double padY = -pcbIUScale.IUTomm( testPoint->GetPosition().y );
231
232 // Find the matching pad in the netlist by comparing coordinates
233 if( std::abs( x - padX ) < 0.00001 && std::abs( y - padY ) < 0.00001 )
234 {
235 BOOST_TEST_MESSAGE( "Cadnet net number " << netNum << " found at coordinates (" << x << ", " << y << ")" );
236
237 const bool isTestPoint = std::find( fields.begin(), fields.end(), "t" ) != fields.end();
238
239 // Non-drilled cadnet points carry the fixed 0.002 placeholder radius
240 // (netlist would be 0)
241 // (The spec isn't that clear here, but if w/h don't exist we don't know the
242 // index for epoint)
243 BOOST_CHECK_EQUAL( fields[NP_IDX_RADIUS], "0.002" );
244
246 BOOST_CHECK( isTestPoint );
247 ++taggedPoints;
248 }
249 }
250
251 BOOST_CHECK_EQUAL( taggedPoints, 1 );
252}
253
254
255BOOST_AUTO_TEST_CASE( OdbConnectedViaIsCadnetMidpoint )
256{
257 std::unique_ptr<BOARD> board = KI_TEST::RequireBoardFromPcbnewQAData( "connect/connect.kicad_pcb" );
258
259 BOOST_REQUIRE( board->BuildConnectivity() );
260 const FAB_TEST_POINT* midpoint = nullptr;
261 std::vector<FAB_TEST_POINT> points = FabTestPoints( *board );
262
263 for( const FAB_TEST_POINT& point : points )
264 {
265 if( point.m_via && point.m_netCode > 0 && ( point.m_front || point.m_back ) && !point.m_netEnd )
266 {
267 midpoint = &point;
268 break;
269 }
270 }
271
272 BOOST_REQUIRE_MESSAGE( midpoint, "connect board has no exposed midpoint via" );
273 fs::path root = ExportOdb( *board, TempDir().Path() );
274 bool found = false;
275
276 for( const std::string& line : ReadLines( root / "steps" / "pcb" / "netlists" / "cadnet" / "netlist" ) )
277 {
278 std::istringstream stream( line );
279 std::vector<std::string> fields;
280 std::string token;
281
282 while( stream >> token )
283 fields.push_back( token );
284
285 const bool isVia = std::find( fields.begin(), fields.end(), "v" ) != fields.end();
286
287 if( fields.size() <= NP_IDX_DRILL_EXP || !isVia || fields[NP_IDX_NET_NUM] != std::to_string( midpoint->m_netCode ) )
288 continue;
289
290 double x = pcbIUScale.IUTomm( midpoint->m_position.x );
291 double y = -pcbIUScale.IUTomm( midpoint->m_position.y );
292
293 if( std::abs( std::stod( fields[NP_IDX_X_COORD] ) - x ) < 0.00001
294 && std::abs( std::stod( fields[NP_IDX_Y_COORD] ) - y ) < 0.00001 )
295 {
296 const double radius = std::stod( fields[NP_IDX_RADIUS] );
297
298 // must have non-zero radius for drilled via fields
299 BOOST_CHECK( radius > 0 );
300
302 found = true;
303 }
304 }
305
306 BOOST_CHECK( found );
307}
308
309
310BOOST_AUTO_TEST_CASE( OdbPressFitUsesDrillToolComponentAndPinRoles )
311{
312 std::unique_ptr<BOARD> board = KI_TEST::RequireBoardFromPcbnewQAData( "odbpp/pad_roles.kicad_pcb" );
313
314 BOOST_REQUIRE_EQUAL( CountPads( *board, PAD_PROP::PRESSFIT, F_Cu ), 1 );
315
316 fs::path root = ExportOdb( *board, TempDir().Path() );
317 ODB_PRODUCT product( root );
318 wxString drillLayer;
319
320 for( const ODB_MATRIX_ROW& row : product.Matrix() )
321 {
322 if( row.m_name.StartsWith( wxS( "drill_plated_" ) ) )
323 drillLayer = row.m_name;
324 }
325
326 BOOST_REQUIRE( !drillLayer.IsEmpty() );
327 OdbFeatureFile drill( fs::path( product.LayerFile( drillLayer, wxS( "features" ) ).ToStdWstring() ) );
328 BOOST_CHECK_EQUAL( drill.CountRecordsWithOption( ".plated_type", "press_fit" ), 1 );
329
330 size_t pressFitTools = 0;
331
332 for( const std::string& line : ReadLines( fs::path( product.LayerFile( drillLayer,
333 wxS( "tools" ) ).ToStdWstring() ) ) )
334 {
335 if( line.find( "TYPE2=PRESS_FIT" ) != std::string::npos )
336 ++pressFitTools;
337 }
338
339 BOOST_CHECK_GT( pressFitTools, 0 );
340
341 size_t pressFitPins = 0;
342
343 for( const std::string& line : ReadLines( root / "steps" / "pcb" / "eda" / "data" ) )
344 {
345 if( line.rfind( "PIN ", 0 ) == 0 && line.size() >= 2
346 && line.compare( line.size() - 2, 2, " P" ) == 0 )
347 ++pressFitPins;
348 }
349
350 BOOST_CHECK_EQUAL( pressFitPins, 1 );
351
352 size_t pressFitComponents = 0;
353
354 for( const ODB_MATRIX_ROW& row : product.Matrix() )
355 {
356 if( row.m_type != wxS( "COMPONENT" ) )
357 continue;
358
359 OdbFeatureFile components( fs::path( product.LayerFile( row.m_name, wxS( "components" ) ).ToStdWstring() ) );
360 pressFitComponents += components.CountRecordsWithOption( ".comp_mount_type", "pressfit" );
361 }
362
363 BOOST_CHECK_EQUAL( pressFitComponents, 1 );
364}
365
366
367BOOST_AUTO_TEST_CASE( OdbUnnumberedNpthUsesToolingHoleRole )
368{
369 std::unique_ptr<BOARD> board = KI_TEST::RequireBoardFromPcbnewQAData( "odbpp/pad_roles.kicad_pcb" );
370
371 ODB_PRODUCT product( ExportOdb( *board, TempDir().Path() ) );
372 wxString drillLayer;
373
374 for( const ODB_MATRIX_ROW& row : product.Matrix() )
375 {
376 if( row.m_name.StartsWith( wxS( "drill_non-plated_" ) ) )
377 drillLayer = row.m_name;
378 }
379
380 BOOST_REQUIRE( !drillLayer.IsEmpty() );
381 OdbFeatureFile drill( fs::path( product.LayerFile( drillLayer, wxS( "features" ) ).ToStdWstring() ) );
382 size_t nonPlated = drill.CountRecordsWithOption( ".drill", "non_plated" );
383 BOOST_REQUIRE_GT( nonPlated, 0 );
384 BOOST_CHECK_EQUAL( drill.CountRecordsWithOption( ".pad_usage", "tooling_hole" ), nonPlated );
385}
386
387
388BOOST_AUTO_TEST_CASE( OdbMicroviasUseLaserAttributesAndTools )
389{
390 std::unique_ptr<BOARD> board = KI_TEST::RequireBoardFromPcbnewQAData( "padstacks.kicad_pcb" );
391
392 size_t microvias = 0;
393
394 for( PCB_TRACK* track : board->Tracks() )
395 {
396 if( track->Type() == PCB_VIA_T && static_cast<PCB_VIA*>( track )->GetViaType() == VIATYPE::MICROVIA )
397 ++microvias;
398 }
399
400 BOOST_REQUIRE_GT( microvias, 0 );
401 ODB_PRODUCT product( ExportOdb( *board, TempDir().Path() ) );
402 size_t laserFeatures = 0;
403 size_t laserTools = 0;
404
405 for( const ODB_MATRIX_ROW& row : product.Matrix() )
406 {
407 if( row.m_type != wxS( "DRILL" ) )
408 continue;
409
410 OdbFeatureFile features( fs::path( product.LayerFile( row.m_name, wxS( "features" ) ).ToStdWstring() ) );
411 laserFeatures += features.CountRecordsWithOption( ".via_type", "laser" );
412
413 for( const std::string& line : ReadLines( fs::path( product.LayerFile( row.m_name,
414 wxS( "tools" ) ).ToStdWstring() ) ) )
415 {
416 if( line.find( "TYPE2=LASER" ) != std::string::npos )
417 ++laserTools;
418 }
419 }
420
421 BOOST_CHECK_EQUAL( laserFeatures, microvias );
422 BOOST_CHECK_GT( laserTools, 0 );
423}
424
425
426BOOST_AUTO_TEST_CASE( OdbNpthPressFitNeverUsesPressFitTool )
427{
428 const std::vector<std::pair<std::string, bool>> fixtures = {
429 { "odbpp/npth_pressfit.kicad_pcb", false }, { "odbpp/npth_pressfit_slot.kicad_pcb", true }
430 };
431
432 for( const auto& [name, isSlot] : fixtures )
433 {
434 std::unique_ptr<BOARD> board = KI_TEST::RequireBoardFromPcbnewQAData( name );
435
436 size_t invalidPads = 0;
437
438 for( const FOOTPRINT* footprint : board->Footprints() )
439 {
440 for( const PAD* pad : footprint->Pads() )
441 {
442 if( pad->GetAttribute() == PAD_ATTRIB::NPTH && pad->GetProperty() == PAD_PROP::PRESSFIT )
443 {
444 ++invalidPads;
445 BOOST_CHECK_EQUAL( pad->GetDrillSize().x != pad->GetDrillSize().y, isSlot );
446 }
447 }
448 }
449
450 BOOST_REQUIRE_EQUAL( invalidPads, 1 );
451 ODB_PRODUCT product( ExportOdb( *board, TempDir().Path() ) );
452 size_t nonPlatedTools = 0;
453 size_t pressFitTools = 0;
454
455 for( const ODB_MATRIX_ROW& row : product.Matrix() )
456 {
457 if( row.m_type != wxS( "DRILL" ) )
458 continue;
459
460 fs::path toolsPath( product.LayerFile( row.m_name, wxS( "tools" ) ).ToStdWstring() );
461
462 for( const std::string& line : ReadLines( toolsPath ) )
463 {
464 if( line.find( "TYPE=NON_PLATED" ) != std::string::npos )
465 ++nonPlatedTools;
466
467 if( line.find( "TYPE2=PRESS_FIT" ) != std::string::npos )
468 ++pressFitTools;
469 }
470 }
471
472 BOOST_CHECK_GT( nonPlatedTools, 0 );
473 BOOST_CHECK_EQUAL( pressFitTools, 0 );
474 }
475}
476
477
478BOOST_AUTO_TEST_CASE( OdbStepBoardThicknessMatchesDefaultStackup )
479{
480 std::unique_ptr<BOARD> board = KI_TEST::RequireBoardFromPcbnewQAData( "custom_pads.kicad_pcb" );
481
482 BOOST_REQUIRE( !board->GetDesignSettings().m_HasStackup );
483
484 fs::path root = ExportOdb( *board, TempDir().Path() );
485 std::vector<std::string> lines = ReadLines( root / "steps" / "pcb" / "attrlist" );
486 BOOST_REQUIRE( !lines.empty() );
487 BOOST_CHECK_EQUAL( lines.front(), "UNITS=MM" );
488 BOOST_CHECK( std::find( lines.begin(), lines.end(), ".board_thickness=1600.0" ) != lines.end() );
489
490 fs::path inchRoot = ExportOdb( *board, TempDir().Path(), "inch" );
491 std::vector<std::string> inchLines = ReadLines( inchRoot / "steps" / "pcb" / "attrlist" );
492 BOOST_REQUIRE( inchLines.size() >= 2 );
493 BOOST_CHECK_EQUAL( inchLines.front(), "UNITS=INCH" );
494 BOOST_CHECK_EQUAL( inchLines[1].substr( 0, 17 ), ".board_thickness=" );
495 BOOST_CHECK_CLOSE( std::stod( inchLines[1].substr( 17 ) ), 62.992126, 0.01 );
496}
497
498
499BOOST_AUTO_TEST_CASE( OdbCopperWeightFollowsOutputUnits )
500{
501 std::unique_ptr<BOARD> board = KI_TEST::RequireBoardFromPcbnewQAData( "custom_pads.kicad_pcb" );
502
503 // Produce an ODB++ export and check that the copper weight follows the output units.
504 const auto checkCopperWeight =
505 [&]( const fs::path& aRootDir, const std::string& aLayer, const std::string& aExpectedUnits,
506 const std::string& aExpectedValue )
507 {
508 ODB_PRODUCT product( aRootDir );
509
510 const fs::path attrlistPath( product.LayerFile( aLayer, wxS( "attrlist" ) ).ToStdWstring() );
511 const std::vector<std::string> attrlist = ReadLines( attrlistPath );
512
513 const std::string expectedUnitsLine = "UNITS=" + aExpectedUnits;
514 const std::string expectedValueLine = ".copper_weight=" + aExpectedValue;
515
516 BOOST_REQUIRE( !attrlist.empty() );
517 BOOST_CHECK_EQUAL( attrlist.front(), expectedUnitsLine );
518 BOOST_CHECK( std::find( attrlist.begin(), attrlist.end(), expectedValueLine ) != attrlist.end() );
519 };
520
521 fs::path metricRoot = ExportOdb( *board, TempDir().Path(), "mm" );
522 checkCopperWeight( metricRoot, "f.cu", "MM", "35.0" );
523
524 fs::path inchRoot = ExportOdb( *board, TempDir().Path(), "inch" );
525 checkCopperWeight( inchRoot, "f.cu", "INCH", "1.0" );
526}
527
528
529BOOST_AUTO_TEST_CASE( OdbLayerMaterialHasUserAttributeDeclaration )
530{
531 std::unique_ptr<BOARD> board = KI_TEST::RequireBoardFromPcbnewQAData( "ipc2581/dielectric-sublayer.kicad_pcb" );
532
533 fs::path root = ExportOdb( *board, TempDir().Path() );
534 ODB_PRODUCT product( root );
535 size_t materials = 0;
536
537 for( const ODB_MATRIX_ROW& row : product.Matrix() )
538 {
539 if( row.m_type != wxS( "DIELECTRIC" ) )
540 continue;
541
542 fs::path attrlistPath( product.LayerFile( row.m_name, wxS( "attrlist" ) ).ToStdWstring() );
543 std::vector<std::string> attrlist = ReadLines( attrlistPath );
544 BOOST_REQUIRE( !attrlist.empty() );
545 BOOST_CHECK_EQUAL( attrlist.front(), "UNITS=MM" );
546
547 for( const std::string& line : attrlist )
548 {
549 if( line.rfind( "material=", 0 ) == 0 )
550 ++materials;
551
552 BOOST_CHECK( line.rfind( ".material=", 0 ) != 0 );
553 }
554 }
555
556 BOOST_CHECK_GT( materials, 0 );
557 std::vector<std::string> declaration = ReadLines( root / "misc" / "userattr" );
558
559 for( const std::string& field : { "UNITS=MM", "TEXT {", "NAME=material", "ENTITY=LAYER", "MAX_LEN=64" } )
560 BOOST_CHECK( std::find( declaration.begin(), declaration.end(), field ) != declaration.end() );
561}
562
563
564BOOST_AUTO_TEST_CASE( OdbBackdrillStopLayerFollowsMustCutCopper )
565{
566 std::unique_ptr<BOARD> board = KI_TEST::RequireBoardFromPcbnewQAData( "issue25021/backdrill.kicad_pcb" );
567
568 ODB_PRODUCT product( ExportOdb( *board, TempDir().Path() ) );
569 const std::map<std::pair<wxString, wxString>, wxString> stopLayers = {
570 { { wxS( "f.cu" ), wxS( "in3.cu" ) }, wxS( "in4.cu" ) },
571 { { wxS( "in6.cu" ), wxS( "b.cu" ) }, wxS( "in5.cu" ) },
572 { { wxS( "in3.cu" ), wxS( "b.cu" ) }, wxS( "in2.cu" ) }
573 };
574 size_t checked = 0;
575
576 for( const ODB_MATRIX_ROW& row : product.Matrix() )
577 {
578 if( row.m_addType != wxS( "BACKDRILL" ) )
579 continue;
580
581 auto it = stopLayers.find( { row.m_startName, row.m_endName } );
582 BOOST_REQUIRE( it != stopLayers.end() );
583 ++checked;
584 std::string expected = ".backdrill_penetrate_stop_layer=" + it->second.ToStdString();
585 fs::path attrlistPath( product.LayerFile( row.m_name, wxS( "attrlist" ) ).ToStdWstring() );
586 std::vector<std::string> lines = ReadLines( attrlistPath );
587 BOOST_CHECK( std::find( lines.begin(), lines.end(), expected ) != lines.end() );
588 }
589
590 BOOST_CHECK_EQUAL( checked, stopLayers.size() );
591}
592
const char * name
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:128
General utilities for PCB file IO for QA programs.
Handle the data for a net.
Definition netinfo.h:50
int GetNetCode() const
Definition netinfo.h:104
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 CountRecordsWithOption(const std::string &aName, const std::string &aOption) const
Definition pad.h:61
VECTOR2I GetPosition() const override
Definition pad.cpp:255
VIATYPE GetViaType() const
Definition pcb_track.h:422
FAB_COMPONENT_ROW MakeFabComponentRow(const FOOTPRINT &aFootprint, const wxString &aVariant)
std::map< int, wxString > FabAnonymousNetNames(const BOARD &aBoard)
Anonymous names NET_1 through NET_n keyed and numbered by net code.
std::vector< FAB_TEST_POINT > FabTestPoints(const BOARD &aBoard)
The caller must build board connectivity before enumerating net endpoints.
@ F_Cu
Definition layer_ids.h:60
std::unique_ptr< BOARD > RequireBoardFromPcbnewQAData(const std::filesystem::path &aFilename)
Require a board from the path relative to the pcbnew QA data directory.
wxString GenLegalEntityName(const wxString &aStr)
Definition odb_util.cpp:212
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition eda_angle.h:437
size_t CountPads(const BOARD &aBoard, PAD_PROP aProperty, PCB_LAYER_ID aLayer)
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()
@ NPTH
like PAD_PTH, but not plated mechanical use only, no connection allowed
Definition padstack.h:102
@ FIDUCIAL_LOCAL
a fiducial (usually a smd) local to the parent footprint
Definition padstack.h:117
@ FIDUCIAL_GLBL
a fiducial (usually a smd) for the full board
Definition padstack.h:116
@ PRESSFIT
a PTH with a hole diameter with tight tolerances for press fit pin
Definition padstack.h:122
@ TESTPOINT
a test point pad
Definition padstack.h:118
Footprint facts resolved for one assembly variant.
wxString Field(const wxString &aName) const
Variant-resolved shown text, empty when the field is missing.
One LAYER block of matrix/matrix.
BOOST_AUTO_TEST_CASE(HorizontalAlignment)
BOOST_AUTO_TEST_SUITE(CadstarPartParser)
BOOST_REQUIRE(intersection.has_value()==c.ExpectedIntersection.has_value())
BOOST_AUTO_TEST_SUITE_END()
KIBIS top(path, &reporter)
VECTOR3I expected(15, 30, 45)
@ NP_IDX_DRILL_EPOINT
@ NP_IDX_NODRILL_EXP
@ NP_IDX_NODRILL_EPOINT
BOOST_AUTO_TEST_CASE(FabComponentRowUsesSavedVariantFields)
int radius
BOOST_TEST_MESSAGE("Polyline has "<< chain.PointCount()<< " points")
BOOST_CHECK_EQUAL(result, "25.4")
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition typeinfo.h:89