KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_pads_sch_parser.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 modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation, either version 3 of the License, or (at your
9 * option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * 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
24
25#include <boost/test/unit_test.hpp>
28
31#include <io/pads/pads_common.h>
32#include <lib_symbol.h>
33#include <sch_shape.h>
34#include <sch_pin.h>
35
36
37BOOST_AUTO_TEST_SUITE( PadsSchParser )
38
39
40BOOST_AUTO_TEST_CASE( CheckFileHeader_ValidLogicFile )
41{
42 std::string testFile = KI_TEST::GetEeschemaTestDataDir() + "/plugins/pads/simple_schematic.txt";
43
44 BOOST_CHECK( PADS_SCH::PADS_SCH_PARSER::CheckFileHeader( testFile ) );
45}
46
47
48BOOST_AUTO_TEST_CASE( CheckFileHeader_ValidPowerLogicFile )
49{
50 std::string testFile = KI_TEST::GetEeschemaTestDataDir() + "/plugins/pads/powerlogic_schematic.txt";
51
52 BOOST_CHECK( PADS_SCH::PADS_SCH_PARSER::CheckFileHeader( testFile ) );
53}
54
55
56BOOST_AUTO_TEST_CASE( CheckFileHeader_InvalidFile )
57{
58 std::string testFile = KI_TEST::GetEeschemaTestDataDir() + "/plugins/pads/nonexistent.txt";
59
60 BOOST_CHECK( !PADS_SCH::PADS_SCH_PARSER::CheckFileHeader( testFile ) );
61}
62
63
64BOOST_AUTO_TEST_CASE( ParseHeader_LogicFormat )
65{
66 std::string testFile = KI_TEST::GetEeschemaTestDataDir() + "/plugins/pads/simple_schematic.txt";
67
69
70 BOOST_REQUIRE( parser.Parse( testFile ) );
71 BOOST_CHECK( parser.IsValid() );
72
73 const auto& header = parser.GetHeader();
74
75 BOOST_CHECK_EQUAL( header.product, "PADS-LOGIC" );
76 BOOST_CHECK_EQUAL( header.version, "V9.0" );
77 BOOST_CHECK( header.description.find( "DESIGN EXPORT FILE" ) != std::string::npos );
78}
79
80
81BOOST_AUTO_TEST_CASE( CheckFileHeader_LogicWithCodePageSuffix )
82{
83 // Regression test for https://gitlab.com/kicad/code/kicad/-/issues/23420
84 // PADS exporters may include the ANSI code page as a suffix in the header
85 // (e.g. *PADS-LOGIC-V9.0-CP1250*). Detection must still succeed.
86 std::string testFile =
87 KI_TEST::GetEeschemaTestDataDir() + "/plugins/pads/issue23420_codepage_schematic.txt";
88
89 BOOST_CHECK( PADS_SCH::PADS_SCH_PARSER::CheckFileHeader( testFile ) );
90}
91
92
93BOOST_AUTO_TEST_CASE( ParseHeader_LogicWithCodePageSuffix )
94{
95 // Regression test for https://gitlab.com/kicad/code/kicad/-/issues/23420
96 std::string testFile =
97 KI_TEST::GetEeschemaTestDataDir() + "/plugins/pads/issue23420_codepage_schematic.txt";
98
100
101 BOOST_REQUIRE( parser.Parse( testFile ) );
102 BOOST_CHECK( parser.IsValid() );
103
104 const auto& header = parser.GetHeader();
105
106 BOOST_CHECK_EQUAL( header.product, "PADS-LOGIC" );
107 BOOST_CHECK_EQUAL( header.version, "V9.0" );
108 BOOST_CHECK_EQUAL( header.codepage, "CP1250" );
109}
110
111
112BOOST_AUTO_TEST_CASE( ParseHeader_PowerLogicFormat )
113{
114 std::string testFile = KI_TEST::GetEeschemaTestDataDir() + "/plugins/pads/powerlogic_schematic.txt";
115
117
118 BOOST_REQUIRE( parser.Parse( testFile ) );
119 BOOST_CHECK( parser.IsValid() );
120
121 const auto& header = parser.GetHeader();
122
123 BOOST_CHECK_EQUAL( header.product, "PADS-POWERLOGIC" );
124 BOOST_CHECK_EQUAL( header.version, "V9.5" );
125}
126
127
128BOOST_AUTO_TEST_CASE( ParseParameters_Units_Mils )
129{
130 std::string testFile = KI_TEST::GetEeschemaTestDataDir() + "/plugins/pads/simple_schematic.txt";
131
133
134 BOOST_REQUIRE( parser.Parse( testFile ) );
135
136 const auto& params = parser.GetParameters();
137
138 BOOST_CHECK( params.units == PADS_SCH::UNIT_TYPE::MILS );
139 BOOST_CHECK_EQUAL( params.grid_x, 100.0 );
140 BOOST_CHECK_EQUAL( params.grid_y, 100.0 );
141 BOOST_CHECK_EQUAL( params.border_template, "Default_A" );
142}
143
144
145BOOST_AUTO_TEST_CASE( ParseParameters_Units_Metric )
146{
147 std::string testFile = KI_TEST::GetEeschemaTestDataDir() + "/plugins/pads/powerlogic_schematic.txt";
148
150
151 BOOST_REQUIRE( parser.Parse( testFile ) );
152
153 const auto& params = parser.GetParameters();
154
155 BOOST_CHECK( params.units == PADS_SCH::UNIT_TYPE::METRIC );
156}
157
158
160{
161 std::string testFile = KI_TEST::GetEeschemaTestDataDir() + "/plugins/pads/simple_schematic.txt";
162
164
165 BOOST_REQUIRE( parser.Parse( testFile ) );
166 BOOST_CHECK_EQUAL( parser.GetVersion(), "V9.0" );
167}
168
169
170BOOST_AUTO_TEST_CASE( ParseParameters_JobName )
171{
172 std::string testFile = KI_TEST::GetEeschemaTestDataDir() + "/plugins/pads/simple_schematic.txt";
173
175
176 BOOST_REQUIRE( parser.Parse( testFile ) );
177
178 const auto& params = parser.GetParameters();
179
180 BOOST_CHECK_EQUAL( params.job_name, "Test Design" );
181}
182
183
184BOOST_AUTO_TEST_CASE( ParseParameters_SheetSize )
185{
186 std::string testFile = KI_TEST::GetEeschemaTestDataDir() + "/plugins/pads/simple_schematic.txt";
187
189
190 BOOST_REQUIRE( parser.Parse( testFile ) );
191
192 const auto& params = parser.GetParameters();
193
194 BOOST_CHECK_EQUAL( params.sheet_size.width, 11000.0 );
195 BOOST_CHECK_EQUAL( params.sheet_size.height, 8500.0 );
196 BOOST_CHECK_EQUAL( params.sheet_size.name, "A" );
197}
198
199
200BOOST_AUTO_TEST_CASE( ParseParameters_TextAndLineDefaults )
201{
202 std::string testFile = KI_TEST::GetEeschemaTestDataDir() + "/plugins/pads/simple_schematic.txt";
203
205
206 BOOST_REQUIRE( parser.Parse( testFile ) );
207
208 const auto& params = parser.GetParameters();
209
210 BOOST_CHECK_EQUAL( params.text_size, 60.0 );
211 BOOST_CHECK_EQUAL( params.line_width, 2.0 );
212}
213
214
215BOOST_AUTO_TEST_CASE( ParseSymbols_Count )
216{
217 std::string testFile = KI_TEST::GetEeschemaTestDataDir() + "/plugins/pads/symbols_schematic.txt";
218
220
221 BOOST_REQUIRE( parser.Parse( testFile ) );
222
223 const auto& symbols = parser.GetSymbolDefs();
224
225 BOOST_CHECK_EQUAL( symbols.size(), 3 );
226}
227
228
229BOOST_AUTO_TEST_CASE( ParseSymbols_V52_DecalWithoutFontLines )
230{
231 std::string testFile = KI_TEST::GetEeschemaTestDataDir() + "/plugins/pads/v52_decals.txt";
232
234
235 BOOST_REQUIRE( parser.Parse( testFile ) );
236 BOOST_CHECK( parser.IsValid() );
237
238 const auto& header = parser.GetHeader();
239
240 BOOST_CHECK_EQUAL( header.product, "PADS-POWERLOGIC" );
241 BOOST_CHECK_EQUAL( header.version, "V5.2" );
242
243 const auto& symbols = parser.GetSymbolDefs();
244
245 BOOST_REQUIRE_EQUAL( symbols.size(), 1 );
246
247 const PADS_SCH::SYMBOL_DEF* pinb = parser.GetSymbolDef( "PINB" );
248 BOOST_REQUIRE( pinb != nullptr );
249
250 BOOST_CHECK_EQUAL( pinb->num_attrs, 4 );
251 BOOST_CHECK_EQUAL( pinb->num_pieces, 2 );
252 BOOST_CHECK_EQUAL( pinb->num_pins, 0 );
254
255 // Verify attribute names were parsed correctly (not misaligned)
256 BOOST_REQUIRE_EQUAL( pinb->attrs.size(), 4 );
257 BOOST_CHECK_EQUAL( pinb->attrs[0].attr_name, "REF-DES" );
258 BOOST_CHECK_EQUAL( pinb->attrs[1].attr_name, "PART-TYPE" );
259 BOOST_CHECK_EQUAL( pinb->attrs[2].attr_name, "*" );
260 BOOST_CHECK_EQUAL( pinb->attrs[3].attr_name, "*" );
261
262 // Verify graphics were parsed correctly
263 BOOST_REQUIRE_EQUAL( pinb->graphics.size(), 2 );
264
265 const auto& openLine = pinb->graphics[0];
266 BOOST_CHECK( openLine.type == PADS_SCH::GRAPHIC_TYPE::POLYLINE );
267 BOOST_CHECK_EQUAL( openLine.line_width, 10.0 );
268 BOOST_REQUIRE_EQUAL( openLine.points.size(), 2 );
269 BOOST_CHECK_EQUAL( openLine.points[0].coord.x, 0.0 );
270 BOOST_CHECK_EQUAL( openLine.points[0].coord.y, 0.0 );
271 BOOST_CHECK_EQUAL( openLine.points[1].coord.x, 140.0 );
272 BOOST_CHECK_EQUAL( openLine.points[1].coord.y, 0.0 );
273
274 const auto& circle = pinb->graphics[1];
275 BOOST_CHECK( circle.type == PADS_SCH::GRAPHIC_TYPE::CIRCLE );
276 BOOST_CHECK_EQUAL( circle.line_width, 10.0 );
277 BOOST_CHECK_EQUAL( circle.center.x, 165.0 );
278 BOOST_CHECK_EQUAL( circle.center.y, 0.0 );
279 BOOST_CHECK_EQUAL( circle.radius, 25.0 );
280
281 // Font names should be empty since V5.2 doesn't have them
282 BOOST_CHECK( pinb->font1.empty() );
283 BOOST_CHECK( pinb->font2.empty() );
284}
285
286
287BOOST_AUTO_TEST_CASE( ParseSymbols_Resistor )
288{
289 std::string testFile = KI_TEST::GetEeschemaTestDataDir() + "/plugins/pads/symbols_schematic.txt";
290
292
293 BOOST_REQUIRE( parser.Parse( testFile ) );
294
295 const PADS_SCH::SYMBOL_DEF* res = parser.GetSymbolDef( "RES_0805" );
296 BOOST_REQUIRE( res != nullptr );
297
298 BOOST_CHECK_EQUAL( res->name, "RES_0805" );
299 BOOST_CHECK_EQUAL( res->gate_count, 1 );
300 BOOST_CHECK_EQUAL( res->graphics.size(), 4 );
301 BOOST_CHECK_EQUAL( res->pins.size(), 2 );
302}
303
304
305BOOST_AUTO_TEST_CASE( ParseSymbols_ResistorPins )
306{
307 std::string testFile = KI_TEST::GetEeschemaTestDataDir() + "/plugins/pads/symbols_schematic.txt";
308
310
311 BOOST_REQUIRE( parser.Parse( testFile ) );
312
313 const PADS_SCH::SYMBOL_DEF* res = parser.GetSymbolDef( "RES_0805" );
314 BOOST_REQUIRE( res != nullptr );
315 BOOST_REQUIRE( res->pins.size() >= 2 );
316
317 // CAEDECAL pins have placeholder numbers and empty names.
318 // Actual pin data comes from PARTTYPE GATE_DEF at build time.
319 const auto& pin1 = res->pins[0];
320 BOOST_CHECK_EQUAL( pin1.number, "1" );
321 BOOST_CHECK_EQUAL( pin1.position.x, -200.0 );
322 BOOST_CHECK_EQUAL( pin1.position.y, 0.0 );
323 BOOST_CHECK( pin1.type == PADS_SCH::PIN_TYPE::UNSPECIFIED );
324
325 const auto& pin2 = res->pins[1];
326 BOOST_CHECK_EQUAL( pin2.number, "2" );
327 BOOST_CHECK_EQUAL( pin2.position.x, 200.0 );
328 BOOST_CHECK_EQUAL( pin2.position.y, 0.0 );
329 BOOST_CHECK( pin2.type == PADS_SCH::PIN_TYPE::UNSPECIFIED );
330
331 // Verify PARTTYPE provides the actual pin numbers/names
332 auto ptIt = parser.GetPartTypes().find( "RES_0805" );
333 BOOST_REQUIRE( ptIt != parser.GetPartTypes().end() );
334 BOOST_REQUIRE( !ptIt->second.gates.empty() );
335
336 const auto& gate = ptIt->second.gates[0];
337 BOOST_REQUIRE_GE( gate.pins.size(), 2u );
338 BOOST_CHECK_EQUAL( gate.pins[0].pin_id, "1" );
339 BOOST_CHECK_EQUAL( gate.pins[0].pin_name, "1" );
340 BOOST_CHECK_EQUAL( gate.pins[1].pin_id, "2" );
341 BOOST_CHECK_EQUAL( gate.pins[1].pin_name, "2" );
342}
343
344
345BOOST_AUTO_TEST_CASE( ParseSymbols_Capacitor )
346{
347 std::string testFile = KI_TEST::GetEeschemaTestDataDir() + "/plugins/pads/symbols_schematic.txt";
348
350
351 BOOST_REQUIRE( parser.Parse( testFile ) );
352
353 const PADS_SCH::SYMBOL_DEF* cap = parser.GetSymbolDef( "CAP_0603" );
354 BOOST_REQUIRE( cap != nullptr );
355
356 BOOST_CHECK_EQUAL( cap->name, "CAP_0603" );
357 BOOST_CHECK_EQUAL( cap->gate_count, 1 );
358 BOOST_CHECK_EQUAL( cap->graphics.size(), 2 );
359 BOOST_CHECK_EQUAL( cap->pins.size(), 2 );
360}
361
362
363BOOST_AUTO_TEST_CASE( ParseSymbols_IC_MultiGate )
364{
365 std::string testFile = KI_TEST::GetEeschemaTestDataDir() + "/plugins/pads/symbols_schematic.txt";
366
368
369 BOOST_REQUIRE( parser.Parse( testFile ) );
370
371 const PADS_SCH::SYMBOL_DEF* ic = parser.GetSymbolDef( "IC_QUAD_NAND" );
372 BOOST_REQUIRE( ic != nullptr );
373
374 BOOST_CHECK_EQUAL( ic->name, "IC_QUAD_NAND" );
375 BOOST_CHECK_EQUAL( ic->graphics.size(), 9 );
376 BOOST_CHECK_EQUAL( ic->pins.size(), 14 );
377
378 // gate_count on SYMBOL_DEF stays at default (1). Multi-gate info lives in PARTTYPE.
379 auto ptIt = parser.GetPartTypes().find( "IC_QUAD_NAND" );
380 BOOST_REQUIRE( ptIt != parser.GetPartTypes().end() );
381 BOOST_CHECK_EQUAL( ptIt->second.num_physical, 4 );
382}
383
384
385BOOST_AUTO_TEST_CASE( ParseSymbols_IC_PinTypes )
386{
387 std::string testFile = KI_TEST::GetEeschemaTestDataDir() + "/plugins/pads/symbols_schematic.txt";
388
390
391 BOOST_REQUIRE( parser.Parse( testFile ) );
392
393 // Pin names/types on SYMBOL_DEF are raw CAEDECAL data (empty/unspecified).
394 // Actual pin data lives in PARTTYPE GATE_DEF and is applied at build time.
395 auto ptIt = parser.GetPartTypes().find( "IC_QUAD_NAND" );
396 BOOST_REQUIRE( ptIt != parser.GetPartTypes().end() );
397 BOOST_REQUIRE( !ptIt->second.gates.empty() );
398
399 const auto& pins = ptIt->second.gates[0].pins;
400 BOOST_REQUIRE_GE( pins.size(), 14u );
401
402 // Check input pins
403 BOOST_CHECK_EQUAL( pins[0].pin_name, "1A" );
404 BOOST_CHECK_EQUAL( pins[0].pin_type, 'L' );
405
406 // Check output pins
407 BOOST_CHECK_EQUAL( pins[2].pin_name, "1Y" );
408 BOOST_CHECK_EQUAL( pins[2].pin_type, 'S' );
409
410 // Check power pins
411 BOOST_CHECK_EQUAL( pins[6].pin_name, "GND" );
412 BOOST_CHECK_EQUAL( pins[6].pin_type, 'G' );
413}
414
415
416BOOST_AUTO_TEST_CASE( ParseSymbols_Graphics )
417{
418 std::string testFile = KI_TEST::GetEeschemaTestDataDir() + "/plugins/pads/symbols_schematic.txt";
419
421
422 BOOST_REQUIRE( parser.Parse( testFile ) );
423
424 const PADS_SCH::SYMBOL_DEF* res = parser.GetSymbolDef( "RES_0805" );
425 BOOST_REQUIRE( res != nullptr );
426 BOOST_REQUIRE( res->graphics.size() >= 1 );
427
428 // First graphic should be a rectangle (CLOSED)
429 const auto& rect = res->graphics[0];
430 BOOST_CHECK( rect.type == PADS_SCH::GRAPHIC_TYPE::RECTANGLE );
431 BOOST_CHECK_EQUAL( rect.line_width, 10.0 );
432 BOOST_REQUIRE( rect.points.size() >= 2 );
433 BOOST_CHECK_EQUAL( rect.points[0].coord.x, -100.0 );
434 BOOST_CHECK_EQUAL( rect.points[0].coord.y, -50.0 );
435 BOOST_CHECK_EQUAL( rect.points[1].coord.x, 100.0 );
436 BOOST_CHECK_EQUAL( rect.points[1].coord.y, 50.0 );
437}
438
439
440BOOST_AUTO_TEST_CASE( GetSymbolDef_NotFound )
441{
442 std::string testFile = KI_TEST::GetEeschemaTestDataDir() + "/plugins/pads/symbols_schematic.txt";
443
445
446 BOOST_REQUIRE( parser.Parse( testFile ) );
447
448 const PADS_SCH::SYMBOL_DEF* notFound = parser.GetSymbolDef( "NONEXISTENT_SYMBOL" );
449 BOOST_CHECK( notFound == nullptr );
450}
451
452
453BOOST_AUTO_TEST_CASE( ParseSymbols_EmptySection )
454{
455 std::string testFile = KI_TEST::GetEeschemaTestDataDir() + "/plugins/pads/simple_schematic.txt";
456
458
459 BOOST_REQUIRE( parser.Parse( testFile ) );
460
461 const auto& symbols = parser.GetSymbolDefs();
462 BOOST_CHECK( symbols.empty() );
463}
464
465
466BOOST_AUTO_TEST_CASE( ParseParts_Count )
467{
468 std::string testFile = KI_TEST::GetEeschemaTestDataDir() + "/plugins/pads/parts_schematic.txt";
469
471
472 BOOST_REQUIRE( parser.Parse( testFile ) );
473
474 const auto& parts = parser.GetPartPlacements();
475
476 BOOST_CHECK_EQUAL( parts.size(), 5 );
477}
478
479
480BOOST_AUTO_TEST_CASE( ParseParts_Resistor )
481{
482 std::string testFile = KI_TEST::GetEeschemaTestDataDir() + "/plugins/pads/parts_schematic.txt";
483
485
486 BOOST_REQUIRE( parser.Parse( testFile ) );
487
488 const PADS_SCH::PART_PLACEMENT* r1 = parser.GetPartPlacement( "R1" );
489 BOOST_REQUIRE( r1 != nullptr );
490
491 BOOST_CHECK_EQUAL( r1->reference, "R1" );
492 BOOST_CHECK_EQUAL( r1->symbol_name, "RES_0805" );
493 BOOST_CHECK_EQUAL( r1->position.x, 1000.0 );
494 BOOST_CHECK_EQUAL( r1->position.y, 2000.0 );
495 BOOST_CHECK_EQUAL( r1->rotation, 0.0 );
498}
499
500
501BOOST_AUTO_TEST_CASE( ParseParts_RotatedPart )
502{
503 std::string testFile = KI_TEST::GetEeschemaTestDataDir() + "/plugins/pads/parts_schematic.txt";
504
506
507 BOOST_REQUIRE( parser.Parse( testFile ) );
508
509 const PADS_SCH::PART_PLACEMENT* r2 = parser.GetPartPlacement( "R2" );
510 BOOST_REQUIRE( r2 != nullptr );
511
512 BOOST_CHECK_EQUAL( r2->rotation, 90.0 );
514}
515
516
517BOOST_AUTO_TEST_CASE( ParseParts_MirroredPart )
518{
519 std::string testFile = KI_TEST::GetEeschemaTestDataDir() + "/plugins/pads/parts_schematic.txt";
520
522
523 BOOST_REQUIRE( parser.Parse( testFile ) );
524
525 const PADS_SCH::PART_PLACEMENT* c1 = parser.GetPartPlacement( "C1" );
526 BOOST_REQUIRE( c1 != nullptr );
527
528 BOOST_CHECK_NE( c1->mirror_flags, 0 );
529}
530
531
532BOOST_AUTO_TEST_CASE( ParseParts_Attributes )
533{
534 std::string testFile = KI_TEST::GetEeschemaTestDataDir() + "/plugins/pads/parts_schematic.txt";
535
537
538 BOOST_REQUIRE( parser.Parse( testFile ) );
539
540 const PADS_SCH::PART_PLACEMENT* r1 = parser.GetPartPlacement( "R1" );
541 BOOST_REQUIRE( r1 != nullptr );
542 BOOST_CHECK_EQUAL( r1->attributes.size(), 2 );
543
544 // Check Ref.Des. attribute
545 bool foundRefDes = false;
546 bool foundValue = false;
547
548 for( const auto& attr : r1->attributes )
549 {
550 if( attr.name == "Ref.Des." )
551 {
552 BOOST_CHECK_EQUAL( attr.value, "R1" );
553 BOOST_CHECK( attr.visible );
554 foundRefDes = true;
555 }
556 else if( attr.name == "Value" )
557 {
558 BOOST_CHECK_EQUAL( attr.value, "10K" );
559 BOOST_CHECK( attr.visible );
560 foundValue = true;
561 }
562 }
563
564 BOOST_CHECK( foundRefDes );
565 BOOST_CHECK( foundValue );
566}
567
568
569BOOST_AUTO_TEST_CASE( ParseParts_AttributeVisibility )
570{
571 std::string testFile = KI_TEST::GetEeschemaTestDataDir() + "/plugins/pads/parts_schematic.txt";
572
574
575 BOOST_REQUIRE( parser.Parse( testFile ) );
576
577 const PADS_SCH::PART_PLACEMENT* r2 = parser.GetPartPlacement( "R2" );
578 BOOST_REQUIRE( r2 != nullptr );
579
580 // R2 has Value attribute with visibility N (hidden)
581 for( const auto& attr : r2->attributes )
582 {
583 if( attr.name == "Value" )
584 {
585 BOOST_CHECK( !attr.visible );
586 break;
587 }
588 }
589}
590
591
592BOOST_AUTO_TEST_CASE( ParseParts_IC_MultipleAttributes )
593{
594 std::string testFile = KI_TEST::GetEeschemaTestDataDir() + "/plugins/pads/parts_schematic.txt";
595
597
598 BOOST_REQUIRE( parser.Parse( testFile ) );
599
600 const PADS_SCH::PART_PLACEMENT* u1 = parser.GetPartPlacement( "U1" );
601 BOOST_REQUIRE( u1 != nullptr );
602
603 BOOST_CHECK_EQUAL( u1->symbol_name, "IC_QUAD_NAND" );
604 BOOST_CHECK_EQUAL( u1->attributes.size(), 4 );
605}
606
607
608BOOST_AUTO_TEST_CASE( ParseParts_MultiGatePart )
609{
610 std::string testFile = KI_TEST::GetEeschemaTestDataDir() + "/plugins/pads/parts_schematic.txt";
611
613
614 BOOST_REQUIRE( parser.Parse( testFile ) );
615
616 const PADS_SCH::PART_PLACEMENT* u1a = parser.GetPartPlacement( "U1.A" );
617 BOOST_REQUIRE( u1a != nullptr );
618
619 BOOST_CHECK_EQUAL( u1a->symbol_name, "IC_QUAD_NAND" );
621}
622
623
624BOOST_AUTO_TEST_CASE( GetPartPlacement_NotFound )
625{
626 std::string testFile = KI_TEST::GetEeschemaTestDataDir() + "/plugins/pads/parts_schematic.txt";
627
629
630 BOOST_REQUIRE( parser.Parse( testFile ) );
631
632 const PADS_SCH::PART_PLACEMENT* notFound = parser.GetPartPlacement( "NONEXISTENT" );
633 BOOST_CHECK( notFound == nullptr );
634}
635
636
637BOOST_AUTO_TEST_CASE( ParseParts_EmptySection )
638{
639 std::string testFile = KI_TEST::GetEeschemaTestDataDir() + "/plugins/pads/simple_schematic.txt";
640
642
643 BOOST_REQUIRE( parser.Parse( testFile ) );
644
645 const auto& parts = parser.GetPartPlacements();
646 BOOST_CHECK( parts.empty() );
647}
648
649
650BOOST_AUTO_TEST_CASE( ParseSignals_Count )
651{
652 std::string testFile = KI_TEST::GetEeschemaTestDataDir() + "/plugins/pads/signals_schematic.txt";
653
655
656 BOOST_REQUIRE( parser.Parse( testFile ) );
657
658 const auto& signals = parser.GetSignals();
659
660 BOOST_CHECK_EQUAL( signals.size(), 4 );
661}
662
663
664BOOST_AUTO_TEST_CASE( ParseSignals_VCC )
665{
666 std::string testFile = KI_TEST::GetEeschemaTestDataDir() + "/plugins/pads/signals_schematic.txt";
667
669
670 BOOST_REQUIRE( parser.Parse( testFile ) );
671
672 const PADS_SCH::SCH_SIGNAL* vcc = parser.GetSignal( "VCC" );
673 BOOST_REQUIRE( vcc != nullptr );
674
675 BOOST_CHECK_EQUAL( vcc->name, "VCC" );
676 BOOST_CHECK_EQUAL( vcc->connections.size(), 2 );
677 BOOST_CHECK_EQUAL( vcc->wires.size(), 2 );
678}
679
680
681BOOST_AUTO_TEST_CASE( ParseSignals_PinConnections )
682{
683 std::string testFile = KI_TEST::GetEeschemaTestDataDir() + "/plugins/pads/signals_schematic.txt";
684
686
687 BOOST_REQUIRE( parser.Parse( testFile ) );
688
689 const PADS_SCH::SCH_SIGNAL* vcc = parser.GetSignal( "VCC" );
690 BOOST_REQUIRE( vcc != nullptr );
691 BOOST_REQUIRE( vcc->connections.size() >= 2 );
692
693 // Check first connection: R1.1
694 BOOST_CHECK_EQUAL( vcc->connections[0].reference, "R1" );
695 BOOST_CHECK_EQUAL( vcc->connections[0].pin_number, "1" );
696
697 // Check second connection: U1.14
698 BOOST_CHECK_EQUAL( vcc->connections[1].reference, "U1" );
699 BOOST_CHECK_EQUAL( vcc->connections[1].pin_number, "14" );
700}
701
702
703BOOST_AUTO_TEST_CASE( ParseSignals_WireSegments )
704{
705 std::string testFile = KI_TEST::GetEeschemaTestDataDir() + "/plugins/pads/signals_schematic.txt";
706
708
709 BOOST_REQUIRE( parser.Parse( testFile ) );
710
711 const PADS_SCH::SCH_SIGNAL* vcc = parser.GetSignal( "VCC" );
712 BOOST_REQUIRE( vcc != nullptr );
713 BOOST_REQUIRE( vcc->wires.size() >= 2 );
714
715 // Check first wire segment
716 const auto& wire1 = vcc->wires[0];
717 BOOST_CHECK_EQUAL( wire1.start.x, 1000.0 );
718 BOOST_CHECK_EQUAL( wire1.start.y, 2000.0 );
719 BOOST_CHECK_EQUAL( wire1.end.x, 2000.0 );
720 BOOST_CHECK_EQUAL( wire1.end.y, 2000.0 );
721 BOOST_CHECK_EQUAL( wire1.sheet_number, 1 );
722
723 // Check second wire segment
724 const auto& wire2 = vcc->wires[1];
725 BOOST_CHECK_EQUAL( wire2.start.x, 2000.0 );
726 BOOST_CHECK_EQUAL( wire2.start.y, 2000.0 );
727 BOOST_CHECK_EQUAL( wire2.end.x, 2000.0 );
728 BOOST_CHECK_EQUAL( wire2.end.y, 3000.0 );
729}
730
731
732BOOST_AUTO_TEST_CASE( ParseSignals_MultipleConnections )
733{
734 std::string testFile = KI_TEST::GetEeschemaTestDataDir() + "/plugins/pads/signals_schematic.txt";
735
737
738 BOOST_REQUIRE( parser.Parse( testFile ) );
739
740 const PADS_SCH::SCH_SIGNAL* net1 = parser.GetSignal( "NET1" );
741 BOOST_REQUIRE( net1 != nullptr );
742
743 // NET1 connects R1.2, R2.1, U1.1, U1.2
744 BOOST_CHECK_EQUAL( net1->connections.size(), 4 );
745 BOOST_CHECK_EQUAL( net1->wires.size(), 4 );
746}
747
748
749BOOST_AUTO_TEST_CASE( ParseSignals_SingleConnection )
750{
751 std::string testFile = KI_TEST::GetEeschemaTestDataDir() + "/plugins/pads/signals_schematic.txt";
752
754
755 BOOST_REQUIRE( parser.Parse( testFile ) );
756
757 const PADS_SCH::SCH_SIGNAL* output = parser.GetSignal( "OUTPUT" );
758 BOOST_REQUIRE( output != nullptr );
759
760 BOOST_CHECK_EQUAL( output->connections.size(), 1 );
761 BOOST_CHECK_EQUAL( output->connections[0].reference, "U1" );
762 BOOST_CHECK_EQUAL( output->connections[0].pin_number, "3" );
763}
764
765
766BOOST_AUTO_TEST_CASE( GetSignal_NotFound )
767{
768 std::string testFile = KI_TEST::GetEeschemaTestDataDir() + "/plugins/pads/signals_schematic.txt";
769
771
772 BOOST_REQUIRE( parser.Parse( testFile ) );
773
774 const PADS_SCH::SCH_SIGNAL* notFound = parser.GetSignal( "NONEXISTENT" );
775 BOOST_CHECK( notFound == nullptr );
776}
777
778
779BOOST_AUTO_TEST_CASE( ParseSignals_EmptySection )
780{
781 std::string testFile = KI_TEST::GetEeschemaTestDataDir() + "/plugins/pads/simple_schematic.txt";
782
784
785 BOOST_REQUIRE( parser.Parse( testFile ) );
786
787 const auto& signals = parser.GetSignals();
788 BOOST_CHECK( signals.empty() );
789}
790
791
792BOOST_AUTO_TEST_CASE( SymbolBuilder_CreateSymbol )
793{
794 std::string testFile = KI_TEST::GetEeschemaTestDataDir() + "/plugins/pads/symbols_schematic.txt";
795
797 BOOST_REQUIRE( parser.Parse( testFile ) );
798
800
801 const PADS_SCH::SYMBOL_DEF* resDef = parser.GetSymbolDef( "RES_0805" );
802 BOOST_REQUIRE( resDef != nullptr );
803
804 LIB_SYMBOL* symbol = builder.BuildSymbol( *resDef );
805 BOOST_REQUIRE( symbol != nullptr );
806
807 BOOST_CHECK_EQUAL( symbol->GetName(), "RES_0805" );
808
809 delete symbol;
810}
811
812
813BOOST_AUTO_TEST_CASE( SymbolBuilder_SymbolHasGraphics )
814{
815 std::string testFile = KI_TEST::GetEeschemaTestDataDir() + "/plugins/pads/symbols_schematic.txt";
816
818 BOOST_REQUIRE( parser.Parse( testFile ) );
819
821
822 const PADS_SCH::SYMBOL_DEF* resDef = parser.GetSymbolDef( "RES_0805" );
823 BOOST_REQUIRE( resDef != nullptr );
824
825 LIB_SYMBOL* symbol = builder.BuildSymbol( *resDef );
826 BOOST_REQUIRE( symbol != nullptr );
827
828 // Count graphics (shapes) in the symbol
829 int shapeCount = 0;
830
831 for( const SCH_ITEM& item : symbol->GetDrawItems() )
832 {
833 if( item.Type() == SCH_SHAPE_T )
834 shapeCount++;
835 }
836
837 BOOST_CHECK_EQUAL( shapeCount, 4 );
838
839 delete symbol;
840}
841
842
843BOOST_AUTO_TEST_CASE( SymbolBuilder_SymbolHasPins )
844{
845 std::string testFile = KI_TEST::GetEeschemaTestDataDir() + "/plugins/pads/symbols_schematic.txt";
846
848 BOOST_REQUIRE( parser.Parse( testFile ) );
849
851
852 const PADS_SCH::SYMBOL_DEF* resDef = parser.GetSymbolDef( "RES_0805" );
853 BOOST_REQUIRE( resDef != nullptr );
854
855 LIB_SYMBOL* symbol = builder.BuildSymbol( *resDef );
856 BOOST_REQUIRE( symbol != nullptr );
857
858 // Count pins in the symbol
859 int pinCount = 0;
860
861 for( const SCH_ITEM& item : symbol->GetDrawItems() )
862 {
863 if( item.Type() == SCH_PIN_T )
864 pinCount++;
865 }
866
867 BOOST_CHECK_EQUAL( pinCount, 2 );
868
869 delete symbol;
870}
871
872
873BOOST_AUTO_TEST_CASE( SymbolBuilder_PinProperties )
874{
875 std::string testFile = KI_TEST::GetEeschemaTestDataDir() + "/plugins/pads/symbols_schematic.txt";
876
878 BOOST_REQUIRE( parser.Parse( testFile ) );
879
881
882 const PADS_SCH::SYMBOL_DEF* resDef = parser.GetSymbolDef( "RES_0805" );
883 BOOST_REQUIRE( resDef != nullptr );
884
885 LIB_SYMBOL* symbol = builder.BuildSymbol( *resDef );
886 BOOST_REQUIRE( symbol != nullptr );
887
888 // Find a pin and verify its properties
889 const SCH_PIN* pin1 = nullptr;
890
891 for( const SCH_ITEM& item : symbol->GetDrawItems() )
892 {
893 if( item.Type() == SCH_PIN_T )
894 {
895 const SCH_PIN* pin = static_cast<const SCH_PIN*>( &item );
896
897 if( pin->GetNumber() == "1" )
898 {
899 pin1 = pin;
900 break;
901 }
902 }
903 }
904
905 BOOST_REQUIRE( pin1 != nullptr );
906 BOOST_CHECK_EQUAL( pin1->GetNumber(), "1" );
907 BOOST_CHECK( pin1->GetType() == ELECTRICAL_PINTYPE::PT_UNSPECIFIED );
908
909 delete symbol;
910}
911
912
913BOOST_AUTO_TEST_CASE( SymbolBuilder_CacheSymbol )
914{
915 std::string testFile = KI_TEST::GetEeschemaTestDataDir() + "/plugins/pads/symbols_schematic.txt";
916
918 BOOST_REQUIRE( parser.Parse( testFile ) );
919
921
922 const PADS_SCH::SYMBOL_DEF* resDef = parser.GetSymbolDef( "RES_0805" );
923 BOOST_REQUIRE( resDef != nullptr );
924
925 // First call creates the symbol
926 BOOST_CHECK( !builder.HasSymbol( "RES_0805" ) );
927
928 LIB_SYMBOL* symbol1 = builder.GetOrCreateSymbol( *resDef );
929 BOOST_REQUIRE( symbol1 != nullptr );
930 BOOST_CHECK( builder.HasSymbol( "RES_0805" ) );
931
932 // Second call returns the same symbol
933 LIB_SYMBOL* symbol2 = builder.GetOrCreateSymbol( *resDef );
934 BOOST_CHECK_EQUAL( symbol1, symbol2 );
935}
936
937
938BOOST_AUTO_TEST_CASE( PowerSymbol_GroundVariants )
939{
947 BOOST_CHECK( PADS_SCH::PADS_SCH_SYMBOL_BUILDER::IsPowerSymbol( "EARTH" ) );
948 BOOST_CHECK( PADS_SCH::PADS_SCH_SYMBOL_BUILDER::IsPowerSymbol( "CHASSIS" ) );
949}
950
951
952BOOST_AUTO_TEST_CASE( PowerSymbol_SupplyVariants )
953{
963}
964
965
966BOOST_AUTO_TEST_CASE( PowerSymbol_VoltagePatterns )
967{
974}
975
976
977BOOST_AUTO_TEST_CASE( PowerSymbol_NonPower )
978{
981 BOOST_CHECK( !PADS_SCH::PADS_SCH_SYMBOL_BUILDER::IsPowerSymbol( "NET1" ) );
982 BOOST_CHECK( !PADS_SCH::PADS_SCH_SYMBOL_BUILDER::IsPowerSymbol( "DATA" ) );
984 BOOST_CHECK( !PADS_SCH::PADS_SCH_SYMBOL_BUILDER::IsPowerSymbol( "RESET" ) );
985}
986
987
988BOOST_AUTO_TEST_CASE( PowerSymbol_KiCadMapping_Ground )
989{
991 BOOST_REQUIRE( gnd.has_value() );
992 BOOST_CHECK_EQUAL( gnd->GetLibNickname().wx_str(), "power" );
993 BOOST_CHECK_EQUAL( gnd->GetLibItemName().wx_str(), "GND" );
994
996 BOOST_REQUIRE( agnd.has_value() );
997 BOOST_CHECK_EQUAL( agnd->GetLibItemName().wx_str(), "GND" );
998
1000 BOOST_REQUIRE( dgnd.has_value() );
1001 BOOST_CHECK_EQUAL( dgnd->GetLibItemName().wx_str(), "GNDD" );
1002}
1003
1004
1005BOOST_AUTO_TEST_CASE( PowerSymbol_KiCadMapping_Supply )
1006{
1008 BOOST_REQUIRE( vcc.has_value() );
1009 BOOST_CHECK_EQUAL( vcc->GetLibNickname().wx_str(), "power" );
1010 BOOST_CHECK_EQUAL( vcc->GetLibItemName().wx_str(), "VCC" );
1011
1013 BOOST_REQUIRE( vdd.has_value() );
1014 BOOST_CHECK_EQUAL( vdd->GetLibItemName().wx_str(), "VDD" );
1015
1017 BOOST_REQUIRE( v5.has_value() );
1018 BOOST_CHECK_EQUAL( v5->GetLibItemName().wx_str(), "+5V" );
1019}
1020
1021
1022BOOST_AUTO_TEST_CASE( PowerSymbol_KiCadMapping_NotFound )
1023{
1025 BOOST_CHECK( !notPower.has_value() );
1026
1028 BOOST_CHECK( !notPower2.has_value() );
1029}
1030
1031
1032BOOST_AUTO_TEST_CASE( PowerSymbol_KiCadMapping_GenericFallback )
1033{
1034 // +V* names not in the explicit table should fall back to VCC
1036 BOOST_REQUIRE( plusV.has_value() );
1037 BOOST_CHECK_EQUAL( plusV->GetLibItemName().wx_str(), "VCC" );
1038
1040 BOOST_REQUIRE( plusV7.has_value() );
1041 BOOST_CHECK_EQUAL( plusV7->GetLibItemName().wx_str(), "VCC" );
1042
1043 // -V* names should fall back to VEE
1045 BOOST_REQUIRE( minusV.has_value() );
1046 BOOST_CHECK_EQUAL( minusV->GetLibItemName().wx_str(), "VEE" );
1047}
1048
1049
1050BOOST_AUTO_TEST_CASE( PowerSymbol_CaseInsensitive )
1051{
1053 BOOST_REQUIRE( gndLower.has_value() );
1054 BOOST_CHECK_EQUAL( gndLower->GetLibItemName().wx_str(), "GND" );
1055
1057 BOOST_REQUIRE( vccMixed.has_value() );
1058 BOOST_CHECK_EQUAL( vccMixed->GetLibItemName().wx_str(), "VCC" );
1059}
1060
1061
1062BOOST_AUTO_TEST_CASE( BuildKiCadPowerSymbol_Styles )
1063{
1064 PADS_SCH::PARAMETERS params;
1066 PADS_SCH::PADS_SCH_SYMBOL_BUILDER builder( params );
1067
1068 // GND style: chevron ground, pin facing down
1069 std::unique_ptr<LIB_SYMBOL> gnd( builder.BuildKiCadPowerSymbol( "GND" ) );
1070 BOOST_REQUIRE( gnd != nullptr );
1071 BOOST_CHECK( gnd->IsPower() );
1072 BOOST_CHECK_EQUAL( gnd->GetName(), "GND" );
1073
1074 // VCC style: arrow up, pin facing up
1075 std::unique_ptr<LIB_SYMBOL> vcc( builder.BuildKiCadPowerSymbol( "VCC" ) );
1076 BOOST_REQUIRE( vcc != nullptr );
1077 BOOST_CHECK( vcc->IsPower() );
1078 BOOST_CHECK_EQUAL( vcc->GetName(), "VCC" );
1079
1080 // VEE style: inverted arrow, pin facing down
1081 std::unique_ptr<LIB_SYMBOL> vee( builder.BuildKiCadPowerSymbol( "VEE" ) );
1082 BOOST_REQUIRE( vee != nullptr );
1083 BOOST_CHECK( vee->IsPower() );
1084
1085 // GNDD style: thick bar ground
1086 std::unique_ptr<LIB_SYMBOL> gndd( builder.BuildKiCadPowerSymbol( "GNDD" ) );
1087 BOOST_REQUIRE( gndd != nullptr );
1088 BOOST_CHECK( gndd->IsPower() );
1089
1090 // Earth style: descending bars
1091 std::unique_ptr<LIB_SYMBOL> earth( builder.BuildKiCadPowerSymbol( "Earth" ) );
1092 BOOST_REQUIRE( earth != nullptr );
1093 BOOST_CHECK( earth->IsPower() );
1094
1095 // PWR_BAR style: thick bar pointing up (for positive rail symbols like +V1)
1096 std::unique_ptr<LIB_SYMBOL> pwrBar( builder.BuildKiCadPowerSymbol( "PWR_BAR" ) );
1097 BOOST_REQUIRE( pwrBar != nullptr );
1098 BOOST_CHECK( pwrBar->IsPower() );
1099
1100 // PWR_TRIANGLE style: filled triangle pointing up (for arrow symbols like +V2)
1101 std::unique_ptr<LIB_SYMBOL> pwrTri( builder.BuildKiCadPowerSymbol( "PWR_TRIANGLE" ) );
1102 BOOST_REQUIRE( pwrTri != nullptr );
1103 BOOST_CHECK( pwrTri->IsPower() );
1104
1105 // Verify each symbol has exactly one pin
1106 BOOST_CHECK_EQUAL( gnd->GetPins().size(), 1u );
1107 BOOST_CHECK_EQUAL( vcc->GetPins().size(), 1u );
1108 BOOST_CHECK_EQUAL( vee->GetPins().size(), 1u );
1109 BOOST_CHECK_EQUAL( gndd->GetPins().size(), 1u );
1110 BOOST_CHECK_EQUAL( earth->GetPins().size(), 1u );
1111 BOOST_CHECK_EQUAL( pwrBar->GetPins().size(), 1u );
1112 BOOST_CHECK_EQUAL( pwrTri->GetPins().size(), 1u );
1113}
1114
1115
1116BOOST_AUTO_TEST_CASE( PowerStyleFromVariant )
1117{
1119
1120 // Ground variants
1121 BOOST_CHECK_EQUAL( PADS_SCH_SYMBOL_BUILDER::GetPowerStyleFromVariant( "GND", "G" ), "GND" );
1122 BOOST_CHECK_EQUAL( PADS_SCH_SYMBOL_BUILDER::GetPowerStyleFromVariant( "AGND", "G" ), "GND" );
1123 BOOST_CHECK_EQUAL( PADS_SCH_SYMBOL_BUILDER::GetPowerStyleFromVariant( "CHGND", "G" ), "Chassis" );
1124
1125 // Rail variants (thick bar)
1126 BOOST_CHECK_EQUAL( PADS_SCH_SYMBOL_BUILDER::GetPowerStyleFromVariant( "+RAIL", "P" ), "PWR_BAR" );
1127 BOOST_CHECK_EQUAL( PADS_SCH_SYMBOL_BUILDER::GetPowerStyleFromVariant( "-RAIL", "P" ), "GNDD" );
1128
1129 // Arrow variants (filled triangle)
1130 BOOST_CHECK_EQUAL( PADS_SCH_SYMBOL_BUILDER::GetPowerStyleFromVariant( "+ARROW", "P" ), "PWR_TRIANGLE" );
1131 BOOST_CHECK_EQUAL( PADS_SCH_SYMBOL_BUILDER::GetPowerStyleFromVariant( "-ARROW", "P" ), "VEE" );
1132
1133 // Bubble variants (map to VCC/VEE)
1134 BOOST_CHECK_EQUAL( PADS_SCH_SYMBOL_BUILDER::GetPowerStyleFromVariant( "+BUBBLE", "P" ), "VCC" );
1135 BOOST_CHECK_EQUAL( PADS_SCH_SYMBOL_BUILDER::GetPowerStyleFromVariant( "-BUBBLE", "P" ), "VEE" );
1136}
1137
1138
1139BOOST_AUTO_TEST_CASE( SheetCount_SingleSheet )
1140{
1141 std::string testFile = KI_TEST::GetEeschemaTestDataDir() + "/plugins/pads/simple_schematic.txt";
1142
1144 BOOST_REQUIRE( parser.Parse( testFile ) );
1145
1146 // Simple schematic with no parts defaults to 1 sheet
1147 BOOST_CHECK_EQUAL( parser.GetSheetCount(), 1 );
1148}
1149
1150
1151BOOST_AUTO_TEST_CASE( SheetNumbers_FromParts )
1152{
1153 std::string testFile = KI_TEST::GetEeschemaTestDataDir() + "/plugins/pads/parts_schematic.txt";
1154
1156 BOOST_REQUIRE( parser.Parse( testFile ) );
1157
1158 std::set<int> sheets = parser.GetSheetNumbers();
1159 BOOST_CHECK( sheets.count( 1 ) > 0 ); // At least sheet 1 should exist
1160}
1161
1162
1163BOOST_AUTO_TEST_CASE( GetPartsOnSheet )
1164{
1165 std::string testFile = KI_TEST::GetEeschemaTestDataDir() + "/plugins/pads/parts_schematic.txt";
1166
1168 BOOST_REQUIRE( parser.Parse( testFile ) );
1169
1170 std::vector<PADS_SCH::PART_PLACEMENT> partsOnSheet1 = parser.GetPartsOnSheet( 1 );
1171
1172 // All parts in test file should be on sheet 1
1173 BOOST_CHECK_EQUAL( partsOnSheet1.size(), parser.GetPartPlacements().size() );
1174}
1175
1176
1177BOOST_AUTO_TEST_CASE( GetSignalsOnSheet )
1178{
1179 std::string testFile = KI_TEST::GetEeschemaTestDataDir() + "/plugins/pads/signals_schematic.txt";
1180
1182 BOOST_REQUIRE( parser.Parse( testFile ) );
1183
1184 std::vector<PADS_SCH::SCH_SIGNAL> signalsOnSheet1 = parser.GetSignalsOnSheet( 1 );
1185
1186 // All signals in test file should have wires on sheet 1
1187 BOOST_CHECK( signalsOnSheet1.size() > 0 );
1188}
1189
1190
1191BOOST_AUTO_TEST_CASE( ParsePartTypes_V52_RegularPart_MultipleDecals )
1192{
1193 std::string testFile = KI_TEST::GetEeschemaTestDataDir() + "/plugins/pads/v52_parttypes.txt";
1194
1196
1197 BOOST_REQUIRE( parser.Parse( testFile ) );
1198 BOOST_CHECK( parser.IsValid() );
1199
1200 const auto& partTypes = parser.GetPartTypes();
1201
1202 // RES0805 should be parsed with G: gate format
1203 auto it = partTypes.find( "RES0805" );
1204 BOOST_REQUIRE( it != partTypes.end() );
1205
1206 const PADS_SCH::PARTTYPE_DEF& res = it->second;
1207 BOOST_CHECK_EQUAL( res.category, "RES" );
1208 BOOST_REQUIRE_EQUAL( res.gates.size(), 1 );
1209
1210 const PADS_SCH::GATE_DEF& resGate = res.gates[0];
1212 BOOST_CHECK_EQUAL( resGate.num_pins, 2 );
1213 BOOST_CHECK_EQUAL( resGate.swap_flag, 0 );
1214
1215 // Decal names parsed from colon-separated G: field
1216 BOOST_REQUIRE_EQUAL( resGate.decal_names.size(), 4 );
1217 BOOST_CHECK_EQUAL( resGate.decal_names[0], "RESZ-H" );
1218 BOOST_CHECK_EQUAL( resGate.decal_names[1], "RESZ-V" );
1219 BOOST_CHECK_EQUAL( resGate.decal_names[2], "RESB-H" );
1220 BOOST_CHECK_EQUAL( resGate.decal_names[3], "RESB-V" );
1221
1222 // Pin definitions from dot-separated tokens
1223 BOOST_REQUIRE_EQUAL( resGate.pins.size(), 2 );
1224 BOOST_CHECK_EQUAL( resGate.pins[0].pin_id, "1" );
1225 BOOST_CHECK_EQUAL( resGate.pins[0].swap_group, 1 );
1226 BOOST_CHECK_EQUAL( resGate.pins[0].pin_type, 'U' );
1227 BOOST_CHECK_EQUAL( resGate.pins[1].pin_id, "2" );
1228 BOOST_CHECK_EQUAL( resGate.pins[1].swap_group, 1 );
1229}
1230
1231
1232BOOST_AUTO_TEST_CASE( ParsePartTypes_V52_MultiGatePart )
1233{
1234 std::string testFile = KI_TEST::GetEeschemaTestDataDir() + "/plugins/pads/v52_parttypes.txt";
1235
1237
1238 BOOST_REQUIRE( parser.Parse( testFile ) );
1239
1240 const auto& partTypes = parser.GetPartTypes();
1241
1242 auto it = partTypes.find( "PS2802-4-A" );
1243 BOOST_REQUIRE( it != partTypes.end() );
1244
1245 const PADS_SCH::PARTTYPE_DEF& ps = it->second;
1246 BOOST_CHECK_EQUAL( ps.category, "SOP" );
1247 BOOST_REQUIRE_EQUAL( ps.gates.size(), 4 );
1248
1249 // Each gate should have 4 pins and reference the PS2802 decal
1250 for( int g = 0; g < 4; g++ )
1251 {
1252 BOOST_CHECK_EQUAL( ps.gates[g].num_pins, 4 );
1253 BOOST_REQUIRE_GE( ps.gates[g].decal_names.size(), 1 );
1254 BOOST_CHECK_EQUAL( ps.gates[g].decal_names[0], "PS2802" );
1255 }
1256
1257 // Verify specific pin names from first gate (AN, CATH, EMIT, COL)
1258 BOOST_REQUIRE_EQUAL( ps.gates[0].pins.size(), 4 );
1259 BOOST_CHECK_EQUAL( ps.gates[0].pins[0].pin_id, "1" );
1260 BOOST_CHECK_EQUAL( ps.gates[0].pins[0].pin_name, "AN" );
1261 BOOST_CHECK_EQUAL( ps.gates[0].pins[1].pin_id, "2" );
1262 BOOST_CHECK_EQUAL( ps.gates[0].pins[1].pin_name, "CATH" );
1263 BOOST_CHECK_EQUAL( ps.gates[0].pins[2].pin_id, "15" );
1264 BOOST_CHECK_EQUAL( ps.gates[0].pins[2].pin_name, "EMIT" );
1265 BOOST_CHECK_EQUAL( ps.gates[0].pins[3].pin_id, "16" );
1266 BOOST_CHECK_EQUAL( ps.gates[0].pins[3].pin_name, "COL" );
1267}
1268
1269
1270BOOST_AUTO_TEST_CASE( ParsePartTypes_V52_Connector )
1271{
1272 std::string testFile = KI_TEST::GetEeschemaTestDataDir() + "/plugins/pads/v52_parttypes.txt";
1273
1275
1276 BOOST_REQUIRE( parser.Parse( testFile ) );
1277
1278 const auto& partTypes = parser.GetPartTypes();
1279
1280 auto it = partTypes.find( "43650-0400" );
1281 BOOST_REQUIRE( it != partTypes.end() );
1282
1283 const PADS_SCH::PARTTYPE_DEF& conn = it->second;
1284 BOOST_CHECK_EQUAL( conn.category, "CON" );
1285 BOOST_CHECK( conn.is_connector );
1286 BOOST_REQUIRE_EQUAL( conn.gates.size(), 1 );
1287 BOOST_CHECK_EQUAL( conn.gates[0].num_pins, 4 );
1288 BOOST_REQUIRE_EQUAL( conn.gates[0].pins.size(), 4 );
1289
1290 // Connector pins should have S type
1291 for( int p = 0; p < 4; p++ )
1292 {
1293 BOOST_CHECK_EQUAL( conn.gates[0].pins[p].pin_type, 'S' );
1294 }
1295}
1296
1297
1298BOOST_AUTO_TEST_CASE( ParsePartTypes_V52_SpecialSymbols )
1299{
1300 std::string testFile = KI_TEST::GetEeschemaTestDataDir() + "/plugins/pads/v52_parttypes.txt";
1301
1303
1304 BOOST_REQUIRE( parser.Parse( testFile ) );
1305
1306 const auto& partTypes = parser.GetPartTypes();
1307
1308 // $GND_SYMS
1309 auto gndIt = partTypes.find( "$GND_SYMS" );
1310 BOOST_REQUIRE( gndIt != partTypes.end() );
1311
1312 const PADS_SCH::PARTTYPE_DEF& gnd = gndIt->second;
1313 BOOST_CHECK_EQUAL( gnd.special_keyword, "GND" );
1314 BOOST_CHECK_EQUAL( gnd.gates.size(), 2 );
1315 BOOST_REQUIRE_EQUAL( gnd.special_variants.size(), 2 );
1316 BOOST_CHECK_EQUAL( gnd.special_variants[0].decal_name, "DGND" );
1317 BOOST_CHECK_EQUAL( gnd.special_variants[0].pin_type, "G" );
1318 BOOST_CHECK_EQUAL( gnd.special_variants[1].decal_name, "PWRGND" );
1319
1320 // V5.2 SIGPIN entries
1321 BOOST_REQUIRE_EQUAL( gnd.sigpins.size(), 2 );
1322 BOOST_CHECK_EQUAL( gnd.sigpins[0].pin_number, "1" );
1323 BOOST_CHECK_EQUAL( gnd.sigpins[0].net_name, "DGND" );
1324 BOOST_CHECK_EQUAL( gnd.sigpins[1].pin_number, "2" );
1325 BOOST_CHECK_EQUAL( gnd.sigpins[1].net_name, "PWRGND" );
1326
1327 // $PWR_SYMS
1328 auto pwrIt = partTypes.find( "$PWR_SYMS" );
1329 BOOST_REQUIRE( pwrIt != partTypes.end() );
1330
1331 const PADS_SCH::PARTTYPE_DEF& pwr = pwrIt->second;
1332 BOOST_CHECK_EQUAL( pwr.special_keyword, "PWR" );
1333 BOOST_CHECK_EQUAL( pwr.gates.size(), 2 );
1334 BOOST_REQUIRE_EQUAL( pwr.special_variants.size(), 2 );
1335 BOOST_CHECK_EQUAL( pwr.special_variants[0].decal_name, "+5V" );
1336 BOOST_CHECK_EQUAL( pwr.special_variants[0].pin_type, "P" );
1337
1338 BOOST_REQUIRE_EQUAL( pwr.sigpins.size(), 2 );
1339 BOOST_CHECK_EQUAL( pwr.sigpins[0].net_name, "+5V" );
1340}
1341
1342
1343BOOST_AUTO_TEST_CASE( ParsePartTypes_V52_SimplePart )
1344{
1345 std::string testFile = KI_TEST::GetEeschemaTestDataDir() + "/plugins/pads/v52_parttypes.txt";
1346
1348
1349 BOOST_REQUIRE( parser.Parse( testFile ) );
1350
1351 const auto& partTypes = parser.GetPartTypes();
1352
1353 // MMSZ5260BT1 should map to ZENER decal
1354 auto it = partTypes.find( "MMSZ5260BT1" );
1355 BOOST_REQUIRE( it != partTypes.end() );
1356
1357 const PADS_SCH::PARTTYPE_DEF& diode = it->second;
1358 BOOST_CHECK_EQUAL( diode.category, "DIO" );
1359 BOOST_REQUIRE_EQUAL( diode.gates.size(), 1 );
1360 BOOST_REQUIRE_EQUAL( diode.gates[0].decal_names.size(), 1 );
1361 BOOST_CHECK_EQUAL( diode.gates[0].decal_names[0], "ZENER" );
1362 BOOST_CHECK_EQUAL( diode.gates[0].num_pins, 2 );
1363 BOOST_REQUIRE_EQUAL( diode.gates[0].pins.size(), 2 );
1364}
1365
1366
1367BOOST_AUTO_TEST_CASE( SymbolBuilder_ConnectorPinSymbol )
1368{
1369 PADS_SCH::PARAMETERS params;
1371 PADS_SCH::PADS_SCH_SYMBOL_BUILDER builder( params );
1372
1373 // Create a minimal connector PARTTYPE and symbol def
1375 connPt.name = "TEST_CONN";
1376 connPt.is_connector = true;
1377 connPt.category = "CON";
1378
1379 PADS_SCH::GATE_DEF gate;
1380 gate.num_pins = 1;
1381 gate.decal_names.push_back( "EXTIN" );
1382
1384 pin.pin_id = "1";
1385 pin.pin_type = 'S';
1386 gate.pins.push_back( pin );
1387 connPt.gates.push_back( gate );
1388
1389 PADS_SCH::SYMBOL_DEF symDef;
1390 symDef.name = "EXTIN";
1391 symDef.gate_count = 1;
1392
1393 PADS_SCH::SYMBOL_PIN symPin;
1394 symPin.number = "1";
1395 symPin.position.x = 0;
1396 symPin.position.y = 0;
1397 symPin.length = 100;
1398 symDef.pins.push_back( symPin );
1399
1400 // Pin 15 variant should have pin number "15"
1401 LIB_SYMBOL* sym15 = builder.GetOrCreateConnectorPinSymbol( connPt, symDef, "15" );
1402 BOOST_REQUIRE( sym15 != nullptr );
1403
1404 auto pins15 = sym15->GetPins();
1405 BOOST_REQUIRE_EQUAL( pins15.size(), 1u );
1406 BOOST_CHECK_EQUAL( pins15[0]->GetNumber(), "15" );
1407
1408 // Pin 1 variant should have pin number "1"
1409 LIB_SYMBOL* sym1 = builder.GetOrCreateConnectorPinSymbol( connPt, symDef, "1" );
1410 BOOST_REQUIRE( sym1 != nullptr );
1411
1412 auto pins1 = sym1->GetPins();
1413 BOOST_REQUIRE_EQUAL( pins1.size(), 1u );
1414 BOOST_CHECK_EQUAL( pins1[0]->GetNumber(), "1" );
1415
1416 // Different pin numbers should produce different cached symbols
1417 BOOST_CHECK_NE( sym15, sym1 );
1418
1419 // Same pin number should return cached symbol
1420 LIB_SYMBOL* sym15again = builder.GetOrCreateConnectorPinSymbol( connPt, symDef, "15" );
1421 BOOST_CHECK_EQUAL( sym15, sym15again );
1422}
1423
1424
1425BOOST_AUTO_TEST_CASE( SymbolBuilder_MultiUnitConnectorSymbol )
1426{
1427 PADS_SCH::PARAMETERS params;
1429 PADS_SCH::PADS_SCH_SYMBOL_BUILDER builder( params );
1430
1432 connPt.name = "TEST_MULTICONN";
1433 connPt.is_connector = true;
1434 connPt.category = "CON";
1435
1436 PADS_SCH::GATE_DEF gate;
1437 gate.num_pins = 4;
1438 gate.decal_names.push_back( "EXTIN" );
1439
1440 for( int i = 1; i <= 4; i++ )
1441 {
1443 pin.pin_id = std::to_string( i );
1444 pin.pin_type = 'S';
1445 gate.pins.push_back( pin );
1446 }
1447
1448 connPt.gates.push_back( gate );
1449
1450 PADS_SCH::SYMBOL_DEF symDef;
1451 symDef.name = "EXTIN";
1452 symDef.gate_count = 1;
1453
1454 PADS_SCH::SYMBOL_PIN symPin;
1455 symPin.number = "1";
1456 symPin.position.x = 0;
1457 symPin.position.y = 0;
1458 symPin.length = 100;
1459 symDef.pins.push_back( symPin );
1460
1461 std::vector<std::string> pinNumbers = { "1", "2", "3", "4" };
1462 std::string cacheKey = "TEST_MULTICONN:conn:J1";
1463
1464 LIB_SYMBOL* multiSym =
1465 builder.GetOrCreateMultiUnitConnectorSymbol( connPt, symDef, pinNumbers, cacheKey );
1466 BOOST_REQUIRE( multiSym != nullptr );
1467
1468 BOOST_CHECK_EQUAL( multiSym->GetUnitCount(), 4 );
1469
1470 for( int unit = 1; unit <= 4; unit++ )
1471 {
1472 std::vector<SCH_PIN*> unitPins;
1473
1474 for( SCH_PIN* pin : multiSym->GetPins() )
1475 {
1476 if( pin->GetUnit() == unit )
1477 unitPins.push_back( pin );
1478 }
1479
1480 BOOST_REQUIRE_EQUAL( unitPins.size(), 1u );
1481 BOOST_CHECK_EQUAL( unitPins[0]->GetNumber(), std::to_string( unit ) );
1482 }
1483
1484 // Same cache key should return the same symbol
1485 LIB_SYMBOL* cached =
1486 builder.GetOrCreateMultiUnitConnectorSymbol( connPt, symDef, pinNumbers, cacheKey );
1487 BOOST_CHECK_EQUAL( multiSym, cached );
1488
1489 // Different cache key should produce a new symbol
1490 LIB_SYMBOL* other =
1491 builder.GetOrCreateMultiUnitConnectorSymbol( connPt, symDef, pinNumbers, "other_key" );
1492 BOOST_CHECK_NE( multiSym, other );
1493}
1494
1495
1496BOOST_AUTO_TEST_CASE( V9_MultiGate_TL082_FromFile )
1497{
1498 std::string testFile = "/home/seth/Downloads/ATS-501 Tape Template (1).txt";
1499
1500 if( !wxFileExists( wxString::FromUTF8( testFile ) ) )
1501 {
1502 BOOST_TEST_MESSAGE( "Skipping: test file not present" );
1503 return;
1504 }
1505
1507 BOOST_REQUIRE( parser.Parse( testFile ) );
1508
1509 const auto& partTypes = parser.GetPartTypes();
1510 auto ptIt = partTypes.find( "TL082" );
1511 BOOST_REQUIRE( ptIt != partTypes.end() );
1512
1513 const auto& tl082 = ptIt->second;
1514 BOOST_REQUIRE_EQUAL( tl082.gates.size(), 2u );
1515 BOOST_CHECK_EQUAL( tl082.gates[0].num_pins, 5 );
1516 BOOST_CHECK_EQUAL( tl082.gates[1].num_pins, 3 );
1517 BOOST_CHECK_EQUAL( tl082.gates[0].decal_names[0], "TL082A" );
1518 BOOST_CHECK_EQUAL( tl082.gates[1].decal_names[0], "TL082" );
1519
1520 const auto& params = parser.GetParameters();
1521 PADS_SCH::PADS_SCH_SYMBOL_BUILDER builder( params );
1522
1523 LIB_SYMBOL* sym = builder.BuildMultiUnitSymbol( tl082, parser.GetSymbolDefs() );
1524 BOOST_REQUIRE( sym != nullptr );
1525 BOOST_CHECK_EQUAL( sym->GetUnitCount(), 2 );
1526
1527 int unit1Pins = 0, unit2Pins = 0;
1528
1529 for( auto* pin : sym->GetPins() )
1530 {
1531 if( pin->GetUnit() == 1 )
1532 unit1Pins++;
1533 else if( pin->GetUnit() == 2 )
1534 unit2Pins++;
1535 }
1536
1537 BOOST_CHECK_EQUAL( unit1Pins, 5 );
1538 BOOST_CHECK_EQUAL( unit2Pins, 3 );
1539
1540 delete sym;
1541}
1542
1543
1545
1546
1547// Schematic Builder tests
1548#include <sch_io/pads/pads_sch_schematic_builder.h>
1550#include <sch_line.h>
1551#include <sch_label.h>
1552#include <sch_junction.h>
1553#include <sch_symbol.h>
1554#include <sch_screen.h>
1555#include <schematic.h>
1556#include <template_fieldnames.h>
1557#include <title_block.h>
1558
1559BOOST_AUTO_TEST_SUITE( PadsSchSchematicBuilder )
1560
1561
1562BOOST_AUTO_TEST_CASE( CreateWire_SingleSegment )
1563{
1564 std::string testFile = KI_TEST::GetEeschemaTestDataDir() + "/plugins/pads/simple_schematic.txt";
1565
1567 BOOST_REQUIRE( parser.Parse( testFile ) );
1568
1569 PADS_SCH::PADS_SCH_SCHEMATIC_BUILDER builder( parser.GetParameters(), nullptr );
1570
1572 wire.start.x = 1000.0;
1573 wire.start.y = 2000.0;
1574 wire.end.x = 3000.0;
1575 wire.end.y = 2000.0;
1576
1577 SCH_LINE* line = builder.CreateWire( wire );
1578 BOOST_REQUIRE( line != nullptr );
1579
1580 BOOST_CHECK( line->GetLayer() == LAYER_WIRE );
1581 BOOST_CHECK( line->GetStartPoint().x != 0 || line->GetStartPoint().y != 0 );
1582 BOOST_CHECK( line->GetEndPoint().x != 0 || line->GetEndPoint().y != 0 );
1583
1584 delete line;
1585}
1586
1587
1588BOOST_AUTO_TEST_CASE( CreateWire_FromSignals )
1589{
1590 std::string testFile = KI_TEST::GetEeschemaTestDataDir() + "/plugins/pads/signals_schematic.txt";
1591
1593 BOOST_REQUIRE( parser.Parse( testFile ) );
1594
1595 PADS_SCH::PADS_SCH_SCHEMATIC_BUILDER builder( parser.GetParameters(), nullptr );
1596
1597 // Count total expected wires
1598 int expectedWires = 0;
1599
1600 for( const auto& signal : parser.GetSignals() )
1601 expectedWires += static_cast<int>( signal.wires.size() );
1602
1603 BOOST_CHECK( expectedWires > 0 );
1604}
1605
1606
1607BOOST_AUTO_TEST_CASE( CreateNetLabel_FromSignal )
1608{
1609 std::string testFile = KI_TEST::GetEeschemaTestDataDir() + "/plugins/pads/simple_schematic.txt";
1610
1612 BOOST_REQUIRE( parser.Parse( testFile ) );
1613
1614 PADS_SCH::PADS_SCH_SCHEMATIC_BUILDER builder( parser.GetParameters(), nullptr );
1615
1616 PADS_SCH::SCH_SIGNAL signal;
1617 signal.name = "VCC";
1618
1620 wire.start.x = 1000.0;
1621 wire.start.y = 2000.0;
1622 wire.end.x = 3000.0;
1623 wire.end.y = 2000.0;
1624 signal.wires.push_back( wire );
1625
1626 VECTOR2I pos( 1000, 2000 );
1627 SCH_GLOBALLABEL* label = builder.CreateNetLabel( signal, pos );
1628 BOOST_REQUIRE( label != nullptr );
1629
1630 BOOST_CHECK_EQUAL( label->GetText(), "VCC" );
1631
1632 delete label;
1633}
1634
1635
1636BOOST_AUTO_TEST_CASE( CreateNetLabel_PreservesSpecialChars )
1637{
1638 std::string testFile = KI_TEST::GetEeschemaTestDataDir() + "/plugins/pads/simple_schematic.txt";
1639
1641 BOOST_REQUIRE( parser.Parse( testFile ) );
1642
1643 PADS_SCH::PADS_SCH_SCHEMATIC_BUILDER builder( parser.GetParameters(), nullptr );
1644
1645 PADS_SCH::SCH_SIGNAL signal;
1646 signal.name = "NET 1";
1647
1648 VECTOR2I pos( 1000, 2000 );
1649 SCH_GLOBALLABEL* label = builder.CreateNetLabel( signal, pos );
1650 BOOST_REQUIRE( label != nullptr );
1651
1652 // Net names pass through unchanged; KiCad handles arbitrary UTF-8 net names
1653 BOOST_CHECK_EQUAL( label->GetText(), "NET 1" );
1654
1655 delete label;
1656}
1657
1658
1659BOOST_AUTO_TEST_CASE( IsBusSignal_BracketNotation )
1660{
1661 // Bus notation with brackets: NAME[n:m] or NAME[n..m]
1662 BOOST_CHECK( PADS_SCH::PADS_SCH_SCHEMATIC_BUILDER::IsBusSignal( "DATA[7:0]" ) );
1663 BOOST_CHECK( PADS_SCH::PADS_SCH_SCHEMATIC_BUILDER::IsBusSignal( "ADDR[15:0]" ) );
1664 BOOST_CHECK( PADS_SCH::PADS_SCH_SCHEMATIC_BUILDER::IsBusSignal( "BUS[0..7]" ) );
1665 BOOST_CHECK( PADS_SCH::PADS_SCH_SCHEMATIC_BUILDER::IsBusSignal( "D[31..0]" ) );
1666}
1667
1668
1669BOOST_AUTO_TEST_CASE( IsBusSignal_AngleNotation )
1670{
1671 // Bus notation with angle brackets: NAME<n:m>
1672 BOOST_CHECK( PADS_SCH::PADS_SCH_SCHEMATIC_BUILDER::IsBusSignal( "DATA<7:0>" ) );
1673 BOOST_CHECK( PADS_SCH::PADS_SCH_SCHEMATIC_BUILDER::IsBusSignal( "ADDR<15:0>" ) );
1674 BOOST_CHECK( PADS_SCH::PADS_SCH_SCHEMATIC_BUILDER::IsBusSignal( "BUS<0..7>" ) );
1675}
1676
1677
1678BOOST_AUTO_TEST_CASE( IsBusSignal_NotABus )
1679{
1680 // Regular signal names that should not be detected as buses
1684 BOOST_CHECK( !PADS_SCH::PADS_SCH_SCHEMATIC_BUILDER::IsBusSignal( "NET1" ) );
1685 BOOST_CHECK( !PADS_SCH::PADS_SCH_SCHEMATIC_BUILDER::IsBusSignal( "DATA0" ) );
1687}
1688
1689
1690BOOST_AUTO_TEST_CASE( CreateBusWire_SingleSegment )
1691{
1692 std::string testFile = KI_TEST::GetEeschemaTestDataDir() + "/plugins/pads/simple_schematic.txt";
1693
1695 BOOST_REQUIRE( parser.Parse( testFile ) );
1696
1697 PADS_SCH::PADS_SCH_SCHEMATIC_BUILDER builder( parser.GetParameters(), nullptr );
1698
1700 wire.start.x = 1000.0;
1701 wire.start.y = 2000.0;
1702 wire.end.x = 3000.0;
1703 wire.end.y = 2000.0;
1704
1705 SCH_LINE* line = builder.CreateBusWire( wire );
1706 BOOST_REQUIRE( line != nullptr );
1707
1708 BOOST_CHECK( line->GetLayer() == LAYER_BUS );
1709
1710 delete line;
1711}
1712
1713
1714BOOST_AUTO_TEST_CASE( ApplyPartAttributes_Reference )
1715{
1716 std::string testFile = KI_TEST::GetEeschemaTestDataDir() + "/plugins/pads/simple_schematic.txt";
1717
1719 BOOST_REQUIRE( parser.Parse( testFile ) );
1720
1721 // Create a test part placement
1722 PADS_SCH::PART_PLACEMENT placement;
1723 placement.reference = "R1";
1724 placement.part_type = "10K";
1725
1726 // Create a mock schematic for testing
1727 SCHEMATIC schematic( nullptr );
1728 schematic.Reset();
1729
1730 PADS_SCH::PADS_SCH_SCHEMATIC_BUILDER builder( parser.GetParameters(), &schematic );
1731
1732 // Create a minimal symbol for testing
1733 LIB_SYMBOL* libSymbol = new LIB_SYMBOL( wxS( "TEST" ) );
1734 LIB_ID libId( wxS( "test" ), wxS( "TEST" ) );
1735
1736 SCH_SYMBOL* symbol = new SCH_SYMBOL( *libSymbol, libId, &schematic.CurrentSheet(), 0 );
1737
1738 builder.ApplyPartAttributes( symbol, placement );
1739
1740 // Verify reference was set
1741 wxString ref = symbol->GetRef( &schematic.CurrentSheet() );
1742 BOOST_CHECK_EQUAL( ref.ToStdString(), "R1" );
1743
1744 // Verify value was set from part_type
1745 BOOST_CHECK_EQUAL( symbol->GetField( FIELD_T::VALUE )->GetText().ToStdString(), "10K" );
1746
1747 delete symbol;
1748 delete libSymbol;
1749}
1750
1751
1752BOOST_AUTO_TEST_CASE( ApplyPartAttributes_Footprint )
1753{
1754 std::string testFile = KI_TEST::GetEeschemaTestDataDir() + "/plugins/pads/simple_schematic.txt";
1755
1757 BOOST_REQUIRE( parser.Parse( testFile ) );
1758
1759 PADS_SCH::PART_PLACEMENT placement;
1760 placement.reference = "C1";
1761 placement.part_type = "100nF";
1762
1763 // Add PCB DECAL attribute for footprint
1764 PADS_SCH::PART_ATTRIBUTE footprintAttr;
1765 footprintAttr.name = "PCB DECAL";
1766 footprintAttr.value = "CAP_0805";
1767 footprintAttr.visible = false;
1768 placement.attributes.push_back( footprintAttr );
1769
1770 SCHEMATIC schematic( nullptr );
1771 schematic.Reset();
1772
1773 PADS_SCH::PADS_SCH_SCHEMATIC_BUILDER builder( parser.GetParameters(), &schematic );
1774
1775 LIB_SYMBOL* libSymbol = new LIB_SYMBOL( wxS( "CAP" ) );
1776 LIB_ID libId( wxS( "test" ), wxS( "CAP" ) );
1777
1778 SCH_SYMBOL* symbol = new SCH_SYMBOL( *libSymbol, libId, &schematic.CurrentSheet(), 0 );
1779
1780 builder.ApplyPartAttributes( symbol, placement );
1781
1782 // Verify footprint was set
1783 BOOST_CHECK_EQUAL( symbol->GetField( FIELD_T::FOOTPRINT )->GetText().ToStdString(), "CAP_0805" );
1784
1785 delete symbol;
1786 delete libSymbol;
1787}
1788
1789
1790BOOST_AUTO_TEST_CASE( ApplyFieldSettings_Visibility )
1791{
1792 std::string testFile = KI_TEST::GetEeschemaTestDataDir() + "/plugins/pads/simple_schematic.txt";
1793
1795 BOOST_REQUIRE( parser.Parse( testFile ) );
1796
1797 PADS_SCH::PART_PLACEMENT placement;
1798 placement.reference = "U1";
1799 placement.part_type = "74HC00";
1800
1801 // Add VALUE attribute with visibility=false
1802 PADS_SCH::PART_ATTRIBUTE valueAttr;
1803 valueAttr.name = "VALUE";
1804 valueAttr.value = "74HC00";
1805 valueAttr.visible = false;
1806 placement.attributes.push_back( valueAttr );
1807
1808 // Add REFDES attribute with visibility=true
1810 refAttr.name = "REFDES";
1811 refAttr.value = "U1";
1812 refAttr.visible = true;
1813 placement.attributes.push_back( refAttr );
1814
1815 SCHEMATIC schematic( nullptr );
1816 schematic.Reset();
1817
1818 PADS_SCH::PADS_SCH_SCHEMATIC_BUILDER builder( parser.GetParameters(), &schematic );
1819
1820 LIB_SYMBOL* libSymbol = new LIB_SYMBOL( wxS( "74HC00" ) );
1821 LIB_ID libId( wxS( "test" ), wxS( "74HC00" ) );
1822
1823 SCH_SYMBOL* symbol = new SCH_SYMBOL( *libSymbol, libId, &schematic.CurrentSheet(), 0 );
1824
1825 builder.ApplyPartAttributes( symbol, placement );
1826
1827 // Verify visibility settings
1828 BOOST_CHECK( !symbol->GetField( FIELD_T::VALUE )->IsVisible() );
1829 BOOST_CHECK( symbol->GetField( FIELD_T::REFERENCE )->IsVisible() );
1830
1831 delete symbol;
1832 delete libSymbol;
1833}
1834
1835
1836BOOST_AUTO_TEST_CASE( ApplyPartAttributes_NullSymbol )
1837{
1838 std::string testFile = KI_TEST::GetEeschemaTestDataDir() + "/plugins/pads/simple_schematic.txt";
1839
1841 BOOST_REQUIRE( parser.Parse( testFile ) );
1842
1843 PADS_SCH::PART_PLACEMENT placement;
1844 placement.reference = "R1";
1845
1846 PADS_SCH::PADS_SCH_SCHEMATIC_BUILDER builder( parser.GetParameters(), nullptr );
1847
1848 // Should not crash when called with nullptr
1849 builder.ApplyPartAttributes( nullptr, placement );
1850
1851 BOOST_CHECK( true ); // If we get here, we passed
1852}
1853
1854
1855BOOST_AUTO_TEST_CASE( CreateCustomFields_ManufacturerAndMPN )
1856{
1857 std::string testFile = KI_TEST::GetEeschemaTestDataDir() + "/plugins/pads/simple_schematic.txt";
1858
1860 BOOST_REQUIRE( parser.Parse( testFile ) );
1861
1862 PADS_SCH::PART_PLACEMENT placement;
1863 placement.reference = "U1";
1864 placement.part_type = "74HC00";
1865
1866 // Add manufacturer attribute (not a standard field)
1868 mfrAttr.name = "Manufacturer";
1869 mfrAttr.value = "Texas Instruments";
1870 mfrAttr.visible = false;
1871 placement.attributes.push_back( mfrAttr );
1872
1873 // Add MPN attribute (not a standard field)
1875 mpnAttr.name = "MPN";
1876 mpnAttr.value = "SN74HC00N";
1877 mpnAttr.visible = false;
1878 placement.attributes.push_back( mpnAttr );
1879
1880 SCHEMATIC schematic( nullptr );
1881 schematic.Reset();
1882
1883 PADS_SCH::PADS_SCH_SCHEMATIC_BUILDER builder( parser.GetParameters(), &schematic );
1884
1885 LIB_SYMBOL* libSymbol = new LIB_SYMBOL( wxS( "74HC00" ) );
1886 LIB_ID libId( wxS( "test" ), wxS( "74HC00" ) );
1887
1888 SCH_SYMBOL* symbol = new SCH_SYMBOL( *libSymbol, libId, &schematic.CurrentSheet(), 0 );
1889
1890 int fieldsCreated = builder.CreateCustomFields( symbol, placement );
1891
1892 // Should have created 2 custom fields
1893 BOOST_CHECK_EQUAL( fieldsCreated, 2 );
1894
1895 // Find and verify the manufacturer field
1896 SCH_FIELD* mfrField = symbol->GetField( wxS( "Manufacturer" ) );
1897 BOOST_REQUIRE( mfrField != nullptr );
1898 BOOST_CHECK_EQUAL( mfrField->GetText().ToStdString(), "Texas Instruments" );
1899 BOOST_CHECK( !mfrField->IsVisible() );
1900
1901 // Find and verify the MPN field
1902 SCH_FIELD* mpnField = symbol->GetField( wxS( "MPN" ) );
1903 BOOST_REQUIRE( mpnField != nullptr );
1904 BOOST_CHECK_EQUAL( mpnField->GetText().ToStdString(), "SN74HC00N" );
1905
1906 delete symbol;
1907 delete libSymbol;
1908}
1909
1910
1911BOOST_AUTO_TEST_CASE( CreateCustomFields_SkipsStandardFields )
1912{
1913 std::string testFile = KI_TEST::GetEeschemaTestDataDir() + "/plugins/pads/simple_schematic.txt";
1914
1916 BOOST_REQUIRE( parser.Parse( testFile ) );
1917
1918 PADS_SCH::PART_PLACEMENT placement;
1919 placement.reference = "R1";
1920
1921 // Add standard field attributes (should be skipped by CreateCustomFields)
1923 refAttr.name = "Ref.Des."; // Standard field
1924 refAttr.value = "R1";
1925 placement.attributes.push_back( refAttr );
1926
1928 valAttr.name = "Part Type"; // Standard field
1929 valAttr.value = "10K";
1930 placement.attributes.push_back( valAttr );
1931
1932 // Add one non-standard field
1933 PADS_SCH::PART_ATTRIBUTE customAttr;
1934 customAttr.name = "Tolerance";
1935 customAttr.value = "5%";
1936 customAttr.visible = true;
1937 placement.attributes.push_back( customAttr );
1938
1939 SCHEMATIC schematic( nullptr );
1940 schematic.Reset();
1941
1942 PADS_SCH::PADS_SCH_SCHEMATIC_BUILDER builder( parser.GetParameters(), &schematic );
1943
1944 LIB_SYMBOL* libSymbol = new LIB_SYMBOL( wxS( "RES" ) );
1945 LIB_ID libId( wxS( "test" ), wxS( "RES" ) );
1946
1947 SCH_SYMBOL* symbol = new SCH_SYMBOL( *libSymbol, libId, &schematic.CurrentSheet(), 0 );
1948
1949 int fieldsCreated = builder.CreateCustomFields( symbol, placement );
1950
1951 // Should have created only 1 custom field (Tolerance)
1952 BOOST_CHECK_EQUAL( fieldsCreated, 1 );
1953
1954 SCH_FIELD* tolField = symbol->GetField( wxS( "Tolerance" ) );
1955 BOOST_REQUIRE( tolField != nullptr );
1956 BOOST_CHECK_EQUAL( tolField->GetText().ToStdString(), "5%" );
1957 BOOST_CHECK( !tolField->IsVisible() );
1958
1959 delete symbol;
1960 delete libSymbol;
1961}
1962
1963
1964BOOST_AUTO_TEST_CASE( CreateCustomFields_SkipsEmptyValues )
1965{
1966 std::string testFile = KI_TEST::GetEeschemaTestDataDir() + "/plugins/pads/simple_schematic.txt";
1967
1969 BOOST_REQUIRE( parser.Parse( testFile ) );
1970
1971 PADS_SCH::PART_PLACEMENT placement;
1972 placement.reference = "U1";
1973
1974 // Add attribute with empty value
1975 PADS_SCH::PART_ATTRIBUTE emptyAttr;
1976 emptyAttr.name = "SerialNumber";
1977 emptyAttr.value = "";
1978 placement.attributes.push_back( emptyAttr );
1979
1980 // Add attribute with actual value
1981 PADS_SCH::PART_ATTRIBUTE validAttr;
1982 validAttr.name = "Revision";
1983 validAttr.value = "A";
1984 validAttr.visible = true;
1985 placement.attributes.push_back( validAttr );
1986
1987 SCHEMATIC schematic( nullptr );
1988 schematic.Reset();
1989
1990 PADS_SCH::PADS_SCH_SCHEMATIC_BUILDER builder( parser.GetParameters(), &schematic );
1991
1992 LIB_SYMBOL* libSymbol = new LIB_SYMBOL( wxS( "IC" ) );
1993 LIB_ID libId( wxS( "test" ), wxS( "IC" ) );
1994
1995 SCH_SYMBOL* symbol = new SCH_SYMBOL( *libSymbol, libId, &schematic.CurrentSheet(), 0 );
1996
1997 int fieldsCreated = builder.CreateCustomFields( symbol, placement );
1998
1999 // Should have created only 1 field (Revision, not empty SerialNumber)
2000 BOOST_CHECK_EQUAL( fieldsCreated, 1 );
2001
2002 // Verify Revision field exists
2003 SCH_FIELD* revField = symbol->GetField( wxS( "Revision" ) );
2004 BOOST_REQUIRE( revField != nullptr );
2005 BOOST_CHECK_EQUAL( revField->GetText().ToStdString(), "A" );
2006
2007 // Verify SerialNumber field was NOT created
2008 SCH_FIELD* snField = symbol->GetField( wxS( "SerialNumber" ) );
2009 BOOST_CHECK( snField == nullptr );
2010
2011 delete symbol;
2012 delete libSymbol;
2013}
2014
2015
2016BOOST_AUTO_TEST_CASE( CreateTitleBlock_AllFields )
2017{
2018 PADS_SCH::PARAMETERS params;
2020 params.fields["Title"] = "Test Design";
2021 params.fields["DATE"] = "2025-01-12";
2022 params.fields["Revision"] = "A";
2023 params.fields["Company Name"] = "Test Company";
2024
2025 PADS_SCH::PADS_SCH_SCHEMATIC_BUILDER builder( params, nullptr );
2026
2027 SCH_SCREEN screen;
2028
2029 builder.CreateTitleBlock( &screen );
2030
2031 const TITLE_BLOCK& tb = screen.GetTitleBlock();
2032
2033 BOOST_CHECK_EQUAL( tb.GetTitle().ToStdString(), "Test Design" );
2034 BOOST_CHECK_EQUAL( tb.GetDate().ToStdString(), "2025-01-12" );
2035 BOOST_CHECK_EQUAL( tb.GetRevision().ToStdString(), "A" );
2036 BOOST_CHECK_EQUAL( tb.GetCompany().ToStdString(), "Test Company" );
2037}
2038
2039
2040BOOST_AUTO_TEST_CASE( CreateTitleBlock_JobNameFallback )
2041{
2042 // Create parameters with job_name but no title
2043 PADS_SCH::PARAMETERS params;
2045 params.job_name = "My Project";
2046
2047 PADS_SCH::PADS_SCH_SCHEMATIC_BUILDER builder( params, nullptr );
2048
2049 SCH_SCREEN screen;
2050
2051 builder.CreateTitleBlock( &screen );
2052
2053 const TITLE_BLOCK& tb = screen.GetTitleBlock();
2054
2055 // Title should fall back to job_name
2056 BOOST_CHECK_EQUAL( tb.GetTitle().ToStdString(), "My Project" );
2057}
2058
2059
2060BOOST_AUTO_TEST_CASE( CreateTitleBlock_EmptyFields )
2061{
2062 // Create empty parameters
2063 PADS_SCH::PARAMETERS params;
2065
2066 PADS_SCH::PADS_SCH_SCHEMATIC_BUILDER builder( params, nullptr );
2067
2068 SCH_SCREEN screen;
2069
2070 builder.CreateTitleBlock( &screen );
2071
2072 const TITLE_BLOCK& tb = screen.GetTitleBlock();
2073
2074 // All fields should be empty
2075 BOOST_CHECK( tb.GetTitle().IsEmpty() );
2076 BOOST_CHECK( tb.GetDate().IsEmpty() );
2077 BOOST_CHECK( tb.GetRevision().IsEmpty() );
2078 BOOST_CHECK( tb.GetCompany().IsEmpty() );
2079}
2080
2081
2082BOOST_AUTO_TEST_CASE( CreateTitleBlock_NullScreen )
2083{
2084 PADS_SCH::PARAMETERS params;
2085 params.fields["Title"] = "Test";
2086
2087 PADS_SCH::PADS_SCH_SCHEMATIC_BUILDER builder( params, nullptr );
2088
2089 // Should not crash with nullptr
2090 builder.CreateTitleBlock( nullptr );
2091
2092 BOOST_CHECK( true ); // If we get here, we passed
2093}
2094
2095
2097
2098
2099// Hierarchical sheet tests
2100#include <sch_sheet.h>
2101#include <sch_sheet_path.h>
2102#include <sch_sheet_pin.h>
2103
2104BOOST_AUTO_TEST_SUITE( PadsSchHierarchicalSheets )
2105
2106
2107BOOST_AUTO_TEST_CASE( GetDefaultSheetSize_ReturnsValidSize )
2108{
2109 PADS_SCH::PARAMETERS params;
2111
2112 SCHEMATIC schematic( nullptr );
2113 schematic.Reset();
2114
2115 PADS_SCH::PADS_SCH_SCHEMATIC_BUILDER builder( params, &schematic );
2116
2117 VECTOR2I size = builder.GetDefaultSheetSize();
2118
2119 BOOST_CHECK( size.x > 0 );
2120 BOOST_CHECK( size.y > 0 );
2121}
2122
2123
2124BOOST_AUTO_TEST_CASE( CalculateSheetPosition_FirstSheet )
2125{
2126 PADS_SCH::PARAMETERS params;
2128
2129 SCHEMATIC schematic( nullptr );
2130 schematic.Reset();
2131
2132 PADS_SCH::PADS_SCH_SCHEMATIC_BUILDER builder( params, &schematic );
2133
2134 VECTOR2I pos = builder.CalculateSheetPosition( 0, 4 );
2135
2136 BOOST_CHECK( pos.x > 0 );
2137 BOOST_CHECK( pos.y > 0 );
2138}
2139
2140
2141BOOST_AUTO_TEST_CASE( CalculateSheetPosition_GridLayout )
2142{
2143 PADS_SCH::PARAMETERS params;
2145
2146 SCHEMATIC schematic( nullptr );
2147 schematic.Reset();
2148
2149 PADS_SCH::PADS_SCH_SCHEMATIC_BUILDER builder( params, &schematic );
2150
2151 VECTOR2I pos0 = builder.CalculateSheetPosition( 0, 4 );
2152 VECTOR2I pos1 = builder.CalculateSheetPosition( 1, 4 );
2153 VECTOR2I pos2 = builder.CalculateSheetPosition( 2, 4 );
2154 VECTOR2I pos3 = builder.CalculateSheetPosition( 3, 4 );
2155
2156 // For 4 sheets, should be 2x2 grid
2157 // pos0 and pos1 should be on same row (same Y)
2158 // pos0 and pos2 should be in same column (same X)
2159 BOOST_CHECK_EQUAL( pos0.y, pos1.y );
2160 BOOST_CHECK_EQUAL( pos2.y, pos3.y );
2161 BOOST_CHECK_EQUAL( pos0.x, pos2.x );
2162 BOOST_CHECK_EQUAL( pos1.x, pos3.x );
2163
2164 // Positions should increase going right and down
2165 BOOST_CHECK( pos1.x > pos0.x );
2166 BOOST_CHECK( pos2.y > pos0.y );
2167}
2168
2169
2170BOOST_AUTO_TEST_CASE( CreateHierarchicalSheet_ReturnsValidSheet )
2171{
2172 PADS_SCH::PARAMETERS params;
2174
2175 SCHEMATIC schematic( nullptr );
2176 schematic.Reset();
2177
2178 // Create root sheet
2179 SCH_SHEET* rootSheet = new SCH_SHEET( &schematic );
2180 SCH_SCREEN* rootScreen = new SCH_SCREEN( &schematic );
2181 rootSheet->SetScreen( rootScreen );
2182
2183 PADS_SCH::PADS_SCH_SCHEMATIC_BUILDER builder( params, &schematic );
2184
2185 SCH_SHEET* sheet = builder.CreateHierarchicalSheet( 1, 3, rootSheet, wxT( "test_design.txt" ) );
2186 BOOST_REQUIRE( sheet != nullptr );
2187
2188 BOOST_CHECK( sheet->GetScreen() != nullptr );
2189
2190 // Verify sheet has valid position
2191 VECTOR2I pos = sheet->GetPosition();
2192 BOOST_CHECK( pos.x >= 0 );
2193 BOOST_CHECK( pos.y >= 0 );
2194
2195 // Verify sheet has valid size
2196 VECTOR2I size = sheet->GetSize();
2197 BOOST_CHECK( size.x > 0 );
2198 BOOST_CHECK( size.y > 0 );
2199
2200 delete rootSheet;
2201}
2202
2203
2204BOOST_AUTO_TEST_CASE( CreateHierarchicalSheet_SetsFilename )
2205{
2206 PADS_SCH::PARAMETERS params;
2208
2209 SCHEMATIC schematic( nullptr );
2210 schematic.Reset();
2211
2212 SCH_SHEET* rootSheet = new SCH_SHEET( &schematic );
2213 SCH_SCREEN* rootScreen = new SCH_SCREEN( &schematic );
2214 rootSheet->SetScreen( rootScreen );
2215
2216 PADS_SCH::PADS_SCH_SCHEMATIC_BUILDER builder( params, &schematic );
2217
2218 SCH_SHEET* sheet = builder.CreateHierarchicalSheet( 2, 3, rootSheet, wxT( "my_design.asc" ) );
2219 BOOST_REQUIRE( sheet != nullptr );
2220
2221 // Verify sheet filename was set
2222 wxString filename = sheet->GetField( FIELD_T::SHEET_FILENAME )->GetText();
2223 BOOST_CHECK( filename.Contains( wxT( "my_design" ) ) );
2224 BOOST_CHECK( filename.Contains( wxT( "sheet2" ) ) );
2225 BOOST_CHECK( filename.EndsWith( wxT( ".kicad_sch" ) ) );
2226
2227 delete rootSheet;
2228}
2229
2230
2231BOOST_AUTO_TEST_CASE( CreateHierarchicalSheet_SetsSheetName )
2232{
2233 PADS_SCH::PARAMETERS params;
2235
2236 SCHEMATIC schematic( nullptr );
2237 schematic.Reset();
2238
2239 SCH_SHEET* rootSheet = new SCH_SHEET( &schematic );
2240 SCH_SCREEN* rootScreen = new SCH_SCREEN( &schematic );
2241 rootSheet->SetScreen( rootScreen );
2242
2243 PADS_SCH::PADS_SCH_SCHEMATIC_BUILDER builder( params, &schematic );
2244
2245 SCH_SHEET* sheet = builder.CreateHierarchicalSheet( 3, 5, rootSheet, wxT( "design.txt" ) );
2246 BOOST_REQUIRE( sheet != nullptr );
2247
2248 // Verify sheet name was set
2249 wxString name = sheet->GetField( FIELD_T::SHEET_NAME )->GetText();
2250 BOOST_CHECK( name.Contains( wxT( "3" ) ) );
2251
2252 delete rootSheet;
2253}
2254
2255
2256BOOST_AUTO_TEST_CASE( CreateHierarchicalSheet_NullParent )
2257{
2258 PADS_SCH::PARAMETERS params;
2260
2261 SCHEMATIC schematic( nullptr );
2262 schematic.Reset();
2263
2264 PADS_SCH::PADS_SCH_SCHEMATIC_BUILDER builder( params, &schematic );
2265
2266 // Should return nullptr when parent is null
2267 SCH_SHEET* sheet = builder.CreateHierarchicalSheet( 1, 3, nullptr, wxT( "test.txt" ) );
2268 BOOST_CHECK( sheet == nullptr );
2269}
2270
2271
2272BOOST_AUTO_TEST_CASE( CreateSheetPin_ValidPin )
2273{
2274 PADS_SCH::PARAMETERS params;
2276
2277 SCHEMATIC schematic( nullptr );
2278 schematic.Reset();
2279
2280 SCH_SHEET* sheet = new SCH_SHEET( &schematic );
2281 sheet->SetPosition( VECTOR2I( 1000, 2000 ) );
2282 sheet->SetSize( VECTOR2I( 3000, 2000 ) );
2283
2284 PADS_SCH::PADS_SCH_SCHEMATIC_BUILDER builder( params, &schematic );
2285
2286 SCH_SHEET_PIN* pin = builder.CreateSheetPin( sheet, "NET1", 0 );
2287 BOOST_REQUIRE( pin != nullptr );
2288
2289 BOOST_CHECK_EQUAL( pin->GetText().ToStdString(), "NET1" );
2290 BOOST_CHECK( pin->GetSide() == SHEET_SIDE::LEFT );
2291
2292 delete sheet;
2293}
2294
2295
2296BOOST_AUTO_TEST_CASE( CreateSheetPin_PreservesName )
2297{
2298 PADS_SCH::PARAMETERS params;
2300
2301 SCHEMATIC schematic( nullptr );
2302 schematic.Reset();
2303
2304 SCH_SHEET* sheet = new SCH_SHEET( &schematic );
2305
2306 PADS_SCH::PADS_SCH_SCHEMATIC_BUILDER builder( params, &schematic );
2307
2308 SCH_SHEET_PIN* pin = builder.CreateSheetPin( sheet, "NET 1", 0 );
2309 BOOST_REQUIRE( pin != nullptr );
2310
2311 // Net names pass through unchanged; KiCad handles arbitrary UTF-8 net names
2312 BOOST_CHECK_EQUAL( pin->GetText().ToStdString(), "NET 1" );
2313
2314 delete sheet;
2315}
2316
2317
2318BOOST_AUTO_TEST_CASE( CreateSheetPin_NullSheet )
2319{
2320 PADS_SCH::PARAMETERS params;
2322
2323 SCHEMATIC schematic( nullptr );
2324 schematic.Reset();
2325
2326 PADS_SCH::PADS_SCH_SCHEMATIC_BUILDER builder( params, &schematic );
2327
2328 SCH_SHEET_PIN* pin = builder.CreateSheetPin( nullptr, "NET1", 0 );
2329 BOOST_CHECK( pin == nullptr );
2330}
2331
2332
2333BOOST_AUTO_TEST_CASE( CreateHierLabel_ValidLabel )
2334{
2335 PADS_SCH::PARAMETERS params;
2337
2338 SCHEMATIC schematic( nullptr );
2339 schematic.Reset();
2340
2341 SCH_SCREEN screen;
2342
2343 PADS_SCH::PADS_SCH_SCHEMATIC_BUILDER builder( params, &schematic );
2344
2345 VECTOR2I pos( 1000, 2000 );
2346 SCH_HIERLABEL* label = builder.CreateHierLabel( "DATA_OUT", pos, &screen );
2347 BOOST_REQUIRE( label != nullptr );
2348
2349 BOOST_CHECK_EQUAL( label->GetText().ToStdString(), "DATA_OUT" );
2350
2351 // Label should be added to screen
2352 BOOST_CHECK( screen.Items().size() > 0 );
2353}
2354
2355
2356
2357BOOST_AUTO_TEST_CASE( IsGlobalSignal_PowerNets )
2358{
2359 std::set<int> singleSheet = { 1 };
2360
2361 BOOST_CHECK( PADS_SCH::PADS_SCH_SCHEMATIC_BUILDER::IsGlobalSignal( "VCC", singleSheet ) );
2362 BOOST_CHECK( PADS_SCH::PADS_SCH_SCHEMATIC_BUILDER::IsGlobalSignal( "GND", singleSheet ) );
2363 BOOST_CHECK( PADS_SCH::PADS_SCH_SCHEMATIC_BUILDER::IsGlobalSignal( "vcc", singleSheet ) );
2364 BOOST_CHECK( PADS_SCH::PADS_SCH_SCHEMATIC_BUILDER::IsGlobalSignal( "AGND", singleSheet ) );
2365 BOOST_CHECK( PADS_SCH::PADS_SCH_SCHEMATIC_BUILDER::IsGlobalSignal( "+5V", singleSheet ) );
2366 BOOST_CHECK( PADS_SCH::PADS_SCH_SCHEMATIC_BUILDER::IsGlobalSignal( "+3V3", singleSheet ) );
2367 BOOST_CHECK( PADS_SCH::PADS_SCH_SCHEMATIC_BUILDER::IsGlobalSignal( "-12V", singleSheet ) );
2368}
2369
2370
2371BOOST_AUTO_TEST_CASE( IsGlobalSignal_MultiSheet )
2372{
2373 std::set<int> multiSheet = { 1, 2, 3 };
2374
2375 // Any signal on multiple sheets should be global
2376 BOOST_CHECK( PADS_SCH::PADS_SCH_SCHEMATIC_BUILDER::IsGlobalSignal( "DATA_BUS", multiSheet ) );
2377 BOOST_CHECK( PADS_SCH::PADS_SCH_SCHEMATIC_BUILDER::IsGlobalSignal( "RANDOM_NET", multiSheet ) );
2378}
2379
2380
2381BOOST_AUTO_TEST_CASE( IsGlobalSignal_NotGlobal )
2382{
2383 std::set<int> singleSheet = { 1 };
2384
2385 BOOST_CHECK( !PADS_SCH::PADS_SCH_SCHEMATIC_BUILDER::IsGlobalSignal( "NET1", singleSheet ) );
2386 BOOST_CHECK( !PADS_SCH::PADS_SCH_SCHEMATIC_BUILDER::IsGlobalSignal( "DATA", singleSheet ) );
2387 BOOST_CHECK( !PADS_SCH::PADS_SCH_SCHEMATIC_BUILDER::IsGlobalSignal( "CLK", singleSheet ) );
2388 BOOST_CHECK( !PADS_SCH::PADS_SCH_SCHEMATIC_BUILDER::IsGlobalSignal( "", singleSheet ) );
2389}
2390
2391
2392// Issue 23855: a resistor Value attribute uses display flag 3 (name+value). The low
2393// bits select what is shown, so it must remain visible. Only the hidden bit (8) hides.
2394BOOST_AUTO_TEST_CASE( Issue23855_ValueVisibleWithDisplayFlag )
2395{
2396 std::string testFile =
2397 KI_TEST::GetEeschemaTestDataDir() + "/plugins/pads/issue23855_schematic.txt";
2398
2400 BOOST_REQUIRE( parser.Parse( testFile ) );
2401
2402 const PADS_SCH::PART_PLACEMENT* r1 = parser.GetPartPlacement( "R1" );
2403 BOOST_REQUIRE( r1 != nullptr );
2404
2405 bool foundValue = false;
2406
2407 for( const auto& attr : r1->attributes )
2408 {
2409 if( attr.name == "Value" )
2410 {
2411 // Display flag 3 (name+value) must stay visible.
2412 BOOST_CHECK( attr.visible );
2413 BOOST_CHECK_EQUAL( attr.justification, 6 );
2414 foundValue = true;
2415 }
2416 }
2417
2418 BOOST_CHECK( foundValue );
2419}
2420
2421
2422// Issue 23855: a "CN" category part type must be flagged as a connector so its pin
2423// numbers are shown, even when the gate is written in the V9 "GATE" format.
2424BOOST_AUTO_TEST_CASE( Issue23855_ConnectorCategoryFlagged )
2425{
2426 std::string testFile =
2427 KI_TEST::GetEeschemaTestDataDir() + "/plugins/pads/issue23855_schematic.txt";
2428
2430 BOOST_REQUIRE( parser.Parse( testFile ) );
2431
2432 auto ptIt = parser.GetPartTypes().find( "91-HDSKT10DH" );
2433 BOOST_REQUIRE( ptIt != parser.GetPartTypes().end() );
2434 BOOST_CHECK( ptIt->second.is_connector );
2435
2436 // A non-connector resistor must not be flagged.
2437 auto resIt = parser.GetPartTypes().find( "71=B2002.2" );
2438 BOOST_REQUIRE( resIt != parser.GetPartTypes().end() );
2439 BOOST_CHECK( !resIt->second.is_connector );
2440}
2441
2442
2443// Issue 23855: a connector part type symbol must show pin numbers even though its pins
2444// carry no pin names.
2445BOOST_AUTO_TEST_CASE( Issue23855_ConnectorShowsPinNumbers )
2446{
2447 std::string testFile =
2448 KI_TEST::GetEeschemaTestDataDir() + "/plugins/pads/issue23855_schematic.txt";
2449
2451 BOOST_REQUIRE( parser.Parse( testFile ) );
2452
2453 auto ptIt = parser.GetPartTypes().find( "91-HDSKT10DH" );
2454 BOOST_REQUIRE( ptIt != parser.GetPartTypes().end() );
2455
2456 const PADS_SCH::SYMBOL_DEF* symDef = parser.GetSymbolDef( "CONNECTOR-10P" );
2457 BOOST_REQUIRE( symDef != nullptr );
2458
2460 LIB_SYMBOL* connSym = builder.GetOrCreatePartTypeSymbol( ptIt->second, *symDef );
2461 BOOST_REQUIRE( connSym != nullptr );
2462 BOOST_CHECK( connSym->GetShowPinNumbers() );
2463
2464 // A resistor (non-connector, no pin names) keeps pin numbers off.
2465 auto resIt = parser.GetPartTypes().find( "71=B2002.2" );
2466 BOOST_REQUIRE( resIt != parser.GetPartTypes().end() );
2467
2468 const PADS_SCH::SYMBOL_DEF* resDef = parser.GetSymbolDef( "RESZ-H" );
2469 BOOST_REQUIRE( resDef != nullptr );
2470
2471 LIB_SYMBOL* resSym = builder.GetOrCreatePartTypeSymbol( resIt->second, *resDef );
2472 BOOST_REQUIRE( resSym != nullptr );
2473 BOOST_CHECK( !resSym->GetShowPinNumbers() );
2474}
2475
2476
2477// Issue 23855: the shared justification decoder maps PADS codes to KiCad alignment so
2478// reference and value fields keep their authored alignment instead of forcing center.
2479BOOST_AUTO_TEST_CASE( Issue23855_DecodeJustification )
2480{
2483
2484 // 0 = bottom-left
2488
2489 // 6 = top-center (band 2..7 -> top, code 6-2=4 -> center)
2493
2494 // 1 = bottom-right
2498
2499 // 12 = middle-center (band 8.. -> middle, code 12-8=4 -> center)
2503}
2504
2505
2506// Issue 23855: the parser must capture the *NETNAMES* offset/justification for each
2507// off-page anchor so the importer can orient global labels correctly, and must read the
2508// placed-frame attribute offsets of a 90 degree rotated part.
2509BOOST_AUTO_TEST_CASE( Issue23855_NetNamesAndRotatedAttributes )
2510{
2511 std::string testFile =
2512 KI_TEST::GetEeschemaTestDataDir() + "/plugins/pads/issue23855_schematic.txt";
2513
2515 BOOST_REQUIRE( parser.Parse( testFile ) );
2516
2517 // The SP1 net carries two off-page anchors with opposite X offsets, so its global
2518 // labels must take opposite orientations from the *NETNAMES* offsets. @@@O14 sits on
2519 // the CN1 side (positive offset) and @@@O22 on the R1 side (negative offset).
2520 const PADS_SCH::NETNAME_LABEL* o0 = nullptr;
2521 const PADS_SCH::NETNAME_LABEL* o1 = nullptr;
2522
2523 for( const auto& nn : parser.GetNetNameLabels() )
2524 {
2525 if( nn.anchor_ref == "@@@O14" )
2526 o0 = &nn;
2527 else if( nn.anchor_ref == "@@@O22" )
2528 o1 = &nn;
2529 }
2530
2531 BOOST_REQUIRE( o0 != nullptr );
2532 BOOST_REQUIRE( o1 != nullptr );
2533 BOOST_CHECK_GT( o0->x_offset, 0 );
2534 BOOST_CHECK_LT( o1->x_offset, 0 );
2535
2536 // The 90 degree rotated diode keeps its placed-frame attribute offsets and text angle.
2537 // PADS writes the named REF-DES and PART-TYPE labels rotated with the part, followed by
2538 // unnamed free-attribute slots ("*") that stay axis-aligned; only the named labels matter.
2539 const PADS_SCH::PART_PLACEMENT* d5 = parser.GetPartPlacement( "D5" );
2540 BOOST_REQUIRE( d5 != nullptr );
2541 BOOST_CHECK_EQUAL( d5->rotation, 90.0 );
2542
2543 bool foundRef = false;
2544 bool foundPartType = false;
2545
2546 for( const auto& attr : d5->attributes )
2547 {
2548 if( attr.name == "REF-DES" )
2549 {
2550 foundRef = true;
2551 BOOST_CHECK_EQUAL( attr.rotation, 90.0 );
2552 BOOST_CHECK_EQUAL( attr.position.x, 210 );
2553 BOOST_CHECK_EQUAL( attr.position.y, 230 );
2554 BOOST_CHECK_EQUAL( attr.justification, 4 );
2555 }
2556 else if( attr.name == "PART-TYPE" )
2557 {
2558 foundPartType = true;
2559 BOOST_CHECK_EQUAL( attr.rotation, 90.0 );
2560 BOOST_CHECK_EQUAL( attr.position.x, -70 );
2561 BOOST_CHECK_EQUAL( attr.position.y, 520 );
2562 BOOST_CHECK_EQUAL( attr.justification, 5 );
2563 }
2564 }
2565
2566 BOOST_CHECK( foundRef );
2567 BOOST_CHECK( foundPartType );
2568}
2569
2570
const char * name
virtual const wxString & GetText() const
Return the string associated with the text object.
Definition eda_text.h:110
virtual bool IsVisible() const
Definition eda_text.h:208
size_t size() const
Return the number of items in the tree.
Definition sch_rtree.h:150
A logical library item identifier and consists of various portions much like a URI.
Definition lib_id.h:45
Define a library symbol object.
Definition lib_symbol.h:79
LIB_ITEMS_CONTAINER & GetDrawItems()
Return a reference to the draw item list.
Definition lib_symbol.h:709
wxString GetName() const override
Definition lib_symbol.h:141
std::vector< SCH_PIN * > GetPins() const override
int GetUnitCount() const override
Parser for PADS Logic schematic design export files.
bool Parse(const std::string &aFileName)
const std::vector< NETNAME_LABEL > & GetNetNameLabels() const
const std::vector< SCH_SIGNAL > & GetSignals() const
const FILE_HEADER & GetHeader() const
std::vector< SCH_SIGNAL > GetSignalsOnSheet(int aSheetNumber) const
std::set< int > GetSheetNumbers() const
static bool CheckFileHeader(const std::string &aFileName)
const std::vector< SYMBOL_DEF > & GetSymbolDefs() const
const std::vector< PART_PLACEMENT > & GetPartPlacements() const
const std::map< std::string, PARTTYPE_DEF > & GetPartTypes() const
std::vector< PART_PLACEMENT > GetPartsOnSheet(int aSheetNumber) const
const SCH_SIGNAL * GetSignal(const std::string &aName) const
const SYMBOL_DEF * GetSymbolDef(const std::string &aName) const
const PARAMETERS & GetParameters() const
const PART_PLACEMENT * GetPartPlacement(const std::string &aReference) const
std::string GetVersion() const
Builder class to create KiCad schematic elements from parsed PADS data.
SCH_LINE * CreateWire(const WIRE_SEGMENT &aWire)
Create a single wire segment.
SCH_SHEET_PIN * CreateSheetPin(SCH_SHEET *aSheet, const std::string &aSignalName, int aPinIndex)
Create hierarchical sheet pin on a sheet symbol.
void ApplyPartAttributes(SCH_SYMBOL *aSymbol, const PART_PLACEMENT &aPlacement)
Apply part attributes to a symbol instance.
void CreateTitleBlock(SCH_SCREEN *aScreen)
Create title block from parsed PADS parameters.
VECTOR2I CalculateSheetPosition(int aSheetIndex, int aTotalSheets) const
Calculate position for a sheet symbol on the parent sheet.
int CreateCustomFields(SCH_SYMBOL *aSymbol, const PART_PLACEMENT &aPlacement)
Create custom fields from non-standard PADS attributes.
SCH_LINE * CreateBusWire(const WIRE_SEGMENT &aWire)
Create a single bus wire segment.
SCH_HIERLABEL * CreateHierLabel(const std::string &aSignalName, const VECTOR2I &aPosition, SCH_SCREEN *aScreen)
Create hierarchical label in a sub-schematic.
SCH_GLOBALLABEL * CreateNetLabel(const SCH_SIGNAL &aSignal, const VECTOR2I &aPosition, SPIN_STYLE aOrientation=SPIN_STYLE::RIGHT)
Create a global net label for a signal.
static bool IsGlobalSignal(const std::string &aSignalName, const std::set< int > &aSheetNumbers)
Check if a signal name represents a global signal.
SCH_SHEET * CreateHierarchicalSheet(int aSheetNumber, int aTotalSheets, SCH_SHEET *aParentSheet, const wxString &aBaseFilename)
Create hierarchical sheet for a sub-schematic page.
VECTOR2I GetDefaultSheetSize() const
Get standard sheet size for a given sheet number.
static bool IsBusSignal(const std::string &aName)
Check if a signal name indicates a bus.
Builder class to convert PADS symbol definitions to KiCad LIB_SYMBOL objects.
LIB_SYMBOL * BuildMultiUnitSymbol(const PARTTYPE_DEF &aPartType, const std::vector< SYMBOL_DEF > &aSymbolDefs)
Build a composite multi-unit LIB_SYMBOL from a multi-gate PARTTYPE.
bool HasSymbol(const std::string &aName) const
Check if a symbol with the given name already exists.
static std::optional< LIB_ID > GetKiCadPowerSymbolId(const std::string &aPadsName)
Get KiCad power library symbol ID for a PADS power symbol.
LIB_SYMBOL * BuildKiCadPowerSymbol(const std::string &aKiCadName)
Build a power symbol using hard-coded KiCad-standard graphics.
static bool IsPowerSymbol(const std::string &aName)
Check if a symbol name indicates a power symbol.
LIB_SYMBOL * GetOrCreateSymbol(const SYMBOL_DEF &aSymbolDef)
Get or create a symbol for the given definition.
LIB_SYMBOL * GetOrCreateConnectorPinSymbol(const PARTTYPE_DEF &aPartType, const SYMBOL_DEF &aSymbolDef, const std::string &aPinNumber)
Get or create a single-pin connector symbol with a specific pin number.
LIB_SYMBOL * GetOrCreateMultiUnitConnectorSymbol(const PARTTYPE_DEF &aPartType, const SYMBOL_DEF &aSymbolDef, const std::vector< std::string > &aPinNumbers, const std::string &aCacheKey)
Get or create a multi-unit connector symbol, cached by base reference.
LIB_SYMBOL * BuildSymbol(const SYMBOL_DEF &aSymbolDef)
Build a KiCad LIB_SYMBOL from a PADS symbol definition.
LIB_SYMBOL * GetOrCreatePartTypeSymbol(const PARTTYPE_DEF &aPartType, const SYMBOL_DEF &aSymbolDef)
Get or create a single-gate symbol with PARTTYPE-specific pin remapping.
Holds all the data relating to one schematic.
Definition schematic.h:90
virtual const wxString & GetText() const override
Return the string associated with the text object.
Definition sch_field.h:128
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition sch_item.h:162
SCH_LAYER_ID GetLayer() const
Return the layer this item is on.
Definition sch_item.h:338
Segment description base class to describe items which have 2 end points (track, wire,...
Definition sch_line.h:38
VECTOR2I GetEndPoint() const
Definition sch_line.h:144
VECTOR2I GetStartPoint() const
Definition sch_line.h:135
EE_RTREE & Items()
Get the full RTree, usually for iterating.
Definition sch_screen.h:115
TITLE_BLOCK & GetTitleBlock()
Definition sch_screen.h:161
Define a sheet pin (label) used in sheets to create hierarchical schematics.
Sheet symbol placed in a schematic, and is the entry point for a sub schematic.
Definition sch_sheet.h:44
void SetSize(const VECTOR2I &aSize)
Definition sch_sheet.h:142
SCH_FIELD * GetField(FIELD_T aFieldType)
Return a mandatory field in this sheet.
void SetPosition(const VECTOR2I &aPosition) override
VECTOR2I GetSize() const
Definition sch_sheet.h:141
SCH_SCREEN * GetScreen() const
Definition sch_sheet.h:139
VECTOR2I GetPosition() const override
Definition sch_sheet.h:490
void SetScreen(SCH_SCREEN *aScreen)
Set the SCH_SCREEN associated with this sheet to aScreen.
Schematic symbol object.
Definition sch_symbol.h:69
const wxString GetRef(const SCH_SHEET_PATH *aSheet, bool aIncludeUnit=false) const override
SCH_FIELD * GetField(FIELD_T aFieldType)
Return a mandatory field in this symbol.
virtual bool GetShowPinNumbers() const
Definition symbol.h:171
Hold the information shown in the lower right corner of a plot, printout, or editing view.
Definition title_block.h:37
const wxString & GetCompany() const
Definition title_block.h:92
const wxString & GetRevision() const
Definition title_block.h:82
const wxString & GetDate() const
Definition title_block.h:72
const wxString & GetTitle() const
Definition title_block.h:59
@ LAYER_WIRE
Definition layer_ids.h:450
@ LAYER_BUS
Definition layer_ids.h:451
std::string GetEeschemaTestDataDir()
Get the configured location of Eeschema test data.
void DecodeJustification(int aJustification, GR_TEXT_H_ALIGN_T &aHJustify, GR_TEXT_V_ALIGN_T &aVJustify)
Decode a PADS text justification code into KiCad horizontal and vertical alignment.
Common utilities and types for parsing PADS file formats.
@ PT_UNSPECIFIED
unknown electrical properties: creates always a warning when connected
Definition pin_type.h:41
Definition of the SCH_SHEET_PATH and SCH_SHEET_LIST classes for Eeschema.
Gate definition within a PARTTYPE.
std::vector< std::string > decal_names
std::vector< PARTTYPE_PIN > pins
Net name label from NETNAMES section.
General schematic parameters from SCH and FIELDS sections.
std::map< std::string, std::string > fields
Part type definition from PARTTYPE section.
std::vector< GATE_DEF > gates
std::vector< SPECIAL_VARIANT > special_variants
std::vector< SIGPIN > sigpins
Pin definition within a PARTTYPE GATE.
Part instance from PART section.
std::vector< PART_ATTRIBUTE > attributes
Signal (net) definition from CONNECTION and SIGNAL sections.
std::vector< PIN_CONNECTION > connections
std::vector< WIRE_SEGMENT > wires
Symbol definition from CAEDECAL section.
std::vector< SYMBOL_GRAPHIC > graphics
std::vector< SYMBOL_PIN > pins
std::vector< CAEDECAL_ATTR > attrs
Pin T/P line pair from CAEDECAL.
Wire segment connecting two endpoints through coordinate vertices.
@ FOOTPRINT
Field Name Module PCB, i.e. "16DIP300".
@ REFERENCE
Field Reference of part, i.e. "IC21".
@ VALUE
Field Value of part, i.e. "3.3K".
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_PIN * pin1
KIBIS_PIN * pin
VECTOR3I res
BOOST_AUTO_TEST_CASE(CheckFileHeader_ValidLogicFile)
std::vector< std::string > header
nlohmann::json output
BOOST_TEST_MESSAGE("\n=== Real-World Polygon PIP Benchmark ===\n"<< formatTable(table))
SHAPE_CIRCLE circle(c.m_circle_center, c.m_circle_radius)
BOOST_CHECK_EQUAL(result, "25.4")
VECTOR2I v5(-70, -70)
GR_TEXT_H_ALIGN_T
This is API surface mapped to common.types.HorizontalAlignment.
@ GR_TEXT_H_ALIGN_CENTER
@ GR_TEXT_H_ALIGN_RIGHT
@ GR_TEXT_H_ALIGN_LEFT
GR_TEXT_V_ALIGN_T
This is API surface mapped to common.types.VertialAlignment.
@ GR_TEXT_V_ALIGN_BOTTOM
@ GR_TEXT_V_ALIGN_CENTER
@ GR_TEXT_V_ALIGN_TOP
@ SCH_SHAPE_T
Definition typeinfo.h:146
@ SCH_PIN_T
Definition typeinfo.h:150
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683