KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_pads_part_structures.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 (C) 2025 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, you may find one here:
18 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
19 * or you may search the http://www.gnu.org website for the version 2 license,
20 * or you may write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22 */
23
24#include <boost/test/unit_test.hpp>
25#include <sstream>
28
29using namespace PADS_IO;
30
31
32BOOST_AUTO_TEST_SUITE( PadsPartStructures )
33
34
35BOOST_AUTO_TEST_CASE( Part_DefaultConstruction )
36{
37 PART part;
38 BOOST_CHECK( part.name.empty() );
39 BOOST_CHECK( part.decal.empty() );
40 BOOST_CHECK( part.alternate_decals.empty() );
41 BOOST_CHECK( part.value.empty() );
42 BOOST_CHECK_EQUAL( part.rotation, 0.0 );
43 BOOST_CHECK_EQUAL( part.bottom_layer, false );
44 BOOST_CHECK_EQUAL( part.glued, false );
45}
46
47
48BOOST_AUTO_TEST_CASE( Part_SingleDecal )
49{
50 PART part;
51 part.decal = "DIP8";
52
53 BOOST_CHECK_EQUAL( part.decal, "DIP8" );
54 BOOST_CHECK( part.alternate_decals.empty() );
55}
56
57
58BOOST_AUTO_TEST_CASE( Part_AlternateDecals )
59{
60 PART part;
61 part.decal = "DIP8";
62 part.alternate_decals.push_back( "SOIC8" );
63 part.alternate_decals.push_back( "QFN8" );
64
65 BOOST_CHECK_EQUAL( part.decal, "DIP8" );
66 BOOST_REQUIRE_EQUAL( part.alternate_decals.size(), 2 );
67 BOOST_CHECK_EQUAL( part.alternate_decals[0], "SOIC8" );
68 BOOST_CHECK_EQUAL( part.alternate_decals[1], "QFN8" );
69}
70
71
72BOOST_AUTO_TEST_CASE( DecalStringSplit_SingleDecal )
73{
74 // Simulate what the parser does with a single decal
75 std::string decal_string = "RESISTOR_0603";
76
77 std::string primary;
78 std::vector<std::string> alternates;
79
80 size_t pos = 0;
81 size_t colon_pos = 0;
82 bool first = true;
83
84 while( ( colon_pos = decal_string.find( ':', pos ) ) != std::string::npos )
85 {
86 std::string decal_name = decal_string.substr( pos, colon_pos - pos );
87
88 if( first )
89 {
90 primary = decal_name;
91 first = false;
92 }
93 else
94 {
95 alternates.push_back( decal_name );
96 }
97
98 pos = colon_pos + 1;
99 }
100
101 std::string last_decal = decal_string.substr( pos );
102
103 if( first )
104 primary = last_decal;
105 else
106 alternates.push_back( last_decal );
107
108 BOOST_CHECK_EQUAL( primary, "RESISTOR_0603" );
109 BOOST_CHECK( alternates.empty() );
110}
111
112
113BOOST_AUTO_TEST_CASE( DecalStringSplit_TwoDecals )
114{
115 std::string decal_string = "DIP8:SOIC8";
116
117 std::string primary;
118 std::vector<std::string> alternates;
119
120 size_t pos = 0;
121 size_t colon_pos = 0;
122 bool first = true;
123
124 while( ( colon_pos = decal_string.find( ':', pos ) ) != std::string::npos )
125 {
126 std::string decal_name = decal_string.substr( pos, colon_pos - pos );
127
128 if( first )
129 {
130 primary = decal_name;
131 first = false;
132 }
133 else
134 {
135 alternates.push_back( decal_name );
136 }
137
138 pos = colon_pos + 1;
139 }
140
141 std::string last_decal = decal_string.substr( pos );
142
143 if( first )
144 primary = last_decal;
145 else
146 alternates.push_back( last_decal );
147
148 BOOST_CHECK_EQUAL( primary, "DIP8" );
149 BOOST_REQUIRE_EQUAL( alternates.size(), 1 );
150 BOOST_CHECK_EQUAL( alternates[0], "SOIC8" );
151}
152
153
154BOOST_AUTO_TEST_CASE( DecalStringSplit_ThreeDecals )
155{
156 std::string decal_string = "DIP8:SOIC8:QFN8";
157
158 std::string primary;
159 std::vector<std::string> alternates;
160
161 size_t pos = 0;
162 size_t colon_pos = 0;
163 bool first = true;
164
165 while( ( colon_pos = decal_string.find( ':', pos ) ) != std::string::npos )
166 {
167 std::string decal_name = decal_string.substr( pos, colon_pos - pos );
168
169 if( first )
170 {
171 primary = decal_name;
172 first = false;
173 }
174 else
175 {
176 alternates.push_back( decal_name );
177 }
178
179 pos = colon_pos + 1;
180 }
181
182 std::string last_decal = decal_string.substr( pos );
183
184 if( first )
185 primary = last_decal;
186 else
187 alternates.push_back( last_decal );
188
189 BOOST_CHECK_EQUAL( primary, "DIP8" );
190 BOOST_REQUIRE_EQUAL( alternates.size(), 2 );
191 BOOST_CHECK_EQUAL( alternates[0], "SOIC8" );
192 BOOST_CHECK_EQUAL( alternates[1], "QFN8" );
193}
194
195
196BOOST_AUTO_TEST_CASE( DecalStringSplit_ManyDecals )
197{
198 std::string decal_string = "PKG1:PKG2:PKG3:PKG4:PKG5";
199
200 std::string primary;
201 std::vector<std::string> alternates;
202
203 size_t pos = 0;
204 size_t colon_pos = 0;
205 bool first = true;
206
207 while( ( colon_pos = decal_string.find( ':', pos ) ) != std::string::npos )
208 {
209 std::string decal_name = decal_string.substr( pos, colon_pos - pos );
210
211 if( first )
212 {
213 primary = decal_name;
214 first = false;
215 }
216 else
217 {
218 alternates.push_back( decal_name );
219 }
220
221 pos = colon_pos + 1;
222 }
223
224 std::string last_decal = decal_string.substr( pos );
225
226 if( first )
227 primary = last_decal;
228 else
229 alternates.push_back( last_decal );
230
231 BOOST_CHECK_EQUAL( primary, "PKG1" );
232 BOOST_REQUIRE_EQUAL( alternates.size(), 4 );
233 BOOST_CHECK_EQUAL( alternates[0], "PKG2" );
234 BOOST_CHECK_EQUAL( alternates[1], "PKG3" );
235 BOOST_CHECK_EQUAL( alternates[2], "PKG4" );
236 BOOST_CHECK_EQUAL( alternates[3], "PKG5" );
237}
238
239
240BOOST_AUTO_TEST_CASE( Parameters_DefaultThermalSettings )
241{
242 PARAMETERS params;
243 BOOST_CHECK_CLOSE( params.thermal_line_width, 30.0, 0.001 );
244 BOOST_CHECK_CLOSE( params.thermal_smd_width, 20.0, 0.001 );
245 BOOST_CHECK_EQUAL( params.thermal_flags, 0 );
246 BOOST_CHECK_CLOSE( params.thermal_min_clearance, 5.0, 0.001 );
248}
249
250
251BOOST_AUTO_TEST_CASE( Parameters_CustomThermalSettings )
252{
253 PARAMETERS params;
254 params.thermal_line_width = 40.0;
255 params.thermal_smd_width = 25.0;
256 params.thermal_flags = 1024;
257 params.thermal_min_clearance = 10.0;
258 params.thermal_min_spokes = 6;
259
260 BOOST_CHECK_CLOSE( params.thermal_line_width, 40.0, 0.001 );
261 BOOST_CHECK_CLOSE( params.thermal_smd_width, 25.0, 0.001 );
262 BOOST_CHECK_EQUAL( params.thermal_flags, 1024 );
263 BOOST_CHECK_CLOSE( params.thermal_min_clearance, 10.0, 0.001 );
265}
266
267
268BOOST_AUTO_TEST_CASE( Pour_DefaultConstruction )
269{
270 POUR pour;
271 BOOST_CHECK( pour.net_name.empty() );
272 BOOST_CHECK_EQUAL( pour.layer, 0 );
273 BOOST_CHECK_EQUAL( pour.priority, 0 );
274 BOOST_CHECK_EQUAL( pour.width, 0.0 );
275 BOOST_CHECK( pour.points.empty() );
276 BOOST_CHECK_EQUAL( pour.is_cutout, false );
277 BOOST_CHECK( pour.owner_pour.empty() );
278}
279
280
281BOOST_AUTO_TEST_CASE( Pour_CutoutFlag )
282{
283 POUR outline;
284 outline.net_name = "GND";
285 outline.layer = 1;
286 outline.is_cutout = false;
287 outline.owner_pour = "POUR1";
288
289 POUR cutout;
290 cutout.net_name = "GND";
291 cutout.layer = 1;
292 cutout.is_cutout = true;
293 cutout.owner_pour = "POUR1";
294
295 BOOST_CHECK_EQUAL( outline.is_cutout, false );
296 BOOST_CHECK_EQUAL( cutout.is_cutout, true );
297 BOOST_CHECK_EQUAL( outline.owner_pour, cutout.owner_pour );
298}
299
300
301BOOST_AUTO_TEST_CASE( ReuseInstance_DefaultConstruction )
302{
303 REUSE_INSTANCE instance;
304 BOOST_CHECK( instance.instance_name.empty() );
305 BOOST_CHECK( instance.part_naming.empty() );
306 BOOST_CHECK( instance.net_naming.empty() );
307 BOOST_CHECK_EQUAL( instance.location.x, 0.0 );
308 BOOST_CHECK_EQUAL( instance.location.y, 0.0 );
309 BOOST_CHECK_EQUAL( instance.rotation, 0.0 );
310 BOOST_CHECK_EQUAL( instance.glued, false );
311}
312
313
314BOOST_AUTO_TEST_CASE( ReuseInstance_WithValues )
315{
316 REUSE_INSTANCE instance;
317 instance.instance_name = "BLOCK1_INST1";
318 instance.part_naming = "PREFIX";
319 instance.net_naming = "APPEND";
320 instance.location.x = 1000.0;
321 instance.location.y = 2000.0;
322 instance.rotation = 90.0;
323 instance.glued = true;
324
325 BOOST_CHECK_EQUAL( instance.instance_name, "BLOCK1_INST1" );
326 BOOST_CHECK_EQUAL( instance.part_naming, "PREFIX" );
327 BOOST_CHECK_EQUAL( instance.net_naming, "APPEND" );
328 BOOST_CHECK_CLOSE( instance.location.x, 1000.0, 0.001 );
329 BOOST_CHECK_CLOSE( instance.location.y, 2000.0, 0.001 );
330 BOOST_CHECK_CLOSE( instance.rotation, 90.0, 0.001 );
331 BOOST_CHECK_EQUAL( instance.glued, true );
332}
333
334
335BOOST_AUTO_TEST_CASE( ReuseBlock_DefaultConstruction )
336{
337 REUSE_BLOCK block;
338 BOOST_CHECK( block.name.empty() );
339 BOOST_CHECK_EQUAL( block.timestamp, 0 );
340 BOOST_CHECK( block.part_naming.empty() );
341 BOOST_CHECK( block.net_naming.empty() );
342 BOOST_CHECK( block.part_names.empty() );
343 BOOST_CHECK( block.nets.empty() );
344 BOOST_CHECK( block.instances.empty() );
345}
346
347
348BOOST_AUTO_TEST_CASE( ReuseBlock_WithContents )
349{
350 REUSE_BLOCK block;
351 block.name = "MEMORY_BLOCK";
352 block.timestamp = 1234567890;
353 block.part_naming = "PREFIX";
354 block.net_naming = "APPEND";
355 block.part_names.push_back( "U1" );
356 block.part_names.push_back( "U2" );
357 block.part_names.push_back( "C1" );
358
359 REUSE_NET net1;
360 net1.merge = true;
361 net1.name = "DATA0";
362 block.nets.push_back( net1 );
363
364 REUSE_NET net2;
365 net2.merge = false;
366 net2.name = "DATA1";
367 block.nets.push_back( net2 );
368
369 REUSE_INSTANCE inst;
370 inst.instance_name = "MEM1";
371 inst.location.x = 5000.0;
372 inst.location.y = 3000.0;
373 block.instances.push_back( inst );
374
375 BOOST_CHECK_EQUAL( block.name, "MEMORY_BLOCK" );
376 BOOST_CHECK_EQUAL( block.timestamp, 1234567890 );
377 BOOST_CHECK_EQUAL( block.part_naming, "PREFIX" );
378 BOOST_CHECK_EQUAL( block.net_naming, "APPEND" );
379 BOOST_REQUIRE_EQUAL( block.part_names.size(), 3 );
380 BOOST_CHECK_EQUAL( block.part_names[0], "U1" );
381 BOOST_CHECK_EQUAL( block.part_names[1], "U2" );
382 BOOST_CHECK_EQUAL( block.part_names[2], "C1" );
383 BOOST_REQUIRE_EQUAL( block.nets.size(), 2 );
384 BOOST_CHECK_EQUAL( block.nets[0].name, "DATA0" );
385 BOOST_CHECK_EQUAL( block.nets[0].merge, true );
386 BOOST_CHECK_EQUAL( block.nets[1].name, "DATA1" );
387 BOOST_CHECK_EQUAL( block.nets[1].merge, false );
388 BOOST_REQUIRE_EQUAL( block.instances.size(), 1 );
389 BOOST_CHECK_EQUAL( block.instances[0].instance_name, "MEM1" );
390}
391
392
393BOOST_AUTO_TEST_CASE( ReuseBlock_PartMembershipMapping )
394{
395 // Test building the part-to-block membership map used during expansion
396 std::map<std::string, REUSE_BLOCK> blocks;
397
398 REUSE_BLOCK memBlock;
399 memBlock.name = "MEMORY";
400 memBlock.part_names.push_back( "U1" );
401 memBlock.part_names.push_back( "U2" );
402 blocks["MEMORY"] = memBlock;
403
404 REUSE_BLOCK pwrBlock;
405 pwrBlock.name = "POWER";
406 pwrBlock.part_names.push_back( "C1" );
407 pwrBlock.part_names.push_back( "C2" );
408 pwrBlock.part_names.push_back( "L1" );
409 blocks["POWER"] = pwrBlock;
410
411 // Build membership map as done in pcb_io_pads.cpp
412 std::map<std::string, std::string> partToBlockMap;
413
414 for( const auto& [blockName, block] : blocks )
415 {
416 for( const std::string& partName : block.part_names )
417 {
418 partToBlockMap[partName] = blockName;
419 }
420 }
421
422 BOOST_REQUIRE_EQUAL( partToBlockMap.size(), 5 );
423 BOOST_CHECK_EQUAL( partToBlockMap["U1"], "MEMORY" );
424 BOOST_CHECK_EQUAL( partToBlockMap["U2"], "MEMORY" );
425 BOOST_CHECK_EQUAL( partToBlockMap["C1"], "POWER" );
426 BOOST_CHECK_EQUAL( partToBlockMap["C2"], "POWER" );
427 BOOST_CHECK_EQUAL( partToBlockMap["L1"], "POWER" );
428}
429
430
431BOOST_AUTO_TEST_CASE( ReuseBlock_EmptyBlocksMap )
432{
433 // Test that empty reuse blocks result in empty membership map
434 std::map<std::string, REUSE_BLOCK> blocks;
435 std::map<std::string, std::string> partToBlockMap;
436
437 for( const auto& [blockName, block] : blocks )
438 {
439 for( const std::string& partName : block.part_names )
440 {
441 partToBlockMap[partName] = blockName;
442 }
443 }
444
445 BOOST_CHECK( partToBlockMap.empty() );
446}
447
448
449BOOST_AUTO_TEST_CASE( ReuseBlock_GroupCreationCondition )
450{
451 // Test conditions for when groups should be created
452 REUSE_BLOCK emptyBlock;
453 emptyBlock.name = "EMPTY";
454
455 REUSE_BLOCK blockWithParts;
456 blockWithParts.name = "WITH_PARTS";
457 blockWithParts.part_names.push_back( "U1" );
458
459 REUSE_BLOCK blockWithInstances;
460 blockWithInstances.name = "WITH_INSTANCES";
461 REUSE_INSTANCE inst;
462 inst.instance_name = "INST1";
463 blockWithInstances.instances.push_back( inst );
464
465 // Groups should be created when block has parts OR instances
466 bool createEmptyGroup = !emptyBlock.instances.empty() || !emptyBlock.part_names.empty();
467 bool createPartsGroup = !blockWithParts.instances.empty() || !blockWithParts.part_names.empty();
468 bool createInstanceGroup = !blockWithInstances.instances.empty() || !blockWithInstances.part_names.empty();
469
470 BOOST_CHECK_EQUAL( createEmptyGroup, false );
471 BOOST_CHECK_EQUAL( createPartsGroup, true );
472 BOOST_CHECK_EQUAL( createInstanceGroup, true );
473}
474
475
476BOOST_AUTO_TEST_CASE( Cluster_DefaultConstruction )
477{
478 CLUSTER cluster;
479 BOOST_CHECK( cluster.name.empty() );
480 BOOST_CHECK_EQUAL( cluster.id, 0 );
481 BOOST_CHECK( cluster.net_names.empty() );
482 BOOST_CHECK( cluster.segment_refs.empty() );
483}
484
485
486BOOST_AUTO_TEST_CASE( Cluster_WithContents )
487{
488 CLUSTER cluster;
489 cluster.name = "MEMORY_BUS";
490 cluster.id = 42;
491 cluster.net_names.push_back( "DATA0" );
492 cluster.net_names.push_back( "DATA1" );
493 cluster.net_names.push_back( "ADDR0" );
494 cluster.segment_refs.push_back( "DATA0.1" );
495 cluster.segment_refs.push_back( "DATA0.2" );
496 cluster.segment_refs.push_back( "DATA1.1" );
497
498 BOOST_CHECK_EQUAL( cluster.name, "MEMORY_BUS" );
499 BOOST_CHECK_EQUAL( cluster.id, 42 );
500 BOOST_REQUIRE_EQUAL( cluster.net_names.size(), 3 );
501 BOOST_CHECK_EQUAL( cluster.net_names[0], "DATA0" );
502 BOOST_CHECK_EQUAL( cluster.net_names[1], "DATA1" );
503 BOOST_CHECK_EQUAL( cluster.net_names[2], "ADDR0" );
504 BOOST_REQUIRE_EQUAL( cluster.segment_refs.size(), 3 );
505 BOOST_CHECK_EQUAL( cluster.segment_refs[0], "DATA0.1" );
506 BOOST_CHECK_EQUAL( cluster.segment_refs[1], "DATA0.2" );
507 BOOST_CHECK_EQUAL( cluster.segment_refs[2], "DATA1.1" );
508}
509
510
511BOOST_AUTO_TEST_CASE( Cluster_SegmentRefDetection )
512{
513 // Test the segment reference detection logic used in parser
514 std::string netName = "DATA0";
515 std::string segRef = "DATA0.1";
516 std::string segRefComplex = "NET_123.45";
517
518 bool netNameHasDot = netName.find( '.' ) != std::string::npos;
519 bool segRefHasDot = segRef.find( '.' ) != std::string::npos;
520 bool segRefComplexHasDot = segRefComplex.find( '.' ) != std::string::npos;
521
522 BOOST_CHECK_EQUAL( netNameHasDot, false );
523 BOOST_CHECK_EQUAL( segRefHasDot, true );
524 BOOST_CHECK_EQUAL( segRefComplexHasDot, true );
525}
526
527
528BOOST_AUTO_TEST_CASE( TestPoint_DefaultConstruction )
529{
531 BOOST_CHECK( tp.type.empty() );
532 BOOST_CHECK_EQUAL( tp.x, 0.0 );
533 BOOST_CHECK_EQUAL( tp.y, 0.0 );
534 BOOST_CHECK_EQUAL( tp.side, 0 );
535 BOOST_CHECK( tp.net_name.empty() );
536 BOOST_CHECK( tp.symbol_name.empty() );
537}
538
539
540BOOST_AUTO_TEST_CASE( TestPoint_ViaType )
541{
543 tp.type = "VIA";
544 tp.x = 7000.0;
545 tp.y = 3450.0;
546 tp.side = 0; // Through
547 tp.net_name = "+5V";
548 tp.symbol_name = "TESTVIATHRU";
549
550 BOOST_CHECK_EQUAL( tp.type, "VIA" );
551 BOOST_CHECK_CLOSE( tp.x, 7000.0, 0.001 );
552 BOOST_CHECK_CLOSE( tp.y, 3450.0, 0.001 );
553 BOOST_CHECK_EQUAL( tp.side, 0 );
554 BOOST_CHECK_EQUAL( tp.net_name, "+5V" );
555 BOOST_CHECK_EQUAL( tp.symbol_name, "TESTVIATHRU" );
556}
557
558
559BOOST_AUTO_TEST_CASE( TestPoint_PinType )
560{
562 tp.type = "PIN";
563 tp.x = 1000.0;
564 tp.y = 2000.0;
565 tp.side = 1; // Top
566 tp.net_name = "GND";
567 tp.symbol_name = "U1.3";
568
569 BOOST_CHECK_EQUAL( tp.type, "PIN" );
570 BOOST_CHECK_EQUAL( tp.side, 1 );
571 BOOST_CHECK_EQUAL( tp.net_name, "GND" );
572 BOOST_CHECK_EQUAL( tp.symbol_name, "U1.3" );
573}
574
575
576BOOST_AUTO_TEST_CASE( TestPoint_SideValues )
577{
578 // Test side value meaning: 0=through, 1=top, 2=bottom
579 TEST_POINT tpThrough, tpTop, tpBottom;
580 tpThrough.side = 0;
581 tpTop.side = 1;
582 tpBottom.side = 2;
583
584 BOOST_CHECK_EQUAL( tpThrough.side, 0 );
585 BOOST_CHECK_EQUAL( tpTop.side, 1 );
586 BOOST_CHECK_EQUAL( tpBottom.side, 2 );
587}
588
589
590BOOST_AUTO_TEST_CASE( Dimension_DefaultConstruction )
591{
592 DIMENSION dim;
593 BOOST_CHECK( dim.name.empty() );
594 BOOST_CHECK_EQUAL( dim.x, 0.0 );
595 BOOST_CHECK_EQUAL( dim.y, 0.0 );
596 BOOST_CHECK_EQUAL( dim.layer, 0 );
597 BOOST_CHECK( dim.points.empty() );
598 BOOST_CHECK( dim.text.empty() );
599 BOOST_CHECK_EQUAL( dim.text_height, 0.0 );
600 BOOST_CHECK_EQUAL( dim.text_width, 0.0 );
601 BOOST_CHECK_EQUAL( dim.rotation, 0.0 );
602}
603
604
605BOOST_AUTO_TEST_CASE( Dimension_WithValues )
606{
607 DIMENSION dim;
608 dim.name = "DIM001";
609 dim.x = 1000.0;
610 dim.y = 2000.0;
611 dim.layer = 26; // Typical doc layer
612 dim.text = "10.5mm";
613 dim.text_height = 50.0;
614 dim.text_width = 8.0;
615 dim.rotation = 90.0;
616
617 dim.points.push_back( { 0.0, 0.0 } );
618 dim.points.push_back( { 1050.0, 0.0 } );
619 dim.points.push_back( { 1050.0, 100.0 } );
620
621 BOOST_CHECK_EQUAL( dim.name, "DIM001" );
622 BOOST_CHECK_CLOSE( dim.x, 1000.0, 0.001 );
623 BOOST_CHECK_CLOSE( dim.y, 2000.0, 0.001 );
624 BOOST_CHECK_EQUAL( dim.layer, 26 );
625 BOOST_CHECK_EQUAL( dim.text, "10.5mm" );
626 BOOST_CHECK_CLOSE( dim.text_height, 50.0, 0.001 );
627 BOOST_CHECK_CLOSE( dim.text_width, 8.0, 0.001 );
628 BOOST_CHECK_CLOSE( dim.rotation, 90.0, 0.001 );
629 BOOST_CHECK_EQUAL( dim.points.size(), 3 );
630}
631
632
633BOOST_AUTO_TEST_CASE( Dimension_NamePrefix )
634{
635 // Test the dimension detection pattern (names starting with "DIM")
636 std::string dimName1 = "DIM001";
637 std::string dimName2 = "DIM_BOARD_WIDTH";
638 std::string nonDimName = "OUTLINE";
639
640 BOOST_CHECK( dimName1.rfind( "DIM", 0 ) == 0 );
641 BOOST_CHECK( dimName2.rfind( "DIM", 0 ) == 0 );
642 BOOST_CHECK( nonDimName.rfind( "DIM", 0 ) != 0 );
643}
644
645
646BOOST_AUTO_TEST_CASE( DesignRules_DefaultConstruction )
647{
648 DESIGN_RULES rules;
649
650 BOOST_CHECK_CLOSE( rules.min_clearance, 8.0, 0.001 );
651 BOOST_CHECK_CLOSE( rules.default_clearance, 10.0, 0.001 );
652 BOOST_CHECK_CLOSE( rules.min_track_width, 6.0, 0.001 );
653 BOOST_CHECK_CLOSE( rules.default_track_width, 10.0, 0.001 );
654 BOOST_CHECK_CLOSE( rules.min_via_size, 20.0, 0.001 );
655 BOOST_CHECK_CLOSE( rules.default_via_size, 40.0, 0.001 );
656 BOOST_CHECK_CLOSE( rules.min_via_drill, 10.0, 0.001 );
657 BOOST_CHECK_CLOSE( rules.default_via_drill, 20.0, 0.001 );
658 BOOST_CHECK_CLOSE( rules.hole_to_hole, 10.0, 0.001 );
659 BOOST_CHECK_CLOSE( rules.silk_clearance, 5.0, 0.001 );
660 BOOST_CHECK_CLOSE( rules.mask_clearance, 3.0, 0.001 );
661}
662
663
664BOOST_AUTO_TEST_CASE( DesignRules_WithValues )
665{
666 DESIGN_RULES rules;
667 rules.min_clearance = 12.0;
668 rules.default_clearance = 15.0;
669 rules.min_track_width = 8.0;
670 rules.default_track_width = 12.0;
671 rules.min_via_size = 25.0;
672 rules.default_via_size = 50.0;
673 rules.min_via_drill = 15.0;
674 rules.default_via_drill = 25.0;
675 rules.hole_to_hole = 12.0;
676 rules.silk_clearance = 6.0;
677 rules.mask_clearance = 4.0;
678
679 BOOST_CHECK_CLOSE( rules.min_clearance, 12.0, 0.001 );
680 BOOST_CHECK_CLOSE( rules.default_clearance, 15.0, 0.001 );
681 BOOST_CHECK_CLOSE( rules.min_track_width, 8.0, 0.001 );
682 BOOST_CHECK_CLOSE( rules.default_track_width, 12.0, 0.001 );
683 BOOST_CHECK_CLOSE( rules.min_via_size, 25.0, 0.001 );
684 BOOST_CHECK_CLOSE( rules.default_via_size, 50.0, 0.001 );
685 BOOST_CHECK_CLOSE( rules.min_via_drill, 15.0, 0.001 );
686 BOOST_CHECK_CLOSE( rules.default_via_drill, 25.0, 0.001 );
687 BOOST_CHECK_CLOSE( rules.hole_to_hole, 12.0, 0.001 );
688 BOOST_CHECK_CLOSE( rules.silk_clearance, 6.0, 0.001 );
689 BOOST_CHECK_CLOSE( rules.mask_clearance, 4.0, 0.001 );
690}
691
692
693BOOST_AUTO_TEST_CASE( NetClassDef_DefaultConstruction )
694{
695 NET_CLASS_DEF nc;
696 BOOST_CHECK( nc.name.empty() );
697 BOOST_CHECK_EQUAL( nc.clearance, 0.0 );
699 BOOST_CHECK_EQUAL( nc.via_size, 0.0 );
700 BOOST_CHECK_EQUAL( nc.via_drill, 0.0 );
703 BOOST_CHECK( nc.net_names.empty() );
704}
705
706
707BOOST_AUTO_TEST_CASE( NetClassDef_WithValues )
708{
709 NET_CLASS_DEF nc;
710 nc.name = "HighSpeed";
711 nc.clearance = 12.0;
712 nc.track_width = 8.0;
713 nc.via_size = 30.0;
714 nc.via_drill = 15.0;
715 nc.diff_pair_gap = 6.0;
716 nc.diff_pair_width = 8.0;
717 nc.net_names.push_back( "CLK" );
718 nc.net_names.push_back( "DATA0" );
719 nc.net_names.push_back( "DATA1" );
720
721 BOOST_CHECK_EQUAL( nc.name, "HighSpeed" );
722 BOOST_CHECK_CLOSE( nc.clearance, 12.0, 0.001 );
723 BOOST_CHECK_CLOSE( nc.track_width, 8.0, 0.001 );
724 BOOST_CHECK_CLOSE( nc.via_size, 30.0, 0.001 );
725 BOOST_CHECK_CLOSE( nc.via_drill, 15.0, 0.001 );
726 BOOST_CHECK_CLOSE( nc.diff_pair_gap, 6.0, 0.001 );
727 BOOST_CHECK_CLOSE( nc.diff_pair_width, 8.0, 0.001 );
728 BOOST_CHECK_EQUAL( nc.net_names.size(), 3 );
729 BOOST_CHECK_EQUAL( nc.net_names[0], "CLK" );
730 BOOST_CHECK_EQUAL( nc.net_names[1], "DATA0" );
731 BOOST_CHECK_EQUAL( nc.net_names[2], "DATA1" );
732}
733
734
735BOOST_AUTO_TEST_CASE( DiffPairDef_DefaultConstruction )
736{
737 DIFF_PAIR_DEF dp;
738 BOOST_CHECK( dp.name.empty() );
739 BOOST_CHECK( dp.positive_net.empty() );
740 BOOST_CHECK( dp.negative_net.empty() );
741 BOOST_CHECK_EQUAL( dp.gap, 0.0 );
742 BOOST_CHECK_EQUAL( dp.width, 0.0 );
743}
744
745
746BOOST_AUTO_TEST_CASE( DiffPairDef_WithValues )
747{
748 DIFF_PAIR_DEF dp;
749 dp.name = "USB_DATA";
750 dp.positive_net = "USB_D+";
751 dp.negative_net = "USB_D-";
752 dp.gap = 8.0;
753 dp.width = 10.0;
754
755 BOOST_CHECK_EQUAL( dp.name, "USB_DATA" );
756 BOOST_CHECK_EQUAL( dp.positive_net, "USB_D+" );
757 BOOST_CHECK_EQUAL( dp.negative_net, "USB_D-" );
758 BOOST_CHECK_CLOSE( dp.gap, 8.0, 0.001 );
759 BOOST_CHECK_CLOSE( dp.width, 10.0, 0.001 );
760}
761
762
General utilities for PCB file IO for QA programs.
A cluster of related route segments that should be grouped together.
std::vector< std::string > segment_refs
References to route segments in cluster.
std::string name
Cluster name/identifier.
int id
Cluster ID number.
std::vector< std::string > net_names
Nets belonging to this cluster.
Design rule definitions from PCB section.
double silk_clearance
Minimum silkscreen clearance (SILKCLEAR)
double default_track_width
Default track width (DEFAULTTRACKWID)
double default_via_drill
Default via drill diameter (DEFAULTVIADRILL)
double min_track_width
Minimum track width (MINTRACKWID)
double min_via_size
Minimum via outer diameter (MINVIASIZE)
double min_via_drill
Minimum via drill diameter (MINVIADRILL)
double min_clearance
Minimum copper clearance (MINCLEAR)
double default_via_size
Default via outer diameter (DEFAULTVIASIZE)
double default_clearance
Default copper clearance (DEFAULTCLEAR)
double hole_to_hole
Minimum hole-to-hole spacing (HOLEHOLE)
double mask_clearance
Solder mask clearance (MASKCLEAR)
Differential pair definition.
double width
Trace width.
std::string positive_net
Positive net name.
std::string negative_net
Negative net name.
double gap
Spacing between traces.
std::string name
Pair name.
A dimension annotation for measurement display.
std::string name
Dimension identifier.
double text_width
Text width.
double y
Origin Y coordinate.
double rotation
Text rotation angle.
double x
Origin X coordinate.
std::string text
Dimension text/value.
int layer
Layer for dimension graphics.
double text_height
Text height.
std::vector< POINT > points
Dimension geometry points (measurement endpoints)
Net class definition with routing constraints.
double via_drill
Via drill diameter (VIADRILL)
double clearance
Copper clearance (CLEARANCE)
std::vector< std::string > net_names
Nets assigned to this class.
double track_width
Track width (TRACKWIDTH)
std::string name
Net class name.
double diff_pair_width
Differential pair width (DIFFPAIRWIDTH)
double diff_pair_gap
Differential pair gap (DIFFPAIRGAP)
double via_size
Via diameter (VIASIZE)
double thermal_line_width
Thermal line width for THT (THERLINEWID)
double thermal_smd_width
Thermal line width for SMD (THERSMDWID)
double thermal_min_clearance
Starved thermal minimum clearance (STMINCLEAR)
int thermal_flags
Thermal relief flags (THERFLAGS)
int thermal_min_spokes
Starved thermal minimum spokes (STMINSPOKES)
std::string value
std::string decal
Primary decal (first in colon-separated list)
std::string name
std::vector< std::string > alternate_decals
Alternate decals (remaining after ':' splits)
bool is_cutout
True if this is a cutout (POCUT) piece.
std::string net_name
std::string owner_pour
Name of parent pour (7th field in header)
std::vector< ARC_POINT > points
Pour outline, may include arc segments.
A reuse block definition containing parts and routes that can be instantiated.
std::vector< REUSE_NET > nets
Nets contained in this block with merge flags.
std::string net_naming
Default net naming scheme.
long timestamp
Creation/modification timestamp.
std::string part_naming
Default part naming scheme.
std::vector< std::string > part_names
Parts contained in this block.
std::vector< REUSE_INSTANCE > instances
Placements of this block.
std::string name
Block type name.
std::string instance_name
Instance name.
std::string part_naming
Part naming scheme (may be multi-word like "PREFIX pref")
std::string net_naming
Net naming scheme (may be multi-word like "SUFFIX suf")
bool glued
True if glued in place.
POINT location
Placement location.
double rotation
Rotation angle in degrees.
A reuse block instance placement.
std::string name
Original net name from reuse definition.
bool merge
True to merge nets, false to rename.
A test point definition for manufacturing/testing access.
int side
Probe side (0=through, 1=top, 2=bottom)
BOOST_AUTO_TEST_CASE(HorizontalAlignment)
BOOST_AUTO_TEST_SUITE(CadstarPartParser)
BOOST_AUTO_TEST_SUITE_END()
BOOST_AUTO_TEST_CASE(Part_DefaultConstruction)
BOOST_CHECK_EQUAL(result, "25.4")
static thread_pool * tp