KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_orcad_sch_import.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
20#include <boost/test/unit_test.hpp>
22#include <richio.h>
23
24#include <font/font.h>
28#include <sch_io/ole_image.h>
29
30#include <schematic.h>
31#include <import_net_map.h>
32#include <connection_graph.h>
33#include <sch_screen.h>
34#include <sch_sheet.h>
35#include <sch_sheet_pin.h>
36#include <sch_sheet_path.h>
37#include <sch_symbol.h>
38#include <sch_label.h>
39#include <sch_line.h>
40#include <sch_junction.h>
41#include <sch_no_connect.h>
42#include <sch_bitmap.h>
43#include <sch_shape.h>
44#include <sch_text.h>
45#include <sch_pin.h>
46#include <lib_symbol.h>
48#include <reporter.h>
51#include <bitmap_base.h>
52
53#include <wx/filename.h>
54
55#include <algorithm>
56#include <filesystem>
57#include <fstream>
58#include <map>
59#include <memory>
60#include <set>
61#include <sstream>
62#include <string>
63
64
66{
68 m_schematic( new SCHEMATIC( nullptr ) )
69 {
70 m_manager.LoadProject( "" );
71 m_schematic->SetProject( &m_manager.Prj() );
72 m_schematic->CurrentSheet().clear();
73 m_schematic->CurrentSheet().push_back( &m_schematic->Root() );
74 }
75
77
79 std::unique_ptr<SCHEMATIC> m_schematic;
81};
82
83
84BOOST_FIXTURE_TEST_SUITE( OrcadSchImport, ORCAD_SCH_IMPORT_FIXTURE )
85
86
87BOOST_AUTO_TEST_CASE( StructurePrefixDepthIsTypeOwned )
88{
89 const std::map<int, int> expected = { { 9, 1 }, { 10, 2 }, { 2, 3 }, { 13, 4 }, { 24, 5 },
90 { 66, 1 }, { 16, 2 }, { 12, 3 }, { 64, 4 } };
91
92 for( const auto& [type, depth] : expected )
93 {
94 std::optional<size_t> actual = OrcadLongPrefixCount( type );
95 BOOST_REQUIRE_MESSAGE( actual, "type " << type );
96 BOOST_CHECK_EQUAL( *actual, static_cast<size_t>( depth ) );
97 }
98
99 BOOST_CHECK( !OrcadLongPrefixCount( 0 ) );
100 BOOST_CHECK( !OrcadLongPrefixCount( 255 ) );
101}
102
103
104BOOST_AUTO_TEST_CASE( CaptureColorPalette )
105{
106 BOOST_CHECK( OrcadColor( 8 ) == KIGFX::COLOR4D( 1.0, 0.0, 0.0, 1.0 ) );
107 BOOST_CHECK( OrcadColor( 18 ) == KIGFX::COLOR4D( 0.0, 1.0, 0.0, 1.0 ) );
108 BOOST_CHECK( OrcadColor( 28 ) == KIGFX::COLOR4D( 0.0, 0.0, 1.0, 1.0 ) );
109 BOOST_CHECK( OrcadColor( 40 ) == KIGFX::COLOR4D( 0.0, 0.0, 0.0, 1.0 ) );
110 BOOST_CHECK( OrcadColor( 47 ) == KIGFX::COLOR4D( 1.0, 1.0, 1.0, 1.0 ) );
111 BOOST_CHECK( OrcadColor( 48 ) == KIGFX::COLOR4D::UNSPECIFIED );
112 BOOST_CHECK( OrcadColor( 0 ) == KIGFX::COLOR4D::UNSPECIFIED );
113}
114
115
116BOOST_AUTO_TEST_CASE( CaptureCompoundFileName )
117{
118 BOOST_CHECK_EQUAL( OrcadNormalizeCfbName( std::string( "Serial I" ) + '\x02' + "O" ), "Serial I/O" );
119 BOOST_CHECK_EQUAL( OrcadNormalizeCfbName( std::string( "Sch 2" ) + '\x03' + " PCI Connector" ),
120 "Sch 2: PCI Connector" );
121}
122
123
124BOOST_AUTO_TEST_CASE( CaptureStrokeAndFillSemantics )
125{
126 BOOST_CHECK_EQUAL( OrcadLineWidthIu( 0 ), schIUScale.MilsToIU( 10 ) );
127 BOOST_CHECK_EQUAL( OrcadLineWidthIu( 1 ), schIUScale.MilsToIU( 30 ) );
128 BOOST_CHECK_EQUAL( OrcadLineWidthIu( 2 ), schIUScale.MilsToIU( 50 ) );
129 BOOST_CHECK_EQUAL( OrcadLineWidthIu( 3 ), schIUScale.MilsToIU( 10 ) );
132
133 BOOST_CHECK( OrcadLineStyle( 0 ) == LINE_STYLE::SOLID );
134 BOOST_CHECK( OrcadLineStyle( 4 ) == LINE_STYLE::DASHDOTDOT );
135 BOOST_CHECK( OrcadLineStyle( 5 ) == LINE_STYLE::DEFAULT );
136
137 BOOST_CHECK_EQUAL( OrcadDashRatios( 2 ).first, 67.0 );
138 BOOST_CHECK_EQUAL( OrcadDashRatios( 2 ).second, 21.0 );
139 BOOST_CHECK_EQUAL( OrcadDashRatios( 3 ).first, 3.0 );
140 BOOST_CHECK_EQUAL( OrcadDashRatios( 3 ).second, 1.0 );
141
142 BOOST_CHECK( OrcadFillType( 0, 0 ) == FILL_T::FILLED_SHAPE );
143 BOOST_CHECK( OrcadFillType( 1, 0 ) == FILL_T::NO_FILL );
144 BOOST_CHECK( OrcadFillType( 2, 0 ) == FILL_T::HATCH );
145 BOOST_CHECK( OrcadFillType( 2, 3 ) == FILL_T::REVERSE_HATCH );
146 BOOST_CHECK( OrcadFillType( 2, 4 ) == FILL_T::CROSS_HATCH );
147 BOOST_CHECK( OrcadFillType( 2, 5 ) == FILL_T::CROSS_HATCH );
148 constexpr uint32_t legacyModified = 0x56A2631C;
149 constexpr uint32_t currentModified = 0x652CEBA8;
150 BOOST_CHECK_EQUAL( OrcadHatchPitchIu( legacyModified ), schIUScale.MilsToIU( 25 ) );
151 BOOST_CHECK_EQUAL( OrcadHatchPitchIu( currentModified ), schIUScale.MilsToIU( 80 ) );
152 BOOST_CHECK_EQUAL( OrcadHatchLineWidthIu( legacyModified ), schIUScale.MilsToIU( 3 ) );
153 BOOST_CHECK_EQUAL( OrcadHatchLineWidthIu( currentModified ), schIUScale.MilsToIU( 10 ) );
154
156 rect.SetPosition( VECTOR2I( 0, 0 ) );
157 rect.SetEnd( VECTOR2I( schIUScale.MilsToIU( 600 ), schIUScale.MilsToIU( 500 ) ) );
158
159 std::vector<SEG> reverseDiagonal = OrcadHatchLines( rect, 3, OrcadHatchPitchIu( legacyModified ) );
160 BOOST_CHECK_GT( reverseDiagonal.size(), 40u );
161 BOOST_CHECK_LT( reverseDiagonal.size(), 48u );
162 BOOST_CHECK( std::all_of( reverseDiagonal.begin(), reverseDiagonal.end(),
163 []( const SEG& aLine )
164 {
165 return static_cast<int64_t>( aLine.B.x - aLine.A.x )
166 * ( aLine.B.y - aLine.A.y )
167 < 0;
168 } ) );
169
170 std::vector<SEG> diagonalCross = OrcadHatchLines( rect, 5, OrcadHatchPitchIu( legacyModified ) );
171 BOOST_CHECK_GT( diagonalCross.size(), 80u );
172 BOOST_CHECK_LT( diagonalCross.size(), 96u );
173
174 std::vector<SEG> orthogonalCross = OrcadHatchLines( rect, 4, OrcadHatchPitchIu( legacyModified ) );
175 BOOST_CHECK_GT( orthogonalCross.size(), 40u );
176 BOOST_CHECK_LT( orthogonalCross.size(), 48u );
177 BOOST_CHECK( std::any_of( orthogonalCross.begin(), orthogonalCross.end(),
178 []( const SEG& aLine ) { return aLine.A.x == aLine.B.x; } ) );
179 BOOST_CHECK( std::any_of( orthogonalCross.begin(), orthogonalCross.end(),
180 []( const SEG& aLine ) { return aLine.A.y == aLine.B.y; } ) );
181
182 std::vector<SEG> currentCross = OrcadHatchLines( rect, 5, OrcadHatchPitchIu( currentModified ) );
183 BOOST_CHECK_GT( currentCross.size(), 20u );
184 BOOST_CHECK_LT( currentCross.size(), 40u );
185}
186
187
188BOOST_AUTO_TEST_CASE( CapturePageOrder )
189{
190 wxString dashed = wxS( "03 - CAN" );
191 wxString dotted = wxS( "02.uC" );
192 wxString colon = wxS( "13:IMU" );
193 wxString folder = wxS( "Sch 7: CAN Drivers" );
194 wxString pager = wxS( "PAGER 8" );
195 wxString underscored = wxS( "PAGE_03_HSMC CONNECTOR" );
196 wxString compact = wxS( "PAGE01 OVERALL BLOCK DIAGRAM" );
197 wxString plain = wxS( "Overview" );
198
199 BOOST_CHECK_EQUAL( OrcadPageOrder( dashed ), 3 );
200 BOOST_CHECK_EQUAL( dashed, wxS( "CAN" ) );
201 BOOST_CHECK_EQUAL( OrcadPageOrder( dotted ), 2 );
202 BOOST_CHECK_EQUAL( dotted, wxS( "02.uC" ) );
203 BOOST_CHECK_EQUAL( OrcadPageOrder( colon ), 13 );
204 BOOST_CHECK_EQUAL( colon, wxS( "13:IMU" ) );
205 BOOST_CHECK_EQUAL( OrcadPageOrder( folder ), 7 );
206 BOOST_CHECK_EQUAL( folder, wxS( "Sch 7: CAN Drivers" ) );
207 BOOST_CHECK_EQUAL( OrcadPageOrder( pager ), -1 );
208 BOOST_CHECK_EQUAL( pager, wxS( "PAGER 8" ) );
209 BOOST_CHECK_EQUAL( OrcadPageOrder( underscored ), 3 );
210 BOOST_CHECK_EQUAL( underscored, wxS( "PAGE_03_HSMC CONNECTOR" ) );
211 BOOST_CHECK_EQUAL( OrcadPageOrder( compact ), 1 );
212 BOOST_CHECK_EQUAL( compact, wxS( "PAGE01 OVERALL BLOCK DIAGRAM" ) );
213 BOOST_CHECK_EQUAL( OrcadPageOrder( plain ), -1 );
214 BOOST_CHECK_EQUAL( plain, wxS( "Overview" ) );
215}
216
217
218BOOST_AUTO_TEST_CASE( EmbeddedImageFillsNonSquareSourceBox )
219{
220 BOOST_CHECK_EQUAL( OrcadStretchedImageSize( 718, 720, 830, 360 ), VECTOR2I( 1660, 720 ) );
221 BOOST_CHECK_EQUAL( OrcadStretchedImageSize( 1702, 528, 1702, 528 ), VECTOR2I( 1702, 528 ) );
222 BOOST_CHECK_EQUAL( OrcadStretchedImageSize( 800, 400, 300, 600 ), VECTOR2I( 800, 1600 ) );
223}
224
225
226BOOST_AUTO_TEST_CASE( WmfRenderUsesEmbeddedBoxAspectBeforeRasterization )
227{
228 BOOST_CHECK_EQUAL( OleWmfRenderSize( 718, 720, 4096, 720, 830.0 / 360.0 ), VECTOR2I( 1660, 720 ) );
229 BOOST_CHECK_EQUAL( OleWmfRenderSize( 1702, 528, 2048, 2048, 0.0 ), VECTOR2I( 1702, 528 ) );
230 BOOST_CHECK_EQUAL( OleWmfRenderSize( 1702, 528, 1000, 1000, 0.0 ), VECTOR2I( 1000, 310 ) );
231}
232
233
234BOOST_AUTO_TEST_CASE( OlePreviewWithMultiplePresentationStreams )
235{
236 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
237
238 if( !corpusEnv || !*corpusEnv )
239 {
240 BOOST_TEST_MESSAGE( "KICAD_ORCAD_CORPUS not set; skipping multi-preview OLE check." );
241 return;
242 }
243
244 std::filesystem::path dsn;
245
246 for( const std::filesystem::directory_entry& entry : std::filesystem::recursive_directory_iterator( corpusEnv ) )
247 {
248 if( entry.is_regular_file() && entry.path().filename() == "reServer industrial J401 Carrier Board v11.DSN" )
249 {
250 dsn = entry.path();
251 break;
252 }
253 }
254
255 if( dsn.empty() )
256 {
257 BOOST_TEST_MESSAGE( "reServer industrial J401 design not present; skipping multi-preview OLE check." );
258 return;
259 }
260
261 std::ifstream stream( dsn, std::ios::binary );
262 std::vector<uint8_t> bytes( std::istreambuf_iterator<char>( stream ), {} );
263 const std::array<std::array<uint8_t, 8>, 2> markers = {
264 std::array<uint8_t, 8>{ 0x6A, 0x31, 0x04, 0x00, 0x00, 0x01, 0x00, 0x00 },
265 std::array<uint8_t, 8>{ 0x16, 0x94, 0x05, 0x00, 0x00, 0x01, 0x00, 0x00 }
266 };
267
268 for( size_t markerIndex = 0; markerIndex < markers.size(); ++markerIndex )
269 {
270 const auto& marker = markers[markerIndex];
271 auto markerPos = std::search( bytes.begin(), bytes.end(), marker.begin(), marker.end() );
272 BOOST_REQUIRE( markerPos != bytes.end() );
273
274 size_t offset = static_cast<size_t>( std::distance( bytes.begin(), markerPos ) );
275 size_t size = static_cast<size_t>( bytes[offset] ) | ( static_cast<size_t>( bytes[offset + 1] ) << 8 )
276 | ( static_cast<size_t>( bytes[offset + 2] ) << 16 )
277 | ( static_cast<size_t>( bytes[offset + 3] ) << 24 );
278 BOOST_REQUIRE_LE( offset + size + 4, bytes.size() );
279
280 OLE_IMAGE_PAYLOAD preview =
281 ExtractOleImageFromPayload( { bytes.begin() + offset, bytes.begin() + offset + size + 4 } );
282 BOOST_CHECK( preview.type == OLE_IMAGE_TYPE::WMF );
283 BOOST_CHECK_GT( preview.data.size(), 200000u );
284
285 wxImage image;
286 BOOST_REQUIRE_MESSAGE( OleRenderWmf( preview.data, 2048, 2048, image ),
287 "marker " << markerIndex << ", preview bytes " << preview.data.size() << ", "
288 << OleDescribeImagePayload( preview.data ) );
289 BOOST_CHECK_GT( image.GetWidth(), 1000 );
290 BOOST_CHECK_GT( image.GetHeight(), 500 );
291 }
292}
293
294
295static std::string terminalToken( const std::string& aRef, const std::string& aPin );
296static std::pair<int, int> checkConnectivity( SCHEMATIC& aSchematic, const std::vector<std::set<std::string>>& aNets,
297 std::vector<std::set<std::string>>* aInconsistent = nullptr );
298
299
300static SCH_SYMBOL* findConvertedSymbol( SCH_SCREEN& aScreen, const SCH_SHEET_PATH& aPath, const wxString& aReference )
301{
302 for( SCH_ITEM* item : aScreen.Items().OfType( SCH_SYMBOL_T ) )
303 {
304 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
305
306 if( symbol->GetRef( &aPath, false ) == aReference )
307 return symbol;
308 }
309
310 return nullptr;
311}
312
313
314static std::filesystem::path findCorpusDesign( const std::filesystem::path& aRoot, const std::string& aFileName );
315
316
317BOOST_AUTO_TEST_CASE( OccurrencePowerAliasChainUsesAuthoritativeName )
318{
319 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
320
321 if( !corpusEnv || !*corpusEnv )
322 return;
323
324 std::filesystem::path dsn = findCorpusDesign( corpusEnv, "DC2331A-4.DSN" );
325
326 if( dsn.empty() )
327 return;
328
329 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
330 SETTINGS_MANAGER manager;
331 manager.LoadProject( "" );
332 schematic->SetProject( &manager.Prj() );
333 schematic->CurrentSheet().clear();
334 schematic->CurrentSheet().push_back( &schematic->Root() );
335
336 SCH_IO_ORCAD plugin;
337 plugin.LoadSchematicFile( dsn.string(), schematic.get() );
338
339 SCH_SHEET_LIST sheets = schematic->BuildSheetListSortedByPageNumbers();
340 schematic->ConnectionGraph()->Recalculate( sheets, true );
341 wxString c1Pin1Net;
342
343 for( const auto& [key, subgraphs] : schematic->ConnectionGraph()->GetNetMap() )
344 {
345 for( CONNECTION_SUBGRAPH* subgraph : subgraphs )
346 {
347 for( SCH_ITEM* item : subgraph->GetItems() )
348 {
349 if( item->Type() != SCH_PIN_T )
350 continue;
351
352 SCH_PIN* pin = static_cast<SCH_PIN*>( item );
353 SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( pin->GetParentSymbol() );
354
355 if( symbol && symbol->GetRef( &subgraph->GetSheet(), false ) == wxS( "C1" )
356 && pin->GetNumber() == wxS( "1" ) )
357 {
358 c1Pin1Net = key.Name;
359 }
360 }
361 }
362 }
363
364 BOOST_CHECK_EQUAL( c1Pin1Net.AfterLast( '/' ), wxS( "GND" ) );
365}
366
367
368BOOST_AUTO_TEST_CASE( OccurrencePowerAliasJoinsDistinctChildNetNames )
369{
370 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
371
372 if( !corpusEnv || !*corpusEnv )
373 return;
374
375 std::filesystem::path dsn = findCorpusDesign( corpusEnv, "M5275EVB.DSN" );
376
377 if( dsn.empty() )
378 return;
379
380 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
381 SETTINGS_MANAGER manager;
382 manager.LoadProject( "" );
383 schematic->SetProject( &manager.Prj() );
384 schematic->CurrentSheet().clear();
385 schematic->CurrentSheet().push_back( &schematic->Root() );
386
387 SCH_IO_ORCAD plugin;
388 plugin.LoadSchematicFile( dsn.string(), schematic.get() );
389
390 SCH_SHEET_LIST sheets = schematic->BuildSheetListSortedByPageNumbers();
391 schematic->ConnectionGraph()->Recalculate( sheets, true );
392 std::map<std::pair<wxString, wxString>, CONNECTION_SUBGRAPH*> pinNets;
393
394 for( const auto& [key, subgraphs] : schematic->ConnectionGraph()->GetNetMap() )
395 {
396 for( CONNECTION_SUBGRAPH* subgraph : subgraphs )
397 {
398 if( !subgraph->GetSheet().LastScreen()->GetFileName().Contains( wxS( "_PSU" ) ) )
399 continue;
400
401 for( SCH_ITEM* item : subgraph->GetItems() )
402 {
403 if( item->Type() != SCH_PIN_T )
404 continue;
405
406 SCH_PIN* pin = static_cast<SCH_PIN*>( item );
407 SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( pin->GetParentSymbol() );
408
409 if( symbol )
410 pinNets[{ symbol->GetRef( &subgraph->GetSheet(), false ), pin->GetNumber() }] = subgraph;
411 }
412 }
413 }
414
415 BOOST_REQUIRE( pinNets.count( { wxS( "C126" ), wxS( "1" ) } ) );
416 BOOST_REQUIRE( pinNets.count( { wxS( "U15" ), wxS( "3" ) } ) );
417 BOOST_REQUIRE( pinNets.count( { wxS( "U15" ), wxS( "8" ) } ) );
418 BOOST_CHECK_EQUAL( pinNets.at( { wxS( "C126" ), wxS( "1" ) } ),
419 pinNets.at( { wxS( "U15" ), wxS( "3" ) } ) );
420 BOOST_CHECK_EQUAL( pinNets.at( { wxS( "U15" ), wxS( "8" ) } ),
421 pinNets.at( { wxS( "U15" ), wxS( "3" ) } ) );
422}
423
424
425BOOST_AUTO_TEST_CASE( RejectsNonexistentFile )
426{
427 wxFileName missing( wxFileName::GetTempDir(), wxS( "qa_orcad_does_not_exist.dsn" ) );
428
429 BOOST_REQUIRE( !missing.FileExists() );
430 BOOST_CHECK( !m_plugin.CanReadSchematicFile( missing.GetFullPath() ) );
431}
432
433
434// Set KICAD_ORCAD_CORPUS to test private DSN files against adjacent NET and BOM exports.
435
436static std::string trimCell( std::string aText )
437{
438 auto notSpace = []( unsigned char c )
439 {
440 return !std::isspace( c );
441 };
442 aText.erase( aText.begin(), std::find_if( aText.begin(), aText.end(), notSpace ) );
443 aText.erase( std::find_if( aText.rbegin(), aText.rend(), notSpace ).base(), aText.end() );
444
445 if( aText.size() >= 2 && aText.front() == '"' && aText.back() == '"' )
446 aText = aText.substr( 1, aText.size() - 2 );
447
448 return aText;
449}
450
451
452static std::vector<std::string> splitRefs( const std::string& aCell )
453{
454 std::vector<std::string> refs;
455 std::string token;
456
457 for( char c : aCell )
458 {
459 if( c == ',' )
460 {
461 std::string r = trimCell( token );
462
463 if( !r.empty() )
464 refs.push_back( r );
465
466 token.clear();
467 }
468 else
469 {
470 token += c;
471 }
472 }
473
474 std::string r = trimCell( token );
475
476 if( !r.empty() )
477 refs.push_back( r );
478
479 return refs;
480}
481
482
483static std::set<std::string> parseBomRefs( const std::string& aPath )
484{
485 std::set<std::string> refs;
486 std::ifstream in( aPath );
487 std::string line;
488 int refCol = -1;
489
490 while( std::getline( in, line ) )
491 {
492 if( !line.empty() && line.back() == '\r' )
493 line.pop_back();
494
495 std::vector<std::string> cols;
496 std::string cell;
497
498 for( char c : line )
499 {
500 if( c == '\t' )
501 {
502 cols.push_back( cell );
503 cell.clear();
504 }
505 else
506 {
507 cell += c;
508 }
509 }
510
511 cols.push_back( cell );
512
513 // Header row names reference column; capture index once
514 if( refCol < 0 )
515 {
516 for( size_t i = 0; i < cols.size(); ++i )
517 {
518 if( trimCell( cols[i] ) == "Reference" )
519 {
520 refCol = static_cast<int>( i );
521 break;
522 }
523 }
524
525 continue;
526 }
527
528 if( refCol < static_cast<int>( cols.size() ) )
529 {
530 for( const std::string& r : splitRefs( trimCell( cols[refCol] ) ) )
531 refs.insert( r );
532 }
533 }
534
535 return refs;
536}
537
538
539static std::set<std::string> parseNetComs( const std::string& aPath )
540{
541 std::set<std::string> refs;
542 std::ifstream in( aPath );
543 std::string line;
544
545 while( std::getline( in, line ) )
546 {
547 if( line.rfind( ".ADD_COM", 0 ) != 0 )
548 continue;
549
550 // .ADD_COM <ref> "<footprint>"
551 std::string rest = trimCell( line.substr( 8 ) );
552 std::string ref;
553
554 for( char c : rest )
555 {
556 if( std::isspace( static_cast<unsigned char>( c ) ) )
557 break;
558
559 ref += c;
560 }
561
562 if( !ref.empty() )
563 refs.insert( ref );
564 }
565
566 return refs;
567}
568
569
572static std::set<std::string> collectImportedRefs( SCHEMATIC& aSchematic )
573{
574 std::set<std::string> refs;
576
577 for( const SCH_SHEET_PATH& path : sheets )
578 {
579 SCH_SCREEN* screen = path.LastScreen();
580
581 if( !screen )
582 continue;
583
584 for( SCH_ITEM* item : screen->Items().OfType( SCH_SYMBOL_T ) )
585 {
586 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
587 wxString ref = symbol->GetRef( &path, false );
588
589 // Leading '#' = power/hidden pseudo-part, not BOM; trailing '?' = unannotated.
590 if( ref.IsEmpty() || ref.StartsWith( wxS( "#" ) ) || ref.EndsWith( wxS( "?" ) ) )
591 continue;
592
593 refs.insert( std::string( ref.ToUTF8() ) );
594 }
595 }
596
597 return refs;
598}
599
600
601static std::string terminalToken( const std::string& aRef, const std::string& aPin )
602{
603 return trimCell( aRef ) + "." + trimCell( aPin );
604}
605
606
609static std::vector<std::set<std::string>> parseNetTerminals( const std::string& aPath )
610{
611 std::vector<std::set<std::string>> nets;
612 std::set<std::string> current;
613 std::ifstream in( aPath );
614 std::string line;
615
616 auto flush = [&]()
617 {
618 if( current.size() >= 2 )
619 nets.push_back( current );
620
621 current.clear();
622 };
623
624 while( std::getline( in, line ) )
625 {
626 if( !line.empty() && line.back() == '\r' )
627 line.pop_back();
628
629 bool addTer = line.rfind( ".ADD_TER", 0 ) == 0;
630 bool ter = line.rfind( ".TER", 0 ) == 0;
631 bool cont = !line.empty() && std::isspace( static_cast<unsigned char>( line[0] ) );
632
633 if( line.rfind( ".END", 0 ) == 0 )
634 break;
635
636 if( addTer )
637 flush();
638
639 if( addTer || ter || cont )
640 {
641 std::istringstream ss( addTer ? line.substr( 8 ) : ter ? line.substr( 4 ) : line );
642 std::string ref, pin;
643
644 if( ss >> ref >> pin )
645 current.insert( terminalToken( ref, pin ) );
646 }
647 }
648
649 flush();
650 return nets;
651}
652
653
656static std::pair<int, int> checkConnectivity( SCHEMATIC& aSchematic, const std::vector<std::set<std::string>>& aNets,
657 std::vector<std::set<std::string>>* aInconsistent )
658{
660 aSchematic.ConnectionGraph()->Recalculate( sheets, true );
661
662 std::map<std::string, int> pinNet;
663 int netId = 0;
664
665 for( const auto& [key, subgraphs] : aSchematic.ConnectionGraph()->GetNetMap() )
666 {
667 for( CONNECTION_SUBGRAPH* subgraph : subgraphs )
668 {
669 for( SCH_ITEM* item : subgraph->GetItems() )
670 {
671 if( item->Type() != SCH_PIN_T )
672 continue;
673
674 SCH_PIN* pin = static_cast<SCH_PIN*>( item );
675 SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( pin->GetParentSymbol() );
676
677 if( !symbol )
678 continue;
679
680 wxString ref = symbol->GetRef( &subgraph->GetSheet(), false );
681
682 if( ref.IsEmpty() || ref.StartsWith( wxS( "#" ) ) || ref.EndsWith( wxS( "?" ) ) )
683 continue;
684
685 pinNet[terminalToken( std::string( ref.ToUTF8() ), std::string( pin->GetNumber().ToUTF8() ) )] = netId;
686 }
687 }
688
689 ++netId;
690 }
691
692 struct CHECKED_NET
693 {
694 const std::set<std::string>* terminals;
695 std::set<int> ids;
696 };
697
698 std::vector<CHECKED_NET> checkedNets;
699 std::map<int, int> sourceNetsPerImportedNet;
700
701 for( const std::set<std::string>& net : aNets )
702 {
703 std::set<int> ids;
704 int resolved = 0;
705
706 for( const std::string& term : net )
707 {
708 auto it = pinNet.find( term );
709
710 if( it != pinNet.end() )
711 {
712 ids.insert( it->second );
713 ++resolved;
714 }
715 }
716
717 if( resolved >= 2 )
718 {
719 if( ids.size() == 1 )
720 sourceNetsPerImportedNet[*ids.begin()]++;
721
722 checkedNets.push_back( { &net, std::move( ids ) } );
723 }
724 }
725
726 int consistent = 0;
727
728 for( const CHECKED_NET& net : checkedNets )
729 {
730 bool exact = net.ids.size() == 1 && sourceNetsPerImportedNet[*net.ids.begin()] == 1;
731
732 if( exact )
733 ++consistent;
734 else if( aInconsistent )
735 aInconsistent->push_back( *net.terminals );
736 }
737
738 return { consistent, static_cast<int>( checkedNets.size() ) };
739}
740
741
742static wxString terminalNetName( SCHEMATIC& aSchematic, const wxString& aReference, const wxString& aPinNumber )
743{
745 aSchematic.ConnectionGraph()->Recalculate( sheets, true );
746
747 for( const auto& [key, subgraphs] : aSchematic.ConnectionGraph()->GetNetMap() )
748 {
749 for( CONNECTION_SUBGRAPH* subgraph : subgraphs )
750 {
751 for( SCH_ITEM* item : subgraph->GetItems() )
752 {
753 if( item->Type() != SCH_PIN_T )
754 continue;
755
756 SCH_PIN* pin = static_cast<SCH_PIN*>( item );
757 SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( pin->GetParentSymbol() );
758
759 if( symbol && symbol->GetRef( &subgraph->GetSheet(), false ) == aReference
760 && pin->GetNumber() == aPinNumber )
761 {
762 return key.Name;
763 }
764 }
765 }
766 }
767
768 return {};
769}
770
771
773static std::set<std::string> expectedRefsFor( const std::filesystem::path& aDsn, std::string& aSource )
774{
775 for( const char* ext : { ".NET", ".net", ".BOM", ".bom" } )
776 {
777 std::filesystem::path candidate = aDsn;
778 candidate.replace_extension( ext );
779
780 if( std::filesystem::exists( candidate ) )
781 {
782 aSource = candidate.filename().string();
783
784 bool isNet = std::string( ext ) == ".NET" || std::string( ext ) == ".net";
785 return isNet ? parseNetComs( candidate.string() ) : parseBomRefs( candidate.string() );
786 }
787 }
788
789 aSource.clear();
790 return {};
791}
792
793
794BOOST_AUTO_TEST_CASE( CorpusValidation )
795{
796 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
797
798 if( !corpusEnv || !*corpusEnv )
799 {
800 BOOST_TEST_MESSAGE( "KICAD_ORCAD_CORPUS not set; skipping OrCAD corpus validation." );
801 return;
802 }
803
804 namespace fs = std::filesystem;
805 fs::path root( corpusEnv );
806
807 BOOST_REQUIRE_MESSAGE( fs::exists( root ), "KICAD_ORCAD_CORPUS path does not exist." );
808
809 std::vector<fs::path> designs;
810
811 for( auto it = fs::recursive_directory_iterator( root, fs::directory_options::skip_permission_denied );
812 it != fs::recursive_directory_iterator(); ++it )
813 {
814 if( !it->is_regular_file() )
815 continue;
816
817 std::string ext = it->path().extension().string();
818 std::transform( ext.begin(), ext.end(), ext.begin(),
819 []( unsigned char c )
820 {
821 return std::tolower( c );
822 } );
823
824 if( ext == ".dsn" )
825 designs.push_back( it->path() );
826 }
827
828 std::sort( designs.begin(), designs.end() );
829
830 BOOST_TEST_MESSAGE( "OrCAD corpus: " << designs.size() << " .DSN files under " << root );
831
832 int imported = 0, crashed = 0, unsupported = 0, rejected = 0, checked = 0;
833 unsigned int totalExpected = 0, totalMatched = 0, totalMissing = 0, totalExtra = 0;
834 int netConsistent = 0, netCheckable = 0, netTotal = 0;
835 uint64_t importedPages = 0, importedComponents = 0, importedPowerSymbols = 0;
836 uint64_t importedPins = 0, importedWires = 0, importedBuses = 0;
837 uint64_t importedLabels = 0, importedShapes = 0, importedTexts = 0, importedBitmaps = 0;
838
839 const char* debugEnv = std::getenv( "KICAD_ORCAD_DEBUG" );
840 std::string debugFilter = debugEnv ? debugEnv : "";
841 const char* filterEnv = std::getenv( "KICAD_ORCAD_FILTER" );
842 std::string designFilter = filterEnv ? filterEnv : "";
843
844 for( const fs::path& dsn : designs )
845 {
846 std::string rel = fs::relative( dsn, root ).string();
847
848 if( !designFilter.empty() && rel.find( designFilter ) == std::string::npos )
849 continue;
850
851 BOOST_TEST_INFO_SCOPE( "OrCAD source: " << rel );
852
853 SCH_IO_ORCAD plugin;
854 uint64_t perDesignPages = 0, perDesignComponents = 0, perDesignPowerSymbols = 0;
855 uint64_t perDesignPins = 0, perDesignWires = 0, perDesignBuses = 0;
856 uint64_t perDesignLabels = 0, perDesignShapes = 0, perDesignTexts = 0;
857 uint64_t perDesignBitmaps = 0;
858 uint64_t perDesignRedWires = 0;
859 uint64_t perDesignVddmPowerSymbols = 0;
860
861 bool debug = !debugFilter.empty() && rel.find( debugFilter ) != std::string::npos;
862
863 if( !plugin.CanReadSchematicFile( dsn.string() ) )
864 {
865 ++rejected;
866 continue;
867 }
868
869 SETTINGS_MANAGER manager;
870 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
871 manager.LoadProject( "" );
872 schematic->SetProject( &manager.Prj() );
873 schematic->CurrentSheet().clear();
874 schematic->CurrentSheet().push_back( &schematic->Root() );
875
877 plugin.SetReporter( &reporter );
878
879 try
880 {
881 plugin.LoadSchematicFile( dsn.string(), schematic.get() );
882 schematic->CurrentSheet().UpdateAllScreenReferences();
883 }
884 catch( const std::exception& e )
885 {
886 // Pre-2003 designs out of scope, rejected cleanly
887 if( std::string( e.what() ).find( "pre-2003" ) != std::string::npos )
888 ++unsupported;
889 else
890 ++crashed;
891
892 BOOST_TEST_MESSAGE( " THROW " << rel << " : " << e.what() );
893 continue;
894 }
895
896 ++imported;
897
898 for( const SCH_SHEET_PATH& path : schematic->BuildSheetListSortedByPageNumbers() )
899 {
900 SCH_SCREEN* screen = path.LastScreen();
901
902 if( !screen )
903 continue;
904
905 ++importedPages;
906 ++perDesignPages;
907
908 for( SCH_ITEM* item : screen->Items() )
909 {
910 switch( item->Type() )
911 {
912 case SCH_SYMBOL_T:
913 {
914 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
915 wxString ref = symbol->GetRef( &path, false );
916
917 if( ref.StartsWith( wxS( "#" ) ) )
918 {
919 ++importedPowerSymbols;
920 ++perDesignPowerSymbols;
921
922 if( symbol->GetValue( &path, RAW_VALUE ) == wxS( "VDDM" ) )
923 ++perDesignVddmPowerSymbols;
924 }
925 else
926 {
927 ++importedComponents;
928 ++perDesignComponents;
929 }
930
931 size_t pinCount = symbol->GetPins( &path ).size();
932 importedPins += pinCount;
933 perDesignPins += pinCount;
934 break;
935 }
936
937 case SCH_LINE_T:
938 {
939 SCH_LINE* line = static_cast<SCH_LINE*>( item );
940
941 if( line->GetLayer() == LAYER_BUS )
942 {
943 ++importedBuses;
944 ++perDesignBuses;
945 }
946 else if( line->GetLayer() == LAYER_WIRE )
947 {
948 ++importedWires;
949 ++perDesignWires;
950
951 if( line->GetLineColor() == OrcadColor( 8 ) )
952 ++perDesignRedWires;
953 }
954
955 break;
956 }
957
958 case SCH_LABEL_T:
960 case SCH_HIER_LABEL_T:
961 ++importedLabels;
962 ++perDesignLabels;
963 break;
964
965 case SCH_SHAPE_T:
966 ++importedShapes;
967 ++perDesignShapes;
968 break;
969
970 case SCH_TEXT_T:
971 ++importedTexts;
972 ++perDesignTexts;
973 break;
974
975 case SCH_BITMAP_T:
976 ++importedBitmaps;
977 ++perDesignBitmaps;
978 break;
979
980 default: break;
981 }
982 }
983 }
984
985 BOOST_TEST_MESSAGE( " AUDIT " << rel << "|pages=" << perDesignPages << "|components=" << perDesignComponents
986 << "|power=" << perDesignPowerSymbols << "|pins=" << perDesignPins
987 << "|wires=" << perDesignWires << "|buses=" << perDesignBuses
988 << "|labels=" << perDesignLabels << "|shapes=" << perDesignShapes
989 << "|texts=" << perDesignTexts << "|bitmaps=" << perDesignBitmaps );
990
991 if( rel == "allegro/beagleboard-xm/SCH/BeagleBoard-xM_ORCAD.DSN" )
992 {
993 BOOST_CHECK_EQUAL( perDesignBitmaps, 10u );
994 BOOST_CHECK_EQUAL( perDesignShapes, 108u );
995 BOOST_CHECK_EQUAL( perDesignTexts, 175u );
996 }
997
998 if( rel
999 == "allegro/OpenCellular-LED/Rev-C/schematic/"
1000 "OpenCellular_Connect-1_LED_Life-3_Schematic.DSN" )
1001 {
1002 BOOST_CHECK_EQUAL( perDesignVddmPowerSymbols, 31u );
1003 }
1004
1005 if( rel
1006 == "orcad/OpenCellular-GBC-Elgon_ARM/Rev-A/schematics/"
1007 "CN81XX_GBCV2_sch_0530.DSN" )
1008 {
1009 BOOST_CHECK_EQUAL( perDesignRedWires, 35u );
1010 }
1011
1012 if( debug )
1013 BOOST_TEST_MESSAGE( " DEBUG " << rel << " warnings:\n"
1014 << std::string( reporter.GetMessages().ToUTF8() ) );
1015
1016 std::set<std::string> got = collectImportedRefs( *schematic );
1017 std::string source;
1018 std::set<std::string> expected = expectedRefsFor( dsn, source );
1019
1020 if( debug )
1021 BOOST_TEST_MESSAGE( " DEBUG " << rel << " imported " << got.size() << " refs" );
1022
1023 if( expected.empty() )
1024 continue;
1025
1026 std::set<std::string> missing, extra;
1027 std::set_difference( expected.begin(), expected.end(), got.begin(), got.end(),
1028 std::inserter( missing, missing.begin() ) );
1029 std::set_difference( got.begin(), got.end(), expected.begin(), expected.end(),
1030 std::inserter( extra, extra.begin() ) );
1031
1032 unsigned int matched = static_cast<unsigned int>( expected.size() - missing.size() );
1033
1034 ++checked;
1035 totalExpected += expected.size();
1036 totalMatched += matched;
1037 totalMissing += missing.size();
1038 totalExtra += extra.size();
1039
1040 BOOST_TEST_MESSAGE( " CHECK " << rel << " : " << matched << "/" << expected.size() << " refs ("
1041 << int( 100.0 * matched / expected.size() ) << "%), extra " << extra.size()
1042 << " [" << source << "]" );
1043
1044 if( debug )
1045 {
1046 for( const std::string& ref : missing )
1047 BOOST_TEST_MESSAGE( " missing ref: " << ref );
1048
1049 for( const std::string& ref : extra )
1050 BOOST_TEST_MESSAGE( " extra ref: " << ref );
1051 }
1052
1053 // .NET ground truth carries terminal connectivity; verify pins group per net after rebuild.
1054 std::filesystem::path net = dsn;
1055 net.replace_extension( source.size() >= 4 && source.substr( source.size() - 4 ) == ".net" ? ".net" : ".NET" );
1056
1057 if( std::filesystem::exists( net ) )
1058 {
1059 std::vector<std::set<std::string>> nets = parseNetTerminals( net.string() );
1060
1061 if( !nets.empty() )
1062 {
1063 std::vector<std::set<std::string>> inconsistent;
1064 auto [consistent, checkableNets] = checkConnectivity( *schematic, nets, &inconsistent );
1065 netConsistent += consistent;
1066 netCheckable += checkableNets;
1067 netTotal += static_cast<int>( nets.size() );
1068
1069 BOOST_TEST_MESSAGE( " connectivity: " << consistent << "/" << checkableNets
1070 << " nets consistent" );
1071
1072 if( debug )
1073 {
1074 for( const std::set<std::string>& terminals : inconsistent )
1075 {
1076 std::string joined;
1077
1078 for( const std::string& terminal : terminals )
1079 {
1080 if( !joined.empty() )
1081 joined += ", ";
1082
1083 joined += terminal;
1084 }
1085
1086 BOOST_TEST_MESSAGE( " inconsistent net: " << joined );
1087 }
1088 }
1089 }
1090 }
1091 }
1092
1093 BOOST_TEST_MESSAGE( "==== OrCAD corpus summary ====" );
1094 BOOST_TEST_MESSAGE( " designs: " << designs.size() << " imported: " << imported << " crashed: " << crashed
1095 << " unsupported: " << unsupported << " rejected: " << rejected );
1096 BOOST_TEST_MESSAGE( " objects: pages "
1097 << importedPages << " components " << importedComponents << " power " << importedPowerSymbols
1098 << " pins " << importedPins << " wires " << importedWires << " buses " << importedBuses
1099 << " labels " << importedLabels << " shapes " << importedShapes << " texts " << importedTexts
1100 << " bitmaps " << importedBitmaps );
1101
1102 if( checked )
1103 {
1104 BOOST_TEST_MESSAGE( " refdes coverage: " << totalMatched << "/" << totalExpected << " ("
1105 << int( 100.0 * totalMatched / totalExpected )
1106 << "%) missing: " << totalMissing << " extra: " << totalExtra );
1107 }
1108
1109 if( netCheckable )
1110 {
1111 BOOST_TEST_MESSAGE( " net connectivity: " << netConsistent << "/" << netCheckable << " ("
1112 << int( 100.0 * netConsistent / netCheckable ) << "%)" );
1113 }
1114
1115 // Only pre-2003 format may throw; anything else is a crash
1116 BOOST_CHECK_MESSAGE( crashed == 0, crashed << " design(s) crashed during import." );
1117
1118 const char* snapshotEnv = std::getenv( "KICAD_ORCAD_CORPUS_SNAPSHOT" );
1119
1120 // Guard against vacuous pass when no companion files present.
1121 if( designFilter.empty() )
1122 BOOST_REQUIRE_MESSAGE( checked > 0, "No ground-truth .BOM/.NET companions were validated." );
1123
1124 if( designFilter.empty() && snapshotEnv && *snapshotEnv )
1125 {
1126 BOOST_CHECK_EQUAL( imported, 92 );
1127 BOOST_CHECK_EQUAL( rejected, 1 );
1128 BOOST_CHECK_EQUAL( importedPages, 854u );
1129 BOOST_CHECK_EQUAL( importedBitmaps, 616u );
1130 }
1131
1132 // Occurrence-annotation decode holds this above 95%; dropped Hierarchy-stream ref overlay collapses it.
1133 if( checked )
1134 BOOST_CHECK_GE( 100.0 * totalMatched / totalExpected, 95.0 );
1135
1136 // Pin-placement/geometry regression breaking connectivity collapses this. Checkable floor
1137 // (>= 2 resolvable terminals per net) stops broad pin loss passing vacuously.
1138 if( netTotal )
1139 {
1140 BOOST_CHECK_GE( 100.0 * netCheckable / netTotal, 80.0 );
1141 BOOST_CHECK_EQUAL( netConsistent, netCheckable );
1142 }
1143}
1144
1145
1146static std::filesystem::path findCorpusDesign( const std::filesystem::path& aRoot, const std::string& aFileName )
1147{
1148 for( const std::filesystem::directory_entry& entry : std::filesystem::recursive_directory_iterator( aRoot ) )
1149 {
1150 if( entry.is_regular_file() && entry.path().filename() == aFileName )
1151 return entry.path();
1152 }
1153
1154 return {};
1155}
1156
1157
1158BOOST_AUTO_TEST_CASE( CisVariantFallbackUsesBytewiseFirstBomName )
1159{
1160 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
1161
1162 if( !corpusEnv || !*corpusEnv )
1163 return;
1164
1165 std::filesystem::path dsn =
1166 std::filesystem::path( corpusEnv ) / "PADS" / "skyworks-eval" / "SI34061FB12V4KIT"
1167 / "si34061-evb-ext_1.7.1.20220111" / "schematic" / "SI34061-EVB-EXT.DSN";
1168
1169 if( !std::filesystem::exists( dsn ) )
1170 {
1171 BOOST_TEST_MESSAGE( "SI34061-EVB-EXT 12 V design not present; skipping CIS variant check." );
1172 return;
1173 }
1174
1175 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
1176 SETTINGS_MANAGER manager;
1177 manager.LoadProject( "" );
1178 schematic->SetProject( &manager.Prj() );
1179 schematic->CurrentSheet().clear();
1180 schematic->CurrentSheet().push_back( &schematic->Root() );
1181
1182 SCH_IO_ORCAD plugin;
1183 plugin.LoadSchematicFile( dsn.string(), schematic.get() );
1184
1185 std::map<wxString, std::pair<SCH_SYMBOL*, SCH_SHEET_PATH>> symbols;
1186
1187 for( const SCH_SHEET_PATH& path : schematic->BuildSheetListSortedByPageNumbers() )
1188 {
1189 for( SCH_ITEM* item : path.LastScreen()->Items().OfType( SCH_SYMBOL_T ) )
1190 {
1191 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
1192 symbols.emplace( symbol->GetRef( &path, false ), std::make_pair( symbol, path ) );
1193 }
1194 }
1195
1196 // Capture's core design is the default variant; the bytewise-first BOM variant is current
1197 BOOST_CHECK( schematic->GetVariantNames() == std::set<wxString>( { wxS( "12V" ), wxS( "5V" ) } ) );
1198 BOOST_CHECK_EQUAL( schematic->GetCurrentVariant(), wxS( "12V" ) );
1199
1200 auto fieldText = [&]( const wxString& aRef, const wxString& aField, const wxString& aVariant )
1201 {
1202 BOOST_REQUIRE( symbols.count( aRef ) );
1203 auto& [symbol, path] = symbols.at( aRef );
1204 return symbol->GetFieldText( aField, &path, aVariant );
1205 };
1206
1207 BOOST_CHECK_EQUAL( fieldText( wxS( "U1" ), wxS( "Value" ), wxS( "12V" ) ), wxS( "NVMFS5C680NLT1G" ) );
1208 BOOST_CHECK_EQUAL( fieldText( wxS( "D13" ), wxS( "Value" ), wxS( "12V" ) ), wxS( "PDS5100" ) );
1209 BOOST_CHECK_EQUAL( fieldText( wxS( "T2" ), wxS( "Value" ), wxS( "12V" ) ), wxS( "LDT1026-50R" ) );
1210 BOOST_CHECK_EQUAL( fieldText( wxS( "LB1" ), wxS( "Value" ), wxS( "12V" ) ),
1211 wxS( "LABEL-Si34061-EVB-EXT-BOM-R1.7-12V" ) );
1212 BOOST_CHECK_EQUAL( fieldText( wxS( "LB1" ), wxS( "Value" ), wxS( "5V" ) ),
1213 wxS( "LABEL-Si34061-EVB-EXT-BOM R1.7" ) );
1214 BOOST_CHECK_EQUAL( fieldText( wxS( "U1" ), wxS( "Voltage" ), wxS( "12V" ) ), wxS( "60V" ) );
1215
1216 // Capture's "UNDEFINED" column placeholder is not a property value
1217 BOOST_REQUIRE( symbols.count( wxS( "C19" ) ) );
1218 BOOST_CHECK( !symbols.at( wxS( "C19" ) ).first->FindFieldCaseInsensitive( wxS( "Part type" ) ) );
1219 BOOST_CHECK( !symbols.at( wxS( "C19" ) ).first->FindFieldCaseInsensitive( wxS( "Power Pins Visible" ) ) );
1220
1221 // Group membership marks C16 not installed in 12V only
1222 BOOST_REQUIRE( symbols.count( wxS( "C16" ) ) );
1223 auto& [c16, c16Path] = symbols.at( wxS( "C16" ) );
1224 BOOST_CHECK( c16->GetDNP( &c16Path, wxS( "12V" ) ) );
1225 BOOST_CHECK( !c16->GetDNP( &c16Path, wxS( "5V" ) ) );
1226 BOOST_CHECK( !c16->GetDNP( &c16Path ) );
1227
1228 for( const wxString& ref : { wxS( "D15" ), wxS( "R35" ), wxS( "TP1" ) } )
1229 {
1230 BOOST_REQUIRE( symbols.count( ref ) );
1231 BOOST_CHECK( symbols.at( ref ).first->GetDNP() );
1232 }
1233
1234 BOOST_CHECK_EQUAL( symbols.at( wxS( "D15" ) ).first->GetField( FIELD_T::VALUE )->GetText(), wxS( "NI" ) );
1235
1236 for( const SCH_SHEET_PATH& path : schematic->BuildSheetListSortedByPageNumbers() )
1237 {
1238 bool variantNameFound = false;
1239
1240 for( SCH_ITEM* item : path.LastScreen()->Items().OfType( SCH_TEXT_T ) )
1241 {
1242 wxString text = static_cast<SCH_TEXT*>( item )->GetShownText( &path, FOR_CANVAS );
1243 BOOST_CHECK_NE( text, wxS( "<Core Design>" ) );
1244 variantNameFound |= text == wxS( "12V" );
1245 }
1246
1247 BOOST_CHECK( variantNameFound );
1248 BOOST_CHECK_EQUAL( path.LastScreen()->GetTitleBlock().GetComment( 1 ), wxS( "Variant Name: ${VARIANT}" ) );
1249 }
1250}
1251
1252
1253BOOST_AUTO_TEST_CASE( UnreferencedSchematicFoldersRemainVisibleButExcludedFromBoard )
1254{
1255 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
1256
1257 if( !corpusEnv || !*corpusEnv )
1258 return;
1259
1260 std::filesystem::path dsn = findCorpusDesign( corpusEnv, "PROJET INDUS.DSN" );
1261
1262 if( dsn.empty() )
1263 return;
1264
1265 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
1266 SETTINGS_MANAGER manager;
1267 manager.LoadProject( "" );
1268 schematic->SetProject( &manager.Prj() );
1269 schematic->CurrentSheet().clear();
1270 schematic->CurrentSheet().push_back( &schematic->Root() );
1271
1272 SCH_IO_ORCAD plugin;
1273 plugin.LoadSchematicFile( dsn.string(), schematic.get() );
1274
1275 SCH_SHEET_LIST sheets = schematic->BuildSheetListSortedByPageNumbers();
1276 size_t excluded = 0;
1277 size_t excludedSymbols = 0;
1278
1279 for( const SCH_SHEET_PATH& path : sheets )
1280 {
1281 excluded += path.GetExcludedFromBoard();
1282
1283 if( !path.GetExcludedFromBoard() )
1284 continue;
1285
1286 for( SCH_ITEM* item : path.LastScreen()->Items().OfType( SCH_SYMBOL_T ) )
1287 {
1288 ++excludedSymbols;
1289 BOOST_CHECK( static_cast<SCH_SYMBOL*>( item )->GetExcludedFromBoard() );
1290 }
1291 }
1292
1293 BOOST_CHECK_EQUAL( sheets.size(), 5u );
1294 BOOST_CHECK_EQUAL( excluded, 4u );
1295 BOOST_CHECK_GT( excludedSymbols, 0u );
1296}
1297
1298
1299BOOST_AUTO_TEST_CASE( ViewsDirectoryIgnoresStaleStoredFolders )
1300{
1301 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
1302
1303 if( !corpusEnv || !*corpusEnv )
1304 return;
1305
1306 std::filesystem::path dsn = findCorpusDesign( corpusEnv, "DC1987A-2.DSN" );
1307
1308 if( dsn.empty() )
1309 return;
1310
1311 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
1312 SETTINGS_MANAGER manager;
1313 manager.LoadProject( "" );
1314 schematic->SetProject( &manager.Prj() );
1315 schematic->CurrentSheet().clear();
1316 schematic->CurrentSheet().push_back( &schematic->Root() );
1317
1318 SCH_IO_ORCAD plugin;
1319 plugin.LoadSchematicFile( dsn.string(), schematic.get() );
1320
1321 SCH_SHEET_LIST sheets = schematic->BuildSheetListSortedByPageNumbers();
1322 BOOST_REQUIRE_EQUAL( sheets.size(), 1u );
1323}
1324
1325
1326BOOST_AUTO_TEST_CASE( ShortFramedPackagePinsKeepTheirSlots )
1327{
1328 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
1329
1330 if( !corpusEnv || !*corpusEnv )
1331 return;
1332
1333 std::filesystem::path dsn = findCorpusDesign( corpusEnv, "1822A.DSN" );
1334
1335 if( dsn.empty() )
1336 return;
1337
1338 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
1339 SETTINGS_MANAGER manager;
1340 manager.LoadProject( "" );
1341 schematic->SetProject( &manager.Prj() );
1342 schematic->CurrentSheet().clear();
1343 schematic->CurrentSheet().push_back( &schematic->Root() );
1344
1345 SCH_IO_ORCAD plugin;
1346 plugin.LoadSchematicFile( dsn.string(), schematic.get() );
1347
1348 SCH_SYMBOL* connector = nullptr;
1349
1350 for( const SCH_SHEET_PATH& path : schematic->BuildSheetListSortedByPageNumbers() )
1351 {
1352 if( !connector )
1353 connector = findConvertedSymbol( *path.LastScreen(), path, wxS( "J7" ) );
1354 }
1355
1356 // Without pin slots the default definition misses an empty slot and a duplicate variant wins
1357 BOOST_REQUIRE( connector );
1358 BOOST_CHECK_EQUAL( connector->GetLibId().GetLibItemName().wx_str(), wxS( "Connex_112404_gnd2_0_pins2" ) );
1359}
1360
1361
1362BOOST_AUTO_TEST_CASE( OccurrenceFlatNetConnectsAcrossPagesWithoutOffpageSymbols )
1363{
1364 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
1365
1366 if( !corpusEnv || !*corpusEnv )
1367 return;
1368
1369 std::filesystem::path dsn = findCorpusDesign( corpusEnv, "M5275EVB.DSN" );
1370
1371 if( dsn.empty() )
1372 return;
1373
1374 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
1375 SETTINGS_MANAGER manager;
1376 manager.LoadProject( "" );
1377 schematic->SetProject( &manager.Prj() );
1378 schematic->CurrentSheet().clear();
1379 schematic->CurrentSheet().push_back( &schematic->Root() );
1380
1381 SCH_IO_ORCAD plugin;
1382 plugin.LoadSchematicFile( dsn.string(), schematic.get() );
1383
1384 auto [consistent, checkable] =
1385 checkConnectivity( *schematic, { { terminalToken( "J3", "38" ), terminalToken( "RP49", "7" ),
1386 terminalToken( "U6", "D10" ) } } );
1387 BOOST_CHECK_EQUAL( checkable, 1 );
1388 BOOST_CHECK_EQUAL( consistent, 1 );
1389}
1390
1391
1392static std::map<std::string, std::vector<std::string>> collectImportedUuids( const SCHEMATIC& aSchematic )
1393{
1394 std::map<std::string, std::vector<std::string>> uuids;
1395
1396 auto itemIdentity = []( const SCH_ITEM& item, const SCH_SHEET_PATH& path )
1397 {
1398 std::ostringstream identity;
1399 const VECTOR2I position = item.GetPosition();
1400 const BOX2I bounds = item.GetBoundingBox();
1401 identity << item.Type() << ':' << item.GetLayer() << ':' << position.x << ',' << position.y
1402 << ':' << bounds.GetX() << ',' << bounds.GetY() << ',' << bounds.GetWidth() << ','
1403 << bounds.GetHeight();
1404
1405 auto text = [&]( const wxString& value )
1406 {
1407 const std::string utf8 = value.ToStdString( wxConvUTF8 );
1408 identity << ':' << utf8.size() << ':' << utf8;
1409 };
1410
1411 if( const auto* line = dynamic_cast<const SCH_LINE*>( &item ) )
1412 {
1413 identity << ':' << line->GetEndPoint().x << ',' << line->GetEndPoint().y;
1414 }
1415
1416 if( const auto* label = dynamic_cast<const EDA_TEXT*>( &item ) )
1417 {
1418 text( label->GetText() );
1419 identity << ':' << label->GetTextAngleDegrees() << ':' << label->GetTextWidth() << ','
1420 << label->GetTextHeight();
1421 }
1422
1423 if( const auto* shape = dynamic_cast<const SCH_SHAPE*>( &item ) )
1424 {
1425 identity << ':' << static_cast<int>( shape->GetShape() ) << ':' << shape->GetStart().x
1426 << ',' << shape->GetStart().y << ':' << shape->GetEnd().x << ',' << shape->GetEnd().y;
1427
1428 if( shape->GetShape() == SHAPE_T::POLY )
1429 identity << ':' << shape->GetPolyShape().Format();
1430 else if( shape->GetShape() == SHAPE_T::ARC )
1431 identity << ':' << shape->GetArcMid().x << ',' << shape->GetArcMid().y;
1432 else if( shape->GetShape() == SHAPE_T::BEZIER )
1433 identity << ':' << shape->GetBezierC1().x << ',' << shape->GetBezierC1().y << ':'
1434 << shape->GetBezierC2().x << ',' << shape->GetBezierC2().y;
1435 }
1436
1437 if( const auto* symbol = dynamic_cast<const SCH_SYMBOL*>( &item ) )
1438 {
1439 text( symbol->GetRef( &path ) );
1440 text( symbol->GetLibId().Format() );
1441 identity << ':' << symbol->GetUnitSelection( &path ) << ':' << symbol->GetOrientation();
1442 }
1443
1444 if( const auto* pin = dynamic_cast<const SCH_PIN*>( &item ) )
1445 {
1446 text( pin->GetNumber() );
1447 text( pin->GetName() );
1448 identity << ':' << pin->GetUnit() << ':' << pin->GetBodyStyle() << ':'
1449 << static_cast<int>( pin->GetOrientation() ) << ':' << pin->GetLength();
1450 }
1451
1452 if( const auto* sheet = dynamic_cast<const SCH_SHEET*>( &item ) )
1453 {
1454 text( sheet->GetName() );
1455 text( sheet->GetFileName() );
1456 }
1457
1458 return identity.str();
1459 };
1460
1461 for( const SCH_SHEET_PATH& path : aSchematic.Hierarchy() )
1462 {
1463 std::string scope;
1464
1465 for( size_t index = 0; index < path.size(); ++index )
1466 {
1467 SCH_SHEET* sheet = path.at( index );
1468 const std::string name = sheet->GetName().ToStdString( wxConvUTF8 );
1469 scope += std::to_string( name.size() ) + ':' + name + '/';
1470 }
1471
1472 SCH_SHEET* sheet = path.Last();
1473 uuids[scope + "sheet"].push_back( sheet->m_Uuid.AsStdString() );
1474 SCH_SCREEN* screen = path.LastScreen();
1475 BOOST_REQUIRE( screen );
1476 uuids[scope + "screen"].push_back( screen->GetUuid().AsStdString() );
1477
1478 for( SCH_ITEM* item : screen->Items() )
1479 {
1480 const std::string identity = scope + itemIdentity( *item, path );
1481 uuids[identity].push_back( item->m_Uuid.AsStdString() );
1482
1483 if( auto* symbol = dynamic_cast<SCH_SYMBOL*>( item ) )
1484 {
1485 for( const std::unique_ptr<SCH_PIN>& pin : symbol->GetRawPins() )
1486 uuids[identity + "/pin/" + itemIdentity( *pin, path )].push_back( pin->m_Uuid.AsStdString() );
1487 }
1488 else if( const auto* child = dynamic_cast<const SCH_SHEET*>( item ) )
1489 {
1490 for( const SCH_SHEET_PIN* pin : child->GetPins() )
1491 uuids[identity + "/pin/" + itemIdentity( *pin, path )].push_back( pin->m_Uuid.AsStdString() );
1492 }
1493 }
1494 }
1495
1496 // Identical overlapping objects are interchangeable, but UUIDs must stay attached to their geometry and owner.
1497 for( auto& [identity, values] : uuids )
1498 std::sort( values.begin(), values.end() );
1499
1500 return uuids;
1501}
1502
1503BOOST_AUTO_TEST_CASE( RepeatedImportHasDeterministicUuids )
1504{
1505 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
1506
1507 if( !corpusEnv || !*corpusEnv )
1508 {
1509 BOOST_TEST_MESSAGE( "KICAD_ORCAD_CORPUS not set; skipping OrCAD determinism check." );
1510 return;
1511 }
1512
1513 std::filesystem::path dsn = findCorpusDesign( corpusEnv, "mc33163.dsn" );
1514
1515 if( dsn.empty() )
1516 {
1517 BOOST_TEST_MESSAGE( "mc33163.dsn not present in corpus; skipping OrCAD determinism check." );
1518 return;
1519 }
1520
1521 auto importUuids = [&]( const std::filesystem::path& aDsn )
1522 {
1523 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
1524 SETTINGS_MANAGER manager;
1525 manager.LoadProject( "" );
1526 schematic->SetProject( &manager.Prj() );
1527 schematic->CurrentSheet().clear();
1528 schematic->CurrentSheet().push_back( &schematic->Root() );
1529
1530 SCH_IO_ORCAD plugin;
1531 plugin.LoadSchematicFile( aDsn.string(), schematic.get() );
1532 return collectImportedUuids( *schematic );
1533 };
1534
1535 const auto first = importUuids( dsn );
1536 const auto second = importUuids( dsn );
1537
1538 BOOST_REQUIRE( !first.empty() );
1539 BOOST_REQUIRE_EQUAL( first.size(), second.size() );
1540
1541 for( const auto& [identity, values] : first )
1542 {
1543 BOOST_TEST_CONTEXT( identity )
1544 {
1545 const auto found = second.find( identity );
1546 BOOST_REQUIRE( found != second.end() );
1547 BOOST_CHECK_EQUAL_COLLECTIONS( values.begin(), values.end(), found->second.begin(), found->second.end() );
1548 }
1549 }
1550}
1551
1552
1553BOOST_AUTO_TEST_CASE( SheetLoadKeepsExistingItemUuids )
1554{
1555 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
1556
1557 if( !corpusEnv || !*corpusEnv )
1558 {
1559 BOOST_TEST_MESSAGE( "KICAD_ORCAD_CORPUS not set; skipping OrCAD sheet-load UUID check." );
1560 return;
1561 }
1562
1563 std::filesystem::path dsn = findCorpusDesign( corpusEnv, "mc33163.dsn" );
1564
1565 if( dsn.empty() )
1566 {
1567 BOOST_TEST_MESSAGE( "mc33163.dsn not present in corpus; skipping OrCAD sheet-load UUID check." );
1568 return;
1569 }
1570
1571 SETTINGS_MANAGER settings;
1572 std::unique_ptr<SCHEMATIC> schematic;
1573 KI_TEST::LoadSchematic( settings, wxS( "netlists/complex_hierarchy_shared/complex_hierarchy" ), schematic );
1574
1575 auto existingUuids = [&]()
1576 {
1577 std::vector<std::string> uuids;
1578
1579 for( const SCH_SHEET_PATH& path : schematic->Hierarchy() )
1580 {
1581 for( SCH_ITEM* item : path.LastScreen()->Items() )
1582 uuids.push_back( item->m_Uuid.AsStdString() );
1583 }
1584
1585 std::sort( uuids.begin(), uuids.end() );
1586 return uuids;
1587 };
1588
1589 const std::vector<std::string> before = existingUuids();
1590 BOOST_REQUIRE( !before.empty() );
1591
1592 // SCH_EDIT_FRAME::LoadSheetFromFile loads into a sheet that is not yet in the hierarchy
1593 SCH_SHEET sheet( schematic.get() );
1594 sheet.SetFileName( dsn.string() );
1595 m_plugin.LoadSchematicFile( dsn.string(), schematic.get(), &sheet );
1596
1597 BOOST_REQUIRE( sheet.GetScreen() && !sheet.GetScreen()->Items().empty() );
1598
1599 const std::vector<std::string> after = existingUuids();
1600 BOOST_CHECK_EQUAL_COLLECTIONS( before.begin(), before.end(), after.begin(), after.end() );
1601}
1602
1603
1604BOOST_AUTO_TEST_CASE( HierarchicalSheetLoadIsRefused )
1605{
1606 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
1607
1608 if( !corpusEnv || !*corpusEnv )
1609 {
1610 BOOST_TEST_MESSAGE( "KICAD_ORCAD_CORPUS not set; skipping OrCAD hierarchical sheet refusal check." );
1611 return;
1612 }
1613
1614 std::filesystem::path dsn = findCorpusDesign( corpusEnv, "mc33163.dsn" );
1615
1616 if( dsn.empty() )
1617 {
1618 BOOST_TEST_MESSAGE( "mc33163.dsn not present in corpus; skipping OrCAD hierarchical sheet refusal check." );
1619 return;
1620 }
1621
1622 SETTINGS_MANAGER settings;
1623 std::unique_ptr<SCHEMATIC> schematic;
1624 KI_TEST::LoadSchematic( settings, wxS( "netlists/complex_hierarchy_shared/complex_hierarchy" ), schematic );
1625
1626 const std::vector<SCH_SHEET*> topLevel = schematic->GetTopLevelSheets();
1627
1628 // SCH_EDIT_FRAME::LoadSheetFromFile passes the live schematic with this property
1629 std::map<std::string, UTF8> props = { { "hierarchical_sheet_load", "1" } };
1630
1631 BOOST_CHECK_THROW( m_plugin.LoadSchematicFile( dsn.string(), schematic.get(), nullptr, &props ), IO_ERROR );
1632 BOOST_CHECK( schematic->GetTopLevelSheets() == topLevel );
1633}
1634
1635
1636BOOST_AUTO_TEST_CASE( PowerSymbolAttachesAtPlacedVariantPin )
1637{
1638 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
1639
1640 if( !corpusEnv || !*corpusEnv )
1641 {
1642 BOOST_TEST_MESSAGE( "KICAD_ORCAD_CORPUS not set; skipping OrCAD graphic variant pin check." );
1643 return;
1644 }
1645
1646 std::filesystem::path dsn = findCorpusDesign( corpusEnv, "OC_CONNECT1_SDR_REV_C_V1P1.DSN" );
1647
1648 if( dsn.empty() )
1649 {
1650 BOOST_TEST_MESSAGE( "OC_CONNECT1_SDR_REV_C_V1P1.DSN not present in corpus; skipping variant pin check." );
1651 return;
1652 }
1653
1654 m_plugin.LoadSchematicFile( dsn.string(), m_schematic.get() );
1655
1656 // Only the later cache variant of this GND symbol puts its pin on the free end of its stub wire
1657 const VECTOR2I stubEnd( schIUScale.mmToIU( 34.29 ), schIUScale.mmToIU( 133.35 ) );
1658 bool attached = false;
1659
1660 for( const SCH_SHEET_PATH& path : m_schematic->Hierarchy() )
1661 {
1662 if( !path.LastScreen()->GetFileName().Contains( wxS( "FPGA_1" ) ) )
1663 continue;
1664
1665 for( SCH_ITEM* item : path.LastScreen()->Items().OfType( SCH_SYMBOL_T ) )
1666 {
1667 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
1668
1669 if( !symbol->IsPower() )
1670 continue;
1671
1672 for( const SCH_PIN* pin : symbol->GetPins( &path ) )
1673 attached |= pin->GetPosition() == stubEnd;
1674 }
1675 }
1676
1677 BOOST_CHECK( attached );
1678}
1679
1680
1681BOOST_AUTO_TEST_CASE( AutoGeneratedNetNamesAreMappedWithoutNamingLabels )
1682{
1683 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
1684
1685 if( !corpusEnv || !*corpusEnv )
1686 {
1687 BOOST_TEST_MESSAGE( "KICAD_ORCAD_CORPUS not set; skipping generated net-name check." );
1688 return;
1689 }
1690
1691 std::filesystem::path dsn = findCorpusDesign( corpusEnv, "CutiePi_V2.3-20210409.DSN" );
1692
1693 if( dsn.empty() )
1694 {
1695 BOOST_TEST_MESSAGE( "CutiePi_V2.3-20210409.DSN not present in corpus; skipping generated "
1696 "net-name check." );
1697 return;
1698 }
1699
1700 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
1701 SETTINGS_MANAGER manager;
1702 manager.LoadProject( "" );
1703 schematic->SetProject( &manager.Prj() );
1704 schematic->CurrentSheet().clear();
1705 schematic->CurrentSheet().push_back( &schematic->Root() );
1706
1707 SCH_IO_ORCAD plugin;
1708 plugin.LoadSchematicFile( dsn.string(), schematic.get() );
1709
1710 const IMPORT_NET_MAP* map = schematic->GetImportNetMap();
1711 BOOST_REQUIRE( map );
1712
1713 for( const auto& [sourceName, reference, number] :
1714 { std::tuple{ wxString( "N12720539" ), wxString( "D10" ), wxString( "A" ) },
1715 std::tuple{ wxString( "N132252170" ), wxString( "D23" ), wxString( "1" ) } } )
1716 {
1717 wxString netName = terminalNetName( *schematic, reference, number );
1718 BOOST_CHECK_NE( netName, sourceName );
1719 bool found = false;
1720
1721 for( const IMPORT_NET_MAP_ENTRY& entry : map->entries )
1722 {
1723 if( entry.originalName == sourceName )
1724 {
1726 BOOST_CHECK_EQUAL( entry.nameAtImport, netName );
1727 BOOST_CHECK( !entry.terminals.empty() );
1728 found = true;
1729 }
1730 }
1731
1732 BOOST_CHECK( found );
1733
1734 for( const SCH_SHEET_PATH& sheet : schematic->BuildSheetListSortedByPageNumbers() )
1735 {
1736 for( SCH_ITEM* item : sheet.LastScreen()->Items() )
1737 {
1738 if( auto* label = dynamic_cast<SCH_LABEL_BASE*>( item ) )
1739 {
1740 BOOST_CHECK_NE( label->GetText(), sourceName );
1741 BOOST_CHECK( label->GetTextColor() == KIGFX::COLOR4D::UNSPECIFIED
1742 || label->GetTextColor().a > 0 );
1743 }
1744 }
1745 }
1746 }
1747}
1748
1749
1750BOOST_AUTO_TEST_CASE( CaptureNetIdsAndBlankUnitLettersAreAuthoritative )
1751{
1752 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
1753
1754 if( !corpusEnv || !*corpusEnv )
1755 {
1756 BOOST_TEST_MESSAGE( "KICAD_ORCAD_CORPUS not set; skipping Capture net/unit regression check." );
1757 return;
1758 }
1759
1760 std::filesystem::path dsn = findCorpusDesign( corpusEnv, "BEAGLEBONEBLK_C3.DSN" );
1761
1762 if( dsn.empty() )
1763 {
1764 BOOST_TEST_MESSAGE( "BEAGLEBONEBLK_C3.DSN not present in corpus; skipping Capture net/unit "
1765 "regression check." );
1766 return;
1767 }
1768
1769 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
1770 SETTINGS_MANAGER manager;
1771 manager.LoadProject( "" );
1772 schematic->SetProject( &manager.Prj() );
1773 schematic->CurrentSheet().clear();
1774 schematic->CurrentSheet().push_back( &schematic->Root() );
1775
1776 SCH_IO_ORCAD plugin;
1777 plugin.LoadSchematicFile( dsn.string(), schematic.get() );
1778
1779 SCH_SHEET_LIST sheets = schematic->BuildSheetListSortedByPageNumbers();
1780 schematic->ConnectionGraph()->Recalculate( sheets, true );
1781
1782 std::map<std::tuple<std::string, std::string, std::string>, int> terminalNets;
1783 int netId = 0;
1784
1785 for( const auto& [key, subgraphs] : schematic->ConnectionGraph()->GetNetMap() )
1786 {
1787 for( CONNECTION_SUBGRAPH* subgraph : subgraphs )
1788 {
1789 std::string page = subgraph->GetSheet().LastScreen()->GetFileName().ToStdString();
1790
1791 for( SCH_ITEM* item : subgraph->GetItems() )
1792 {
1793 if( item->Type() != SCH_PIN_T )
1794 continue;
1795
1796 SCH_PIN* pin = static_cast<SCH_PIN*>( item );
1797 SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( pin->GetParentSymbol() );
1798
1799 if( symbol )
1800 {
1801 terminalNets[{ page, symbol->GetRef( &subgraph->GetSheet(), false ).ToStdString(),
1802 pin->GetNumber().ToStdString() }] = netId;
1803 }
1804 }
1805 }
1806
1807 ++netId;
1808 }
1809
1810 auto findNet = [&]( const std::string& aPage, const std::string& aRef, const std::string& aPin )
1811 {
1812 for( const auto& [terminal, id] : terminalNets )
1813 {
1814 if( std::get<0>( terminal ).find( aPage ) != std::string::npos && std::get<1>( terminal ) == aRef
1815 && std::get<2>( terminal ) == aPin )
1816 {
1817 return id;
1818 }
1819 }
1820
1821 return -1;
1822 };
1823
1824 int ground = findNet( "AM335x 2_3, USB", "J1", "1" );
1825 int rx = findNet( "AM335x 2_3, USB", "J1", "4" );
1826 int tx = findNet( "AM335x 2_3, USB", "J1", "5" );
1827
1828 BOOST_REQUIRE_NE( ground, -1 );
1829 BOOST_REQUIRE_NE( rx, -1 );
1830 BOOST_REQUIRE_NE( tx, -1 );
1831 BOOST_CHECK_NE( ground, rx );
1832 BOOST_CHECK_NE( ground, tx );
1833 BOOST_CHECK_NE( rx, tx );
1834
1835 BOOST_CHECK_EQUAL( findNet( "LED, Config", "D3", "1" ), findNet( "LED, Config", "Q1", "3" ) );
1836 BOOST_CHECK_EQUAL( findNet( "LED, Config", "R77", "1" ), findNet( "LED, Config", "Q1", "5" ) );
1837
1838 std::set<int> q1Units;
1839 std::set<wxString> q1ShownReferences;
1840
1841 for( const SCH_SHEET_PATH& path : sheets )
1842 {
1843 if( path.LastScreen()->GetFileName().Find( wxS( "LED, Config" ) ) == wxNOT_FOUND )
1844 continue;
1845
1846 for( SCH_ITEM* item : path.LastScreen()->Items().OfType( SCH_SYMBOL_T ) )
1847 {
1848 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
1849
1850 if( symbol->GetRef( &path, false ) == wxS( "Q1" ) )
1851 {
1852 q1Units.insert( symbol->GetUnit() );
1853 q1ShownReferences.insert( symbol->GetRef( &path, true ) );
1854 }
1855 }
1856 }
1857
1858 const std::set<int> expectedUnits = { 1, 2 };
1859 BOOST_CHECK_EQUAL_COLLECTIONS( q1Units.begin(), q1Units.end(), expectedUnits.begin(), expectedUnits.end() );
1860
1861 const std::set<wxString> expectedShownReferences = { wxS( "Q1" ) };
1862 BOOST_CHECK_EQUAL_COLLECTIONS( q1ShownReferences.begin(), q1ShownReferences.end(),
1863 expectedShownReferences.begin(), expectedShownReferences.end() );
1864}
1865
1866
1867BOOST_AUTO_TEST_CASE( BusMembersWithoutLocalWiresRetainNetMapReferences )
1868{
1869 const char* corpus = std::getenv( "KICAD_ORCAD_CORPUS" );
1870
1871 if( !corpus || !*corpus )
1872 return;
1873
1874 std::filesystem::path dsn = findCorpusDesign( corpus, "SCH-20380.DSN" );
1875
1876 if( dsn.empty() )
1877 return;
1878
1879 SETTINGS_MANAGER manager;
1880 manager.LoadProject( "" );
1881 SCHEMATIC schematic( &manager.Prj() );
1882 SCH_IO_ORCAD plugin;
1883 plugin.LoadSchematicFile( dsn.string(), &schematic );
1884 schematic.RefreshHierarchy();
1885 const IMPORT_NET_MAP* map = schematic.GetImportNetMap();
1886 BOOST_REQUIRE( map );
1887 bool memberFound = false;
1888 bool bundleFound = false;
1889 wxString actualName = terminalNetName( schematic, wxS( "J10" ), wxS( "25" ) );
1890 BOOST_REQUIRE( !actualName.IsEmpty() );
1891
1892 for( const IMPORT_NET_MAP_ENTRY& entry : map->entries )
1893 {
1894 if( entry.view != wxS( "Hierarchical Interconnects" ) )
1895 continue;
1896
1897 if( entry.originalName == wxS( "/IRQ[7:1]" ) )
1898 {
1899 bundleFound = true;
1901 BOOST_CHECK( !entry.nameAtImport.IsEmpty() );
1902 BOOST_CHECK( !entry.terminals.empty() );
1903 }
1904
1905 if( entry.originalName != wxS( "/IRQ7" ) )
1906 continue;
1907
1908 memberFound = true;
1910 BOOST_CHECK_EQUAL( entry.nameAtImport, actualName );
1911 BOOST_CHECK( !entry.itemUuids.empty() );
1912 std::set<std::string> mappedTerminals;
1913
1914 for( const IMPORT_NET_TERMINAL& terminal : entry.terminals )
1915 {
1917 auto* symbol = dynamic_cast<SCH_SYMBOL*>( schematic.ResolveItem( terminal.symbolUuid, &path ) );
1918 BOOST_REQUIRE( symbol );
1919 mappedTerminals.insert( terminalToken( symbol->GetRef( &path, false ).ToStdString(),
1920 terminal.pinNumber.ToStdString() ) );
1921 }
1922
1923 const std::set<std::string> expected = { "J10.25", "R77.2", "RP16.7", "U10.R8" };
1924 BOOST_CHECK( mappedTerminals == expected );
1925 }
1926
1927 BOOST_CHECK( memberFound );
1928 BOOST_CHECK( bundleFound );
1929}
1930
1931
1932BOOST_AUTO_TEST_CASE( NetMapIncludesPinsConnectedByImportJunctionCleanup )
1933{
1934 const char* corpus = std::getenv( "KICAD_ORCAD_CORPUS" );
1935
1936 if( !corpus || !*corpus )
1937 return;
1938
1939 std::filesystem::path dsn = findCorpusDesign( corpus, "DC1096B-1.DSN" );
1940
1941 if( dsn.empty() )
1942 return;
1943
1944 SETTINGS_MANAGER manager;
1945 manager.LoadProject( "" );
1946 SCHEMATIC schematic( &manager.Prj() );
1947 SCH_IO_ORCAD plugin;
1948 plugin.LoadSchematicFile( dsn.string(), &schematic );
1949 const IMPORT_NET_MAP* map = schematic.GetImportNetMap();
1950 BOOST_REQUIRE( map );
1951 bool found = false;
1952
1953 for( const IMPORT_NET_MAP_ENTRY& entry : map->entries )
1954 {
1955 if( entry.sourceNetId == 16903175 && entry.originalName == wxS( "GND" ) )
1956 {
1957 found = true;
1959 BOOST_CHECK_EQUAL( entry.nameAtImport, wxString( "GND" ) );
1960 }
1961 }
1962
1963 BOOST_CHECK( found );
1964
1965 for( const wxString& reference : { wxString( "U1" ), wxString( "U2" ), wxString( "U4" ) } )
1966 BOOST_CHECK_EQUAL( terminalNetName( schematic, reference, wxS( "11" ) ), wxString( "GND" ) );
1967}
1968
1969
1970BOOST_AUTO_TEST_CASE( HierarchicalBusMembersMapToPropagatedScalarNets )
1971{
1972 const char* corpus = std::getenv( "KICAD_ORCAD_CORPUS" );
1973
1974 if( !corpus || !*corpus )
1975 return;
1976
1977 std::filesystem::path dsn = findCorpusDesign( corpus, "meta_carrier_sch_rev1.dsn" );
1978
1979 if( dsn.empty() )
1980 return;
1981
1982 SETTINGS_MANAGER manager;
1983 manager.LoadProject( "" );
1984 SCHEMATIC schematic( &manager.Prj() );
1985 SCH_IO_ORCAD plugin;
1986 plugin.LoadSchematicFile( dsn.string(), &schematic );
1987 schematic.RefreshHierarchy();
1988 const IMPORT_NET_MAP* map = schematic.GetImportNetMap();
1989 BOOST_REQUIRE( map );
1990 bool found = false;
1991
1992 for( const IMPORT_NET_MAP_ENTRY& entry : map->entries )
1993 {
1994 if( entry.sourceNetId != 9438909 || entry.originalName != wxS( "OUT_P6" )
1995 || entry.occurrence != std::vector<wxString>( { "TOP", "9136400", "TILE_AD9523" } ) )
1996 continue;
1997
1998 found = true;
2000 BOOST_CHECK_EQUAL( entry.nameAtImport, wxString( "CLK_P5" ) );
2001 BOOST_REQUIRE( !entry.terminals.empty() );
2002
2003 for( const IMPORT_NET_TERMINAL& terminal : entry.terminals )
2004 {
2006 auto* symbol = dynamic_cast<SCH_SYMBOL*>( schematic.ResolveItem( terminal.symbolUuid, &path ) );
2007 BOOST_REQUIRE( symbol );
2008 BOOST_CHECK_EQUAL( terminalNetName( schematic, symbol->GetRef( &path, false ), terminal.pinNumber ),
2009 entry.nameAtImport );
2010 }
2011 }
2012
2013 BOOST_CHECK( found );
2014}
2015
2016
2017BOOST_AUTO_TEST_CASE( NamedWirelessPinUsesOccurrenceNetName )
2018{
2019 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
2020
2021 if( !corpusEnv || !*corpusEnv )
2022 {
2023 BOOST_TEST_MESSAGE( "KICAD_ORCAD_CORPUS not set; skipping named wireless-pin check." );
2024 return;
2025 }
2026
2027 std::filesystem::path dsn = findCorpusDesign( corpusEnv, "CURRENT_SENSOR.DSN" );
2028
2029 if( dsn.empty() )
2030 {
2031 BOOST_TEST_MESSAGE( "CURRENT_SENSOR.DSN not present in corpus; skipping named wireless-pin check." );
2032 return;
2033 }
2034
2035 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
2036 SETTINGS_MANAGER manager;
2037 manager.LoadProject( "" );
2038 schematic->SetProject( &manager.Prj() );
2039 schematic->CurrentSheet().clear();
2040 schematic->CurrentSheet().push_back( &schematic->Root() );
2041
2042 SCH_IO_ORCAD plugin;
2043 plugin.LoadSchematicFile( dsn.string(), schematic.get() );
2044
2045 wxString netName = terminalNetName( *schematic, wxS( "U1" ), wxS( "7" ) );
2046 BOOST_CHECK_EQUAL( netName.AfterLast( '/' ), wxString( "VCC" ) );
2047 const IMPORT_NET_MAP* map = schematic->GetImportNetMap();
2048 BOOST_REQUIRE( map );
2049 bool mapped = false;
2050
2051 for( const IMPORT_NET_MAP_ENTRY& entry : map->entries )
2052 {
2053 if( entry.originalName == wxS( "VCC" ) )
2054 {
2056 BOOST_CHECK_EQUAL( entry.nameAtImport, netName );
2057 BOOST_CHECK( !entry.terminals.empty() );
2058 mapped = true;
2059 }
2060 }
2061
2062 BOOST_CHECK( mapped );
2063}
2064
2065
2066BOOST_AUTO_TEST_CASE( ReservedDatasheetPropertyUsesStandardField )
2067{
2068 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
2069
2070 if( !corpusEnv || !*corpusEnv )
2071 return;
2072
2073 std::filesystem::path dsn = findCorpusDesign( corpusEnv, "reComputer J202_V1.0.DSN" );
2074
2075 if( dsn.empty() )
2076 return;
2077
2078 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
2079 SETTINGS_MANAGER manager;
2080 manager.LoadProject( "" );
2081 schematic->SetProject( &manager.Prj() );
2082 schematic->CurrentSheet().clear();
2083 schematic->CurrentSheet().push_back( &schematic->Root() );
2084
2085 SCH_IO_ORCAD plugin;
2086 plugin.LoadSchematicFile( dsn.string(), schematic.get() );
2087 SCH_SYMBOL* resistor = nullptr;
2088
2089 for( const SCH_SHEET_PATH& path : schematic->BuildSheetListSortedByPageNumbers() )
2090 {
2091 for( SCH_ITEM* item : path.LastScreen()->Items().OfType( SCH_SYMBOL_T ) )
2092 {
2093 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
2094
2095 if( symbol->GetRef( &path, false ) == wxS( "R48" ) )
2096 {
2097 resistor = symbol;
2098 break;
2099 }
2100 }
2101
2102 if( resistor )
2103 break;
2104 }
2105
2106 BOOST_REQUIRE( resistor );
2107 size_t datasheetFields = std::count_if(
2108 resistor->GetFields().begin(), resistor->GetFields().end(),
2109 []( const SCH_FIELD& field ) { return field.GetName() == wxS( "Datasheet" ); } );
2110 BOOST_CHECK_EQUAL( datasheetFields, 1u );
2112 resistor->GetField( FIELD_T::DATASHEET )->GetText(),
2113 wxS( "Y:\\01_Cadence_Library\\05_Datasheet\\301010000_YAGEO_RC0402JR-070RL_Datasheet.pdf" ) );
2114}
2115
2116
2117BOOST_AUTO_TEST_CASE( ReservedFootprintPropertyUsesDistinctMetadataField )
2118{
2119 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
2120
2121 if( !corpusEnv || !*corpusEnv )
2122 return;
2123
2124 std::filesystem::path dsn = findCorpusDesign( corpusEnv, "CY4532 Power Board Schematic.DSN" );
2125
2126 if( dsn.empty() )
2127 return;
2128
2129 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
2130 SETTINGS_MANAGER manager;
2131 manager.LoadProject( "" );
2132 schematic->SetProject( &manager.Prj() );
2133 schematic->CurrentSheet().clear();
2134 schematic->CurrentSheet().push_back( &schematic->Root() );
2135
2136 SCH_IO_ORCAD plugin;
2137 plugin.LoadSchematicFile( dsn.string(), schematic.get() );
2138 SCH_SYMBOL* capacitor = nullptr;
2139
2140 for( const SCH_SHEET_PATH& path : schematic->BuildSheetListSortedByPageNumbers() )
2141 {
2142 for( SCH_ITEM* item : path.LastScreen()->Items().OfType( SCH_SYMBOL_T ) )
2143 {
2144 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
2145
2146 if( symbol->GetRef( &path, false ) == wxS( "C45" ) )
2147 {
2148 capacitor = symbol;
2149 break;
2150 }
2151 }
2152
2153 if( capacitor )
2154 break;
2155 }
2156
2157 BOOST_REQUIRE( capacitor );
2158 size_t footprintFields = std::count_if(
2159 capacitor->GetFields().begin(), capacitor->GetFields().end(),
2160 []( const SCH_FIELD& field ) { return field.GetName() == wxS( "Footprint" ); } );
2161 BOOST_CHECK_EQUAL( footprintFields, 1u );
2162 BOOST_CHECK( capacitor->GetField( FIELD_T::FOOTPRINT )->GetText().IsEmpty() );
2163
2164 SCH_FIELD* metadata = capacitor->GetField( wxS( "OrCAD Footprint Property" ) );
2165 BOOST_REQUIRE( metadata );
2166 BOOST_CHECK_EQUAL( metadata->GetText(), wxS( "0402" ) );
2167}
2168
2169
2170BOOST_AUTO_TEST_CASE( DisplayedPropertiesUseCaptureNameMatchingAndStandardFields )
2171{
2172 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
2173
2174 if( !corpusEnv || !*corpusEnv )
2175 return;
2176
2177 std::filesystem::path dsn = findCorpusDesign( corpusEnv, "CY4532 Power Board Schematic.DSN" );
2178
2179 if( dsn.empty() )
2180 return;
2181
2182 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
2183 SETTINGS_MANAGER manager;
2184 manager.LoadProject( "" );
2185 schematic->SetProject( &manager.Prj() );
2186 schematic->CurrentSheet().clear();
2187 schematic->CurrentSheet().push_back( &schematic->Root() );
2188
2189 SCH_IO_ORCAD plugin;
2190 plugin.LoadSchematicFile( dsn.string(), schematic.get() );
2191 SCH_SYMBOL* capacitor = nullptr;
2192 SCH_SYMBOL* resistor = nullptr;
2193 SCH_SYMBOL* testPoint = nullptr;
2194
2195 for( const SCH_SHEET_PATH& path : schematic->BuildSheetListSortedByPageNumbers() )
2196 {
2197 for( SCH_ITEM* item : path.LastScreen()->Items().OfType( SCH_SYMBOL_T ) )
2198 {
2199 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
2200 wxString reference = symbol->GetRef( &path, false );
2201
2202 if( reference == wxS( "C104" ) )
2203 capacitor = symbol;
2204 else if( reference == wxS( "R58" ) )
2205 resistor = symbol;
2206 else if( reference == wxS( "TP8" ) )
2207 testPoint = symbol;
2208 }
2209 }
2210
2211 BOOST_REQUIRE( capacitor );
2212 SCH_FIELD* voltage = capacitor->GetField( wxS( "Voltage" ) );
2213 BOOST_REQUIRE( voltage );
2214 BOOST_CHECK( voltage->IsVisible() );
2215 BOOST_CHECK( voltage->GetDrawRotation() == ANGLE_HORIZONTAL );
2216 BOOST_CHECK( voltage->GetPosition()
2217 == OrcadDbuToIu( 722, 929 )
2218 + VECTOR2I( 0, OrcadTextBaselineOffset( voltage->GetTextSize().y ) ) );
2219
2220 BOOST_REQUIRE( resistor );
2221 BOOST_CHECK( resistor->GetField( FIELD_T::VALUE )->IsVisible() );
2222
2223 BOOST_REQUIRE( testPoint );
2224 SCH_FIELD* description = testPoint->GetField( FIELD_T::DESCRIPTION );
2225 BOOST_REQUIRE( description );
2226 BOOST_CHECK( description->IsVisible() );
2227 BOOST_CHECK( description->GetDrawRotation() == ANGLE_HORIZONTAL );
2228 BOOST_CHECK( description->GetPosition()
2229 == OrcadDbuToIu( 810, 945 )
2230 + VECTOR2I( 0, OrcadTextBaselineOffset( description->GetTextSize().y ) ) );
2231}
2232
2233
2234BOOST_AUTO_TEST_CASE( DisplayedInheritedPartFieldsArePreserved )
2235{
2236 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
2237
2238 if( !corpusEnv || !*corpusEnv )
2239 return;
2240
2241 std::filesystem::path dsn = findCorpusDesign( corpusEnv, "DC1859A-2.DSN" );
2242
2243 if( dsn.empty() )
2244 return;
2245
2246 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
2247 SETTINGS_MANAGER manager;
2248 manager.LoadProject( "" );
2249 schematic->SetProject( &manager.Prj() );
2250 schematic->CurrentSheet().clear();
2251 schematic->CurrentSheet().push_back( &schematic->Root() );
2252
2253 SCH_IO_ORCAD plugin;
2254 plugin.LoadSchematicFile( dsn.string(), schematic.get() );
2255 SCH_SYMBOL* capacitor = nullptr;
2256
2257 for( const SCH_SHEET_PATH& path : schematic->BuildSheetListSortedByPageNumbers() )
2258 {
2259 for( SCH_ITEM* item : path.LastScreen()->Items().OfType( SCH_SYMBOL_T ) )
2260 {
2261 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
2262
2263 if( symbol->GetRef( &path, false ) == wxS( "C2" ) )
2264 {
2265 capacitor = symbol;
2266 break;
2267 }
2268 }
2269
2270 if( capacitor )
2271 break;
2272 }
2273
2274 BOOST_REQUIRE( capacitor );
2275 SCH_FIELD* voltage = capacitor->GetField( wxS( "1st Part Field" ) );
2276 BOOST_REQUIRE( voltage );
2277 BOOST_CHECK_EQUAL( voltage->GetText(), wxS( "10V" ) );
2278 BOOST_CHECK( voltage->IsVisible() );
2279}
2280
2281
2282BOOST_AUTO_TEST_CASE( DisplayTypeTwoPropertiesRenderNamesAndValues )
2283{
2284 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
2285
2286 if( !corpusEnv || !*corpusEnv )
2287 return;
2288
2289 std::filesystem::path dsn = findCorpusDesign( corpusEnv, "SCH-48278.DSN" );
2290
2291 if( dsn.empty() )
2292 return;
2293
2294 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
2295 SETTINGS_MANAGER manager;
2296 manager.LoadProject( "" );
2297 schematic->SetProject( &manager.Prj() );
2298 schematic->CurrentSheet().clear();
2299 schematic->CurrentSheet().push_back( &schematic->Root() );
2300
2301 SCH_IO_ORCAD plugin;
2302 plugin.LoadSchematicFile( dsn.string(), schematic.get() );
2303 SCH_FIELD* jumper = nullptr;
2304 bool renderedJumper = false;
2305
2306 for( const SCH_SHEET_PATH& path : schematic->BuildSheetListSortedByPageNumbers() )
2307 {
2308 for( SCH_ITEM* item : path.LastScreen()->Items().OfType( SCH_SYMBOL_T ) )
2309 {
2310 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
2311
2312 if( symbol->GetRef( &path, false ) == wxS( "J2" ) )
2313 {
2314 jumper = symbol->GetField( wxS( "JUMPER(DEFAULT)" ) );
2315 break;
2316 }
2317 }
2318
2319 for( SCH_ITEM* item : path.LastScreen()->Items().OfType( SCH_TEXT_T ) )
2320 {
2321 SCH_TEXT* text = static_cast<SCH_TEXT*>( item );
2322 renderedJumper = renderedJumper
2323 || text->GetText() == wxS( "JUMPER(DEFAULT) = OFF:POLARITY_SEL_L" );
2324 }
2325
2326 if( jumper )
2327 break;
2328 }
2329
2330 BOOST_REQUIRE( jumper );
2331 BOOST_CHECK_EQUAL( jumper->GetText(), wxS( "OFF:POLARITY_SEL_L" ) );
2332 BOOST_CHECK( renderedJumper );
2333}
2334
2335
2336BOOST_AUTO_TEST_CASE( GeneratedWirelessNetOverridesPeerPinName )
2337{
2338 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
2339
2340 if( !corpusEnv || !*corpusEnv )
2341 {
2342 BOOST_TEST_MESSAGE( "KICAD_ORCAD_CORPUS not set; skipping wireless peer-net check." );
2343 return;
2344 }
2345
2346 std::filesystem::path dsn = findCorpusDesign( corpusEnv, "CY3280_MBR3 EVK Schematic.DSN" );
2347
2348 if( dsn.empty() )
2349 {
2350 BOOST_TEST_MESSAGE( "CY3280_MBR3 EVK Schematic.DSN not present; skipping wireless peer-net check." );
2351 return;
2352 }
2353
2354 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
2355 SETTINGS_MANAGER manager;
2356 manager.LoadProject( "" );
2357 schematic->SetProject( &manager.Prj() );
2358 schematic->CurrentSheet().clear();
2359 schematic->CurrentSheet().push_back( &schematic->Root() );
2360
2361 SCH_IO_ORCAD plugin;
2362 plugin.LoadSchematicFile( dsn.string(), schematic.get() );
2363
2364 SCH_SHEET_LIST sheets = schematic->BuildSheetListSortedByPageNumbers();
2365 schematic->ConnectionGraph()->Recalculate( sheets, true );
2366 std::map<std::pair<wxString, wxString>, CONNECTION_SUBGRAPH*> pinNets;
2367
2368 for( const auto& [key, subgraphs] : schematic->ConnectionGraph()->GetNetMap() )
2369 {
2370 for( CONNECTION_SUBGRAPH* subgraph : subgraphs )
2371 {
2372 for( SCH_ITEM* item : subgraph->GetItems() )
2373 {
2374 if( item->Type() != SCH_PIN_T )
2375 continue;
2376
2377 SCH_PIN* pin = static_cast<SCH_PIN*>( item );
2378 SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( pin->GetParentSymbol() );
2379
2380 if( symbol )
2381 pinNets[{ symbol->GetRef( &subgraph->GetSheet(), false ), pin->GetNumber() }] = subgraph;
2382 }
2383 }
2384 }
2385
2386 BOOST_REQUIRE_EQUAL( pinNets.count( { wxS( "R51" ), wxS( "1" ) } ), 1u );
2387 BOOST_REQUIRE_EQUAL( pinNets.count( { wxS( "R51" ), wxS( "2" ) } ), 1u );
2388 BOOST_REQUIRE_EQUAL( pinNets.count( { wxS( "R52" ), wxS( "1" ) } ), 1u );
2389 BOOST_REQUIRE_EQUAL( pinNets.count( { wxS( "R52" ), wxS( "2" ) } ), 1u );
2390 CONNECTION_SUBGRAPH* r51Pin1 = pinNets.at( { wxS( "R51" ), wxS( "1" ) } );
2391 CONNECTION_SUBGRAPH* r51Pin2 = pinNets.at( { wxS( "R51" ), wxS( "2" ) } );
2392 CONNECTION_SUBGRAPH* r52Pin1 = pinNets.at( { wxS( "R52" ), wxS( "1" ) } );
2393 CONNECTION_SUBGRAPH* r52Pin2 = pinNets.at( { wxS( "R52" ), wxS( "2" ) } );
2394
2395 BOOST_CHECK_NE( r51Pin1, r51Pin2 );
2396 BOOST_CHECK_NE( r52Pin1, r52Pin2 );
2397}
2398
2399
2400BOOST_AUTO_TEST_CASE( GlobalNetNamesAreCaseInsensitive )
2401{
2402 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
2403
2404 if( !corpusEnv || !*corpusEnv )
2405 {
2406 BOOST_TEST_MESSAGE( "KICAD_ORCAD_CORPUS not set; skipping global-net case check." );
2407 return;
2408 }
2409
2410 std::filesystem::path dsn = findCorpusDesign( corpusEnv, "BeagleBoard-xM_ORCAD.DSN" );
2411
2412 if( dsn.empty() )
2413 {
2414 BOOST_TEST_MESSAGE( "BeagleBoard-xM_ORCAD.DSN not present in corpus; skipping global-net case check." );
2415 return;
2416 }
2417
2418 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
2419 SETTINGS_MANAGER manager;
2420 manager.LoadProject( "" );
2421 schematic->SetProject( &manager.Prj() );
2422 schematic->CurrentSheet().clear();
2423 schematic->CurrentSheet().push_back( &schematic->Root() );
2424
2425 SCH_IO_ORCAD plugin;
2426 plugin.LoadSchematicFile( dsn.string(), schematic.get() );
2427
2428 SCH_SHEET_LIST sheets = schematic->BuildSheetListSortedByPageNumbers();
2429 schematic->ConnectionGraph()->Recalculate( sheets, true );
2430
2431 std::map<std::tuple<std::string, std::string, std::string>, int> terminalNets;
2432 int netId = 0;
2433
2434 for( const auto& [key, subgraphs] : schematic->ConnectionGraph()->GetNetMap() )
2435 {
2436 for( CONNECTION_SUBGRAPH* subgraph : subgraphs )
2437 {
2438 std::string page = subgraph->GetSheet().LastScreen()->GetFileName().ToStdString();
2439
2440 for( SCH_ITEM* item : subgraph->GetItems() )
2441 {
2442 if( item->Type() != SCH_PIN_T )
2443 continue;
2444
2445 SCH_PIN* pin = static_cast<SCH_PIN*>( item );
2446 SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( pin->GetParentSymbol() );
2447
2448 if( symbol )
2449 {
2450 terminalNets[{ page, symbol->GetRef( &subgraph->GetSheet(), false ).ToStdString(),
2451 pin->GetNumber().ToStdString() }] = netId;
2452 }
2453 }
2454 }
2455
2456 ++netId;
2457 }
2458
2459 auto findNet = [&]( const std::string& aPage, const std::string& aRef, const std::string& aPin )
2460 {
2461 for( const auto& [terminal, id] : terminalNets )
2462 {
2463 if( std::get<0>( terminal ).find( aPage ) != std::string::npos && std::get<1>( terminal ) == aRef
2464 && std::get<2>( terminal ) == aPin )
2465 {
2466 return id;
2467 }
2468 }
2469
2470 return -1;
2471 };
2472
2473 int processor = findNet( "PROCESSOR_C", "C75", "1" );
2474 int power = findNet( "PMIC _POWER", "C122", "1" );
2475
2476 BOOST_REQUIRE_NE( processor, -1 );
2477 BOOST_REQUIRE_NE( power, -1 );
2478 BOOST_CHECK_EQUAL( processor, power );
2479}
2480
2481
2482BOOST_AUTO_TEST_CASE( CaptureBusRangesUseKiCadSyntax )
2483{
2484 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
2485
2486 if( !corpusEnv || !*corpusEnv )
2487 {
2488 BOOST_TEST_MESSAGE( "KICAD_ORCAD_CORPUS not set; skipping bus-range syntax check." );
2489 return;
2490 }
2491
2492 std::filesystem::path dsn = findCorpusDesign( corpusEnv, "parallella_e16_z7020_schematic.dsn" );
2493
2494 if( dsn.empty() )
2495 {
2496 BOOST_TEST_MESSAGE( "parallella_e16_z7020_schematic.dsn not present in corpus; skipping bus-range check." );
2497 return;
2498 }
2499
2500 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
2501 SETTINGS_MANAGER manager;
2502 manager.LoadProject( "" );
2503 schematic->SetProject( &manager.Prj() );
2504 schematic->CurrentSheet().clear();
2505 schematic->CurrentSheet().push_back( &schematic->Root() );
2506
2507 SCH_IO_ORCAD plugin;
2508 plugin.LoadSchematicFile( dsn.string(), schematic.get() );
2509
2510 bool found = false;
2511
2512 for( const SCH_SHEET_PATH& path : schematic->BuildSheetListSortedByPageNumbers() )
2513 {
2514 for( SCH_ITEM* item : path.LastScreen()->Items().OfType( SCH_GLOBAL_LABEL_T ) )
2515 {
2516 wxString text = static_cast<SCH_GLOBALLABEL*>( item )->GetText();
2517 BOOST_CHECK_NE( text, wxS( "DDR_DQ[31:0]" ) );
2518 found |= text == wxS( "DDR_DQ[31..0]" );
2519 }
2520 }
2521
2522 BOOST_CHECK( found );
2523}
2524
2525
2526BOOST_AUTO_TEST_CASE( CanonicalPropertiesIgnoreCaseInsensitiveDuplicates )
2527{
2528 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
2529
2530 if( !corpusEnv || !*corpusEnv )
2531 return;
2532
2533 std::filesystem::path dsn = findCorpusDesign( corpusEnv, "parallella_e16_z7020_schematic.dsn" );
2534
2535 if( dsn.empty() )
2536 return;
2537
2538 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
2539 SETTINGS_MANAGER manager;
2540 manager.LoadProject( "" );
2541 schematic->SetProject( &manager.Prj() );
2542 schematic->CurrentSheet().clear();
2543 schematic->CurrentSheet().push_back( &schematic->Root() );
2544
2545 SCH_IO_ORCAD plugin;
2546 plugin.LoadSchematicFile( dsn.string(), schematic.get() );
2547 SCH_SYMBOL* platedHole = nullptr;
2548
2549 for( const SCH_SHEET_PATH& path : schematic->BuildSheetListSortedByPageNumbers() )
2550 {
2551 for( SCH_ITEM* item : path.LastScreen()->Items().OfType( SCH_SYMBOL_T ) )
2552 {
2553 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
2554
2555 if( symbol->GetRef( &path, false ) == wxS( "PTH1" ) )
2556 {
2557 platedHole = symbol;
2558 break;
2559 }
2560 }
2561
2562 if( platedHole )
2563 break;
2564 }
2565
2566 BOOST_REQUIRE( platedHole );
2567 size_t valueFields = std::count_if(
2568 platedHole->GetFields().begin(), platedHole->GetFields().end(),
2569 []( const SCH_FIELD& aField ) { return aField.GetName().CmpNoCase( wxS( "Value" ) ) == 0; } );
2570 BOOST_CHECK_EQUAL( valueFields, 1u );
2571 BOOST_CHECK_EQUAL( platedHole->GetField( FIELD_T::VALUE )->GetShownText( FOR_CANVAS ), wxS( "PTH125_200PAD" ) );
2572}
2573
2574
2575BOOST_AUTO_TEST_CASE( CollidingOccurrenceAliasesRemainSeparate )
2576{
2577 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
2578
2579 if( !corpusEnv || !*corpusEnv )
2580 return;
2581
2582 std::filesystem::path dsn = findCorpusDesign( corpusEnv, "parallella_e16_z7020_schematic.dsn" );
2583
2584 if( dsn.empty() )
2585 return;
2586
2587 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
2588 SETTINGS_MANAGER manager;
2589 manager.LoadProject( "" );
2590 schematic->SetProject( &manager.Prj() );
2591 schematic->CurrentSheet().clear();
2592 schematic->CurrentSheet().push_back( &schematic->Root() );
2593
2594 SCH_IO_ORCAD plugin;
2595 plugin.LoadSchematicFile( dsn.string(), schematic.get() );
2596
2597 SCH_SHEET_LIST sheets = schematic->BuildSheetListSortedByPageNumbers();
2598 schematic->ConnectionGraph()->Recalculate( sheets, true );
2599
2600 std::map<std::string, int> terminalNets;
2601 int netId = 0;
2602
2603 for( const auto& [key, subgraphs] : schematic->ConnectionGraph()->GetNetMap() )
2604 {
2605 for( CONNECTION_SUBGRAPH* subgraph : subgraphs )
2606 {
2607 for( SCH_ITEM* item : subgraph->GetItems() )
2608 {
2609 if( item->Type() != SCH_PIN_T )
2610 continue;
2611
2612 SCH_PIN* pin = static_cast<SCH_PIN*>( item );
2613 SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( pin->GetParentSymbol() );
2614
2615 if( symbol )
2616 {
2617 terminalNets[terminalToken( symbol->GetRef( &subgraph->GetSheet(), false ).ToStdString(),
2618 pin->GetNumber().ToStdString() )] = netId;
2619 }
2620 }
2621 }
2622
2623 ++netId;
2624 }
2625
2626 BOOST_REQUIRE( terminalNets.count( "R41.2" ) );
2627 BOOST_REQUIRE( terminalNets.count( "U26.53" ) );
2628 BOOST_REQUIRE( terminalNets.count( "R56.2" ) );
2629 BOOST_REQUIRE( terminalNets.count( "U11.1" ) );
2630 BOOST_REQUIRE( terminalNets.count( "U11.3" ) );
2631 BOOST_REQUIRE( terminalNets.count( "U14.1" ) );
2632 BOOST_CHECK_EQUAL( terminalNets["R41.2"], terminalNets["U26.53"] );
2633 BOOST_CHECK_EQUAL( terminalNets["R56.2"], terminalNets["U11.1"] );
2634 BOOST_CHECK_EQUAL( terminalNets["R56.2"], terminalNets["U11.3"] );
2635 BOOST_CHECK_EQUAL( terminalNets["R56.2"], terminalNets["U14.1"] );
2636 BOOST_CHECK_NE( terminalNets["R41.2"], terminalNets["R56.2"] );
2637}
2638
2639
2640BOOST_AUTO_TEST_CASE( CollidingOffpageOccurrenceAliasesRemainSeparate )
2641{
2642 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
2643
2644 if( !corpusEnv || !*corpusEnv )
2645 return;
2646
2647 std::filesystem::path dsn = findCorpusDesign( corpusEnv, "parallella_gen0.dsn" );
2648
2649 if( dsn.empty() )
2650 return;
2651
2652 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
2653 SETTINGS_MANAGER manager;
2654 manager.LoadProject( "" );
2655 schematic->SetProject( &manager.Prj() );
2656 schematic->CurrentSheet().clear();
2657 schematic->CurrentSheet().push_back( &schematic->Root() );
2658
2659 SCH_IO_ORCAD plugin;
2660 plugin.LoadSchematicFile( dsn.string(), schematic.get() );
2661
2662 auto [consistent, checkable] = checkConnectivity(
2663 *schematic,
2664 { { terminalToken( "R41", "2" ), terminalToken( "U26", "53" ) },
2665 { terminalToken( "R57", "2" ), terminalToken( "R97", "1" ), terminalToken( "U14", "1" ) } } );
2666 BOOST_CHECK_EQUAL( checkable, 2 );
2667 BOOST_CHECK_EQUAL( consistent, 2 );
2668}
2669
2670
2671BOOST_AUTO_TEST_CASE( FlatFolderOccurrenceAliasesConnectDisplacedPins )
2672{
2673 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
2674
2675 if( !corpusEnv || !*corpusEnv )
2676 return;
2677
2678 std::filesystem::path dsn = findCorpusDesign( corpusEnv, "SCH-20380.DSN" );
2679
2680 if( dsn.empty() )
2681 return;
2682
2683 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
2684 SETTINGS_MANAGER manager;
2685 manager.LoadProject( "" );
2686 schematic->SetProject( &manager.Prj() );
2687 schematic->CurrentSheet().clear();
2688 schematic->CurrentSheet().push_back( &schematic->Root() );
2689
2690 SCH_IO_ORCAD plugin;
2691 plugin.LoadSchematicFile( dsn.string(), schematic.get() );
2692
2693 auto [consistent, checkable] = checkConnectivity(
2694 *schematic, { { terminalToken( "J10", "29" ), terminalToken( "RP15", "3" ), terminalToken( "U10", "R9" ),
2695 terminalToken( "U33", "30" ) },
2696 { terminalToken( "J10", "30" ), terminalToken( "JP62", "1" ), terminalToken( "RP16", "1" ),
2697 terminalToken( "U10", "P9" ) },
2698 { terminalToken( "J7", "50" ), terminalToken( "RP14", "5" ), terminalToken( "U10", "D12" ),
2699 terminalToken( "U33", "21" ), terminalToken( "U5", "C" ) } } );
2700 BOOST_CHECK_EQUAL( checkable, 3 );
2701 BOOST_CHECK_EQUAL( consistent, 3 );
2702}
2703
2704
2705BOOST_AUTO_TEST_CASE( PowerNetNameWinsOverSecondaryOccurrencePortAlias )
2706{
2707 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
2708
2709 if( !corpusEnv || !*corpusEnv )
2710 return;
2711
2712 std::filesystem::path dsn = findCorpusDesign( corpusEnv, "SCH-38863_CX1.DSN" );
2713
2714 if( dsn.empty() )
2715 return;
2716
2717 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
2718 SETTINGS_MANAGER manager;
2719 manager.LoadProject( "" );
2720 schematic->SetProject( &manager.Prj() );
2721 schematic->CurrentSheet().clear();
2722 schematic->CurrentSheet().push_back( &schematic->Root() );
2723
2724 SCH_IO_ORCAD plugin;
2725 plugin.LoadSchematicFile( dsn.string(), schematic.get() );
2726
2727 const IMPORT_NET_MAP* map = schematic->GetImportNetMap();
2728 BOOST_REQUIRE( map );
2729 std::set<wxString> netNames;
2730
2731 for( const auto& [reference, peer, sourceName] :
2732 { std::tuple{ wxString( "J16" ), wxString( "C520" ), wxString( "GNDISOHU" ) },
2733 std::tuple{ wxString( "J17" ), wxString( "C590" ), wxString( "GNDISOHV" ) },
2734 std::tuple{ wxString( "J18" ), wxString( "C661" ), wxString( "GNDISOHW" ) } } )
2735 {
2736 wxString netName = terminalNetName( *schematic, reference, wxS( "1" ) );
2737 BOOST_CHECK_EQUAL( netName.AfterLast( '/' ).Upper(), sourceName );
2738 BOOST_CHECK_EQUAL( terminalNetName( *schematic, peer, wxS( "2" ) ), netName );
2739 netNames.insert( netName );
2740 bool mapped = false;
2741
2742 for( const IMPORT_NET_MAP_ENTRY& entry : map->entries )
2743 {
2744 if( entry.originalName.CmpNoCase( sourceName ) == 0 )
2745 {
2747 BOOST_CHECK_EQUAL( entry.nameAtImport, netName );
2748 mapped = true;
2749 }
2750 }
2751
2752 BOOST_CHECK( mapped );
2753 }
2754
2755 BOOST_CHECK_EQUAL( netNames.size(), 3u );
2756}
2757
2758
2759BOOST_AUTO_TEST_CASE( DuplicatePageNetIdsPreserveAliases )
2760{
2761 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
2762
2763 if( !corpusEnv || !*corpusEnv )
2764 {
2765 BOOST_TEST_MESSAGE( "KICAD_ORCAD_CORPUS not set; skipping duplicate page-net check." );
2766 return;
2767 }
2768
2769 std::filesystem::path dsn = findCorpusDesign( corpusEnv, "MTB4.DSN" );
2770
2771 if( dsn.empty() )
2772 {
2773 BOOST_TEST_MESSAGE( "MTB4.DSN not present in corpus; skipping duplicate page-net check." );
2774 return;
2775 }
2776
2777 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
2778 SETTINGS_MANAGER manager;
2779 manager.LoadProject( "" );
2780 schematic->SetProject( &manager.Prj() );
2781 schematic->CurrentSheet().clear();
2782 schematic->CurrentSheet().push_back( &schematic->Root() );
2783
2784 SCH_IO_ORCAD plugin;
2785 plugin.LoadSchematicFile( dsn.string(), schematic.get() );
2786
2787 SCH_SHEET_LIST sheets = schematic->BuildSheetListSortedByPageNumbers();
2788 schematic->ConnectionGraph()->Recalculate( sheets, true );
2789
2790 std::map<std::string, wxString> pinNets;
2791
2792 for( const auto& [key, subgraphs] : schematic->ConnectionGraph()->GetNetMap() )
2793 {
2794 for( CONNECTION_SUBGRAPH* subgraph : subgraphs )
2795 {
2796 if( subgraph->GetSheet().LastScreen()->GetFileName().Find( wxS( "PAGE 2 - MCU" ) ) == wxNOT_FOUND )
2797 continue;
2798
2799 for( SCH_ITEM* item : subgraph->GetItems() )
2800 {
2801 if( item->Type() != SCH_PIN_T )
2802 continue;
2803
2804 SCH_PIN* pin = static_cast<SCH_PIN*>( item );
2805 SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( pin->GetParentSymbol() );
2806
2807 if( symbol && symbol->GetRef( &subgraph->GetSheet(), false ) == wxS( "U1" ) )
2808 pinNets[pin->GetNumber().ToStdString()] = key.Name;
2809 }
2810 }
2811 }
2812
2813 BOOST_REQUIRE_EQUAL( pinNets.count( "F3" ), 1u );
2814 BOOST_REQUIRE_EQUAL( pinNets.count( "F8" ), 1u );
2815 BOOST_REQUIRE_EQUAL( pinNets.count( "G3" ), 1u );
2816 BOOST_REQUIRE_EQUAL( pinNets.count( "G7" ), 1u );
2817 BOOST_CHECK( pinNets["F3"].EndsWith( wxS( "I2C2_SDA" ) ) );
2818 BOOST_CHECK( pinNets["F8"].EndsWith( wxS( "I2C2_SDA" ) ) );
2819 BOOST_CHECK( pinNets["G3"].EndsWith( wxS( "I2C2_SCL" ) ) );
2820 BOOST_CHECK( pinNets["G7"].EndsWith( wxS( "I2C2_SCL" ) ) );
2821}
2822
2823
2824BOOST_AUTO_TEST_CASE( DuplicatePageNetIdsDoNotShortDistinctNets )
2825{
2826 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
2827
2828 if( !corpusEnv || !*corpusEnv )
2829 {
2830 BOOST_TEST_MESSAGE( "KICAD_ORCAD_CORPUS not set; skipping duplicate page-net isolation check." );
2831 return;
2832 }
2833
2834 std::filesystem::path dsn = findCorpusDesign( corpusEnv, "DC2125A-2.DSN" );
2835
2836 if( dsn.empty() )
2837 {
2838 BOOST_TEST_MESSAGE( "DC2125A-2.DSN not present; skipping duplicate page-net isolation check." );
2839 return;
2840 }
2841
2842 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
2843 SETTINGS_MANAGER manager;
2844 manager.LoadProject( "" );
2845 schematic->SetProject( &manager.Prj() );
2846 schematic->CurrentSheet().clear();
2847 schematic->CurrentSheet().push_back( &schematic->Root() );
2848
2849 SCH_IO_ORCAD plugin;
2850 plugin.LoadSchematicFile( dsn.string(), schematic.get() );
2851
2852 SCH_SHEET_LIST sheets = schematic->BuildSheetListSortedByPageNumbers();
2853 schematic->ConnectionGraph()->Recalculate( sheets, true );
2854 std::map<std::pair<wxString, wxString>, CONNECTION_SUBGRAPH*> pinNets;
2855
2856 for( const auto& [key, subgraphs] : schematic->ConnectionGraph()->GetNetMap() )
2857 {
2858 for( CONNECTION_SUBGRAPH* subgraph : subgraphs )
2859 {
2860 for( SCH_ITEM* item : subgraph->GetItems() )
2861 {
2862 if( item->Type() != SCH_PIN_T )
2863 continue;
2864
2865 SCH_PIN* pin = static_cast<SCH_PIN*>( item );
2866 SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( pin->GetParentSymbol() );
2867
2868 if( symbol )
2869 pinNets[{ symbol->GetRef( &subgraph->GetSheet(), false ), pin->GetNumber() }] = subgraph;
2870 }
2871 }
2872 }
2873
2874 CONNECTION_SUBGRAPH* earth = pinNets.at( { wxS( "J2" ), wxS( "9" ) } );
2875 CONNECTION_SUBGRAPH* vportn = pinNets.at( { wxS( "C1" ), wxS( "2" ) } );
2876 BOOST_CHECK_NE( earth, vportn );
2877}
2878
2879
2880BOOST_AUTO_TEST_CASE( AliasAtCrossingDoesNotShortCaptureNets )
2881{
2882 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
2883
2884 if( !corpusEnv || !*corpusEnv )
2885 {
2886 BOOST_TEST_MESSAGE( "KICAD_ORCAD_CORPUS not set; skipping alias-at-crossing isolation check." );
2887 return;
2888 }
2889
2890 std::filesystem::path dsn = findCorpusDesign( corpusEnv, "OC_CONNECT1_FRONTEND_REV_C_V1P1.DSN" );
2891
2892 if( dsn.empty() )
2893 {
2894 BOOST_TEST_MESSAGE( "OC_CONNECT1_FRONTEND_REV_C_V1P1.DSN not present; skipping alias-at-crossing check." );
2895 return;
2896 }
2897
2898 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
2899 SETTINGS_MANAGER manager;
2900 manager.LoadProject( "" );
2901 schematic->SetProject( &manager.Prj() );
2902 schematic->CurrentSheet().clear();
2903 schematic->CurrentSheet().push_back( &schematic->Root() );
2904
2905 SCH_IO_ORCAD plugin;
2906 plugin.LoadSchematicFile( dsn.string(), schematic.get() );
2907
2908 SCH_SHEET_LIST sheets = schematic->BuildSheetListSortedByPageNumbers();
2909 schematic->ConnectionGraph()->Recalculate( sheets, true );
2910 std::map<std::pair<wxString, wxString>, CONNECTION_SUBGRAPH*> pinNets;
2911
2912 for( const auto& [key, subgraphs] : schematic->ConnectionGraph()->GetNetMap() )
2913 {
2914 for( CONNECTION_SUBGRAPH* subgraph : subgraphs )
2915 {
2916 for( SCH_ITEM* item : subgraph->GetItems() )
2917 {
2918 if( item->Type() != SCH_PIN_T )
2919 continue;
2920
2921 SCH_PIN* pin = static_cast<SCH_PIN*>( item );
2922 SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( pin->GetParentSymbol() );
2923
2924 if( symbol )
2925 pinNets[{ symbol->GetRef( &subgraph->GetSheet(), false ), pin->GetNumber() }] = subgraph;
2926 }
2927 }
2928 }
2929
2930 CONNECTION_SUBGRAPH* a0 = pinNets.at( { wxS( "U7157" ), wxS( "3" ) } );
2931 CONNECTION_SUBGRAPH* a2 = pinNets.at( { wxS( "U7157" ), wxS( "5" ) } );
2932 CONNECTION_SUBGRAPH* io1 = pinNets.at( { wxS( "U7157" ), wxS( "7" ) } );
2933 BOOST_CHECK_EQUAL( a0, pinNets.at( { wxS( "R1150" ), wxS( "2" ) } ) );
2934 BOOST_CHECK_EQUAL( a0, pinNets.at( { wxS( "R1153" ), wxS( "1" ) } ) );
2935 BOOST_CHECK_EQUAL( a2, pinNets.at( { wxS( "R1152" ), wxS( "2" ) } ) );
2936 BOOST_CHECK_EQUAL( a2, pinNets.at( { wxS( "R1155" ), wxS( "1" ) } ) );
2937 BOOST_CHECK_EQUAL( io1, pinNets.at( { wxS( "R954" ), wxS( "2" ) } ) );
2938 BOOST_CHECK_NE( a0, a2 );
2939 BOOST_CHECK_NE( a0, io1 );
2940 BOOST_CHECK_NE( a2, io1 );
2941}
2942
2943
2944BOOST_AUTO_TEST_CASE( WirelessPinUsesPageNetId )
2945{
2946 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
2947
2948 if( !corpusEnv || !*corpusEnv )
2949 {
2950 BOOST_TEST_MESSAGE( "KICAD_ORCAD_CORPUS not set; skipping wireless page-net check." );
2951 return;
2952 }
2953
2954 std::filesystem::path dsn = findCorpusDesign( corpusEnv, "Si828X-BW-GDB.DSN" );
2955
2956 if( dsn.empty() )
2957 {
2958 BOOST_TEST_MESSAGE( "Si828X-BW-GDB.DSN not present; skipping wireless page-net check." );
2959 return;
2960 }
2961
2962 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
2963 SETTINGS_MANAGER manager;
2964 manager.LoadProject( "" );
2965 schematic->SetProject( &manager.Prj() );
2966 schematic->CurrentSheet().clear();
2967 schematic->CurrentSheet().push_back( &schematic->Root() );
2968
2969 SCH_IO_ORCAD plugin;
2970 plugin.LoadSchematicFile( dsn.string(), schematic.get() );
2971
2972 SCH_SHEET_LIST sheets = schematic->BuildSheetListSortedByPageNumbers();
2973 schematic->ConnectionGraph()->Recalculate( sheets, true );
2974 std::map<std::pair<wxString, wxString>, wxString> pinNames;
2975
2976 for( const auto& [key, subgraphs] : schematic->ConnectionGraph()->GetNetMap() )
2977 {
2978 for( CONNECTION_SUBGRAPH* subgraph : subgraphs )
2979 {
2980 for( SCH_ITEM* item : subgraph->GetItems() )
2981 {
2982 if( item->Type() != SCH_PIN_T )
2983 continue;
2984
2985 SCH_PIN* pin = static_cast<SCH_PIN*>( item );
2986 SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( pin->GetParentSymbol() );
2987
2988 if( symbol )
2989 pinNames[{ symbol->GetRef( &subgraph->GetSheet(), false ), pin->GetNumber() }] = key.Name;
2990 }
2991 }
2992 }
2993
2994 BOOST_CHECK_EQUAL( pinNames.at( { wxS( "CB28" ), wxS( "2" ) } ), wxS( "5V" ) );
2995 BOOST_CHECK_EQUAL( pinNames.at( { wxS( "RT17" ), wxS( "2" ) } ), wxS( "5V" ) );
2996}
2997
2998
2999BOOST_AUTO_TEST_CASE( AmbiguousWirelessPinUsesOccurrenceNetName )
3000{
3001 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
3002
3003 if( !corpusEnv || !*corpusEnv )
3004 {
3005 BOOST_TEST_MESSAGE( "KICAD_ORCAD_CORPUS not set; skipping ambiguous wireless-net check." );
3006 return;
3007 }
3008
3009 std::filesystem::path dsn = findCorpusDesign( corpusEnv, "DC2047A-3-A.DSN" );
3010
3011 if( dsn.empty() )
3012 {
3013 BOOST_TEST_MESSAGE( "DC2047A-3-A.DSN not present; skipping ambiguous wireless-net check." );
3014 return;
3015 }
3016
3017 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
3018 SETTINGS_MANAGER manager;
3019 manager.LoadProject( "" );
3020 schematic->SetProject( &manager.Prj() );
3021 schematic->CurrentSheet().clear();
3022 schematic->CurrentSheet().push_back( &schematic->Root() );
3023
3024 SCH_IO_ORCAD plugin;
3025 plugin.LoadSchematicFile( dsn.string(), schematic.get() );
3026
3027 SCH_SHEET_LIST sheets = schematic->BuildSheetListSortedByPageNumbers();
3028 schematic->ConnectionGraph()->Recalculate( sheets, true );
3029 wxString d15Pin2;
3030
3031 for( const auto& [key, subgraphs] : schematic->ConnectionGraph()->GetNetMap() )
3032 {
3033 for( CONNECTION_SUBGRAPH* subgraph : subgraphs )
3034 {
3035 for( SCH_ITEM* item : subgraph->GetItems() )
3036 {
3037 if( item->Type() != SCH_PIN_T )
3038 continue;
3039
3040 SCH_PIN* pin = static_cast<SCH_PIN*>( item );
3041 SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( pin->GetParentSymbol() );
3042
3043 if( symbol && symbol->GetRef( &subgraph->GetSheet(), false ) == wxS( "D15" )
3044 && pin->GetNumber() == wxS( "2" ) )
3045 {
3046 d15Pin2 = key.Name;
3047 }
3048 }
3049 }
3050 }
3051
3052 BOOST_CHECK_EQUAL( d15Pin2, wxS( "AUX_RECTIFIED-" ) );
3053}
3054
3055
3056BOOST_AUTO_TEST_CASE( BlankPackagePinNumbersUseLogicalNames )
3057{
3058 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
3059
3060 if( !corpusEnv || !*corpusEnv )
3061 return;
3062
3063 std::filesystem::path dsn = findCorpusDesign( corpusEnv, "DC2084A-3.DSN" );
3064
3065 if( dsn.empty() )
3066 return;
3067
3068 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
3069 SETTINGS_MANAGER manager;
3070 manager.LoadProject( "" );
3071 schematic->SetProject( &manager.Prj() );
3072 schematic->CurrentSheet().clear();
3073 schematic->CurrentSheet().push_back( &schematic->Root() );
3074
3075 SCH_IO_ORCAD plugin;
3076 plugin.LoadSchematicFile( dsn.string(), schematic.get() );
3077
3078 std::map<wxString, std::set<wxString>> pinNumbers;
3079
3080 for( const SCH_SHEET_PATH& sheet : schematic->BuildSheetListSortedByPageNumbers() )
3081 {
3082 for( SCH_ITEM* item : sheet.LastScreen()->Items().OfType( SCH_SYMBOL_T ) )
3083 {
3084 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
3085
3086 wxString reference = symbol->GetRef( &sheet, false );
3087
3088 if( reference != wxS( "C31" ) && reference != wxS( "C42" ) )
3089 continue;
3090
3091 for( const std::unique_ptr<SCH_PIN>& pin : symbol->GetRawPins() )
3092 pinNumbers[reference].insert( pin->GetNumber() );
3093 }
3094 }
3095
3096 BOOST_CHECK( pinNumbers[wxS( "C31" )] == std::set<wxString>( { wxS( "1" ), wxS( "2" ) } ) );
3097 BOOST_CHECK( pinNumbers[wxS( "C42" )] == std::set<wxString>( { wxS( "1" ), wxS( "2" ) } ) );
3098}
3099
3100
3101BOOST_AUTO_TEST_CASE( FallbackPackagePrefersNumberedPins )
3102{
3103 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
3104
3105 if( !corpusEnv || !*corpusEnv )
3106 return;
3107
3108 std::filesystem::path dsn = findCorpusDesign( corpusEnv, "DC2091A-3.DSN" );
3109
3110 if( dsn.empty() )
3111 return;
3112
3113 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
3114 SETTINGS_MANAGER manager;
3115 manager.LoadProject( "" );
3116 schematic->SetProject( &manager.Prj() );
3117 schematic->CurrentSheet().clear();
3118 schematic->CurrentSheet().push_back( &schematic->Root() );
3119
3120 SCH_IO_ORCAD plugin;
3121 plugin.LoadSchematicFile( dsn.string(), schematic.get() );
3122
3123 std::map<wxString, std::set<wxString>> pinNumbers;
3124
3125 for( const SCH_SHEET_PATH& sheet : schematic->BuildSheetListSortedByPageNumbers() )
3126 {
3127 for( SCH_ITEM* item : sheet.LastScreen()->Items().OfType( SCH_SYMBOL_T ) )
3128 {
3129 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
3130 wxString reference = symbol->GetRef( &sheet, false );
3131
3132 if( reference != wxS( "C1" ) && reference != wxS( "C2" ) )
3133 continue;
3134
3135 for( const std::unique_ptr<SCH_PIN>& pin : symbol->GetRawPins() )
3136 pinNumbers[reference].insert( pin->GetNumber() );
3137 }
3138 }
3139
3140 const std::set<wxString> expected = { wxS( "1" ), wxS( "2" ) };
3141 BOOST_CHECK( pinNumbers[wxS( "C1" )] == expected );
3142 BOOST_CHECK( pinNumbers[wxS( "C2" )] == expected );
3143}
3144
3145
3146BOOST_AUTO_TEST_CASE( FallbackPackageUsesLogicalPinOrder )
3147{
3148 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
3149
3150 if( !corpusEnv || !*corpusEnv )
3151 return;
3152
3153 std::filesystem::path dsn = findCorpusDesign( corpusEnv, "DC2228A-3.DSN" );
3154
3155 if( dsn.empty() )
3156 return;
3157
3158 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
3159 SETTINGS_MANAGER manager;
3160 manager.LoadProject( "" );
3161 schematic->SetProject( &manager.Prj() );
3162 schematic->CurrentSheet().clear();
3163 schematic->CurrentSheet().push_back( &schematic->Root() );
3164
3165 SCH_IO_ORCAD plugin;
3166 plugin.LoadSchematicFile( dsn.string(), schematic.get() );
3167
3168 std::vector<std::set<std::string>> expected = { { terminalToken( "R188", "2" ), terminalToken( "C100", "2" ) } };
3169 auto [consistent, checkable] = checkConnectivity( *schematic, expected );
3170 BOOST_CHECK_EQUAL( checkable, 1 );
3171 BOOST_CHECK_EQUAL( consistent, 1 );
3172}
3173
3174
3175BOOST_AUTO_TEST_CASE( NumericUnitNamesUseNaturalOrder )
3176{
3177 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
3178
3179 if( !corpusEnv || !*corpusEnv )
3180 return;
3181
3182 std::filesystem::path dsn = findCorpusDesign( corpusEnv, "DC2228A-3.DSN" );
3183
3184 if( dsn.empty() )
3185 return;
3186
3187 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
3188 SETTINGS_MANAGER manager;
3189 manager.LoadProject( "" );
3190 schematic->SetProject( &manager.Prj() );
3191 schematic->CurrentSheet().clear();
3192 schematic->CurrentSheet().push_back( &schematic->Root() );
3193
3194 SCH_IO_ORCAD plugin;
3195 plugin.LoadSchematicFile( dsn.string(), schematic.get() );
3196
3197 std::map<int, size_t> pinCounts;
3198
3199 for( const SCH_SHEET_PATH& sheet : schematic->BuildSheetListSortedByPageNumbers() )
3200 {
3201 for( SCH_ITEM* item : sheet.LastScreen()->Items().OfType( SCH_SYMBOL_T ) )
3202 {
3203 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
3204
3205 if( symbol->GetRef( &sheet, false ) == wxS( "U9" ) )
3206 pinCounts[symbol->GetUnit()] = symbol->GetGraphicalPins( ALL_UNITS, ALL_BODY_STYLES ).size();
3207 }
3208 }
3209
3210 const std::map<int, size_t> expected = {
3211 { 1, 2 }, { 2, 21 }, { 3, 14 }, { 4, 9 }, { 5, 11 }, { 6, 8 }, { 7, 13 },
3212 { 8, 8 }, { 9, 9 }, { 10, 7 }, { 11, 5 }, { 12, 7 }, { 13, 15 }, { 14, 17 },
3213 { 15, 16 }, { 16, 12 }, { 17, 32 }, { 18, 6 }, { 19, 22 }, { 20, 22 },
3214 };
3215 BOOST_CHECK( pinCounts == expected );
3216}
3217
3218
3219BOOST_AUTO_TEST_CASE( OccurrenceReferencesOverridePlacedTemplateReferences )
3220{
3221 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
3222
3223 if( !corpusEnv || !*corpusEnv )
3224 return;
3225
3226 std::filesystem::path dsn = findCorpusDesign( corpusEnv, "SI82AX-CX_NB8_EVB.DSN" );
3227
3228 if( dsn.empty() )
3229 return;
3230
3231 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
3232 SETTINGS_MANAGER manager;
3233 manager.LoadProject( "" );
3234 schematic->SetProject( &manager.Prj() );
3235 schematic->CurrentSheet().clear();
3236 schematic->CurrentSheet().push_back( &schematic->Root() );
3237
3238 SCH_IO_ORCAD plugin;
3239 plugin.LoadSchematicFile( dsn.string(), schematic.get() );
3240
3241 bool foundR2 = false;
3242 bool foundR17 = false;
3243 wxString refsAtR2Position;
3244 wxString refsAtR17Position;
3245
3246 for( const SCH_SHEET_PATH& sheet : schematic->BuildSheetListSortedByPageNumbers() )
3247 {
3248 for( SCH_ITEM* item : sheet.LastScreen()->Items().OfType( SCH_SYMBOL_T ) )
3249 {
3250 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
3251 wxString reference = symbol->GetRef( &sheet, false );
3252
3253 if( symbol->GetPosition() == OrcadDbuToIu( 890, 200 ) )
3254 {
3255 refsAtR2Position += reference + wxS( " " );
3256 foundR2 = foundR2 || reference == wxS( "R2" );
3257 }
3258
3259 if( symbol->GetPosition() == OrcadDbuToIu( 1060, 395 ) )
3260 {
3261 refsAtR17Position += reference + wxS( " " );
3262 foundR17 = foundR17 || reference == wxS( "R17" );
3263 }
3264 }
3265 }
3266
3267 BOOST_CHECK_MESSAGE( foundR2, "references at R2 position: " << refsAtR2Position );
3268 BOOST_CHECK_MESSAGE( foundR17, "references at R17 position: " << refsAtR17Position );
3269}
3270
3271
3272BOOST_AUTO_TEST_CASE( RootOccurrenceTargetIdsOverridePlacedTemplateReferences )
3273{
3274 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
3275
3276 if( !corpusEnv || !*corpusEnv )
3277 return;
3278
3279 std::filesystem::path dsn = findCorpusDesign( corpusEnv, "Low EMI demo board.DSN" );
3280
3281 if( dsn.empty() )
3282 return;
3283
3284 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
3285 SETTINGS_MANAGER manager;
3286 manager.LoadProject( "" );
3287 schematic->SetProject( &manager.Prj() );
3288 schematic->CurrentSheet().clear();
3289 schematic->CurrentSheet().push_back( &schematic->Root() );
3290
3291 SCH_IO_ORCAD plugin;
3292 plugin.LoadSchematicFile( dsn.string(), schematic.get() );
3293
3294 std::set<std::string> refs = collectImportedRefs( *schematic );
3295
3296 BOOST_CHECK( refs.count( "C1" ) );
3297 BOOST_CHECK( refs.count( "U1" ) );
3298 BOOST_CHECK( !refs.count( "C71" ) );
3299 BOOST_CHECK( !refs.count( "U12" ) );
3300}
3301
3302
3303BOOST_AUTO_TEST_CASE( FlatModernOccurrenceReferencesOverridePlacedReferences )
3304{
3305 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
3306
3307 if( !corpusEnv || !*corpusEnv )
3308 return;
3309
3310 std::filesystem::path dsn;
3311
3312 for( const std::filesystem::directory_entry& entry :
3313 std::filesystem::recursive_directory_iterator( corpusEnv ) )
3314 {
3315 if( entry.is_regular_file() && entry.path().filename() == "BDC.DSN"
3316 && entry.path().string().find( "backpack-bdc" ) != std::string::npos )
3317 {
3318 dsn = entry.path();
3319 break;
3320 }
3321 }
3322
3323 if( dsn.empty() )
3324 return;
3325
3326 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
3327 SETTINGS_MANAGER manager;
3328 manager.LoadProject( "" );
3329 schematic->SetProject( &manager.Prj() );
3330 schematic->CurrentSheet().clear();
3331 schematic->CurrentSheet().push_back( &schematic->Root() );
3332
3333 SCH_IO_ORCAD plugin;
3334 plugin.LoadSchematicFile( dsn.string(), schematic.get() );
3335
3336 wxString reference;
3337
3338 for( const SCH_SHEET_PATH& sheet : schematic->BuildSheetListSortedByPageNumbers() )
3339 {
3340 for( SCH_ITEM* item : sheet.LastScreen()->Items().OfType( SCH_SYMBOL_T ) )
3341 {
3342 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
3343
3344 if( symbol->GetPosition() == OrcadDbuToIu( 485, 270 ) )
3345 reference = symbol->GetRef( &sheet, false );
3346 }
3347 }
3348
3349 BOOST_CHECK_EQUAL( reference, wxString( "U10" ) );
3350}
3351
3352
3353BOOST_AUTO_TEST_CASE( SuperSpeedOccurrencePropertiesOverrideReusableComponents )
3354{
3355 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
3356
3357 if( !corpusEnv || !*corpusEnv )
3358 return;
3359
3360 std::filesystem::path dsn = findCorpusDesign( corpusEnv, "SuperSpeed Explorer Kit Schematic.DSN" );
3361
3362 if( dsn.empty() )
3363 return;
3364
3365 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
3366 SETTINGS_MANAGER manager;
3367 manager.LoadProject( "" );
3368 schematic->SetProject( &manager.Prj() );
3369 schematic->CurrentSheet().clear();
3370 schematic->CurrentSheet().push_back( &schematic->Root() );
3371
3372 SCH_IO_ORCAD plugin;
3373 plugin.LoadSchematicFile( dsn.string(), schematic.get() );
3374
3375 std::map<wxString, SCH_SYMBOL*> switches;
3376
3377 for( const SCH_SHEET_PATH& sheet : schematic->BuildSheetListSortedByPageNumbers() )
3378 {
3379 for( SCH_ITEM* item : sheet.LastScreen()->Items().OfType( SCH_SYMBOL_T ) )
3380 {
3381 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
3382 wxString reference = symbol->GetRef( &sheet, false );
3383
3384 if( reference == wxS( "SW1" ) || reference == wxS( "SW2" ) )
3385 switches[reference] = symbol;
3386 }
3387 }
3388
3389 BOOST_REQUIRE_EQUAL( switches.size(), 2u );
3390
3391 for( const wxString& reference : { wxS( "SW1" ), wxS( "SW2" ) } )
3392 {
3393 SCH_SYMBOL* symbol = switches.at( reference );
3394 BOOST_CHECK_EQUAL( symbol->GetField( FIELD_T::VALUE )->GetText(), wxS( "434 123 050 816" ) );
3395 BOOST_REQUIRE( symbol->GetField( wxS( "Manufacturer" ) ) );
3396 BOOST_CHECK_EQUAL( symbol->GetField( wxS( "Manufacturer" ) )->GetText(),
3397 wxS( "Wurth Electronics Inc" ) );
3398 BOOST_REQUIRE( symbol->GetField( wxS( "OrCAD Footprint" ) ) );
3399 BOOST_CHECK_EQUAL( symbol->GetField( wxS( "OrCAD Footprint" ) )->GetText(), wxS( "EVQ-PE105K" ) );
3400 }
3401}
3402
3403
3404BOOST_AUTO_TEST_CASE( EmptyOccurrencePropertiesClearTemplateMetadata )
3405{
3406 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
3407
3408 if( !corpusEnv || !*corpusEnv )
3409 return;
3410
3411 auto checkFieldCleared = [&]( const wxString& aFileName, const wxString& aReference,
3412 const wxString& aFieldName )
3413 {
3414 std::filesystem::path dsn = findCorpusDesign( corpusEnv, aFileName.ToStdString() );
3415 BOOST_REQUIRE( !dsn.empty() );
3416
3417 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
3418 SETTINGS_MANAGER manager;
3419 manager.LoadProject( "" );
3420 schematic->SetProject( &manager.Prj() );
3421 schematic->CurrentSheet().clear();
3422 schematic->CurrentSheet().push_back( &schematic->Root() );
3423
3424 SCH_IO_ORCAD plugin;
3425 plugin.LoadSchematicFile( dsn.string(), schematic.get() );
3426
3427 SCH_FIELD* field = nullptr;
3428
3429 for( const SCH_SHEET_PATH& sheet : schematic->BuildSheetListSortedByPageNumbers() )
3430 {
3431 for( SCH_ITEM* item : sheet.LastScreen()->Items().OfType( SCH_SYMBOL_T ) )
3432 {
3433 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
3434
3435 if( symbol->GetRef( &sheet, false ) == aReference )
3436 field = symbol->GetField( aFieldName );
3437 }
3438 }
3439
3440 BOOST_CHECK( !field || field->GetText().IsEmpty() );
3441 };
3442
3443 checkFieldCleared( wxS( "DC2596A-3.DSN" ), wxS( "L2" ), wxS( "4th Part Field" ) );
3444 checkFieldCleared( wxS( "CY4532 Power Board Schematic.DSN" ), wxS( "J2" ), wxS( "PART_NUMBER" ) );
3445}
3446
3447
3448BOOST_AUTO_TEST_CASE( CapturePseudoGlobalWirelessPinsConnectAcrossPages )
3449{
3450 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
3451
3452 if( !corpusEnv || !*corpusEnv )
3453 return;
3454
3455 std::filesystem::path dsn = findCorpusDesign( corpusEnv, "DC2641A3-SCH.DSN" );
3456
3457 if( dsn.empty() )
3458 return;
3459
3460 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
3461 SETTINGS_MANAGER manager;
3462 manager.LoadProject( "" );
3463 schematic->SetProject( &manager.Prj() );
3464 schematic->CurrentSheet().clear();
3465 schematic->CurrentSheet().push_back( &schematic->Root() );
3466
3467 SCH_IO_ORCAD plugin;
3468 plugin.LoadSchematicFile( dsn.string(), schematic.get() );
3469
3470 std::vector<std::set<std::string>> expected = { { terminalToken( "L1", "3" ), terminalToken( "L4", "3" ) } };
3471 auto [consistent, checkable] = checkConnectivity( *schematic, expected );
3472 BOOST_CHECK_EQUAL( checkable, 1 );
3473 BOOST_CHECK_EQUAL( consistent, 1 );
3474 size_t nativePowerPins = 0;
3475
3476 for( const SCH_SHEET_PATH& path : schematic->BuildSheetListSortedByPageNumbers() )
3477 {
3478 for( SCH_ITEM* item : path.LastScreen()->Items() )
3479 {
3480 if( item->Type() == SCH_SYMBOL_T )
3481 {
3482 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
3483 wxString ref = symbol->GetRef( &path, false );
3484
3485 if( ref != wxS( "L1" ) && ref != wxS( "L4" ) )
3486 continue;
3487
3489 {
3490 if( pin->GetNumber() != wxS( "3" ) )
3491 continue;
3492
3493 BOOST_CHECK( pin->GetType() == ELECTRICAL_PINTYPE::PT_POWER_IN );
3494 BOOST_CHECK( !pin->IsVisible() );
3495 BOOST_CHECK( pin->IsGlobalPower() );
3496 BOOST_CHECK_EQUAL( pin->GetName(), wxString( "$$$1" ) );
3497 ++nativePowerPins;
3498 }
3499 }
3500 else if( SCH_LABEL_BASE* label = dynamic_cast<SCH_LABEL_BASE*>( item ) )
3501 {
3502 BOOST_CHECK_NE( label->GetText(), wxString( "$$$1" ) );
3503 }
3504 }
3505 }
3506
3507 BOOST_CHECK_EQUAL( nativePowerPins, 2 );
3508
3509}
3510
3511
3512BOOST_AUTO_TEST_CASE( PowerAliasesConnectAcrossPages )
3513{
3514 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
3515
3516 if( !corpusEnv || !*corpusEnv )
3517 return;
3518
3519 std::filesystem::path dsn = findCorpusDesign( corpusEnv, "SCH-54852_A5.DSN" );
3520
3521 if( dsn.empty() )
3522 return;
3523
3524 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
3525 SETTINGS_MANAGER manager;
3526 manager.LoadProject( "" );
3527 schematic->SetProject( &manager.Prj() );
3528 schematic->CurrentSheet().clear();
3529 schematic->CurrentSheet().push_back( &schematic->Root() );
3530
3531 SCH_IO_ORCAD plugin;
3532 plugin.LoadSchematicFile( dsn.string(), schematic.get() );
3533
3534 std::vector<std::set<std::string>> expected = { { terminalToken( "R711", "2" ), terminalToken( "C104", "1" ) } };
3535 auto [consistent, checkable] = checkConnectivity( *schematic, expected );
3536 BOOST_CHECK_EQUAL( checkable, 1 );
3537 BOOST_CHECK_EQUAL( consistent, 1 );
3538}
3539
3540
3541BOOST_AUTO_TEST_CASE( ReusedPowerNetIdsDoNotJoinDisconnectedNets )
3542{
3543 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
3544
3545 if( !corpusEnv || !*corpusEnv )
3546 return;
3547
3548 std::filesystem::path dsn = findCorpusDesign( corpusEnv, "SCH-31399_C4.DSN" );
3549
3550 if( dsn.empty() )
3551 return;
3552
3553 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
3554 SETTINGS_MANAGER manager;
3555 manager.LoadProject( "" );
3556 schematic->SetProject( &manager.Prj() );
3557 schematic->CurrentSheet().clear();
3558 schematic->CurrentSheet().push_back( &schematic->Root() );
3559
3560 SCH_IO_ORCAD plugin;
3561 plugin.LoadSchematicFile( dsn.string(), schematic.get() );
3562
3563 std::vector<std::set<std::string>> expected = { { terminalToken( "C105", "1" ), terminalToken( "C106", "1" ) },
3564 { terminalToken( "C127", "1" ), terminalToken( "C128", "1" ) } };
3565 auto [consistent, checkable] = checkConnectivity( *schematic, expected );
3566 BOOST_CHECK_EQUAL( checkable, 2 );
3567 BOOST_CHECK_EQUAL( consistent, 2 );
3568}
3569
3570
3571BOOST_AUTO_TEST_CASE( ReusedPowerNetAliasesRemainPhysicallyScoped )
3572{
3573 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
3574
3575 if( !corpusEnv || !*corpusEnv )
3576 return;
3577
3578 std::filesystem::path dsn = findCorpusDesign( corpusEnv, "DC607A.DSN" );
3579
3580 if( dsn.empty() )
3581 return;
3582
3583 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
3584 SETTINGS_MANAGER manager;
3585 manager.LoadProject( "" );
3586 schematic->SetProject( &manager.Prj() );
3587 schematic->CurrentSheet().clear();
3588 schematic->CurrentSheet().push_back( &schematic->Root() );
3589
3590 SCH_IO_ORCAD plugin;
3591 plugin.LoadSchematicFile( dsn.string(), schematic.get() );
3592
3593 std::vector<std::set<std::string>> expected = { { terminalToken( "C1", "2" ), terminalToken( "C13", "2" ) },
3594 { terminalToken( "C14", "2" ), terminalToken( "C2", "2" ) },
3595 { terminalToken( "C3", "1" ), terminalToken( "C4", "1" ) },
3596 { terminalToken( "C13", "1" ), terminalToken( "C31", "1" ) } };
3597 auto [consistent, checkable] = checkConnectivity( *schematic, expected );
3598 BOOST_CHECK_EQUAL( checkable, 4 );
3599 BOOST_CHECK_EQUAL( consistent, 4 );
3600}
3601
3602
3603BOOST_AUTO_TEST_CASE( SinglePowerMeaningPropagatesAcrossReusedNetId )
3604{
3605 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
3606
3607 if( !corpusEnv || !*corpusEnv )
3608 return;
3609
3610 std::filesystem::path dsn =
3611 findCorpusDesign( corpusEnv, "630-60651-01_04_CYW9BTM2BASE3_20829_BaseBoard_Schematics.DSN" );
3612
3613 if( dsn.empty() )
3614 return;
3615
3616 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
3617 SETTINGS_MANAGER manager;
3618 manager.LoadProject( "" );
3619 schematic->SetProject( &manager.Prj() );
3620 schematic->CurrentSheet().clear();
3621 schematic->CurrentSheet().push_back( &schematic->Root() );
3622
3623 SCH_IO_ORCAD plugin;
3624 plugin.LoadSchematicFile( dsn.string(), schematic.get() );
3625
3626 std::vector<std::set<std::string>> expected = { { terminalToken( "C1", "2" ), terminalToken( "U5", "62" ),
3627 terminalToken( "U5", "63" ), terminalToken( "U5", "65" ) } };
3628 auto [consistent, checkable] = checkConnectivity( *schematic, expected );
3629 BOOST_CHECK_EQUAL( checkable, 1 );
3630 BOOST_CHECK_EQUAL( consistent, 1 );
3631}
3632
3633
3634BOOST_AUTO_TEST_CASE( RepeatedLocalNetNamesRemainSheetScoped )
3635{
3636 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
3637
3638 if( !corpusEnv || !*corpusEnv )
3639 return;
3640
3641 std::filesystem::path dsn = findCorpusDesign( corpusEnv, "parallella_gen0.dsn" );
3642
3643 if( dsn.empty() )
3644 return;
3645
3646 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
3647 SETTINGS_MANAGER manager;
3648 manager.LoadProject( "" );
3649 schematic->SetProject( &manager.Prj() );
3650 schematic->CurrentSheet().clear();
3651 schematic->CurrentSheet().push_back( &schematic->Root() );
3652
3653 SCH_IO_ORCAD plugin;
3654 plugin.LoadSchematicFile( dsn.string(), schematic.get() );
3655
3656 std::vector<std::set<std::string>> expected = { { terminalToken( "R41", "2" ), terminalToken( "U26", "53" ) },
3657 { terminalToken( "R57", "2" ), terminalToken( "R97", "1" ),
3658 terminalToken( "U14", "1" ) } };
3659 auto [consistent, checkable] = checkConnectivity( *schematic, expected );
3660 BOOST_CHECK_EQUAL( checkable, 2 );
3661 BOOST_CHECK_EQUAL( consistent, 2 );
3662}
3663
3664
3665BOOST_AUTO_TEST_CASE( RepeatedHierarchicalPortNamesRemainSheetScoped )
3666{
3667 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
3668
3669 if( !corpusEnv || !*corpusEnv )
3670 return;
3671
3672 std::filesystem::path dsn = findCorpusDesign( corpusEnv, "SI347X-DC-EB.DSN" );
3673
3674 if( dsn.empty() )
3675 return;
3676
3677 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
3678 SETTINGS_MANAGER manager;
3679 manager.LoadProject( "" );
3680 schematic->SetProject( &manager.Prj() );
3681 schematic->CurrentSheet().clear();
3682 schematic->CurrentSheet().push_back( &schematic->Root() );
3683
3684 SCH_IO_ORCAD plugin;
3685 plugin.LoadSchematicFile( dsn.string(), schematic.get() );
3686
3687 std::vector<std::set<std::string>> expected;
3688
3689 for( int channel = 1; channel <= 8; ++channel )
3690 {
3691 std::string q = "Q" + std::to_string( channel );
3692 std::string r = "R" + std::to_string( channel );
3693 std::string gatePin = std::to_string( std::array{ 1, 7, 8, 14, 29, 35, 36, 42 }[channel - 1] );
3694 std::string sourcePin = std::to_string( std::array{ 2, 6, 9, 13, 30, 34, 37, 41 }[channel - 1] );
3695 expected.push_back( { terminalToken( q, "G" ), terminalToken( "U1", gatePin ) } );
3696 expected.push_back( { terminalToken( q, "2" ), terminalToken( q, "3" ), terminalToken( q, "S" ),
3697 terminalToken( r, "2" ), terminalToken( "U1", sourcePin ) } );
3698 }
3699
3700 auto [consistent, checkable] = checkConnectivity( *schematic, expected );
3701 BOOST_CHECK_EQUAL( checkable, 16 );
3702 BOOST_CHECK_EQUAL( consistent, 16 );
3703}
3704
3705
3706BOOST_AUTO_TEST_CASE( RepeatedHierarchicalOffpageNamesRemainSheetScoped )
3707{
3708 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
3709
3710 if( !corpusEnv || !*corpusEnv )
3711 return;
3712
3713 std::filesystem::path dsn = findCorpusDesign( corpusEnv, "Si828X-BW-GDB.DSN" );
3714
3715 if( dsn.empty() )
3716 return;
3717
3718 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
3719 SETTINGS_MANAGER manager;
3720 manager.LoadProject( "" );
3721 schematic->SetProject( &manager.Prj() );
3722 schematic->CurrentSheet().clear();
3723 schematic->CurrentSheet().push_back( &schematic->Root() );
3724
3725 SCH_IO_ORCAD plugin;
3726 plugin.LoadSchematicFile( dsn.string(), schematic.get() );
3727
3728 std::vector<std::set<std::string>> expected = {
3729 { terminalToken( "D317", "K" ), terminalToken( "JT1", "2" ), terminalToken( "Q13", "E" ) },
3730 { terminalToken( "D322", "K" ), terminalToken( "JT6", "2" ), terminalToken( "Q12", "E" ) },
3731 { terminalToken( "Q6", "C" ), terminalToken( "Q7", "C" ), terminalToken( "Q15", "C" ),
3732 terminalToken( "U204", "9" ) },
3733 { terminalToken( "Q10", "C" ), terminalToken( "Q11", "C" ), terminalToken( "Q20", "C" ),
3734 terminalToken( "U2", "13" ) }
3735 };
3736 auto [consistent, checkable] = checkConnectivity( *schematic, expected );
3737 BOOST_CHECK_EQUAL( checkable, 4 );
3738 BOOST_CHECK_EQUAL( consistent, 4 );
3739}
3740
3741
3742BOOST_AUTO_TEST_CASE( NestedOccurrenceReferencesOverridePlacedTemplateReferences )
3743{
3744 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
3745
3746 if( !corpusEnv || !*corpusEnv )
3747 return;
3748
3749 std::filesystem::path dsn = findCorpusDesign( corpusEnv, "SI8284v2-EVB.DSN" );
3750
3751 if( dsn.empty() )
3752 return;
3753
3754 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
3755 SETTINGS_MANAGER manager;
3756 manager.LoadProject( "" );
3757 schematic->SetProject( &manager.Prj() );
3758 schematic->CurrentSheet().clear();
3759 schematic->CurrentSheet().push_back( &schematic->Root() );
3760
3761 SCH_IO_ORCAD plugin;
3762 plugin.LoadSchematicFile( dsn.string(), schematic.get() );
3763
3764 std::set<std::string> refs = collectImportedRefs( *schematic );
3765
3766 BOOST_CHECK( refs.count( "Q1-1" ) );
3767 BOOST_CHECK( refs.count( "Q1-2" ) );
3768 BOOST_CHECK( refs.count( "Q8-1" ) );
3769 BOOST_CHECK( refs.count( "Q8-2" ) );
3770}
3771
3772
3773BOOST_AUTO_TEST_CASE( SharedOccurrenceNetWithoutTerminalPeerKeepsBaseName )
3774{
3775 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
3776
3777 if( !corpusEnv || !*corpusEnv )
3778 return;
3779
3780 std::filesystem::path dsn = findCorpusDesign( corpusEnv, "SI8284v2-EVB.DSN" );
3781
3782 if( dsn.empty() )
3783 return;
3784
3785 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
3786 SETTINGS_MANAGER manager;
3787 manager.LoadProject( "" );
3788 schematic->SetProject( &manager.Prj() );
3789 schematic->CurrentSheet().clear();
3790 schematic->CurrentSheet().push_back( &schematic->Root() );
3791
3792 SCH_IO_ORCAD plugin;
3793 plugin.LoadSchematicFile( dsn.string(), schematic.get() );
3794
3795 BOOST_CHECK_EQUAL( terminalNetName( *schematic, wxS( "J15" ), wxS( "2" ) ).AfterLast( '/' ).Lower(),
3796 wxString( wxS( "s3" ) ) );
3797}
3798
3799
3800BOOST_AUTO_TEST_CASE( PowerNetNameOverridesLocalWireAlias )
3801{
3802 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
3803
3804 if( !corpusEnv || !*corpusEnv )
3805 return;
3806
3807 std::filesystem::path dsn = findCorpusDesign( corpusEnv, "SI8284v2-EVB.DSN" );
3808
3809 if( dsn.empty() )
3810 return;
3811
3812 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
3813 SETTINGS_MANAGER manager;
3814 manager.LoadProject( "" );
3815 schematic->SetProject( &manager.Prj() );
3816 schematic->CurrentSheet().clear();
3817 schematic->CurrentSheet().push_back( &schematic->Root() );
3818
3819 SCH_IO_ORCAD plugin;
3820 plugin.LoadSchematicFile( dsn.string(), schematic.get() );
3821
3822 wxString boostNet = terminalNetName( *schematic, wxS( "C36" ), wxS( "1" ) );
3823 wxString supplyNet = terminalNetName( *schematic, wxS( "C12" ), wxS( "1" ) );
3824 BOOST_CHECK_EQUAL( boostNet.AfterLast( '/' ), wxString( "VDDB" ) );
3825 BOOST_CHECK_EQUAL( terminalNetName( *schematic, wxS( "Q4" ), wxS( "C" ) ), boostNet );
3826 BOOST_CHECK_EQUAL( terminalNetName( *schematic, wxS( "U2" ), wxS( "20" ) ), supplyNet );
3827 BOOST_CHECK_NE( boostNet, supplyNet );
3828
3829 const IMPORT_NET_MAP* map = schematic->GetImportNetMap();
3830 BOOST_REQUIRE( map );
3831 auto mapped = std::find_if( map->entries.begin(), map->entries.end(),
3832 []( const IMPORT_NET_MAP_ENTRY& entry )
3833 {
3834 return entry.sourceNetId == 16939716
3835 && entry.originalName == wxS( "N16864826" );
3836 } );
3837 BOOST_REQUIRE( mapped != map->entries.end() );
3839 BOOST_CHECK_EQUAL( mapped->nameAtImport, boostNet );
3840}
3841
3842
3843BOOST_AUTO_TEST_CASE( NativePowerNamesIgnoreSourceCase )
3844{
3845 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
3846
3847 if( !corpusEnv || !*corpusEnv )
3848 return;
3849
3850 for( const char* filename : { "Si828X-BW-GDB.DSN", "SI828X-AW-GDB.DSN" } )
3851 {
3852 BOOST_TEST_CONTEXT( filename )
3853 {
3854 std::filesystem::path dsn = findCorpusDesign( corpusEnv, filename );
3855 BOOST_REQUIRE_MESSAGE( !dsn.empty(), filename << " not present in corpus." );
3856 SETTINGS_MANAGER manager;
3857 manager.LoadProject( "" );
3858 SCHEMATIC schematic( &manager.Prj() );
3859 SCH_IO_ORCAD plugin;
3860 plugin.LoadSchematicFile( dsn.string(), &schematic );
3861
3862 wxString netName = terminalNetName( schematic, wxS( "C306" ), wxS( "1" ) );
3863 BOOST_CHECK_EQUAL( netName.Upper(), wxString( "LS-SOURCE" ) );
3864
3865 for( const auto& [reference, pin] :
3866 { std::pair{ wxString( "U2" ), wxString( "16" ) },
3867 std::pair{ wxString( "C307" ), wxString( "2" ) },
3868 std::pair{ wxString( "C309" ), wxString( "1" ) },
3869 std::pair{ wxString( "C310" ), wxString( "1" ) },
3870 std::pair{ wxString( "R333" ), wxString( "1" ) },
3871 std::pair{ wxString( "UB8" ), wxString( "3" ) },
3872 std::pair{ wxString( "UT6" ), wxString( "5" ) } } )
3873 {
3874 BOOST_CHECK_EQUAL( terminalNetName( schematic, reference, pin ), netName );
3875 }
3876
3877 std::set<wxString> powerNames;
3878
3879 for( const SCH_SHEET_PATH& sheet : schematic.BuildSheetListSortedByPageNumbers() )
3880 {
3881 for( SCH_ITEM* item : sheet.LastScreen()->Items().OfType( SCH_SYMBOL_T ) )
3882 {
3883 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
3884
3885 if( symbol->GetField( FIELD_T::VALUE )->GetText().CmpNoCase( wxS( "LS-SOURCE" ) ) == 0
3886 && symbol->GetLibSymbolRef()->IsGlobalPower() )
3887 {
3888 powerNames.insert( symbol->GetField( FIELD_T::VALUE )->GetText() );
3889 }
3890 }
3891 }
3892
3893 BOOST_REQUIRE_EQUAL( powerNames.size(), 1u );
3894 BOOST_CHECK_EQUAL( *powerNames.begin(), netName );
3895 }
3896 }
3897}
3898
3899
3900BOOST_AUTO_TEST_CASE( HiddenWireLabelsAvoidBusCrossings )
3901{
3902 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
3903
3904 if( !corpusEnv || !*corpusEnv )
3905 return;
3906
3907 std::filesystem::path dsn = findCorpusDesign( corpusEnv, "CN81XX_GBCV2_sch_0530.DSN" );
3908
3909 if( dsn.empty() )
3910 return;
3911
3912 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
3913 SETTINGS_MANAGER manager;
3914 manager.LoadProject( "" );
3915 schematic->SetProject( &manager.Prj() );
3916 schematic->CurrentSheet().clear();
3917 schematic->CurrentSheet().push_back( &schematic->Root() );
3918
3919 SCH_IO_ORCAD plugin;
3920 plugin.LoadSchematicFile( dsn.string(), schematic.get() );
3921
3922 SCH_SHEET_LIST sheets = schematic->BuildSheetListSortedByPageNumbers();
3923 schematic->ConnectionGraph()->Recalculate( sheets, true );
3924 wxString r13Pin1Net;
3925
3926 for( const auto& [key, subgraphs] : schematic->ConnectionGraph()->GetNetMap() )
3927 {
3928 for( CONNECTION_SUBGRAPH* subgraph : subgraphs )
3929 {
3930 for( SCH_ITEM* item : subgraph->GetItems() )
3931 {
3932 if( item->Type() != SCH_PIN_T )
3933 continue;
3934
3935 SCH_PIN* pin = static_cast<SCH_PIN*>( item );
3936 SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( pin->GetParentSymbol() );
3937
3938 if( symbol && symbol->GetRef( &subgraph->GetSheet(), false ) == wxS( "R13" )
3939 && pin->GetNumber() == wxS( "1" ) )
3940 {
3941 r13Pin1Net = key.Name;
3942 }
3943 }
3944 }
3945 }
3946
3947 BOOST_CHECK_EQUAL( r13Pin1Net.AfterLast( '/' ), wxS( "DDR0_DM1" ) );
3948}
3949
3950
3951BOOST_AUTO_TEST_CASE( IncompleteCachedSymbolsUsePlacedPins )
3952{
3953 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
3954
3955 if( !corpusEnv || !*corpusEnv )
3956 return;
3957
3958 std::filesystem::path dsn = findCorpusDesign( corpusEnv, "1979A.DSN" );
3959
3960 if( dsn.empty() )
3961 return;
3962
3963 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
3964 SETTINGS_MANAGER manager;
3965 manager.LoadProject( "" );
3966 schematic->SetProject( &manager.Prj() );
3967 schematic->CurrentSheet().clear();
3968 schematic->CurrentSheet().push_back( &schematic->Root() );
3969
3970 SCH_IO_ORCAD plugin;
3971 plugin.LoadSchematicFile( dsn.string(), schematic.get() );
3972
3973 std::vector<std::set<std::string>> expected = { { terminalToken( "J1", "1" ), terminalToken( "R5", "1" ) },
3974 { terminalToken( "J2", "1" ), terminalToken( "R6", "2" ) },
3975 { terminalToken( "J1", "2" ), terminalToken( "J1", "3" ),
3976 terminalToken( "J2", "2" ) } };
3977 auto [consistent, checkable] = checkConnectivity( *schematic, expected );
3978 BOOST_CHECK_EQUAL( checkable, 3 );
3979 BOOST_CHECK_EQUAL( consistent, 3 );
3980}
3981
3982
3983BOOST_AUTO_TEST_CASE( LegacyOffpageConnectorsKeepDistinctPinPositions )
3984{
3985 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
3986
3987 if( !corpusEnv || !*corpusEnv )
3988 return;
3989
3990 std::filesystem::path dsn = findCorpusDesign( corpusEnv, "DC726A-1.DSN" );
3991
3992 if( dsn.empty() )
3993 return;
3994
3995 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
3996 SETTINGS_MANAGER manager;
3997 manager.LoadProject( "" );
3998 schematic->SetProject( &manager.Prj() );
3999 schematic->CurrentSheet().clear();
4000 schematic->CurrentSheet().push_back( &schematic->Root() );
4001
4002 SCH_IO_ORCAD plugin;
4003 plugin.LoadSchematicFile( dsn.string(), schematic.get() );
4004
4005 std::vector<std::set<std::string>> expected = { { terminalToken( "J1", "4" ), terminalToken( "R40", "1" ),
4006 terminalToken( "R9", "1" ), terminalToken( "U5", "14" ) },
4007 { terminalToken( "J1", "6" ), terminalToken( "R10", "1" ),
4008 terminalToken( "U5", "13" ) },
4009 { terminalToken( "J1", "7" ), terminalToken( "R39", "1" ),
4010 terminalToken( "R5", "1" ), terminalToken( "U5", "16" ) } };
4011 auto [consistent, checkable] = checkConnectivity( *schematic, expected );
4012 BOOST_CHECK_EQUAL( checkable, 3 );
4013 BOOST_CHECK_EQUAL( consistent, 3 );
4014}
4015
4016
4017BOOST_AUTO_TEST_CASE( EmptyPackagePinNumbersUseLogicalPinNames )
4018{
4019 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
4020
4021 if( !corpusEnv || !*corpusEnv )
4022 return;
4023
4024 std::filesystem::path dsn = findCorpusDesign( corpusEnv, "DC2283A-2.DSN" );
4025
4026 if( dsn.empty() )
4027 return;
4028
4029 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
4030 SETTINGS_MANAGER manager;
4031 manager.LoadProject( "" );
4032 schematic->SetProject( &manager.Prj() );
4033 schematic->CurrentSheet().clear();
4034 schematic->CurrentSheet().push_back( &schematic->Root() );
4035
4036 SCH_IO_ORCAD plugin;
4037 plugin.LoadSchematicFile( dsn.string(), schematic.get() );
4038
4039 std::vector<std::set<std::string>> expected = { { terminalToken( "C41", "1" ), terminalToken( "C42", "1" ) },
4040 { terminalToken( "C42", "2" ), terminalToken( "J12", "1" ) } };
4041 auto [consistent, checkable] = checkConnectivity( *schematic, expected );
4042 BOOST_CHECK_EQUAL( checkable, 2 );
4043 BOOST_CHECK_EQUAL( consistent, 2 );
4044}
4045
4046
4047BOOST_AUTO_TEST_CASE( EmptyPackagePinNumbersPreserveAlphabeticNames )
4048{
4049 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
4050
4051 if( !corpusEnv || !*corpusEnv )
4052 return;
4053
4054 std::filesystem::path dsn = findCorpusDesign( corpusEnv, "EMS4_0.DSN" );
4055
4056 if( dsn.empty() )
4057 return;
4058
4059 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
4060 SETTINGS_MANAGER manager;
4061 manager.LoadProject( "" );
4062 schematic->SetProject( &manager.Prj() );
4063 schematic->CurrentSheet().clear();
4064 schematic->CurrentSheet().push_back( &schematic->Root() );
4065
4066 SCH_IO_ORCAD plugin;
4067 plugin.LoadSchematicFile( dsn.string(), schematic.get() );
4068
4069 std::vector<std::set<std::string>> expected = { { terminalToken( "DL1", "C" ), terminalToken( "R14", "1" ) },
4070 { terminalToken( "TP1", "A" ), terminalToken( "U1", "7" ) },
4071 { terminalToken( "U10", "PAD" ), terminalToken( "U11", "PAD" ) } };
4072 auto [consistent, checkable] = checkConnectivity( *schematic, expected );
4073 BOOST_CHECK_EQUAL( checkable, 3 );
4074 BOOST_CHECK_EQUAL( consistent, 3 );
4075}
4076
4077
4078BOOST_AUTO_TEST_CASE( EmbeddedTwoPinPackageNamesDoNotReplacePhysicalNumbers )
4079{
4080 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
4081
4082 if( !corpusEnv || !*corpusEnv )
4083 return;
4084
4085 std::filesystem::path dsn = findCorpusDesign( corpusEnv, "DC2793A-3.DSN" );
4086
4087 if( dsn.empty() )
4088 return;
4089
4090 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
4091 SETTINGS_MANAGER manager;
4092 manager.LoadProject( "" );
4093 schematic->SetProject( &manager.Prj() );
4094 schematic->CurrentSheet().clear();
4095 schematic->CurrentSheet().push_back( &schematic->Root() );
4096
4097 SCH_IO_ORCAD plugin;
4098 plugin.LoadSchematicFile( dsn.string(), schematic.get() );
4099
4100 std::vector<std::set<std::string>> expected = { { terminalToken( "D1", "1" ), terminalToken( "R6", "2" ) },
4101 { terminalToken( "D1", "2" ), terminalToken( "R4", "2" ) } };
4102 auto [consistent, checkable] = checkConnectivity( *schematic, expected );
4103 BOOST_CHECK_EQUAL( checkable, 2 );
4104 BOOST_CHECK_EQUAL( consistent, 2 );
4105}
4106
4107
4108BOOST_AUTO_TEST_CASE( DirectPowerSymbolSharesComponentPin )
4109{
4110 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
4111
4112 if( !corpusEnv || !*corpusEnv )
4113 return;
4114
4115 std::filesystem::path dsn = findCorpusDesign( corpusEnv, "CY4532 Power Board Schematic.DSN" );
4116
4117 if( dsn.empty() )
4118 return;
4119
4120 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
4121 SETTINGS_MANAGER manager;
4122 manager.LoadProject( "" );
4123 schematic->SetProject( &manager.Prj() );
4124 schematic->CurrentSheet().clear();
4125 schematic->CurrentSheet().push_back( &schematic->Root() );
4126
4127 SCH_IO_ORCAD plugin;
4128 plugin.LoadSchematicFile( dsn.string(), schematic.get() );
4129
4130 std::vector<std::set<std::string>> expected = { { terminalToken( "C70", "2" ), terminalToken( "C72", "2" ) } };
4131 auto [consistent, checkable] = checkConnectivity( *schematic, expected );
4132 BOOST_CHECK_EQUAL( checkable, 1 );
4133 BOOST_CHECK_EQUAL( consistent, 1 );
4134}
4135
4136
4137BOOST_AUTO_TEST_CASE( DirectPowerSymbolUsesComponentPinNet )
4138{
4139 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
4140
4141 if( !corpusEnv || !*corpusEnv )
4142 return;
4143
4144 std::filesystem::path dsn = findCorpusDesign( corpusEnv, "DC2047A-3-A.DSN" );
4145
4146 if( dsn.empty() )
4147 return;
4148
4149 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
4150 SETTINGS_MANAGER manager;
4151 manager.LoadProject( "" );
4152 schematic->SetProject( &manager.Prj() );
4153 schematic->CurrentSheet().clear();
4154 schematic->CurrentSheet().push_back( &schematic->Root() );
4155
4156 SCH_IO_ORCAD plugin;
4157 plugin.LoadSchematicFile( dsn.string(), schematic.get() );
4158
4159 std::vector<std::set<std::string>> expected = { { terminalToken( "D15", "2" ), terminalToken( "D16", "2" ) } };
4160 auto [consistent, checkable] = checkConnectivity( *schematic, expected );
4161 BOOST_CHECK_EQUAL( checkable, 1 );
4162 BOOST_CHECK_EQUAL( consistent, 1 );
4163}
4164
4165
4166BOOST_AUTO_TEST_CASE( NearbyOffpageConnectorsRemainDistinct )
4167{
4168 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
4169
4170 if( !corpusEnv || !*corpusEnv )
4171 return;
4172
4173 std::filesystem::path dsn =
4174 findCorpusDesign( corpusEnv, "630-60651-01_04_CYW9BTM2BASE3_20829_BaseBoard_Schematics.DSN" );
4175
4176 if( dsn.empty() )
4177 return;
4178
4179 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
4180 SETTINGS_MANAGER manager;
4181 manager.LoadProject( "" );
4182 schematic->SetProject( &manager.Prj() );
4183 schematic->CurrentSheet().clear();
4184 schematic->CurrentSheet().push_back( &schematic->Root() );
4185
4186 SCH_IO_ORCAD plugin;
4187 plugin.LoadSchematicFile( dsn.string(), schematic.get() );
4188
4189 std::vector<std::set<std::string>> expected = { { terminalToken( "J14", "1" ), terminalToken( "J16", "17" ),
4190 terminalToken( "R110", "2" ) },
4191 { terminalToken( "J16", "19" ), terminalToken( "J2", "2" ) },
4192 { terminalToken( "J16", "67" ), terminalToken( "J2", "3" ) } };
4193 auto [consistent, checkable] = checkConnectivity( *schematic, expected );
4194 BOOST_CHECK_EQUAL( checkable, 3 );
4195 BOOST_CHECK_EQUAL( consistent, 3 );
4196}
4197
4198
4199BOOST_AUTO_TEST_CASE( MatchingSymbolAndPackageVariantsPreservePhysicalPinNames )
4200{
4201 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
4202
4203 if( !corpusEnv || !*corpusEnv )
4204 return;
4205
4206 std::filesystem::path dsn = findCorpusDesign( corpusEnv, "CY4532 Power Board Schematic.DSN" );
4207
4208 if( dsn.empty() )
4209 return;
4210
4211 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
4212 SETTINGS_MANAGER manager;
4213 manager.LoadProject( "" );
4214 schematic->SetProject( &manager.Prj() );
4215 schematic->CurrentSheet().clear();
4216 schematic->CurrentSheet().push_back( &schematic->Root() );
4217
4218 SCH_IO_ORCAD plugin;
4219 plugin.LoadSchematicFile( dsn.string(), schematic.get() );
4220
4221 std::vector<std::set<std::string>> expected = { { terminalToken( "D5", "A" ), terminalToken( "U2", "24" ) },
4222 { terminalToken( "D5", "K" ), terminalToken( "U2", "20" ) } };
4223 auto [consistent, checkable] = checkConnectivity( *schematic, expected );
4224 BOOST_CHECK_EQUAL( checkable, 2 );
4225 BOOST_CHECK_EQUAL( consistent, 2 );
4226}
4227
4228
4229BOOST_AUTO_TEST_CASE( OffpageParentBindingKeepsVisibleLabelsApart )
4230{
4231 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
4232
4233 if( !corpusEnv || !*corpusEnv )
4234 return;
4235
4236 std::filesystem::path dsn = findCorpusDesign( corpusEnv, "SI347XY_MB_EVB.DSN" );
4237
4238 if( dsn.empty() )
4239 return;
4240
4241 SCHEMATIC schematic( nullptr );
4242 SETTINGS_MANAGER manager;
4243 manager.LoadProject( "" );
4244 schematic.SetProject( &manager.Prj() );
4245 SCH_IO_ORCAD plugin;
4246 plugin.LoadSchematicFile( dsn.string(), &schematic );
4247 int checked = 0;
4248
4249 for( const SCH_SHEET_PATH& sheet : schematic.BuildSheetListSortedByPageNumbers() )
4250 {
4251 for( SCH_ITEM* item : sheet.LastScreen()->Items().OfType( SCH_GLOBAL_LABEL_T ) )
4252 {
4253 SCH_GLOBALLABEL* label = static_cast<SCH_GLOBALLABEL*>( item );
4254
4255 if( label->GetText() != "VDD" )
4256 continue;
4257
4258 for( SCH_ITEM* other : sheet.LastScreen()->Items().OfType( SCH_HIER_LABEL_T ) )
4259 {
4260 SCH_HIERLABEL* hierLabel = static_cast<SCH_HIERLABEL*>( other );
4261
4262 if( hierLabel->GetText() != label->GetText() || hierLabel->GetPosition() != label->GetPosition() )
4263 continue;
4264
4265 ++checked;
4266 BOOST_CHECK( hierLabel->GetSpinStyle()
4267 == label->GetSpinStyle().RotateCCW().RotateCCW().Spin() );
4268 BOOST_CHECK( label->GetTextColor() == KIGFX::COLOR4D::UNSPECIFIED
4269 || label->GetTextColor().a > 0 );
4270 BOOST_CHECK( hierLabel->GetTextColor() == KIGFX::COLOR4D::UNSPECIFIED
4271 || hierLabel->GetTextColor().a > 0 );
4272 }
4273 }
4274 }
4275
4276 BOOST_CHECK_EQUAL( checked, 1 );
4277}
4278
4279
4280BOOST_AUTO_TEST_CASE( OffpageAtWireBranchHasVisibleSafeAnchor )
4281{
4282 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
4283
4284 if( !corpusEnv || !*corpusEnv )
4285 return;
4286
4287 std::filesystem::path dsn = findCorpusDesign( corpusEnv, "DC2659A-4.DSN" );
4288
4289 if( dsn.empty() )
4290 return;
4291
4292 SCHEMATIC schematic( nullptr );
4293 SETTINGS_MANAGER manager;
4294 manager.LoadProject( "" );
4295 schematic.SetProject( &manager.Prj() );
4296 SCH_IO_ORCAD plugin;
4297 plugin.LoadSchematicFile( dsn.string(), &schematic );
4298 int checked = 0;
4299
4300 for( const SCH_SHEET_PATH& sheet : schematic.BuildSheetListSortedByPageNumbers() )
4301 {
4302 for( SCH_ITEM* item : sheet.LastScreen()->Items() )
4303 {
4304 if( item->Type() != SCH_GLOBAL_LABEL_T )
4305 continue;
4306
4307 SCH_GLOBALLABEL* label = static_cast<SCH_GLOBALLABEL*>( item );
4308
4309 // PAGE2's source connector starts at a three-wire branch; nearby labels belong to other source objects.
4310 if( label->GetText() != "VOUT2"
4311 || ( label->GetPosition() - VECTOR2I( 4140200, 901700 ) ).SquaredEuclideanNorm()
4312 > int64_t( 15000 ) * 15000 )
4313 continue;
4314
4315 ++checked;
4316 BOOST_CHECK( label->GetTextColor() == KIGFX::COLOR4D::UNSPECIFIED
4317 || label->GetTextColor().a > 0 );
4318 int incident = 0;
4319
4320 for( SCH_ITEM* other : sheet.LastScreen()->Items() )
4321 {
4322 if( other->Type() != SCH_LINE_T )
4323 continue;
4324
4325 SCH_LINE* line = static_cast<SCH_LINE*>( other );
4326
4327 if( ( line->GetLayer() == LAYER_WIRE || line->GetLayer() == LAYER_BUS )
4328 && line->GetSeg().Contains( label->GetPosition() ) )
4329 ++incident;
4330 }
4331
4332 BOOST_CHECK_LE( incident, 1 );
4333 }
4334 }
4335
4336 BOOST_CHECK_EQUAL( checked, 1 );
4337}
4338
4339
4340BOOST_AUTO_TEST_CASE( OffpageDisplayNameDoesNotChangeConnectivity )
4341{
4342 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
4343
4344 if( !corpusEnv || !*corpusEnv )
4345 {
4346 BOOST_TEST_MESSAGE( "KICAD_ORCAD_CORPUS not set; skipping off-page display-name check." );
4347 return;
4348 }
4349
4350 std::filesystem::path dsn = findCorpusDesign( corpusEnv, "DC2509A-1.DSN" );
4351
4352 if( dsn.empty() )
4353 {
4354 BOOST_TEST_MESSAGE( "DC2509A-1.DSN not present in corpus; skipping off-page display-name check." );
4355 return;
4356 }
4357
4358 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
4359 SETTINGS_MANAGER manager;
4360 manager.LoadProject( "" );
4361 schematic->SetProject( &manager.Prj() );
4362 schematic->CurrentSheet().clear();
4363 schematic->CurrentSheet().push_back( &schematic->Root() );
4364
4365 SCH_IO_ORCAD plugin;
4366 plugin.LoadSchematicFile( dsn.string(), schematic.get() );
4367
4368 SCH_SHEET_LIST sheets = schematic->BuildSheetListSortedByPageNumbers();
4369 schematic->ConnectionGraph()->Recalculate( sheets, true );
4370
4371 std::map<std::string, CONNECTION_SUBGRAPH*> pinNets;
4372
4373 for( const auto& [key, subgraphs] : schematic->ConnectionGraph()->GetNetMap() )
4374 {
4375 for( CONNECTION_SUBGRAPH* subgraph : subgraphs )
4376 {
4377 for( SCH_ITEM* item : subgraph->GetItems() )
4378 {
4379 if( item->Type() != SCH_PIN_T )
4380 continue;
4381
4382 SCH_PIN* pin = static_cast<SCH_PIN*>( item );
4383 SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( pin->GetParentSymbol() );
4384
4385 if( !symbol || pin->GetNumber() != wxS( "1" ) )
4386 continue;
4387
4388 wxString ref = symbol->GetRef( &subgraph->GetSheet(), false );
4389
4390 if( ref == wxS( "U3" ) || ref == wxS( "U4" ) )
4391 pinNets[ref.ToStdString()] = subgraph;
4392 }
4393 }
4394 }
4395
4396 BOOST_REQUIRE_EQUAL( pinNets.count( "U3" ), 1u );
4397 BOOST_REQUIRE_EQUAL( pinNets.count( "U4" ), 1u );
4398 BOOST_CHECK_EQUAL( pinNets["U3"], pinNets["U4"] );
4399}
4400
4401
4402BOOST_AUTO_TEST_CASE( SingleLeafOccurrencePreservesNamedNet )
4403{
4404 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
4405
4406 if( !corpusEnv || !*corpusEnv )
4407 {
4408 BOOST_TEST_MESSAGE( "KICAD_ORCAD_CORPUS not set; skipping single leaf occurrence check." );
4409 return;
4410 }
4411
4412 std::filesystem::path dsn = findCorpusDesign( corpusEnv, "SCH-28988.DSN" );
4413
4414 if( dsn.empty() )
4415 {
4416 BOOST_TEST_MESSAGE( "SCH-28988.DSN not present in corpus; skipping single leaf occurrence check." );
4417 return;
4418 }
4419
4420 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
4421 SETTINGS_MANAGER manager;
4422 manager.LoadProject( "" );
4423 schematic->SetProject( &manager.Prj() );
4424 schematic->CurrentSheet().clear();
4425 schematic->CurrentSheet().push_back( &schematic->Root() );
4426
4427 SCH_IO_ORCAD plugin;
4428 plugin.LoadSchematicFile( dsn.string(), schematic.get() );
4429
4430 SCH_SHEET_LIST sheets = schematic->BuildSheetListSortedByPageNumbers();
4431 schematic->ConnectionGraph()->Recalculate( sheets, true );
4432 wxString netName;
4433
4434 for( const auto& [key, subgraphs] : schematic->ConnectionGraph()->GetNetMap() )
4435 {
4436 for( CONNECTION_SUBGRAPH* subgraph : subgraphs )
4437 {
4438 for( SCH_ITEM* item : subgraph->GetItems() )
4439 {
4440 if( item->Type() != SCH_PIN_T )
4441 continue;
4442
4443 SCH_PIN* pin = static_cast<SCH_PIN*>( item );
4444 SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( pin->GetParentSymbol() );
4445
4446 if( symbol && symbol->GetRef( &subgraph->GetSheet(), false ) == wxS( "C3" )
4447 && pin->GetNumber() == wxS( "1" ) )
4448 {
4449 netName = key.Name;
4450 }
4451 }
4452 }
4453 }
4454
4455 BOOST_CHECK_EQUAL( netName.AfterLast( '/' ), wxString( "ANALOG5V" ) );
4456 BOOST_CHECK_EQUAL( terminalNetName( *schematic, wxS( "SH2" ), wxS( "2" ) ), netName );
4457 BOOST_CHECK_EQUAL( terminalNetName( *schematic, wxS( "U1" ), wxS( "4" ) ), netName );
4458 BOOST_CHECK_EQUAL( terminalNetName( *schematic, wxS( "VOUT_5" ), wxS( "1" ) ), netName );
4459
4460 const IMPORT_NET_MAP* map = schematic->GetImportNetMap();
4461 BOOST_REQUIRE( map );
4462 auto mapped = std::find_if( map->entries.begin(), map->entries.end(),
4463 []( const IMPORT_NET_MAP_ENTRY& entry )
4464 {
4465 return entry.sourceNetId == 16645429
4466 && entry.originalName == wxS( "ANALOG5V" );
4467 } );
4468 BOOST_REQUIRE( mapped != map->entries.end() );
4470 BOOST_CHECK_EQUAL( mapped->nameAtImport, netName );
4471 BOOST_REQUIRE( !mapped->occurrence.empty() );
4472 BOOST_CHECK_EQUAL( mapped->occurrence.back(), wxString( "BRKTSTBCDP5004" ) );
4473}
4474
4475
4476BOOST_AUTO_TEST_CASE( PackageVariantsAndIgnoredPinsArePreserved )
4477{
4478 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
4479
4480 if( !corpusEnv || !*corpusEnv )
4481 {
4482 BOOST_TEST_MESSAGE( "KICAD_ORCAD_CORPUS not set; skipping package-variant check." );
4483 return;
4484 }
4485
4486 auto load = [&]( const char* aName )
4487 {
4488 std::filesystem::path dsn = findCorpusDesign( corpusEnv, aName );
4489 BOOST_REQUIRE_MESSAGE( !dsn.empty(), aName << " not present in corpus." );
4490
4491 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
4492 SETTINGS_MANAGER* manager = new SETTINGS_MANAGER;
4493 manager->LoadProject( "" );
4494 schematic->SetProject( &manager->Prj() );
4495 schematic->CurrentSheet().clear();
4496 schematic->CurrentSheet().push_back( &schematic->Root() );
4497
4498 SCH_IO_ORCAD plugin;
4499 plugin.LoadSchematicFile( dsn.string(), schematic.get() );
4500 return std::pair( std::move( schematic ), std::unique_ptr<SETTINGS_MANAGER>( manager ) );
4501 };
4502
4503 auto [breakout, breakoutManager] = load( "OC_CONNECT_1_BRKOUT_BRD.DSN" );
4504 std::map<wxString, std::set<wxString>> switchPins;
4505
4506 for( const SCH_SHEET_PATH& path : breakout->BuildSheetListSortedByPageNumbers() )
4507 {
4508 for( SCH_ITEM* item : path.LastScreen()->Items().OfType( SCH_SYMBOL_T ) )
4509 {
4510 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
4511 wxString ref = symbol->GetRef( &path, false );
4512
4513 if( ref != wxS( "S1" ) && ref != wxS( "S3" ) )
4514 continue;
4515
4516 for( SCH_PIN* pin : symbol->GetPins( &path ) )
4517 switchPins[ref].insert( pin->GetNumber() );
4518 }
4519 }
4520
4521 const std::set<wxString> expectedS1 = { wxS( "1" ), wxS( "2" ), wxS( "3" ), wxS( "4" ) };
4522 const std::set<wxString> expectedS3 = { wxS( "1" ), wxS( "2" ), wxS( "3" ) };
4523 BOOST_CHECK_EQUAL_COLLECTIONS( switchPins[wxS( "S1" )].begin(), switchPins[wxS( "S1" )].end(), expectedS1.begin(),
4524 expectedS1.end() );
4525 BOOST_CHECK_EQUAL_COLLECTIONS( switchPins[wxS( "S3" )].begin(), switchPins[wxS( "S3" )].end(), expectedS3.begin(),
4526 expectedS3.end() );
4527
4528 auto [j401, j401Manager] = load( "reServer industrial J401 Carrier Board v11.DSN" );
4529 std::set<wxString> j10Pins;
4530
4531 for( const SCH_SHEET_PATH& path : j401->BuildSheetListSortedByPageNumbers() )
4532 {
4533 for( SCH_ITEM* item : path.LastScreen()->Items().OfType( SCH_SYMBOL_T ) )
4534 {
4535 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
4536
4537 if( symbol->GetRef( &path, false ) != wxS( "J10" ) )
4538 continue;
4539
4540 for( SCH_PIN* pin : symbol->GetPins( &path ) )
4541 j10Pins.insert( pin->GetNumber() );
4542 }
4543 }
4544
4545 BOOST_CHECK_EQUAL( j10Pins.size(), 53u );
4546 BOOST_CHECK_EQUAL( j10Pins.count( wxS( "SS1" ) ), 0u );
4547 BOOST_CHECK_EQUAL( j10Pins.count( wxS( "SS2" ) ), 0u );
4548}
4549
4550
4551BOOST_AUTO_TEST_CASE( LegacyDesignCachePackagePinMapsArePreserved )
4552{
4553 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
4554
4555 if( !corpusEnv || !*corpusEnv )
4556 {
4557 BOOST_TEST_MESSAGE( "KICAD_ORCAD_CORPUS not set; skipping legacy package-map check." );
4558 return;
4559 }
4560
4561 std::filesystem::path dsn = findCorpusDesign( corpusEnv, "EXAMPLE.DSN" );
4562
4563 if( dsn.empty() )
4564 {
4565 BOOST_TEST_MESSAGE( "EXAMPLE.DSN not present in corpus; skipping legacy package-map check." );
4566 return;
4567 }
4568
4569 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
4570 SETTINGS_MANAGER manager;
4571 manager.LoadProject( "" );
4572 schematic->SetProject( &manager.Prj() );
4573 schematic->CurrentSheet().clear();
4574 schematic->CurrentSheet().push_back( &schematic->Root() );
4575
4576 SCH_IO_ORCAD plugin;
4577 plugin.LoadSchematicFile( dsn.string(), schematic.get() );
4578
4579 std::set<wxString> pins;
4580
4581 for( const SCH_SHEET_PATH& path : schematic->BuildSheetListSortedByPageNumbers() )
4582 {
4583 for( SCH_ITEM* item : path.LastScreen()->Items().OfType( SCH_SYMBOL_T ) )
4584 {
4585 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
4586
4587 if( symbol->GetRef( &path, false ) != wxS( "U4" ) )
4588 continue;
4589
4590 for( SCH_PIN* pin : symbol->GetPins( &path ) )
4591 pins.insert( pin->GetNumber() );
4592 }
4593 }
4594
4595 const std::set<wxString> expected = { wxS( "2" ), wxS( "3" ), wxS( "4" ), wxS( "5" ), wxS( "12" ) };
4596 BOOST_CHECK_EQUAL_COLLECTIONS( pins.begin(), pins.end(), expected.begin(), expected.end() );
4597}
4598
4599
4600BOOST_AUTO_TEST_CASE( S593487_PartialConnectorPinOrderIsPreserved )
4601{
4602 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
4603
4604 if( !corpusEnv || !*corpusEnv )
4605 {
4606 BOOST_TEST_MESSAGE( "KICAD_ORCAD_CORPUS not set; skipping S-593487 connector check." );
4607 return;
4608 }
4609
4610 std::filesystem::path dsn = findCorpusDesign( corpusEnv, "S-593487-REV-B.DSN" );
4611 BOOST_REQUIRE_MESSAGE( !dsn.empty(), "S-593487-REV-B.DSN not present in corpus." );
4612
4613 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
4614 SETTINGS_MANAGER manager;
4615 manager.LoadProject( "" );
4616 schematic->SetProject( &manager.Prj() );
4617 schematic->CurrentSheet().clear();
4618 schematic->CurrentSheet().push_back( &schematic->Root() );
4619
4620 SCH_IO_ORCAD plugin;
4621 plugin.LoadSchematicFile( dsn.string(), schematic.get() );
4622
4623 auto [consistent, checkable] =
4624 checkConnectivity( *schematic, { { terminalToken( "JCA2", "1" ), terminalToken( "RT1", "1" ) },
4625 { terminalToken( "JCA2", "2" ), terminalToken( "RT2", "1" ) },
4626 { terminalToken( "JCA2", "3" ), terminalToken( "RT3", "1" ) },
4627 { terminalToken( "JCA2", "4" ), terminalToken( "RT4", "1" ) } } );
4628 BOOST_CHECK_EQUAL( checkable, 4 );
4629 BOOST_CHECK_EQUAL( consistent, 4 );
4630}
4631
4632
4633BOOST_AUTO_TEST_CASE( M5275_ExplicitPowerPinsRemainVisible )
4634{
4635 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
4636
4637 if( !corpusEnv || !*corpusEnv )
4638 {
4639 BOOST_TEST_MESSAGE( "KICAD_ORCAD_CORPUS not set; skipping M5275 visible-power-pin check." );
4640 return;
4641 }
4642
4643 std::filesystem::path dsn = findCorpusDesign( corpusEnv, "M5275EVB.DSN" );
4644 BOOST_REQUIRE_MESSAGE( !dsn.empty(), "M5275EVB.DSN not present in corpus." );
4645
4646 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
4647 SETTINGS_MANAGER manager;
4648 manager.LoadProject( "" );
4649 schematic->SetProject( &manager.Prj() );
4650 schematic->CurrentSheet().clear();
4651 schematic->CurrentSheet().push_back( &schematic->Root() );
4652
4653 SCH_IO_ORCAD plugin;
4654 plugin.LoadSchematicFile( dsn.string(), schematic.get() );
4655
4656 std::map<wxString, bool> visible;
4657
4658 for( const SCH_SHEET_PATH& path : schematic->BuildSheetListSortedByPageNumbers() )
4659 {
4660 for( SCH_ITEM* item : path.LastScreen()->Items().OfType( SCH_SYMBOL_T ) )
4661 {
4662 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
4663
4664 if( symbol->GetRef( &path, false ) != wxS( "U21" ) )
4665 continue;
4666
4667 for( SCH_PIN* pin : symbol->GetPins( &path ) )
4668 visible[pin->GetNumber()] = pin->IsVisible();
4669 }
4670 }
4671
4672 BOOST_REQUIRE_EQUAL( visible.size(), 6u );
4673 BOOST_CHECK( visible[wxS( "2" )] );
4674 BOOST_CHECK( visible[wxS( "5" )] );
4675}
4676
4677
4678BOOST_AUTO_TEST_CASE( SI34062_StackedSwitchPinsShareNet )
4679{
4680 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
4681
4682 if( !corpusEnv || !*corpusEnv )
4683 {
4684 BOOST_TEST_MESSAGE( "KICAD_ORCAD_CORPUS not set; skipping SI34062 stacked-pin check." );
4685 return;
4686 }
4687
4688 std::filesystem::path dsn = findCorpusDesign( corpusEnv, "SI34062-ISO-FB-EVB.DSN" );
4689 BOOST_REQUIRE_MESSAGE( !dsn.empty(), "SI34062-ISO-FB-EVB.DSN not present in corpus." );
4690
4691 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
4692 SETTINGS_MANAGER manager;
4693 manager.LoadProject( "" );
4694 schematic->SetProject( &manager.Prj() );
4695 schematic->CurrentSheet().clear();
4696 schematic->CurrentSheet().push_back( &schematic->Root() );
4697
4698 SCH_IO_ORCAD plugin;
4699 plugin.LoadSchematicFile( dsn.string(), schematic.get() );
4700
4701 auto [consistent, checkable] = checkConnectivity(
4702 *schematic, { { terminalToken( "S2", "1" ), terminalToken( "S2", "2" ), terminalToken( "U5", "K" ) } } );
4703 BOOST_CHECK_EQUAL( checkable, 1 );
4704 BOOST_CHECK_EQUAL( consistent, 1 );
4705}
4706
4707
4708BOOST_AUTO_TEST_CASE( DuplicatePowerAliasTextDoesNotMergeDistinctNets )
4709{
4710 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
4711
4712 if( !corpusEnv || !*corpusEnv )
4713 {
4714 BOOST_TEST_MESSAGE( "KICAD_ORCAD_CORPUS not set; skipping duplicate power-alias check." );
4715 return;
4716 }
4717
4718 std::filesystem::path dsn = findCorpusDesign( corpusEnv, "SI8284v2-EVB.DSN" );
4719 BOOST_REQUIRE_MESSAGE( !dsn.empty(), "SI8284v2-EVB.DSN not present in corpus." );
4720
4721 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
4722 SETTINGS_MANAGER manager;
4723 manager.LoadProject( "" );
4724 schematic->SetProject( &manager.Prj() );
4725 schematic->CurrentSheet().clear();
4726 schematic->CurrentSheet().push_back( &schematic->Root() );
4727
4728 SCH_IO_ORCAD plugin;
4729 plugin.LoadSchematicFile( dsn.string(), schematic.get() );
4730
4731 auto [consistent, checkable] =
4732 checkConnectivity( *schematic, { { terminalToken( "C12", "1" ), terminalToken( "U2", "20" ) },
4733 { terminalToken( "C36", "1" ), terminalToken( "Q4", "C" ) } } );
4734 BOOST_CHECK_EQUAL( checkable, 2 );
4735 BOOST_CHECK_EQUAL( consistent, 2 );
4736}
4737
4738
4739BOOST_AUTO_TEST_CASE( LogicalPowerPinNameDoesNotOverrideConnectedWire )
4740{
4741 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
4742
4743 if( !corpusEnv || !*corpusEnv )
4744 {
4745 BOOST_TEST_MESSAGE( "KICAD_ORCAD_CORPUS not set; skipping logical power-pin check." );
4746 return;
4747 }
4748
4749 std::filesystem::path dsn = findCorpusDesign( corpusEnv, "CY8CKIT-041-41XX Schematic.DSN" );
4750 BOOST_REQUIRE_MESSAGE( !dsn.empty(), "CY8CKIT-041-41XX Schematic.DSN not present in corpus." );
4751
4752 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
4753 SETTINGS_MANAGER manager;
4754 manager.LoadProject( "" );
4755 schematic->SetProject( &manager.Prj() );
4756 schematic->CurrentSheet().clear();
4757 schematic->CurrentSheet().push_back( &schematic->Root() );
4758
4759 SCH_IO_ORCAD plugin;
4760 plugin.LoadSchematicFile( dsn.string(), schematic.get() );
4761
4762 bool checkedHeaderPin = false;
4763
4764 for( const SCH_SHEET_PATH& path : schematic->Hierarchy() )
4765 {
4766 for( SCH_ITEM* item : path.LastScreen()->Items().OfType( SCH_SYMBOL_T ) )
4767 {
4768 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
4769
4770 if( symbol->GetRef( &path, false ) != wxS( "J8" )
4771 || !path.Last()->GetName().Contains( wxS( "PSoC 5LP Programmer" ) ) )
4772 {
4773 continue;
4774 }
4775
4777 {
4778 if( pin->GetNumber() == wxS( "3" ) )
4779 {
4780 BOOST_CHECK( pin->GetPosition() == OrcadDbuToIu( 1192, 1006 ) );
4781 checkedHeaderPin = true;
4782 }
4783 }
4784 }
4785 }
4786
4787 BOOST_REQUIRE( checkedHeaderPin );
4788
4789 auto [consistent, checkable] =
4790 checkConnectivity( *schematic, { { terminalToken( "L4", "2" ), terminalToken( "U1", "40" ) },
4791 { terminalToken( "C59", "1" ), terminalToken( "U15", "44" ) },
4792 { terminalToken( "J8", "3" ), terminalToken( "U15", "28" ),
4793 terminalToken( "R40", "1" ) } } );
4794 BOOST_CHECK_EQUAL( checkable, 3 );
4795 BOOST_CHECK_EQUAL( consistent, 3 );
4796}
4797
4798
4799BOOST_AUTO_TEST_CASE( PinNameOverbarsUseKiCadMarkup )
4800{
4801 BOOST_CHECK_EQUAL( OrcadPinNameMarkup( wxS( "\\\\C\\S\\ ADD1" ) ), wxS( "~{CS} ADD1" ) );
4802 BOOST_CHECK_EQUAL( OrcadPinNameMarkup( wxS( "\\\\I\\N\\T\\" ) ), wxS( "~{INT}" ) );
4803 BOOST_CHECK_EQUAL( OrcadPinNameMarkup( wxS( "READY" ) ), wxS( "READY" ) );
4804}
4805
4806
4807BOOST_AUTO_TEST_CASE( RenamedPowerNetsRemainElectricallyDistinct )
4808{
4809 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
4810
4811 if( !corpusEnv || !*corpusEnv )
4812 return;
4813
4814 std::filesystem::path dsn = findCorpusDesign( corpusEnv, "CY4605_Schematic.dsn" );
4815
4816 if( dsn.empty() )
4817 return;
4818
4819 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
4820 SETTINGS_MANAGER manager;
4821 manager.LoadProject( "" );
4822 schematic->SetProject( &manager.Prj() );
4823 schematic->CurrentSheet().clear();
4824 schematic->CurrentSheet().push_back( &schematic->Root() );
4825
4826 SCH_IO_ORCAD plugin;
4827 plugin.LoadSchematicFile( dsn.string(), schematic.get() );
4828
4829 std::vector<std::set<std::string>> expected = { { terminalToken( "C16", "1" ), terminalToken( "TP4", "1" ) },
4830 { terminalToken( "C14", "1" ), terminalToken( "TP2", "1" ) } };
4831 auto [consistent, checkable] = checkConnectivity( *schematic, expected );
4832 BOOST_CHECK_EQUAL( checkable, 2 );
4833 BOOST_CHECK_EQUAL( consistent, 2 );
4834}
4835
4836
4837BOOST_AUTO_TEST_CASE( MinorityPowerOccurrenceAliasDoesNotMergeGlobalNets )
4838{
4839 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
4840
4841 if( !corpusEnv || !*corpusEnv )
4842 return;
4843
4844 std::filesystem::path dsn = findCorpusDesign( corpusEnv, "CYW920706WCDEVAL Evaluation Kit Schematics.DSN" );
4845
4846 if( dsn.empty() )
4847 return;
4848
4849 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
4850 SETTINGS_MANAGER manager;
4851 manager.LoadProject( "" );
4852 schematic->SetProject( &manager.Prj() );
4853 schematic->CurrentSheet().clear();
4854 schematic->CurrentSheet().push_back( &schematic->Root() );
4855
4856 SCH_IO_ORCAD plugin;
4857 plugin.LoadSchematicFile( dsn.string(), schematic.get() );
4858
4859 std::vector<std::set<std::string>> expected = { { terminalToken( "C1", "2" ), terminalToken( "C10", "2" ) },
4860 { terminalToken( "C26", "2" ), terminalToken( "C27", "2" ) } };
4861 auto [consistent, checkable] = checkConnectivity( *schematic, expected );
4862 BOOST_CHECK_EQUAL( checkable, 2 );
4863 BOOST_CHECK_EQUAL( consistent, 2 );
4864}
4865
4866
4867BOOST_AUTO_TEST_CASE( OccurrenceSuffixedPowerNetsRemainDistinct )
4868{
4869 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
4870
4871 if( !corpusEnv || !*corpusEnv )
4872 return;
4873
4874 std::filesystem::path dsn = findCorpusDesign( corpusEnv, "DC2263A-2.DSN" );
4875
4876 if( dsn.empty() )
4877 return;
4878
4879 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
4880 SETTINGS_MANAGER manager;
4881 manager.LoadProject( "" );
4882 schematic->SetProject( &manager.Prj() );
4883 schematic->CurrentSheet().clear();
4884 schematic->CurrentSheet().push_back( &schematic->Root() );
4885
4886 SCH_IO_ORCAD plugin;
4887 plugin.LoadSchematicFile( dsn.string(), schematic.get() );
4888
4889 std::vector<std::set<std::string>> expected = { { terminalToken( "C13", "1" ), terminalToken( "C18", "1" ) },
4890 { terminalToken( "C40", "1" ), terminalToken( "R61", "1" ) } };
4891 auto [consistent, checkable] = checkConnectivity( *schematic, expected );
4892 BOOST_CHECK_EQUAL( checkable, 2 );
4893 BOOST_CHECK_EQUAL( consistent, 2 );
4894}
4895
4896
4897BOOST_AUTO_TEST_CASE( GeneratedOccurrenceNetNameRemainsDistinct )
4898{
4899 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
4900
4901 if( !corpusEnv || !*corpusEnv )
4902 return;
4903
4904 std::filesystem::path dsn = findCorpusDesign( corpusEnv, "OC_CONNECT-1_BB_BOARD_20072023_01.DSN" );
4905
4906 if( dsn.empty() )
4907 return;
4908
4909 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
4910 SETTINGS_MANAGER manager;
4911 manager.LoadProject( "" );
4912 schematic->SetProject( &manager.Prj() );
4913 schematic->CurrentSheet().clear();
4914 schematic->CurrentSheet().push_back( &schematic->Root() );
4915
4916 SCH_IO_ORCAD plugin;
4917 plugin.LoadSchematicFile( dsn.string(), schematic.get() );
4918
4919 std::vector<std::set<std::string>> expected = { { terminalToken( "C1", "2" ), terminalToken( "C10", "2" ) },
4920 { terminalToken( "LED14", "1" ), terminalToken( "R304", "1" ) } };
4921 auto [consistent, checkable] = checkConnectivity( *schematic, expected );
4922 BOOST_CHECK_EQUAL( checkable, 2 );
4923 BOOST_CHECK_EQUAL( consistent, 2 );
4924}
4925
4926
4927BOOST_AUTO_TEST_CASE( PackageVariantPreservesPhysicalPinMap )
4928{
4929 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
4930
4931 if( !corpusEnv || !*corpusEnv )
4932 return;
4933
4934 std::filesystem::path dsn = findCorpusDesign( corpusEnv, "710-DC2693A_REV02_PCA_SCHEMATIC.DSN" );
4935
4936 if( dsn.empty() )
4937 return;
4938
4939 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
4940 SETTINGS_MANAGER manager;
4941 manager.LoadProject( "" );
4942 schematic->SetProject( &manager.Prj() );
4943 schematic->CurrentSheet().clear();
4944 schematic->CurrentSheet().push_back( &schematic->Root() );
4945
4946 SCH_IO_ORCAD plugin;
4947 plugin.LoadSchematicFile( dsn.string(), schematic.get() );
4948
4949 std::vector<std::set<std::string>> expected = { { terminalToken( "C101", "1" ), terminalToken( "U101", "6" ) },
4950 { terminalToken( "R105", "2" ), terminalToken( "U101", "3" ) },
4951 { terminalToken( "R106", "1" ), terminalToken( "U101", "4" ) } };
4952 auto [consistent, checkable] = checkConnectivity( *schematic, expected );
4953 BOOST_CHECK_EQUAL( checkable, 3 );
4954 BOOST_CHECK_EQUAL( consistent, 3 );
4955}
4956
4957
4958BOOST_AUTO_TEST_CASE( DistinctPowerAndOffpageInterfaceNamesRemainDistinct )
4959{
4960 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
4961
4962 if( !corpusEnv || !*corpusEnv )
4963 return;
4964
4965 std::filesystem::path dsn = findCorpusDesign( corpusEnv, "OpenCellular_Connect-1_GBC_Life-3_Schematic_v1.2.DSN" );
4966
4967 if( dsn.empty() )
4968 return;
4969
4970 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
4971 SETTINGS_MANAGER manager;
4972 manager.LoadProject( "" );
4973 schematic->SetProject( &manager.Prj() );
4974 schematic->CurrentSheet().clear();
4975 schematic->CurrentSheet().push_back( &schematic->Root() );
4976
4977 SCH_IO_ORCAD plugin;
4978 plugin.LoadSchematicFile( dsn.string(), schematic.get() );
4979
4980 std::vector<std::set<std::string>> expected = { { terminalToken( "C1559", "2" ), terminalToken( "D8", "2" ) },
4981 { terminalToken( "L28", "3" ), terminalToken( "R1009", "1" ) } };
4982 auto [consistent, checkable] = checkConnectivity( *schematic, expected );
4983 BOOST_CHECK_EQUAL( checkable, 2 );
4984 BOOST_CHECK_EQUAL( consistent, 2 );
4985}
4986
4987
4988BOOST_AUTO_TEST_CASE( FlatTopLevelOffpageConnectsAcrossPages )
4989{
4990 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
4991
4992 if( !corpusEnv || !*corpusEnv )
4993 return;
4994
4995 std::filesystem::path dsn = findCorpusDesign( corpusEnv, "DC1931B.DSN" );
4996
4997 if( dsn.empty() )
4998 return;
4999
5000 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
5001 SETTINGS_MANAGER manager;
5002 manager.LoadProject( "" );
5003 schematic->SetProject( &manager.Prj() );
5004 schematic->CurrentSheet().clear();
5005 schematic->CurrentSheet().push_back( &schematic->Root() );
5006
5007 SCH_IO_ORCAD plugin;
5008 plugin.LoadSchematicFile( dsn.string(), schematic.get() );
5009
5010 std::vector<std::set<std::string>> expected = { { terminalToken( "J4", "H26" ), terminalToken( "U2", "N1" ) } };
5011 auto [consistent, checkable] = checkConnectivity( *schematic, expected );
5012 BOOST_CHECK_EQUAL( checkable, 1 );
5013 BOOST_CHECK_EQUAL( consistent, 1 );
5014}
5015
5016
5017BOOST_AUTO_TEST_CASE( PowerAliasConnectsAcrossPages )
5018{
5019 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
5020
5021 if( !corpusEnv || !*corpusEnv )
5022 return;
5023
5024 std::filesystem::path dsn = findCorpusDesign( corpusEnv, "710-DC2222A_REV07_PCA_SCHEMATIC.DSN" );
5025
5026 if( dsn.empty() )
5027 return;
5028
5029 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
5030 SETTINGS_MANAGER manager;
5031 manager.LoadProject( "" );
5032 schematic->SetProject( &manager.Prj() );
5033 schematic->CurrentSheet().clear();
5034 schematic->CurrentSheet().push_back( &schematic->Root() );
5035
5036 SCH_IO_ORCAD plugin;
5037 plugin.LoadSchematicFile( dsn.string(), schematic.get() );
5038
5039 std::vector<std::set<std::string>> expected = { { terminalToken( "C10", "2" ), terminalToken( "C29", "1" ),
5040 terminalToken( "C30", "1" ), terminalToken( "C9", "2" ),
5041 terminalToken( "E2", "1" ), terminalToken( "R19", "1" ),
5042 terminalToken( "U1", "3" ), terminalToken( "U14", "1" ) } };
5043 auto [consistent, checkable] = checkConnectivity( *schematic, expected );
5044 BOOST_CHECK_EQUAL( checkable, 1 );
5045 BOOST_CHECK_EQUAL( consistent, 1 );
5046}
5047
5048
5049BOOST_AUTO_TEST_CASE( PhysicalConnectorPinsUseDefinitionNumbers )
5050{
5051 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
5052
5053 if( !corpusEnv || !*corpusEnv )
5054 return;
5055
5056 std::filesystem::path dsn = findCorpusDesign( corpusEnv, "DC2382A-1.DSN" );
5057
5058 if( dsn.empty() )
5059 return;
5060
5061 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
5062 SETTINGS_MANAGER manager;
5063 manager.LoadProject( "" );
5064 schematic->SetProject( &manager.Prj() );
5065 schematic->CurrentSheet().clear();
5066 schematic->CurrentSheet().push_back( &schematic->Root() );
5067
5068 SCH_IO_ORCAD plugin;
5069 plugin.LoadSchematicFile( dsn.string(), schematic.get() );
5070
5071 std::set<wxString> pinNumbers;
5072
5073 for( const SCH_SHEET_PATH& path : schematic->BuildSheetListSortedByPageNumbers() )
5074 {
5075 for( SCH_ITEM* item : path.LastScreen()->Items().OfType( SCH_SYMBOL_T ) )
5076 {
5077 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
5078
5079 if( symbol->GetRef( &path, false ) != wxS( "J3" ) )
5080 continue;
5081
5082 for( SCH_PIN* pin : symbol->GetPins( &path ) )
5083 pinNumbers.insert( pin->GetNumber() );
5084 }
5085 }
5086
5087 std::set<wxString> expected;
5088
5089 for( int pin = 1; pin <= 20; ++pin )
5090 expected.insert( wxString::Format( wxS( "%d" ), pin ) );
5091
5092 BOOST_CHECK_EQUAL_COLLECTIONS( pinNumbers.begin(), pinNumbers.end(), expected.begin(), expected.end() );
5093
5094 std::vector<std::set<std::string>> expectedNets = { { terminalToken( "J3", "1" ), terminalToken( "Q1", "3" ) },
5095 { terminalToken( "J3", "2" ), terminalToken( "J1", "2" ) },
5096 { terminalToken( "J3", "3" ), terminalToken( "J1", "3" ) },
5097 { terminalToken( "J3", "10" ), terminalToken( "J1", "10" ) },
5098 { terminalToken( "J3", "13" ), terminalToken( "R19", "1" ) } };
5099 auto [consistent, checkable] = checkConnectivity( *schematic, expectedNets );
5100 BOOST_CHECK_EQUAL( checkable, 5 );
5101 BOOST_CHECK_EQUAL( consistent, 5 );
5102}
5103
5104
5105BOOST_AUTO_TEST_CASE( ModernPackageStreamsSupplyEmbeddedSymbolGeometry )
5106{
5107 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
5108
5109 if( !corpusEnv || !*corpusEnv )
5110 return;
5111
5112 std::filesystem::path dsn = findCorpusDesign( corpusEnv, "DC2382A-1.DSN" );
5113
5114 if( dsn.empty() )
5115 return;
5116
5117 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
5118 SETTINGS_MANAGER manager;
5119 manager.LoadProject( "" );
5120 schematic->SetProject( &manager.Prj() );
5121 schematic->CurrentSheet().clear();
5122 schematic->CurrentSheet().push_back( &schematic->Root() );
5123
5124 SCH_IO_ORCAD plugin;
5125 plugin.LoadSchematicFile( dsn.string(), schematic.get() );
5126
5127 SCH_SYMBOL* jumper = nullptr;
5128
5129 for( const SCH_SHEET_PATH& path : schematic->BuildSheetListSortedByPageNumbers() )
5130 {
5131 for( SCH_ITEM* item : path.LastScreen()->Items().OfType( SCH_SYMBOL_T ) )
5132 {
5133 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
5134
5135 if( symbol->GetRef( &path, false ) == wxS( "JP4" ) )
5136 jumper = symbol;
5137 }
5138 }
5139
5140 BOOST_REQUIRE( jumper );
5141 BOOST_REQUIRE( jumper->GetLibSymbolRef() );
5142
5143 int bodyRectangles = 0;
5144
5145 for( const SCH_ITEM& item : jumper->GetLibSymbolRef()->GetDrawItems() )
5146 {
5147 if( item.Type() == SCH_SHAPE_T && static_cast<const SCH_SHAPE&>( item ).GetShape() == SHAPE_T::RECTANGLE )
5148 ++bodyRectangles;
5149 }
5150
5151 BOOST_CHECK_EQUAL( bodyRectangles, 3 );
5152}
5153
5154
5155BOOST_AUTO_TEST_CASE( LegacyDisplayTypesRemainVisible )
5156{
5157 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
5158
5159 if( !corpusEnv || !*corpusEnv )
5160 return;
5161
5162 std::filesystem::path dsn = findCorpusDesign( corpusEnv, "X375D_VER72.DSN" );
5163
5164 if( dsn.empty() )
5165 return;
5166
5167 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
5168 SETTINGS_MANAGER manager;
5169 manager.LoadProject( "" );
5170 schematic->SetProject( &manager.Prj() );
5171 schematic->CurrentSheet().clear();
5172 schematic->CurrentSheet().push_back( &schematic->Root() );
5173
5174 SCH_IO_ORCAD plugin;
5175 plugin.LoadSchematicFile( dsn.string(), schematic.get() );
5176
5177 int checked = 0;
5178
5179 for( const SCH_SHEET_PATH& path : schematic->BuildSheetListSortedByPageNumbers() )
5180 {
5181 for( SCH_ITEM* item : path.LastScreen()->Items().OfType( SCH_SYMBOL_T ) )
5182 {
5183 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
5184
5185 if( symbol->GetRef( &path, false ) != wxS( "CLKOUT0" ) )
5186 continue;
5187
5188 BOOST_CHECK( symbol->GetField( FIELD_T::REFERENCE )->IsVisible() );
5189 ++checked;
5190 }
5191 }
5192
5193 BOOST_CHECK_EQUAL( checked, 1 );
5194}
5195
5196
5197BOOST_AUTO_TEST_CASE( UnreferencedPagesDoNotOverwriteHierarchicalSheets )
5198{
5199 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
5200
5201 if( !corpusEnv || !*corpusEnv )
5202 return;
5203
5204 std::filesystem::path dsn = findCorpusDesign( corpusEnv, "SI8281V2-EVB.DSN" );
5205
5206 if( dsn.empty() )
5207 return;
5208
5209 m_plugin.LoadSchematicFile( dsn.string(), m_schematic.get() );
5210 std::map<wxString, SCH_SCREEN*> screensByFile;
5211 size_t connectorPins = 0;
5212
5213 for( const SCH_SHEET_PATH& path : m_schematic->BuildSheetListSortedByPageNumbers() )
5214 {
5215 SCH_SCREEN* screen = path.LastScreen();
5216 auto [entry, inserted] = screensByFile.emplace( screen->GetFileName().Lower(), screen );
5217 BOOST_CHECK_MESSAGE( inserted || entry->second == screen,
5218 "Distinct sheets share output file " << screen->GetFileName() );
5219
5220 for( SCH_ITEM* item : screen->Items().OfType( SCH_SYMBOL_T ) )
5221 {
5222 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
5223
5224 if( symbol->GetRef( &path, false ) == wxS( "J24" ) )
5225 connectorPins += symbol->GetGraphicalPins( ALL_UNITS, ALL_BODY_STYLES ).size();
5226 }
5227 }
5228
5229 BOOST_CHECK_EQUAL( connectorPins, 16 );
5230}
5231
5232
5233BOOST_AUTO_TEST_CASE( HierarchicalPortsUseVisibleNativeLabels )
5234{
5235 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
5236
5237 if( !corpusEnv || !*corpusEnv )
5238 return;
5239
5240 std::filesystem::path dsn = findCorpusDesign( corpusEnv, "M5275EVB.DSN" );
5241
5242 if( dsn.empty() )
5243 return;
5244
5245 m_plugin.LoadSchematicFile( dsn.string(), m_schematic.get() );
5246 size_t ports = 0;
5247
5248 for( const SCH_SHEET_PATH& path : m_schematic->BuildSheetListSortedByPageNumbers() )
5249 {
5250 for( SCH_ITEM* item : path.LastScreen()->Items().OfType( SCH_HIER_LABEL_T ) )
5251 {
5252 SCH_HIERLABEL* label = static_cast<SCH_HIERLABEL*>( item );
5253 BOOST_CHECK_MESSAGE( label->GetTextColor() == KIGFX::COLOR4D::UNSPECIFIED
5254 || label->GetTextColor().a > 0,
5255 "Hidden hierarchical port " << label->GetText() );
5256 ++ports;
5257 }
5258 }
5259
5260 BOOST_CHECK_GT( ports, 0 );
5261}
5262
5263
5264BOOST_AUTO_TEST_CASE( LegacyHierarchicalBlockImport )
5265{
5266 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
5267
5268 if( !corpusEnv || !*corpusEnv )
5269 {
5270 BOOST_TEST_MESSAGE( "KICAD_ORCAD_CORPUS not set; skipping legacy hierarchy check." );
5271 return;
5272 }
5273
5274 std::filesystem::path dsn = findCorpusDesign( corpusEnv, "SCH-20380.DSN" );
5275
5276 if( dsn.empty() )
5277 {
5278 BOOST_TEST_MESSAGE( "SCH-20380.DSN not present in corpus; skipping legacy hierarchy check." );
5279 return;
5280 }
5281
5282 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
5283 SETTINGS_MANAGER manager;
5284 manager.LoadProject( "" );
5285 schematic->SetProject( &manager.Prj() );
5286 schematic->CurrentSheet().clear();
5287 schematic->CurrentSheet().push_back( &schematic->Root() );
5288
5289 SCH_IO_ORCAD plugin;
5290 plugin.LoadSchematicFile( dsn.string(), schematic.get() );
5291 schematic->CurrentSheet().UpdateAllScreenReferences();
5292
5293 size_t pages = 0;
5294 size_t components = 0;
5295
5296 for( const SCH_SHEET_PATH& path : schematic->BuildSheetListSortedByPageNumbers() )
5297 {
5298 SCH_SCREEN* screen = path.LastScreen();
5299
5300 if( !screen )
5301 continue;
5302
5303 ++pages;
5304
5305 for( SCH_ITEM* item : screen->Items() )
5306 {
5307 if( item->Type() == SCH_SYMBOL_T
5308 && !static_cast<SCH_SYMBOL*>( item )->GetRef( &path, false ).StartsWith( wxS( "#" ) ) )
5309 {
5310 ++components;
5311 }
5312
5313 }
5314 }
5315
5316 BOOST_CHECK_EQUAL( pages, 16u );
5317 BOOST_CHECK_EQUAL( components, 491u );
5318 auto [consistent, checkable] = checkConnectivity(
5319 *schematic, { { "J7.54", "RP17.3", "U10.C13" },
5320 { "J10.35", "J12.9", "RP15.7", "U10.N10" } } );
5321 BOOST_CHECK_EQUAL( checkable, 2 );
5322 BOOST_CHECK_EQUAL( consistent, 2 );
5323}
5324
5325
5326BOOST_AUTO_TEST_CASE( LegacyDsnDiodePinsUseLogicalPolarity )
5327{
5328 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
5329
5330 if( !corpusEnv || !*corpusEnv )
5331 return;
5332
5333 std::filesystem::path dsn = findCorpusDesign( corpusEnv, "SCH-20380.DSN" );
5334
5335 if( dsn.empty() )
5336 return;
5337
5338 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
5339 SETTINGS_MANAGER manager;
5340 manager.LoadProject( "" );
5341 schematic->SetProject( &manager.Prj() );
5342 schematic->CurrentSheet().clear();
5343 schematic->CurrentSheet().push_back( &schematic->Root() );
5344
5345 SCH_IO_ORCAD plugin;
5346 plugin.LoadSchematicFile( dsn.string(), schematic.get() );
5347
5348 const std::vector<std::set<std::string>> expected = {
5349 { terminalToken( "D1", "2" ), terminalToken( "C37", "1" ) },
5350 { terminalToken( "D1", "1" ), terminalToken( "R15", "1" ) }
5351 };
5352 auto [consistent, checkable] = checkConnectivity( *schematic, expected );
5353 BOOST_CHECK_EQUAL( checkable, expected.size() );
5354 BOOST_CHECK_EQUAL( consistent, expected.size() );
5355}
5356
5357
5358BOOST_AUTO_TEST_CASE( LegacyDsnEmbeddedSlashNetNameIsAuthoritative )
5359{
5360 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
5361
5362 if( !corpusEnv || !*corpusEnv )
5363 return;
5364
5365 std::filesystem::path dsn = findCorpusDesign( corpusEnv, "SCH-20380.DSN" );
5366
5367 if( dsn.empty() )
5368 return;
5369
5370 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
5371 SETTINGS_MANAGER manager;
5372 manager.LoadProject( "" );
5373 schematic->SetProject( &manager.Prj() );
5374 schematic->CurrentSheet().clear();
5375 schematic->CurrentSheet().push_back( &schematic->Root() );
5376
5377 SCH_IO_ORCAD plugin;
5378 plugin.LoadSchematicFile( dsn.string(), schematic.get() );
5379
5380 SCH_SHEET_LIST sheets = schematic->BuildSheetListSortedByPageNumbers();
5381 schematic->ConnectionGraph()->Recalculate( sheets, true );
5382 wxString j1Pin7Net;
5383
5384 for( const auto& [key, subgraphs] : schematic->ConnectionGraph()->GetNetMap() )
5385 {
5386 for( CONNECTION_SUBGRAPH* subgraph : subgraphs )
5387 {
5388 for( SCH_ITEM* item : subgraph->GetItems() )
5389 {
5390 if( item->Type() != SCH_PIN_T )
5391 continue;
5392
5393 SCH_PIN* pin = static_cast<SCH_PIN*>( item );
5394 SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( pin->GetParentSymbol() );
5395
5396 if( symbol && symbol->GetRef( &subgraph->GetSheet(), false ) == wxS( "J1" )
5397 && pin->GetNumber() == wxS( "7" ) )
5398 {
5399 j1Pin7Net = key.Name;
5400 }
5401 }
5402 }
5403 }
5404
5405 BOOST_CHECK( !j1Pin7Net.IsEmpty() );
5406 BOOST_CHECK_EQUAL( terminalNetName( *schematic, wxS( "RP13" ), wxS( "7" ) ), j1Pin7Net );
5407 BOOST_CHECK_EQUAL( terminalNetName( *schematic, wxS( "U26" ), wxS( "B" ) ), j1Pin7Net );
5408
5409 const IMPORT_NET_MAP* map = schematic->GetImportNetMap();
5410 BOOST_REQUIRE( map );
5411 auto mapped = std::find_if( map->entries.begin(), map->entries.end(),
5412 []( const IMPORT_NET_MAP_ENTRY& entry )
5413 {
5414 return entry.sourceNetId == 3221698
5415 && entry.originalName == wxS( "BDM_/RSTIN" );
5416 } );
5417 BOOST_REQUIRE( mapped != map->entries.end() );
5419 BOOST_CHECK_EQUAL( mapped->nameAtImport, j1Pin7Net );
5420
5421 bool sourcePort = false;
5422
5423 for( const SCH_SHEET_PATH& sheet : sheets )
5424 {
5425 for( SCH_ITEM* item : sheet.LastScreen()->Items().OfType( SCH_HIER_LABEL_T ) )
5426 sourcePort |= static_cast<SCH_HIERLABEL*>( item )->GetText() == wxS( "BDM_/RSTIN" );
5427 }
5428
5429 BOOST_CHECK( sourcePort );
5430}
5431
5432
5433BOOST_AUTO_TEST_CASE( UninstantiatedLegacyPageIsExcludedFromBoard )
5434{
5435 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
5436
5437 if( !corpusEnv || !*corpusEnv )
5438 return;
5439
5440 std::filesystem::path dsn = findCorpusDesign( corpusEnv, "SCH-21095.DSN" );
5441
5442 if( dsn.empty() )
5443 return;
5444
5445 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
5446 SETTINGS_MANAGER manager;
5447 manager.LoadProject( "" );
5448 schematic->SetProject( &manager.Prj() );
5449 schematic->CurrentSheet().clear();
5450 schematic->CurrentSheet().push_back( &schematic->Root() );
5451
5452 SCH_IO_ORCAD plugin;
5453 plugin.LoadSchematicFile( dsn.string(), schematic.get() );
5454 schematic->CurrentSheet().UpdateAllScreenReferences();
5455
5456 SCH_SHEET* mram = nullptr;
5457 SCH_SHEET* reset = nullptr;
5458
5459 for( const SCH_SHEET_PATH& path : schematic->BuildSheetListSortedByPageNumbers() )
5460 {
5461 SCH_SHEET* sheet = path.Last();
5462
5463 if( path.LastScreen()->GetFileName().Upper().Contains( wxS( "MRAM" ) ) )
5464 mram = sheet;
5465 else if( path.LastScreen()->GetFileName().Upper().Contains( wxS( "RESET" ) ) )
5466 reset = sheet;
5467 }
5468
5469 BOOST_REQUIRE( mram );
5470 BOOST_REQUIRE( reset );
5471 BOOST_CHECK( mram->GetExcludedFromBoard() );
5472 BOOST_CHECK( !reset->GetExcludedFromBoard() );
5473}
5474
5475
5476BOOST_AUTO_TEST_CASE( LegacyFlatPageImport )
5477{
5478 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
5479
5480 if( !corpusEnv || !*corpusEnv )
5481 {
5482 BOOST_TEST_MESSAGE( "KICAD_ORCAD_CORPUS not set; skipping legacy flat-page check." );
5483 return;
5484 }
5485
5486 std::filesystem::path dsn = findCorpusDesign( corpusEnv, "X375D_VER72.DSN" );
5487
5488 if( dsn.empty() )
5489 {
5490 BOOST_TEST_MESSAGE( "X375D_VER72.DSN not present in corpus; skipping legacy flat-page check." );
5491 return;
5492 }
5493
5494 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
5495 SETTINGS_MANAGER manager;
5496 manager.LoadProject( "" );
5497 schematic->SetProject( &manager.Prj() );
5498 schematic->CurrentSheet().clear();
5499 schematic->CurrentSheet().push_back( &schematic->Root() );
5500
5501 SCH_IO_ORCAD plugin;
5503 plugin.SetReporter( &reporter );
5504
5505 try
5506 {
5507 plugin.LoadSchematicFile( dsn.string(), schematic.get() );
5508 }
5509 catch( const std::exception& e )
5510 {
5511 BOOST_FAIL( e.what() << "\n" << reporter.GetMessages() );
5512 return;
5513 }
5514
5515 size_t pages = 0;
5516 size_t components = 0;
5517
5518 for( const SCH_SHEET_PATH& path : schematic->BuildSheetListSortedByPageNumbers() )
5519 {
5520 SCH_SCREEN* screen = path.LastScreen();
5521
5522 if( !screen )
5523 continue;
5524
5525 ++pages;
5526
5527 for( SCH_ITEM* item : screen->Items() )
5528 {
5529 if( item->Type() == SCH_SYMBOL_T
5530 && !static_cast<SCH_SYMBOL*>( item )->GetRef( &path, false ).StartsWith( wxS( "#" ) ) )
5531 {
5532 ++components;
5533 }
5534
5535 }
5536 }
5537
5538 BOOST_CHECK_EQUAL( pages, 1u );
5539 BOOST_CHECK_EQUAL( components, 256u );
5540}
5541
5542
5543BOOST_AUTO_TEST_CASE( LegacyHierarchyPowerTableImport )
5544{
5545 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
5546
5547 if( !corpusEnv || !*corpusEnv )
5548 {
5549 BOOST_TEST_MESSAGE( "KICAD_ORCAD_CORPUS not set; skipping legacy power-table check." );
5550 return;
5551 }
5552
5553 std::filesystem::path dsn = findCorpusDesign( corpusEnv, "DC1414B.DSN" );
5554
5555 if( dsn.empty() )
5556 {
5557 BOOST_TEST_MESSAGE( "DC1414B.DSN not present in corpus; skipping legacy power-table check." );
5558 return;
5559 }
5560
5561 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
5562 SETTINGS_MANAGER manager;
5563 manager.LoadProject( "" );
5564 schematic->SetProject( &manager.Prj() );
5565 schematic->CurrentSheet().clear();
5566 schematic->CurrentSheet().push_back( &schematic->Root() );
5567
5568 SCH_IO_ORCAD plugin;
5569 plugin.LoadSchematicFile( dsn.string(), schematic.get() );
5570 schematic->CurrentSheet().UpdateAllScreenReferences();
5571
5572 size_t pages = 0;
5573 size_t components = 0;
5574
5575 for( const SCH_SHEET_PATH& path : schematic->BuildSheetListSortedByPageNumbers() )
5576 {
5577 SCH_SCREEN* screen = path.LastScreen();
5578
5579 if( !screen )
5580 continue;
5581
5582 ++pages;
5583
5584 for( SCH_ITEM* item : screen->Items() )
5585 {
5586 if( item->Type() == SCH_SYMBOL_T
5587 && !static_cast<SCH_SYMBOL*>( item )->GetRef( &path, false ).StartsWith( wxS( "#" ) ) )
5588 {
5589 ++components;
5590 }
5591
5592 }
5593 }
5594
5595 BOOST_CHECK_EQUAL( pages, 1u );
5596 BOOST_CHECK_EQUAL( components, 74u );
5597}
5598
5599
5600BOOST_AUTO_TEST_CASE( Issue25005Hierarchy )
5601{
5602 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
5603
5604 if( !corpusEnv || !*corpusEnv )
5605 {
5606 BOOST_TEST_MESSAGE( "KICAD_ORCAD_CORPUS not set; skipping issue 25005." );
5607 return;
5608 }
5609
5610 std::filesystem::path dsn = findCorpusDesign( corpusEnv, "CFW-002.DSN" );
5611
5612 if( dsn.empty() )
5613 {
5614 BOOST_TEST_MESSAGE( "CFW-002.DSN not present in corpus; skipping issue 25005." );
5615 return;
5616 }
5617
5618 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
5619 SETTINGS_MANAGER manager;
5620 manager.LoadProject( "" );
5621 schematic->SetProject( &manager.Prj() );
5622 schematic->CurrentSheet().clear();
5623 schematic->CurrentSheet().push_back( &schematic->Root() );
5624
5625 SCH_IO_ORCAD plugin;
5626 plugin.LoadSchematicFile( dsn.string(), schematic.get() );
5627
5628 std::vector<SCH_SHEET*> topSheets = schematic->GetTopLevelSheets();
5629 BOOST_REQUIRE_EQUAL( topSheets.size(), 1u );
5630
5631 SCH_SCREEN* rootScreen = topSheets.front()->GetScreen();
5632 size_t sheets = 0;
5633 size_t sheetPins = 0;
5634
5635 for( SCH_ITEM* item : rootScreen->Items().OfType( SCH_SHEET_T ) )
5636 {
5637 SCH_SHEET* sheet = static_cast<SCH_SHEET*>( item );
5638 ++sheets;
5639 sheetPins += sheet->GetPins().size();
5640 }
5641
5642 const std::vector<wxString> expectedNames = { wxS( "PAG_2" ), wxS( "PAG_3" ), wxS( "PAG_4" ), wxS( "PAG_5" ),
5643 wxS( "PAG_6" ), wxS( "PAG_7" ), wxS( "PAG_8" ), wxS( "PAG_9" ) };
5644 const std::vector<size_t> expectedPinCounts = { 31, 24, 39, 47, 40, 33, 26, 10 };
5645 SCH_SHEET_LIST hierarchy = schematic->BuildSheetListSortedByPageNumbers();
5646 std::vector<wxString> sheetNames;
5647 std::vector<size_t> pinCounts;
5648
5649 for( auto it = std::next( hierarchy.begin() ); it != hierarchy.end(); ++it )
5650 {
5651 SCH_SHEET* sheet = it->Last();
5652 std::set<wxString> sheetPinNames;
5653 std::set<wxString> hierarchicalLabelNames;
5654
5655 sheetNames.push_back( sheet->GetField( FIELD_T::SHEET_NAME )->GetText() );
5656 pinCounts.push_back( sheet->GetPins().size() );
5657
5658 for( const SCH_SHEET_PIN* pin : sheet->GetPins() )
5659 sheetPinNames.insert( pin->GetText() );
5660
5661 for( SCH_ITEM* item : sheet->GetScreen()->Items().OfType( SCH_HIER_LABEL_T ) )
5662 hierarchicalLabelNames.insert( static_cast<SCH_HIERLABEL*>( item )->GetText() );
5663
5664 BOOST_CHECK_EQUAL_COLLECTIONS( sheetPinNames.begin(), sheetPinNames.end(), hierarchicalLabelNames.begin(),
5665 hierarchicalLabelNames.end() );
5666 }
5667
5668 schematic->ConnectionGraph()->Recalculate( hierarchy, true );
5669
5670 BOOST_CHECK_EQUAL( hierarchy.size(), 9u );
5671 BOOST_CHECK_EQUAL( sheets, 8u );
5672 BOOST_CHECK_EQUAL( sheetPins, 250u );
5673 BOOST_CHECK_EQUAL_COLLECTIONS( sheetNames.begin(), sheetNames.end(), expectedNames.begin(), expectedNames.end() );
5674 BOOST_CHECK_EQUAL_COLLECTIONS( pinCounts.begin(), pinCounts.end(), expectedPinCounts.begin(),
5675 expectedPinCounts.end() );
5676}
5677
5678
5679BOOST_AUTO_TEST_CASE( Issue25009PageOrderAndGraphics )
5680{
5681 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
5682
5683 if( !corpusEnv || !*corpusEnv )
5684 {
5685 BOOST_TEST_MESSAGE( "KICAD_ORCAD_CORPUS not set; skipping issue 25009." );
5686 return;
5687 }
5688
5689 std::filesystem::path dsn = findCorpusDesign( corpusEnv, "SE_NGFOC-L_01.DSN" );
5690
5691 if( dsn.empty() )
5692 {
5693 BOOST_TEST_MESSAGE( "SE_NGFOC-L_01.DSN not present in corpus; skipping issue 25009." );
5694 return;
5695 }
5696
5697 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
5698 SETTINGS_MANAGER manager;
5699 manager.LoadProject( "" );
5700 schematic->SetProject( &manager.Prj() );
5701 schematic->CurrentSheet().clear();
5702 schematic->CurrentSheet().push_back( &schematic->Root() );
5703
5704 SCH_IO_ORCAD plugin;
5705 plugin.LoadSchematicFile( dsn.string(), schematic.get() );
5706
5707 const std::vector<wxString> expectedNames = { wxS( "01.REV.HISTORY" ), wxS( "02.uC" ), wxS( "03.CAN" ),
5708 wxS( "04. Ethercat" ), wxS( "05.EtherSynch" ), wxS( "06.RS-485" ),
5709 wxS( "11.GPIO" ), wxS( "12.Analog" ), wxS( "13:IMU" ),
5710 wxS( "14.Bridge" ), wxS( "15.Encoder" ), wxS( "29.uCPower" ),
5711 wxS( "30.PowerSupply" ), wxS( "31.Expansion" ) };
5712
5713 std::vector<SCH_SHEET*> sheets = schematic->GetTopLevelSheets();
5714 BOOST_REQUIRE_EQUAL( sheets.size(), expectedNames.size() );
5715
5716 size_t wires = 0;
5717 size_t shapes = 0;
5718 size_t texts = 0;
5719 size_t tables = 0;
5720
5721 for( size_t i = 0; i < sheets.size(); ++i )
5722 {
5723 BOOST_CHECK_EQUAL( sheets[i]->GetField( FIELD_T::SHEET_NAME )->GetText(), expectedNames[i] );
5724
5725 for( SCH_ITEM* item : sheets[i]->GetScreen()->Items() )
5726 {
5727 if( item->Type() == SCH_LINE_T )
5728 {
5729 SCH_LINE* line = static_cast<SCH_LINE*>( item );
5730 BOOST_CHECK_EQUAL( line->GetLineWidth(), 0 );
5731 ++wires;
5732 }
5733 else if( item->Type() == SCH_SHAPE_T )
5734 {
5735 SCH_SHAPE* shape = static_cast<SCH_SHAPE*>( item );
5736
5737 if( i == 0 )
5738 BOOST_CHECK( shape->GetFillMode() == FILL_T::NO_FILL );
5739
5740 ++shapes;
5741 }
5742 else if( item->Type() == SCH_TEXT_T )
5743 {
5744 ++texts;
5745 }
5746 else if( item->Type() == SCH_TABLE_T )
5747 {
5748 ++tables;
5749 }
5750 }
5751 }
5752
5753 BOOST_CHECK_EQUAL( wires, 1921u );
5754 BOOST_CHECK_EQUAL( shapes, 168u );
5755 BOOST_CHECK_EQUAL( texts, 206u );
5756 BOOST_CHECK_EQUAL( tables, 0u );
5757}
5758
5759
5760// Set KICAD_ORCAD_CORPUS to verify that private OLB files yield pins or graphics.
5761
5762BOOST_AUTO_TEST_CASE( OlbLibraryImport )
5763{
5764 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
5765
5766 if( !corpusEnv || !*corpusEnv )
5767 {
5768 BOOST_TEST_MESSAGE( "KICAD_ORCAD_CORPUS not set; skipping OrCAD OLB library import." );
5769 return;
5770 }
5771
5772 namespace fs = std::filesystem;
5773 std::vector<fs::path> libs;
5774
5775 for( auto it = fs::recursive_directory_iterator( fs::path( corpusEnv ),
5776 fs::directory_options::skip_permission_denied );
5777 it != fs::recursive_directory_iterator(); ++it )
5778 {
5779 if( !it->is_regular_file() )
5780 continue;
5781
5782 std::string ext = it->path().extension().string();
5783 std::transform( ext.begin(), ext.end(), ext.begin(),
5784 []( unsigned char c )
5785 {
5786 return std::tolower( c );
5787 } );
5788
5789 if( ext == ".olb" )
5790 libs.push_back( it->path() );
5791 }
5792
5793 std::sort( libs.begin(), libs.end() );
5794 BOOST_TEST_MESSAGE( "OrCAD OLB libraries: " << libs.size() );
5795
5796 int totalSymbols = 0, emptySymbols = 0, checkedLibs = 0, rejectedLibs = 0, crashedLibs = 0;
5797
5798 for( const fs::path& lib : libs )
5799 {
5800 SCH_IO_ORCAD plugin;
5801 std::vector<LIB_SYMBOL*> symbols;
5802
5803 if( !plugin.CanReadLibrary( lib.string() ) )
5804 {
5805 ++rejectedLibs;
5806 continue;
5807 }
5808
5809 try
5810 {
5811 // Vector overload materializes all symbols O(n); per-name LoadSymbol rescans O(n^2).
5812 plugin.EnumerateSymbolLib( symbols, lib.string() );
5813 }
5814 catch( const std::exception& e )
5815 {
5816 ++crashedLibs;
5817 BOOST_TEST_MESSAGE( " THROW " << lib.filename().string() << " : " << e.what() );
5818 continue;
5819 }
5820
5821 ++checkedLibs;
5822 int withGeometry = 0;
5823
5824 for( LIB_SYMBOL* symbol : symbols )
5825 {
5826 BOOST_REQUIRE( symbol );
5827 ++totalSymbols;
5828
5829 if( symbol->GetPinCount() > 0 || !symbol->GetDrawItems().empty() )
5830 ++withGeometry;
5831 else
5832 ++emptySymbols;
5833 }
5834
5835 BOOST_TEST_MESSAGE( " " << lib.filename().string() << " : " << symbols.size() << " symbols, " << withGeometry
5836 << " with pins/graphics" );
5837 }
5838
5839 BOOST_TEST_MESSAGE( "OLB summary: " << checkedLibs << " libs, " << rejectedLibs << " rejected, " << crashedLibs
5840 << " crashed, " << totalSymbols << " symbols, " << emptySymbols << " empty" );
5841
5842 // Bad streams must degrade gracefully, not throw; wholesale empty result means decode broke.
5843 BOOST_CHECK_EQUAL( crashedLibs, 0 );
5844 BOOST_CHECK_GT( totalSymbols, 0 );
5845
5846 if( totalSymbols )
5847 BOOST_CHECK_LT( emptySymbols, totalSymbols / 2 );
5848}
5849
5850
5851// A library package becomes one symbol with a unit per device and a DeMorgan body per Convert view
5852BOOST_AUTO_TEST_CASE( OlbPackageKeepsEveryUnitAndView )
5853{
5854 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
5855
5856 if( !corpusEnv || !*corpusEnv )
5857 return;
5858
5859 std::filesystem::path olb = findCorpusDesign( corpusEnv, "LT8550-UKG.OLB" );
5860
5861 // No corpus OLB has a Convert view, so read one from a design cache through the library loader
5862 std::filesystem::path dsn = findCorpusDesign( corpusEnv, "BeagleBoard-xM_ORCAD.DSN" );
5863
5864 if( olb.empty() || dsn.empty() )
5865 return;
5866
5867 SCH_IO_ORCAD plugin;
5868 LIB_SYMBOL* symbol = plugin.LoadSymbol( olb.string(), wxS( "LT8550-UKG" ) );
5869
5870 BOOST_REQUIRE( symbol );
5871 BOOST_CHECK_EQUAL( symbol->GetUnitCount(), 3 );
5872
5873 std::set<wxString> numbers;
5874
5875 for( const SCH_PIN* pin : symbol->GetGraphicalPins() )
5876 BOOST_CHECK_MESSAGE( numbers.insert( pin->GetNumber() ).second, "duplicate pin " << pin->GetNumber() );
5877
5878 LIB_SYMBOL* demorgan = plugin.LoadSymbol( dsn.string(), wxS( "SN65C3221EPW" ) );
5879
5880 BOOST_REQUIRE( demorgan );
5881 BOOST_CHECK( demorgan->HasDeMorganBodyStyles() );
5882}
5883
5884
5885// CutiePi (3 pages) imports as three sibling top-level sheets; off-page connectors keep own
5886// names; reference/value fields honor source display positions.
5887BOOST_AUTO_TEST_CASE( MultiPageFlatImport )
5888{
5889 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
5890
5891 if( !corpusEnv || !*corpusEnv )
5892 {
5893 BOOST_TEST_MESSAGE( "KICAD_ORCAD_CORPUS not set; skipping OrCAD multi-page import." );
5894 return;
5895 }
5896
5897 namespace fs = std::filesystem;
5898 fs::path dsn = fs::path( corpusEnv ) / "cutiepi-board" / "CutiePi_V2.3-20210409.DSN";
5899
5900 if( !fs::exists( dsn ) )
5901 {
5902 BOOST_TEST_MESSAGE( "CutiePi design not present in corpus; skipping." );
5903 return;
5904 }
5905
5906 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
5907 SETTINGS_MANAGER manager;
5908 manager.LoadProject( "" );
5909 schematic->SetProject( &manager.Prj() );
5910 schematic->CurrentSheet().clear();
5911 schematic->CurrentSheet().push_back( &schematic->Root() );
5912
5913 SCH_IO_ORCAD plugin;
5914 plugin.LoadSchematicFile( dsn.string(), schematic.get() );
5915
5916 BOOST_CHECK_EQUAL( schematic->Settings().m_DashedLineDashRatio, 3.0 );
5917 BOOST_CHECK_EQUAL( schematic->Settings().m_DashedLineGapRatio, 1.0 );
5918
5919 // Pages become flat ordered top-level sheets, not a stitching root w/ children; "N - " prefix orders them.
5920 std::vector<SCH_SHEET*> tops = schematic->GetTopLevelSheets();
5921 BOOST_REQUIRE_EQUAL( tops.size(), 3u );
5922
5923 BOOST_CHECK_EQUAL( tops[0]->GetField( FIELD_T::SHEET_NAME )->GetText(), wxS( "CONTENTS" ) );
5924 BOOST_CHECK_EQUAL( tops[1]->GetField( FIELD_T::SHEET_NAME )->GetText(), wxS( "CM4,USB HUB,AUDIO,MIC" ) );
5925 BOOST_CHECK_EQUAL( tops[2]->GetField( FIELD_T::SHEET_NAME )->GetText(), wxS( "CSI, DSI, HDMI, MCU" ) );
5926
5927 std::set<wxString> globalLabels;
5928
5929 for( SCH_SHEET* top : tops )
5930 {
5931 for( SCH_ITEM* item : top->GetScreen()->Items().OfType( SCH_GLOBAL_LABEL_T ) )
5932 globalLabels.insert( static_cast<SCH_LABEL_BASE*>( item )->GetText() );
5933 }
5934
5935 // Off-page connectors carry own name (CAM0_IO1), not the local wire net (GPIO19) they sit on.
5936 BOOST_CHECK( globalLabels.count( wxS( "CAM0_IO1" ) ) );
5937 BOOST_CHECK( globalLabels.count( wxS( "AMP_SHUTDOWN" ) ) );
5938
5939 // R3197 reference honors OrCAD display position (left of body), not computed fallback (right).
5940 bool checkedField = false;
5941
5942 for( const SCH_SHEET_PATH& path : schematic->BuildSheetListSortedByPageNumbers() )
5943 {
5944 for( SCH_ITEM* item : path.LastScreen()->Items().OfType( SCH_SYMBOL_T ) )
5945 {
5946 SCH_SYMBOL* sym = static_cast<SCH_SYMBOL*>( item );
5947
5948 if( sym->GetRef( &path, false ) == wxS( "R3197" ) )
5949 {
5950 BOOST_CHECK_LT( sym->GetField( FIELD_T::REFERENCE )->GetPosition().x, sym->GetPosition().x );
5951 checkedField = true;
5952 }
5953 }
5954 }
5955
5956 BOOST_CHECK( checkedField );
5957}
5958
5959
5960BOOST_AUTO_TEST_CASE( HierarchicalSymbolInstancesUseCanonicalSheetPaths )
5961{
5962 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
5963
5964 if( !corpusEnv || !*corpusEnv )
5965 {
5966 BOOST_TEST_MESSAGE( "KICAD_ORCAD_CORPUS not set; skipping flat-page instance-path check." );
5967 return;
5968 }
5969
5970 std::filesystem::path dsn = findCorpusDesign( corpusEnv, "OCTOPAES_10.DSN" );
5971
5972 if( dsn.empty() )
5973 {
5974 BOOST_TEST_MESSAGE( "OCTOPAES_10.DSN not present in corpus; skipping instance-path check." );
5975 return;
5976 }
5977
5978 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
5979 SETTINGS_MANAGER manager;
5980 manager.LoadProject( "" );
5981 schematic->SetProject( &manager.Prj() );
5982 schematic->CurrentSheet().clear();
5983 schematic->CurrentSheet().push_back( &schematic->Root() );
5984
5985 SCH_IO_ORCAD plugin;
5986 plugin.LoadSchematicFile( dsn.string(), schematic.get() );
5987
5988 int checked = 0;
5989
5990 for( const SCH_SHEET_PATH& path : schematic->BuildSheetListSortedByPageNumbers() )
5991 {
5992 if( !path.LastScreen()->GetFileName().Contains( wxS( "CPLD Power" ) ) )
5993 continue;
5994
5995 for( SCH_ITEM* item : path.LastScreen()->Items().OfType( SCH_SYMBOL_T ) )
5996 {
5997 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
5998
5999 if( symbol->GetRef( &path, false ) != wxS( "P?" ) )
6000 continue;
6001
6002 bool canonical = std::any_of( symbol->GetInstances().begin(), symbol->GetInstances().end(),
6003 [&]( const SCH_SYMBOL_INSTANCE& aInstance )
6004 {
6005 return aInstance.m_Path == path.Path();
6006 } );
6007 std::string stored;
6008
6009 for( const SCH_SYMBOL_INSTANCE& instance : symbol->GetInstances() )
6010 stored += instance.m_Path.AsString().ToStdString() + " ";
6011
6012 BOOST_CHECK_MESSAGE( canonical, "expected=" << path.Path().AsString() << " stored=" << stored );
6013 ++checked;
6014 }
6015 }
6016
6017 BOOST_CHECK_EQUAL( checked, 3 );
6018}
6019
6020
6021BOOST_AUTO_TEST_CASE( MultiPageHierarchyPreservesPortConnectivity )
6022{
6023 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
6024
6025 if( !corpusEnv || !*corpusEnv )
6026 {
6027 BOOST_TEST_MESSAGE( "KICAD_ORCAD_CORPUS not set; skipping multi-page hierarchy check." );
6028 return;
6029 }
6030
6031 std::filesystem::path dsn = findCorpusDesign( corpusEnv, "HB1A-AAFM.DSN" );
6032
6033 if( dsn.empty() )
6034 {
6035 BOOST_TEST_MESSAGE( "HB1A-AAFM.DSN not present in corpus; skipping multi-page hierarchy check." );
6036 return;
6037 }
6038
6039 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
6040 SETTINGS_MANAGER manager;
6041 manager.LoadProject( "" );
6042 schematic->SetProject( &manager.Prj() );
6043 schematic->CurrentSheet().clear();
6044 schematic->CurrentSheet().push_back( &schematic->Root() );
6045
6046 SCH_IO_ORCAD plugin;
6047 plugin.LoadSchematicFile( dsn.string(), schematic.get() );
6048
6049 SCH_SHEET_LIST sheets = schematic->BuildSheetListSortedByPageNumbers();
6050 schematic->ConnectionGraph()->Recalculate( sheets, true );
6051
6052 std::map<std::tuple<std::string, std::string, std::string>, int> terminalNets;
6053 int netId = 0;
6054
6055 for( const auto& [key, subgraphs] : schematic->ConnectionGraph()->GetNetMap() )
6056 {
6057 for( CONNECTION_SUBGRAPH* subgraph : subgraphs )
6058 {
6059 std::string page = subgraph->GetSheet().LastScreen()->GetFileName().ToStdString();
6060
6061 for( SCH_ITEM* item : subgraph->GetItems() )
6062 {
6063 if( item->Type() != SCH_PIN_T )
6064 continue;
6065
6066 SCH_PIN* pin = static_cast<SCH_PIN*>( item );
6067 SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( pin->GetParentSymbol() );
6068
6069 if( symbol )
6070 {
6071 terminalNets[{ page, symbol->GetRef( &subgraph->GetSheet(), false ).ToStdString(),
6072 pin->GetNumber().ToStdString() }] = netId;
6073 }
6074 }
6075 }
6076
6077 ++netId;
6078 }
6079
6080 auto findNet = [&]( const std::string& aPage, const std::string& aRef, const std::string& aPin )
6081 {
6082 for( const auto& [terminal, id] : terminalNets )
6083 {
6084 if( std::get<0>( terminal ).find( aPage ) != std::string::npos && std::get<1>( terminal ) == aRef
6085 && std::get<2>( terminal ) == aPin )
6086 {
6087 return id;
6088 }
6089 }
6090
6091 return -1;
6092 };
6093
6094 int clockNet = findNet( "Clock Generator", "J1", "1" );
6095 int fmcNet = findNet( "FMC Connector", "P1", "H38" );
6096 BOOST_REQUIRE_NE( clockNet, -1 );
6097 BOOST_REQUIRE_NE( fmcNet, -1 );
6098 BOOST_CHECK_EQUAL( clockNet, fmcNet );
6099
6100 BOOST_CHECK_EQUAL( findNet( "Clock Generator", "J1", "2" ), findNet( "FMC Connector", "P1", "G37" ) );
6101 BOOST_CHECK_EQUAL( findNet( "Clock Generator", "U12", "7" ), findNet( "MAX II CPLD", "U9", "73" ) );
6102 BOOST_CHECK_EQUAL( findNet( "Current Sense", "U2", "2" ), findNet( "PROM & Misc", "U6", "4" ) );
6103
6104 int mvddUr = findNet( "Power & Control", "TP13", "1" );
6105 int mvddUl = findNet( "Power & Control", "TP30", "1" );
6106 int mvddLr = findNet( "Power & Control", "TP14", "1" );
6107 int mvddLl = findNet( "Power & Control", "TP31", "1" );
6108 BOOST_REQUIRE_NE( mvddUr, -1 );
6109 BOOST_REQUIRE_NE( mvddUl, -1 );
6110 BOOST_REQUIRE_NE( mvddLr, -1 );
6111 BOOST_REQUIRE_NE( mvddLl, -1 );
6112 BOOST_CHECK_NE( mvddUr, mvddUl );
6113 BOOST_CHECK_NE( mvddUr, mvddLr );
6114 BOOST_CHECK_NE( mvddUr, mvddLl );
6115 BOOST_CHECK_NE( mvddUl, mvddLr );
6116 BOOST_CHECK_NE( mvddUl, mvddLl );
6117 BOOST_CHECK_NE( mvddLr, mvddLl );
6118
6119 int urLclk = findNet( "Link Ports NORTH_SOUTH", "U4", "V6" );
6120 int lrLclk = findNet( "Link Ports NORTH_SOUTH", "U8", "A13" );
6121 BOOST_REQUIRE_NE( urLclk, -1 );
6122 BOOST_REQUIRE_NE( lrLclk, -1 );
6123 BOOST_CHECK_EQUAL( urLclk, lrLclk );
6124 BOOST_CHECK_NE( urLclk, findNet( "Link Ports NORTH_SOUTH", "U4", "V7" ) );
6125
6126 int p1c35 = findNet( "FMC Connector", "P1", "C35" );
6127 int p1c37 = findNet( "FMC Connector", "P1", "C37" );
6128 BOOST_REQUIRE_NE( p1c35, -1 );
6129 BOOST_REQUIRE_NE( p1c37, -1 );
6130 BOOST_CHECK_EQUAL( p1c35, p1c37 );
6131 BOOST_CHECK_EQUAL( terminalNetName( *schematic, wxS( "R265" ), wxS( "1" ) ).Lower(),
6132 wxString( wxS( "ll_ul_ns_data_p_0" ) ) );
6133}
6134
6135
6136BOOST_AUTO_TEST_CASE( PowerSymbolPinSharesPartNet )
6137{
6138 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
6139
6140 if( !corpusEnv || !*corpusEnv )
6141 {
6142 BOOST_TEST_MESSAGE( "KICAD_ORCAD_CORPUS not set; skipping power-symbol connectivity check." );
6143 return;
6144 }
6145
6146 std::filesystem::path dsn = findCorpusDesign( corpusEnv, "POWER_SOURCE_BOARD_20180717.DSN" );
6147
6148 if( dsn.empty() )
6149 {
6150 BOOST_TEST_MESSAGE( "POWER_SOURCE_BOARD_20180717.DSN not present; skipping power-symbol connectivity check." );
6151 return;
6152 }
6153
6154 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
6155 SETTINGS_MANAGER manager;
6156 manager.LoadProject( "" );
6157 schematic->SetProject( &manager.Prj() );
6158 schematic->CurrentSheet().clear();
6159 schematic->CurrentSheet().push_back( &schematic->Root() );
6160
6161 SCH_IO_ORCAD plugin;
6162 plugin.LoadSchematicFile( dsn.string(), schematic.get() );
6163
6164 std::vector<std::set<std::string>> expected = {
6165 { terminalToken( "R410", "2" ), terminalToken( "R122", "1" ) }
6166 };
6167 auto [consistent, checkable] = checkConnectivity( *schematic, expected );
6168 BOOST_CHECK_EQUAL( checkable, 1 );
6169 BOOST_CHECK_EQUAL( consistent, 1 );
6170}
6171
6172
6173BOOST_AUTO_TEST_CASE( RepeatedHierarchicalBusPinsRemainScoped )
6174{
6175 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
6176
6177 if( !corpusEnv || !*corpusEnv )
6178 return;
6179
6180 std::filesystem::path dsn = findCorpusDesign( corpusEnv, "meta_carrier_sch_rev1.dsn" );
6181
6182 if( dsn.empty() )
6183 {
6184 BOOST_TEST_MESSAGE( "meta_carrier_sch_rev1.dsn not present in corpus; skipping repeated bus check." );
6185 return;
6186 }
6187
6188 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
6189 SETTINGS_MANAGER manager;
6190 manager.LoadProject( "" );
6191 schematic->SetProject( &manager.Prj() );
6192 schematic->CurrentSheet().clear();
6193 schematic->CurrentSheet().push_back( &schematic->Root() );
6194
6195 SCH_IO_ORCAD plugin;
6196 plugin.LoadSchematicFile( dsn.string(), schematic.get() );
6197
6198 std::vector<std::set<std::string>> expected = {
6199 { "J1.1", "J11.1" },
6200 { "J12.239", "J5.239" },
6201 { "J15.239", "J3.239" },
6202 { "J4.1", "J6.1" },
6203 };
6204
6205 auto [consistent, checkable] = checkConnectivity( *schematic, expected );
6206 BOOST_CHECK_EQUAL( checkable, 4 );
6207 BOOST_CHECK_EQUAL( consistent, 4 );
6208 wxString netName = terminalNetName( *schematic, wxS( "C103" ), wxS( "1" ) );
6209 BOOST_CHECK_EQUAL( netName.AfterLast( '/' ), wxString( "LF2_EXT_CAP" ) );
6210 BOOST_CHECK_EQUAL( terminalNetName( *schematic, wxS( "U7" ), wxS( "11" ) ), netName );
6211 const IMPORT_NET_MAP* map = schematic->GetImportNetMap();
6212 BOOST_REQUIRE( map );
6213 std::set<wxString> occurrenceNames;
6214
6215 for( const IMPORT_NET_MAP_ENTRY& entry : map->entries )
6216 {
6217 if( entry.sourceNetId == 9438967 && entry.originalName == wxS( "LF2_EXT_CAP" ) )
6218 {
6220 occurrenceNames.insert( entry.nameAtImport );
6221 }
6222 }
6223
6224 BOOST_CHECK( occurrenceNames.contains( netName ) );
6225 BOOST_CHECK_EQUAL( occurrenceNames.size(), 2u );
6226}
6227
6228
6229BOOST_AUTO_TEST_CASE( NestedHierarchicalBusRangesPreserveConnectivity )
6230{
6231 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
6232
6233 if( !corpusEnv || !*corpusEnv )
6234 return;
6235
6236 std::filesystem::path dsn = findCorpusDesign( corpusEnv, "meta_module_sch_rev1.dsn" );
6237
6238 if( dsn.empty() )
6239 {
6240 BOOST_TEST_MESSAGE( "meta_module_sch_rev1.dsn not present in corpus; skipping nested bus check." );
6241 return;
6242 }
6243
6244 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
6245 SETTINGS_MANAGER manager;
6246 manager.LoadProject( "" );
6247 schematic->SetProject( &manager.Prj() );
6248 schematic->CurrentSheet().clear();
6249 schematic->CurrentSheet().push_back( &schematic->Root() );
6250
6251 SCH_IO_ORCAD plugin;
6252 plugin.LoadSchematicFile( dsn.string(), schematic.get() );
6253
6254 std::vector<std::set<std::string>> expected = {
6255 { terminalToken( "J1", "10" ), terminalToken( "U5", "B15" ) },
6256 { terminalToken( "J1", "100" ), terminalToken( "U7", "D11" ) },
6257 };
6258
6259 auto [consistent, checkable] = checkConnectivity( *schematic, expected );
6260 BOOST_CHECK_EQUAL( checkable, 2 );
6261 BOOST_CHECK_EQUAL( consistent, 2 );
6262 wxString netName = terminalNetName( *schematic, wxS( "R138" ), wxS( "1" ) );
6263 BOOST_CHECK_EQUAL( netName.AfterLast( '/' ), wxString( "WE_WAIT_WR_P0" ) );
6264 BOOST_CHECK_EQUAL( terminalNetName( *schematic, wxS( "U1" ), wxS( "K3" ) ), netName );
6265 const IMPORT_NET_MAP* map = schematic->GetImportNetMap();
6266 BOOST_REQUIRE( map );
6267 std::set<wxString> occurrenceNames;
6268
6269 for( const IMPORT_NET_MAP_ENTRY& entry : map->entries )
6270 {
6271 if( entry.sourceNetId == 7937073 && entry.originalName == wxS( "WE_WAIT_WR_P0" ) )
6272 {
6274 occurrenceNames.insert( entry.nameAtImport );
6275 }
6276 }
6277
6278 BOOST_CHECK( occurrenceNames.contains( netName ) );
6279 BOOST_CHECK_EQUAL( occurrenceNames.size(), 4u );
6280}
6281
6282
6283BOOST_AUTO_TEST_CASE( RenamedHierarchicalBusMembersPreserveConnectivity )
6284{
6285 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
6286
6287 if( !corpusEnv || !*corpusEnv )
6288 return;
6289
6290 std::filesystem::path dsn = findCorpusDesign( corpusEnv, "HB1A-AAFM.DSN" );
6291
6292 if( dsn.empty() )
6293 {
6294 BOOST_TEST_MESSAGE( "HB1A-AAFM.DSN not present in corpus; skipping renamed bus check." );
6295 return;
6296 }
6297
6298 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
6299 SETTINGS_MANAGER manager;
6300 manager.LoadProject( "" );
6301 schematic->SetProject( &manager.Prj() );
6302 schematic->CurrentSheet().clear();
6303 schematic->CurrentSheet().push_back( &schematic->Root() );
6304
6305 SCH_IO_ORCAD plugin;
6306 plugin.LoadSchematicFile( dsn.string(), schematic.get() );
6307
6308 std::vector<std::set<std::string>> expected = {
6309 { terminalToken( "P1", "G36" ), terminalToken( "U9", "36" ) },
6310 { terminalToken( "P1", "H37" ), terminalToken( "U9", "35" ) },
6311 };
6312
6313 auto [consistent, checkable] = checkConnectivity( *schematic, expected );
6314 BOOST_CHECK_EQUAL( checkable, 2 );
6315 BOOST_CHECK_EQUAL( consistent, 2 );
6316}
6317
6318
6319BOOST_AUTO_TEST_CASE( RepeatedMultiPageFoldersKeepLeafNetsScoped )
6320{
6321 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
6322
6323 if( !corpusEnv || !*corpusEnv )
6324 return;
6325
6326 std::filesystem::path dsn = findCorpusDesign( corpusEnv, "HB1A-AAFM.DSN" );
6327
6328 if( dsn.empty() )
6329 return;
6330
6331 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
6332 SETTINGS_MANAGER manager;
6333 manager.LoadProject( "" );
6334 schematic->SetProject( &manager.Prj() );
6335 schematic->CurrentSheet().clear();
6336 schematic->CurrentSheet().push_back( &schematic->Root() );
6337
6338 SCH_IO_ORCAD plugin;
6339 plugin.LoadSchematicFile( dsn.string(), schematic.get() );
6340
6341 auto [consistent, checkable] =
6342 checkConnectivity( *schematic, { { terminalToken( "P1", "C10" ), terminalToken( "U4", "E16" ) },
6343 { terminalToken( "P1", "K10" ), terminalToken( "U8", "E16" ) },
6344 { terminalToken( "P1", "C11" ), terminalToken( "U4", "D16" ) },
6345 { terminalToken( "P1", "K11" ), terminalToken( "U8", "D16" ) } } );
6346 BOOST_CHECK_EQUAL( checkable, 4 );
6347 BOOST_CHECK_EQUAL( consistent, 4 );
6348}
6349
6350
6351BOOST_AUTO_TEST_CASE( DegenerateHierarchicalPinPlacementsUseDefinitionGeometry )
6352{
6353 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
6354
6355 if( !corpusEnv || !*corpusEnv )
6356 return;
6357
6358 std::filesystem::path dsn = findCorpusDesign( corpusEnv, "buddy_sch_rev1.dsn" );
6359
6360 if( dsn.empty() )
6361 {
6362 BOOST_TEST_MESSAGE( "buddy_sch_rev1.dsn not present in corpus; skipping block-pin geometry check." );
6363 return;
6364 }
6365
6366 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
6367 SETTINGS_MANAGER manager;
6368 manager.LoadProject( "" );
6369 schematic->SetProject( &manager.Prj() );
6370 schematic->CurrentSheet().clear();
6371 schematic->CurrentSheet().push_back( &schematic->Root() );
6372
6373 SCH_IO_ORCAD plugin;
6374 plugin.LoadSchematicFile( dsn.string(), schematic.get() );
6375
6376 std::vector<std::set<std::string>> expected = {
6377 { "J1.C10", "U1.J19" }, { "J1.C11", "U1.K19" }, { "J3.C10", "U1.W33" },
6378 { "J3.F28", "U1.J35" }, { "J1.E33", "U1.B38" }, { "J3.E33", "U1.AV40" },
6379 };
6380
6381 auto [consistent, checkable] = checkConnectivity( *schematic, expected );
6382 BOOST_CHECK_EQUAL( checkable, 6 );
6383 BOOST_CHECK_EQUAL( consistent, 6 );
6384 wxString netName = terminalNetName( *schematic, wxS( "J6" ), wxS( "F35" ) );
6385 BOOST_CHECK( !netName.IsEmpty() );
6386 const IMPORT_NET_MAP* map = schematic->GetImportNetMap();
6387 BOOST_REQUIRE( map );
6388 auto mapped = std::find_if( map->entries.begin(), map->entries.end(),
6389 [&]( const IMPORT_NET_MAP_ENTRY& entry )
6390 {
6391 return entry.sourceNetId == 9578237
6392 && entry.originalName == wxS( "CTRL_N3" )
6393 && entry.nameAtImport == netName;
6394 } );
6395 BOOST_REQUIRE( mapped != map->entries.end() );
6397 BOOST_REQUIRE_EQUAL( mapped->occurrence.size(), 3u );
6398 BOOST_CHECK_EQUAL( mapped->occurrence[1], wxString( "10280601" ) );
6399}
6400
6401
6402BOOST_AUTO_TEST_CASE( PlacedUnitsSelectPackagePinMaps )
6403{
6404 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
6405
6406 if( !corpusEnv || !*corpusEnv )
6407 {
6408 BOOST_TEST_MESSAGE( "KICAD_ORCAD_CORPUS not set; skipping placed-unit check." );
6409 return;
6410 }
6411
6412 std::filesystem::path dsn = findCorpusDesign( corpusEnv, "MC2_REV1_16_2.DSN" );
6413
6414 if( dsn.empty() )
6415 {
6416 BOOST_TEST_MESSAGE( "MC2_REV1_16_2.DSN not present; skipping placed-unit check." );
6417 return;
6418 }
6419
6420 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
6421 SETTINGS_MANAGER manager;
6422 manager.LoadProject( "" );
6423 schematic->SetProject( &manager.Prj() );
6424 schematic->CurrentSheet().clear();
6425 schematic->CurrentSheet().push_back( &schematic->Root() );
6426
6427 SCH_IO_ORCAD plugin;
6428 plugin.LoadSchematicFile( dsn.string(), schematic.get() );
6429
6430 std::vector<std::set<std::string>> expected = { { terminalToken( "CC2", "5" ), terminalToken( "FL11", "1" ) },
6431 { terminalToken( "CC2", "6" ), terminalToken( "FL11", "2" ) } };
6432 auto [consistent, checkable] = checkConnectivity( *schematic, expected );
6433 BOOST_CHECK_EQUAL( checkable, 2 );
6434 BOOST_CHECK_EQUAL( consistent, 2 );
6435}
6436
6437
6438BOOST_AUTO_TEST_CASE( Dc2693aSmaOnlyDisplaysCenterPinNumber )
6439{
6440 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
6441
6442 if( !corpusEnv || !*corpusEnv )
6443 return;
6444
6445 std::filesystem::path dsn = findCorpusDesign( corpusEnv, "710-DC2693A_REV02_PCA_SCHEMATIC.DSN" );
6446
6447 if( dsn.empty() )
6448 return;
6449
6450 SETTINGS_MANAGER manager;
6451 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
6452 manager.LoadProject( "" );
6453 schematic->SetProject( &manager.Prj() );
6454 schematic->CurrentSheet().clear();
6455 schematic->CurrentSheet().push_back( &schematic->Root() );
6456
6457 SCH_IO_ORCAD plugin;
6458 plugin.LoadSchematicFile( dsn.string(), schematic.get() );
6459
6460 SCH_SYMBOL* j1 = nullptr;
6461
6462 for( const SCH_SHEET_PATH& path : schematic->BuildSheetListSortedByPageNumbers() )
6463 {
6464 j1 = findConvertedSymbol( *path.LastScreen(), path, wxS( "J1" ) );
6465
6466 if( j1 )
6467 break;
6468 }
6469
6470 BOOST_REQUIRE( j1 );
6471 BOOST_REQUIRE_EQUAL( j1->GetGraphicalPins( ALL_UNITS, ALL_BODY_STYLES ).size(), 5u );
6472
6473 std::vector<wxString> displayedNumbers;
6474
6475 for( const SCH_PIN* pin : j1->GetGraphicalPins( ALL_UNITS, ALL_BODY_STYLES ) )
6476 {
6477 if( j1->GetShowPinNumbers() && pin->IsVisible() && pin->GetNumberTextSize() > 0 )
6478 displayedNumbers.push_back( pin->GetNumber() );
6479 }
6480
6481 BOOST_REQUIRE_EQUAL( displayedNumbers.size(), 1u );
6482 BOOST_CHECK_EQUAL( displayedNumbers.front(), wxS( "1" ) );
6483}
6484
6485
6486BOOST_AUTO_TEST_CASE( Cy8cproto040tDisplaysComponentPinNumbers )
6487{
6488 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
6489
6490 if( !corpusEnv || !*corpusEnv )
6491 return;
6492
6493 std::filesystem::path dsn = findCorpusDesign(
6494 corpusEnv, "CY8CPROTO-040T_PSoC_4000T_CapSense_Prototyping_Board_Schematic.DSN" );
6495
6496 if( dsn.empty() )
6497 return;
6498
6499 SETTINGS_MANAGER manager;
6500 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
6501 manager.LoadProject( "" );
6502 schematic->SetProject( &manager.Prj() );
6503 schematic->CurrentSheet().clear();
6504 schematic->CurrentSheet().push_back( &schematic->Root() );
6505
6506 SCH_IO_ORCAD plugin;
6507 plugin.LoadSchematicFile( dsn.string(), schematic.get() );
6508
6509 auto displayedPinNumbers =
6510 []( SCH_SYMBOL& aSymbol )
6511 {
6512 std::set<wxString> numbers;
6513
6514 if( aSymbol.GetShowPinNumbers() )
6515 {
6516 for( const SCH_PIN* pin : aSymbol.GetGraphicalPins( ALL_UNITS, ALL_BODY_STYLES ) )
6517 {
6518 if( pin->IsVisible() && pin->GetNumberTextSize() > 0 )
6519 numbers.insert( pin->GetNumber() );
6520 }
6521 }
6522
6523 return numbers;
6524 };
6525
6526 SCH_SYMBOL* j1 = nullptr;
6527 SCH_SYMBOL* u1 = nullptr;
6528 SCH_SYMBOL* j11 = nullptr;
6529
6530 for( const SCH_SHEET_PATH& path : schematic->BuildSheetListSortedByPageNumbers() )
6531 {
6532 if( !j1 )
6533 j1 = findConvertedSymbol( *path.LastScreen(), path, wxS( "J1" ) );
6534
6535 if( !u1 )
6536 u1 = findConvertedSymbol( *path.LastScreen(), path, wxS( "U1" ) );
6537
6538 if( !j11 )
6539 j11 = findConvertedSymbol( *path.LastScreen(), path, wxS( "J11" ) );
6540 }
6541
6542 BOOST_REQUIRE( j1 );
6543 BOOST_REQUIRE( u1 );
6544 BOOST_REQUIRE( j11 );
6545
6546 BOOST_CHECK( j1->GetShowPinNames() );
6547 BOOST_CHECK( j1->GetShowPinNumbers() );
6548 BOOST_REQUIRE_EQUAL( j1->GetGraphicalPins( ALL_UNITS, ALL_BODY_STYLES ).size(), 20u );
6549
6550 std::set<wxString> j1PinText;
6551
6552 for( const SCH_PIN* pin : j1->GetGraphicalPins( ALL_UNITS, ALL_BODY_STYLES ) )
6553 {
6554 BOOST_CHECK( !pin->GetName().IsEmpty() );
6555 BOOST_CHECK( !pin->GetNumber().IsEmpty() );
6556 BOOST_CHECK_GT( pin->GetNameTextSize(), 0 );
6557 BOOST_CHECK_GT( pin->GetNumberTextSize(), 0 );
6558 j1PinText.insert( pin->GetName() );
6559 j1PinText.insert( pin->GetNumber() );
6560 }
6561
6562 for( const SCH_ITEM& item : j1->GetLibSymbolRef()->GetDrawItems() )
6563 {
6564 if( item.Type() == SCH_TEXT_T )
6565 BOOST_CHECK( !j1PinText.contains( static_cast<const SCH_TEXT&>( item ).GetText() ) );
6566 }
6567
6568 BOOST_CHECK_EQUAL( displayedPinNumbers( *u1 ).size(), 25u );
6569 BOOST_CHECK_EQUAL( displayedPinNumbers( *j11 ).size(), 10u );
6570 BOOST_CHECK( displayedPinNumbers( *u1 ).contains( wxS( "23" ) ) );
6571 BOOST_CHECK( displayedPinNumbers( *u1 ).contains( wxS( "H" ) ) );
6572 BOOST_CHECK( displayedPinNumbers( *j11 ).contains( wxS( "1" ) ) );
6573 BOOST_CHECK( displayedPinNumbers( *j11 ).contains( wxS( "10" ) ) );
6574}
6575
6576
6577BOOST_AUTO_TEST_CASE( Cy8ckit149EmbeddedBlockDiagramIsComplete )
6578{
6579 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
6580
6581 if( !corpusEnv || !*corpusEnv )
6582 return;
6583
6584 std::filesystem::path dsn = findCorpusDesign( corpusEnv, "CY8CKIT-149 Schematic.DSN" );
6585
6586 if( dsn.empty() )
6587 return;
6588
6589 SETTINGS_MANAGER manager;
6590 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
6591 manager.LoadProject( "" );
6592 schematic->SetProject( &manager.Prj() );
6593 schematic->CurrentSheet().clear();
6594 schematic->CurrentSheet().push_back( &schematic->Root() );
6595
6596 SCH_IO_ORCAD plugin;
6597 plugin.LoadSchematicFile( dsn.string(), schematic.get() );
6598
6599 const wxImage* blockDiagram = nullptr;
6600 size_t redShapesOnBlockDiagram = 0;
6601 size_t elephantNotes = 0;
6602
6603 for( const SCH_SHEET_PATH& path : schematic->BuildSheetListSortedByPageNumbers() )
6604 {
6605 if( path.Last()->GetName().Contains( wxS( "Block Diagram" ) ) )
6606 {
6607 for( SCH_ITEM* item : path.LastScreen()->Items().OfType( SCH_SHAPE_T ) )
6608 {
6609 const SCH_SHAPE& shape = static_cast<const SCH_SHAPE&>( *item );
6610
6611 if( shape.GetStroke().GetColor() == KIGFX::COLOR4D( 1.0, 0.0, 0.0, 1.0 ) )
6612 ++redShapesOnBlockDiagram;
6613 }
6614 }
6615
6616 for( SCH_ITEM* item : path.LastScreen()->Items().OfType( SCH_TEXT_T ) )
6617 {
6618 const SCH_TEXT& text = static_cast<const SCH_TEXT&>( *item );
6619
6620 if( text.GetText() == wxS( "*All Test Points are No Load" ) )
6621 {
6622 BOOST_REQUIRE( text.GetFont() );
6623 BOOST_CHECK_EQUAL( text.GetFont()->GetName(), wxS( "KiCad OrCAD Elephant" ) );
6624 int sourceBottom = path.Last()->GetName().Contains( wxS( "PSoC 4100S" ) ) ? 623 : 613;
6625 BOX2I ink = text.GetEffectiveTextShape( false, BOX2I(), ANGLE_0 )->BBox();
6626 ink.Offset( text.GetSchematicTextOffset( nullptr ) + text.GetOffsetToMatchSCH_FIELD( nullptr ) );
6627 BOOST_CHECK_SMALL( ink.GetY() - OrcadDbuToIu( 0, sourceBottom - 10 ).y, OrcadDbuToIu( 0, 1 ).y );
6628 BOOST_CHECK_SMALL( ink.GetBottom() - OrcadDbuToIu( 0, sourceBottom ).y, OrcadDbuToIu( 0, 1 ).y );
6629 ++elephantNotes;
6630 }
6631 }
6632
6633 for( SCH_ITEM* item : path.LastScreen()->Items().OfType( SCH_BITMAP_T ) )
6634 {
6635 const wxImage* image = static_cast<SCH_BITMAP*>( item )->GetReferenceImage().GetImage().GetImageData();
6636
6637 if( image && image->IsOk() && image->GetWidth() >= 2500 && image->GetHeight() >= 1000
6638 && ( !blockDiagram
6639 || image->GetWidth() * image->GetHeight()
6640 > blockDiagram->GetWidth() * blockDiagram->GetHeight() ) )
6641 {
6642 blockDiagram = image;
6643 }
6644 }
6645 }
6646
6647 BOOST_REQUIRE_MESSAGE( blockDiagram, "CY8CKIT-149 block diagram was not rendered at full size" );
6648 BOOST_CHECK_EQUAL( redShapesOnBlockDiagram, 0u );
6649 BOOST_CHECK_EQUAL( elephantNotes, 2u );
6650 BOOST_CHECK( schematic->GetEmbeddedFiles()->HasFile( wxS( "KiCadOrCADElephant-Black.ttf" ) ) );
6651 BOOST_CHECK( schematic->GetAreFontsEmbedded() );
6652
6653 std::array<size_t, 3> blueColumns{};
6654 std::array<size_t, 3> blueRows{};
6655 const int width = blockDiagram->GetWidth();
6656 const int height = blockDiagram->GetHeight();
6657
6658 for( int y = 0; y < height; ++y )
6659 {
6660 for( int x = 0; x < width; ++x )
6661 {
6662 int red = blockDiagram->GetRed( x, y );
6663 int green = blockDiagram->GetGreen( x, y );
6664 int blue = blockDiagram->GetBlue( x, y );
6665
6666 if( blue > red + 30 && blue > green + 20 )
6667 {
6668 ++blueColumns[std::min( 2, x * 3 / width )];
6669 ++blueRows[std::min( 2, y * 3 / height )];
6670 }
6671 }
6672 }
6673
6674 const size_t minimumBluePixels = static_cast<size_t>( width ) * height / 500;
6675
6676 BOOST_TEST_MESSAGE( "block diagram size=" << width << 'x' << height << " columns=" << blueColumns[0] << ','
6677 << blueColumns[1] << ',' << blueColumns[2] << " rows="
6678 << blueRows[0] << ',' << blueRows[1] << ',' << blueRows[2] );
6679
6680 for( size_t count : blueColumns )
6681 BOOST_CHECK_GE( count, minimumBluePixels );
6682
6683 for( size_t count : blueRows )
6684 BOOST_CHECK_GE( count, minimumBluePixels );
6685}
6686
6687
6688// CutiePi component fidelity: pin number/name visibility, off-page label orientation, hidden
6689// fields, no-connect markers.
6690BOOST_AUTO_TEST_CASE( ComponentDetailImport )
6691{
6692 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
6693
6694 if( !corpusEnv || !*corpusEnv )
6695 {
6696 BOOST_TEST_MESSAGE( "KICAD_ORCAD_CORPUS not set; skipping OrCAD component detail." );
6697 return;
6698 }
6699
6700 namespace fs = std::filesystem;
6701 fs::path dsn = fs::path( corpusEnv ) / "cutiepi-board" / "CutiePi_V2.3-20210409.DSN";
6702
6703 if( !fs::exists( dsn ) )
6704 {
6705 BOOST_TEST_MESSAGE( "CutiePi design not present in corpus; skipping." );
6706 return;
6707 }
6708
6709 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
6710 SETTINGS_MANAGER manager;
6711 manager.LoadProject( "" );
6712 schematic->SetProject( &manager.Prj() );
6713 schematic->CurrentSheet().clear();
6714 schematic->CurrentSheet().push_back( &schematic->Root() );
6715
6716 SCH_IO_ORCAD plugin;
6717 plugin.LoadSchematicFile( dsn.string(), schematic.get() );
6718
6719 std::map<wxString, SCH_SYMBOL*> symbols;
6720 std::multimap<wxString, int> labelSpins;
6721 int noConnects = 0;
6722
6723 for( SCH_SHEET* top : schematic->GetTopLevelSheets() )
6724 {
6726 path.push_back( top );
6727
6728 for( SCH_ITEM* item : top->GetScreen()->Items() )
6729 {
6730 if( item->Type() == SCH_SYMBOL_T )
6731 {
6732 SCH_SYMBOL* sym = static_cast<SCH_SYMBOL*>( item );
6733 symbols[sym->GetRef( &path, false )] = sym;
6734 }
6735 else if( item->Type() == SCH_GLOBAL_LABEL_T )
6736 {
6737 SCH_LABEL_BASE* lbl = static_cast<SCH_LABEL_BASE*>( item );
6738 labelSpins.emplace( lbl->GetText(), (int) lbl->GetSpinStyle() );
6739 }
6740 else if( item->Type() == SCH_NO_CONNECT_T )
6741 {
6742 ++noConnects;
6743 }
6744 }
6745 }
6746
6747 // Pin numbers/names show on ICs (flags 0x3), hide on passives (0x6)
6748 BOOST_REQUIRE( symbols.count( wxS( "U3" ) ) );
6749 BOOST_CHECK( symbols[wxS( "U3" )]->GetShowPinNumbers() );
6750 BOOST_CHECK( symbols[wxS( "U3" )]->GetShowPinNames() );
6751 BOOST_REQUIRE( symbols.count( wxS( "R3174" ) ) );
6752 BOOST_CHECK( !symbols[wxS( "R3174" )]->GetShowPinNumbers() );
6753 BOOST_CHECK( !symbols[wxS( "R3174" )]->GetShowPinNames() );
6754
6755 // Ferrite bead value hidden, reference visible
6756 BOOST_REQUIRE( symbols.count( wxS( "FB8" ) ) );
6757 BOOST_CHECK( !symbols[wxS( "FB8" )]->GetField( FIELD_T::VALUE )->IsVisible() );
6758 BOOST_CHECK( symbols[wxS( "FB8" )]->GetField( FIELD_T::REFERENCE )->IsVisible() );
6759
6760 // Display-prop field positions are canvas-space (anchor + offset), not through body-orientation
6761 // transform. FB8 (90-deg ferrite) reference lands right of origin; rotation transform would flip left.
6762 SCH_FIELD* fb8Ref = symbols[wxS( "FB8" )]->GetField( FIELD_T::REFERENCE );
6763 BOOST_CHECK_GT( fb8Ref->GetPosition().x, symbols[wxS( "FB8" )]->GetPosition().x );
6764 BOOST_CHECK( fb8Ref->GetHorizJustify() == GR_TEXT_H_ALIGN_LEFT );
6765
6766 // FB8 stored angle compensates for KiCad re-rotating fields on 90-deg symbol, so text stays horizontal.
6767 BOOST_CHECK( fb8Ref->GetDrawRotation() == ANGLE_HORIZONTAL );
6768
6769 // References render horizontal even on rotated symbols (ferrites, vertical R/C).
6770 for( const wxString& ref : { wxS( "R3186" ), wxS( "C2517" ), wxS( "R3189" ), wxS( "FB13" ), wxS( "FB9" ) } )
6771 {
6772 BOOST_REQUIRE_MESSAGE( symbols.count( ref ), ref );
6773 BOOST_CHECK( symbols[ref]->GetField( FIELD_T::REFERENCE )->GetDrawRotation() == ANGLE_HORIZONTAL );
6774 }
6775
6776 // Value rotation is per-field from source: FB9 part number horizontal, C2517 "47pF" stays vertical.
6777 BOOST_CHECK( symbols[wxS( "FB9" )]->GetField( FIELD_T::VALUE )->GetDrawRotation() == ANGLE_HORIZONTAL );
6778 BOOST_CHECK( symbols[wxS( "C2517" )]->GetField( FIELD_T::VALUE )->GetDrawRotation() == ANGLE_VERTICAL );
6779
6780 // Power net names read horizontal even on rotated power symbols (REG1V8/REG3V3).
6781 bool checkedPower = false;
6782
6783 for( SCH_SHEET* top : schematic->GetTopLevelSheets() )
6784 {
6785 for( SCH_ITEM* item : top->GetScreen()->Items().OfType( SCH_SYMBOL_T ) )
6786 {
6787 SCH_SYMBOL* sym = static_cast<SCH_SYMBOL*>( item );
6788 wxString val = sym->GetField( FIELD_T::VALUE )->GetText();
6789
6790 if( val == wxS( "REG1V8" ) || val == wxS( "REG3V3" ) )
6791 {
6792 BOOST_CHECK( sym->GetField( FIELD_T::VALUE )->GetDrawRotation() == ANGLE_HORIZONTAL );
6793 checkedPower = true;
6794 }
6795 }
6796 }
6797
6798 BOOST_CHECK( checkedPower );
6799
6800 // Unconnected IC pins get no-connect markers (U3 15 NC + U580 NC/ORG)
6801 BOOST_CHECK_GE( noConnects, 17 );
6802
6803 // Off-page connectors on vertical wires point up/down, not left/right.
6804 auto hasSpin = [&]( const wxString& aText, SPIN_STYLE::SPIN aSpin )
6805 {
6806 auto range = labelSpins.equal_range( aText );
6807
6808 for( auto it = range.first; it != range.second; ++it )
6809 {
6810 if( it->second == (int) aSpin )
6811 return true;
6812 }
6813
6814 return false;
6815 };
6816
6817 BOOST_CHECK( hasSpin( wxS( "VOLDN" ), SPIN_STYLE::UP ) );
6818 BOOST_CHECK( hasSpin( wxS( "MUTEP" ), SPIN_STYLE::BOTTOM ) );
6819}
6820
6821
6822BOOST_AUTO_TEST_CASE( HierarchicalBusAliasesConnectWithoutHiddenLabels )
6823{
6824 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
6825
6826 if( !corpusEnv || !*corpusEnv )
6827 return;
6828
6829 std::filesystem::path dsn = findCorpusDesign( corpusEnv, "buddy_sch_rev1.dsn" );
6830 BOOST_REQUIRE_MESSAGE( !dsn.empty(), "buddy_sch_rev1.dsn not present in corpus." );
6831
6832 SETTINGS_MANAGER manager;
6833 manager.LoadProject( "" );
6834 SCHEMATIC schematic( &manager.Prj() );
6835 SCH_IO_ORCAD plugin;
6836 plugin.LoadSchematicFile( dsn.string(), &schematic );
6837
6838 std::set<SCH_SCREEN*> visited;
6839
6840 for( const SCH_SHEET_PATH& path : schematic.BuildSheetListSortedByPageNumbers() )
6841 {
6842 SCH_SCREEN* screen = path.LastScreen();
6843
6844 if( !visited.insert( screen ).second )
6845 continue;
6846
6847 std::vector<SCH_ITEM*> hidden;
6848
6849 for( SCH_ITEM* item : screen->Items() )
6850 {
6851 if( item->Type() != SCH_LABEL_T && item->Type() != SCH_GLOBAL_LABEL_T )
6852 continue;
6853
6854 SCH_LABEL_BASE* label = static_cast<SCH_LABEL_BASE*>( item );
6855
6856 if( label->GetTextColor() != KIGFX::COLOR4D::UNSPECIFIED && label->GetTextColor().a == 0 )
6857 hidden.push_back( item );
6858 }
6859
6860 for( SCH_ITEM* item : hidden )
6861 screen->DeleteItem( item );
6862 }
6863
6864 auto [consistent, checkable] =
6865 checkConnectivity( schematic, { { "U1.BB5", "J6.J30" }, { "U1.BA5", "J6.K31" } } );
6866 BOOST_CHECK_EQUAL( checkable, 2 );
6867 BOOST_CHECK_EQUAL( consistent, 2 );
6868}
6869
6870
6871BOOST_AUTO_TEST_CASE( EscapedHierarchicalBusMembersConnectWithoutGlobalHelpers )
6872{
6873 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
6874
6875 if( !corpusEnv || !*corpusEnv )
6876 return;
6877
6878 std::filesystem::path dsn = findCorpusDesign( corpusEnv, "M5275EVB.DSN" );
6879 BOOST_REQUIRE_MESSAGE( !dsn.empty(), "M5275EVB.DSN not present in corpus." );
6880
6881 SETTINGS_MANAGER manager;
6882 manager.LoadProject( "" );
6883 SCHEMATIC schematic( &manager.Prj() );
6884 SCH_IO_ORCAD plugin;
6885 plugin.LoadSchematicFile( dsn.string(), &schematic );
6886
6887 std::set<SCH_SCREEN*> visited;
6888
6889 for( const SCH_SHEET_PATH& path : schematic.BuildSheetListSortedByPageNumbers() )
6890 {
6891 SCH_SCREEN* screen = path.LastScreen();
6892
6893 if( !visited.insert( screen ).second )
6894 continue;
6895
6896 std::vector<SCH_LABEL_BASE*> hidden;
6897
6898 for( SCH_ITEM* item : screen->Items().OfType( SCH_GLOBAL_LABEL_T ) )
6899 {
6900 SCH_LABEL_BASE* label = static_cast<SCH_LABEL_BASE*>( item );
6901
6902 if( label->GetTextColor() != KIGFX::COLOR4D::UNSPECIFIED && label->GetTextColor().a == 0 )
6903 hidden.push_back( label );
6904 }
6905
6906 for( SCH_LABEL_BASE* label : hidden )
6907 {
6908 screen->Append( new SCH_LABEL( label->GetPosition(), label->GetText() ) );
6909 screen->DeleteItem( label );
6910 }
6911 }
6912
6913 const std::vector<std::set<std::string>> expected = {
6914 { "J3.38", "RP49.7", "U6.D10" },
6915 { "J3.40", "RP49.5", "U6.D11" },
6916 { "J3.42", "RP49.3", "U6.D12" },
6917 { "J3.44", "RP49.1", "U6.D13" },
6918 { "J4.43", "RP16.4", "RP45.5", "RP9.8", "TP33.1", "U1.40", "U7.47" },
6919 { "J4.45", "RP16.8", "RP45.3", "RP9.6", "TP35.1", "U1.39", "U7.20" },
6920 { "J4.47", "RP9.4" },
6921 { "J4.49", "RP15.2", "RP9.2", "TP31.1", "U7.24" },
6922 { "J5.21", "R37.2", "RP50.1", "U6.G13" },
6923 { "J5.22", "RP47.3", "U6.E13" },
6924 { "J5.23", "RP50.3", "U6.H16" },
6925 { "J5.24", "RP47.1", "U2.6", "U6.F13" },
6926 { "J5.25", "RP50.5", "U6.H15" },
6927 { "J5.27", "RP50.7", "U6.H14" },
6928 { "J5.29", "J9.11", "RP48.3", "U6.J14" },
6929 { "J5.31", "RP48.5", "U6.J13", "U8.25" },
6930 { "J5.33", "RP48.7", "U6.K13", "U9.25" },
6931 { "J6.15", "RP47.7", "TP4.1", "U11.12", "U12.A8", "U2.1", "U6.R6" },
6932 { "J6.21", "RP47.5", "U1.6", "U2.3", "U6.N7" },
6933 };
6934
6935 auto [consistent, checkable] = checkConnectivity( schematic, expected );
6936 BOOST_CHECK_EQUAL( checkable, expected.size() );
6937 BOOST_CHECK_EQUAL( consistent, expected.size() );
6938}
6939
6940
6941BOOST_AUTO_TEST_CASE( RepeatedSheetPinNamesDoNotJoinLocalRepairs )
6942{
6943 const char* corpusEnv = std::getenv( "KICAD_ORCAD_CORPUS" );
6944
6945 if( !corpusEnv || !*corpusEnv )
6946 return;
6947
6948 const std::filesystem::path dsn = findCorpusDesign( corpusEnv, "HB1A-AAFM.DSN" );
6949
6950 if( dsn.empty() )
6951 {
6952 BOOST_TEST_MESSAGE( "HB1A-AAFM.DSN not present in corpus; skipping sheet-pin repair check." );
6953 return;
6954 }
6955
6956 SETTINGS_MANAGER manager;
6957 manager.LoadProject( "" );
6958 SCHEMATIC schematic( &manager.Prj() );
6959 SCH_IO_ORCAD plugin;
6960 plugin.LoadSchematicFile( dsn.string(), &schematic );
6961 schematic.ConnectionGraph()->Recalculate( schematic.BuildSheetListSortedByPageNumbers(), true );
6962 std::set<std::set<std::string>> partitions;
6963
6964 for( const auto& [key, subgraphs] : schematic.ConnectionGraph()->GetNetMap() )
6965 {
6966 std::set<std::string> terminals;
6967
6968 for( CONNECTION_SUBGRAPH* subgraph : subgraphs )
6969 {
6970 for( SCH_ITEM* item : subgraph->GetItems() )
6971 {
6972 if( item->Type() != SCH_PIN_T )
6973 continue;
6974
6975 SCH_PIN* pin = static_cast<SCH_PIN*>( item );
6976 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( pin->GetParentSymbol() );
6977 wxString reference = symbol->GetRef( &subgraph->GetSheet(), false );
6978
6979 if( !reference.IsEmpty() && !reference.StartsWith( wxS( "#" ) ) )
6980 {
6981 terminals.insert( terminalToken( reference.ToStdString( wxConvUTF8 ),
6982 pin->GetNumber().ToStdString( wxConvUTF8 ) ) );
6983 }
6984 }
6985 }
6986
6987 partitions.insert( std::move( terminals ) );
6988 }
6989
6990 // These distinct Capture clock nets share interface pin names across repeated ANEMONE instances.
6991 const std::map<std::string, std::set<std::string>> expected = {
6992 { "UL_UL_WW_LCLK_P", { terminalToken( "R278", "1" ), terminalToken( "U3", "F1" ),
6993 terminalToken( "U3", "M1" ) } },
6994 { "UR_UL_WE_LCLK_P", { terminalToken( "R175", "1" ), terminalToken( "U3", "N18" ),
6995 terminalToken( "U4", "F1" ) } }
6996 };
6997
6998 for( const auto& [name, terminals] : expected )
6999 {
7001 {
7002 BOOST_CHECK( partitions.count( terminals ) == 1 );
7003 }
7004 }
7005}
7006
7007
7008BOOST_AUTO_TEST_CASE( WireFreePinJunctionsRetainVisibleLocalConnections )
7009{
7010 const char* corpus = std::getenv( "KICAD_ORCAD_CORPUS" );
7011
7012 if( !corpus || !*corpus )
7013 return;
7014
7015 std::filesystem::path dsn = findCorpusDesign( corpus, "1822A.DSN" );
7016
7017 if( dsn.empty() )
7018 return;
7019
7020 SETTINGS_MANAGER manager;
7021 manager.LoadProject( "" );
7022 SCHEMATIC schematic( &manager.Prj() );
7023 SCH_IO_ORCAD plugin;
7024 BOOST_REQUIRE_NO_THROW( plugin.LoadSchematicFile( dsn.string(), &schematic ) );
7025 bool found = false;
7026
7027 for( const SCH_SHEET_PATH& path : schematic.BuildSheetListSortedByPageNumbers() )
7028 {
7029 SCH_SYMBOL* symbol = findConvertedSymbol( *path.LastScreen(), path, wxS( "COUT1" ) );
7030
7031 if( !symbol )
7032 continue;
7033
7034 for( SCH_PIN* pin : symbol->GetPins( &path ) )
7035 {
7036 if( pin->GetNumber() != wxS( "3" ) )
7037 continue;
7038
7039 found = true;
7040 bool labelled = false;
7041 bool junction = false;
7042
7043 for( SCH_ITEM* item : path.LastScreen()->Items() )
7044 {
7045 if( auto* wire = dynamic_cast<SCH_LINE*>( item ); wire && wire->GetLayer() == LAYER_WIRE )
7046 BOOST_CHECK( !wire->GetSeg().Contains( pin->GetPosition() ) );
7047
7048 if( item->GetPosition() != pin->GetPosition() )
7049 continue;
7050
7051 junction |= item->Type() == SCH_JUNCTION_T;
7052
7053 if( auto* label = dynamic_cast<SCH_LABEL*>( item ) )
7054 {
7055 labelled |= label->GetText() == wxS( "Agnd" );
7056 BOOST_CHECK( label->GetTextColor() == KIGFX::COLOR4D::UNSPECIFIED
7057 || label->GetTextColor().a > 0 );
7058 }
7059 }
7060
7061 BOOST_CHECK( junction );
7062 BOOST_CHECK( labelled );
7063 }
7064 }
7065
7066 BOOST_CHECK( found );
7067
7068 for( const wxString& reference : { wxString( "COUT1" ), wxString( "COUT2" ), wxString( "COUT3" ) } )
7069 BOOST_CHECK_EQUAL( terminalNetName( schematic, reference, wxS( "3" ) ), wxString( "Agnd" ) );
7070}
7071
7072
int blue
int red
int green
int index
const char * name
constexpr EDA_IU_SCALE schIUScale
Definition base_units.h:130
BOX2< VECTOR2I > BOX2I
Definition box2.h:914
constexpr coord_type GetY() const
Definition box2.h:205
constexpr size_type GetWidth() const
Definition box2.h:211
constexpr coord_type GetX() const
Definition box2.h:204
constexpr size_type GetHeight() const
Definition box2.h:212
constexpr void Offset(coord_type dx, coord_type dy)
Definition box2.h:256
constexpr coord_type GetBottom() const
Definition box2.h:219
const NET_MAP & GetNetMap() const
void Recalculate(const SCH_SHEET_LIST &aSheetList, bool aUnconditional=false, std::function< void(SCH_ITEM *)> *aChangedItemHandler=nullptr, PROGRESS_REPORTER *aProgressReporter=nullptr)
Update the connection graph for the given list of sheets.
A subgraph is a set of items that are electrically connected on a single sheet.
virtual VECTOR2I GetPosition() const
Definition eda_item.h:348
virtual const BOX2I GetBoundingBox() const
Return the orthogonal bounding box of this object for display purposes.
Definition eda_item.cpp:270
const KIID m_Uuid
Definition eda_item.h:599
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:110
virtual void SetEnd(const VECTOR2I &aEnd)
Definition eda_shape.h:329
FILL_T GetFillMode() const
Definition eda_shape.h:148
SHAPE_T GetShape() const
Definition eda_shape.h:175
A mix-in class (via multiple inheritance) that handles texts such as labels, parts,...
Definition eda_text.h:94
virtual VECTOR2I GetTextSize() const
Definition eda_text.h:301
COLOR4D GetTextColor() const
Definition eda_text.h:310
virtual const wxString & GetText() const
Return the string associated with the text object.
Definition eda_text.h:118
virtual bool IsVisible() const
Definition eda_text.h:226
GR_TEXT_H_ALIGN_T GetHorizJustify() const
Definition eda_text.h:239
bool empty() const
Definition sch_rtree.h:182
EE_TYPE OfType(KICAD_T aType) const
Definition sch_rtree.h:248
virtual void SetReporter(REPORTER *aReporter)
Set an optional reporter for warnings/errors.
Definition io_base.h:89
Hold an error message and may be used when throwing exceptions containing meaningful error messages.
A color representation with 4 components: red, green, blue, alpha.
Definition color4d.h:101
double a
Alpha component.
Definition color4d.h:394
static const COLOR4D UNSPECIFIED
For legacy support; used as a value to indicate color hasn't been set yet.
Definition color4d.h:400
std::string AsStdString() const
Definition kiid.cpp:270
const UTF8 & GetLibItemName() const
Definition lib_id.h:98
Define a library symbol object.
Definition lib_symbol.h:114
LIB_ITEMS_CONTAINER & GetDrawItems()
Return a reference to the draw item list.
Definition lib_symbol.h:843
bool HasDeMorganBodyStyles() const override
bool IsGlobalPower() const override
int GetUnitCount() const override
std::vector< SCH_PIN * > GetGraphicalPins(int aUnit=0, int aBodyStyle=0) const override
Graphical pins: Return schematic pin objects as drawn (unexpanded), filtered by unit/body.
Holds all the data relating to one schematic.
Definition schematic.h:149
SCH_SHEET_LIST BuildSheetListSortedByPageNumbers() const
SCH_ITEM * ResolveItem(const KIID &aID, SCH_SHEET_PATH *aPathOut=nullptr, bool aAllowNullptrReturn=false) const
Definition schematic.h:194
SCH_SHEET_LIST Hierarchy() const
Return the full schematic flattened hierarchical sheet list.
const IMPORT_NET_MAP * GetImportNetMap() const
Definition schematic.h:163
void SetProject(PROJECT *aPrj)
CONNECTION_GRAPH * ConnectionGraph() const
Definition schematic.h:319
void RefreshHierarchy()
Object to handle a bitmap image that can be inserted in a schematic.
Definition sch_bitmap.h:36
VECTOR2I GetPosition() const override
EDA_ANGLE GetDrawRotation() const override
Adjusters to allow EDA_TEXT to draw/print/etc.
virtual const wxString & GetText() const override
Return the string associated with the text object.
Definition sch_field.h:139
wxString GetShownText(const SCH_SHEET_PATH *aPath, RESOLUTION_CONTEXT aContext, const wxString &aVariantName=wxEmptyString, int aDepth=0) const
A SCH_IO derivation for loading OrCAD Capture schematic designs (.dsn).
bool CanReadSchematicFile(const wxString &aFileName) const override
The .dsn extension also identifies SPECCTRA files.
SCH_SHEET * LoadSchematicFile(const wxString &aFileName, SCHEMATIC *aSchematic, SCH_SHEET *aAppendToMe=nullptr, const std::map< std::string, UTF8 > *aProperties=nullptr) override
Load information from some input file format that this SCH_IO implementation knows about,...
bool CanReadLibrary(const wxString &aFileName) const override
Checks if this IO object can read the specified library file/directory.
LIB_SYMBOL * LoadSymbol(const wxString &aLibraryPath, const wxString &aAliasName, const std::map< std::string, UTF8 > *aProperties=nullptr) override
Load a LIB_SYMBOL object having aPartName from the aLibraryPath containing a library format that this...
void EnumerateSymbolLib(wxArrayString &aSymbolNameList, const wxString &aLibraryPath, const std::map< std::string, UTF8 > *aProperties=nullptr) override
Populate a list of LIB_SYMBOL alias names contained within the library aLibraryPath.
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition sch_item.h:170
int GetUnit() const
Definition sch_item.h:243
SCH_LAYER_ID GetLayer() const
Return the layer this item is on.
Definition sch_item.h:353
SPIN_STYLE GetSpinStyle() const
Segment description base class to describe items which have 2 end points (track, wire,...
Definition sch_line.h:39
SEG GetSeg() const
Get the geometric aspect of the wire as a SEG.
Definition sch_line.h:155
int GetLineWidth() const
Definition sch_line.h:195
KIGFX::COLOR4D GetLineColor() const
Return COLOR4D::UNSPECIFIED if a custom color hasn't been set for this line.
Definition sch_line.cpp:395
void Append(SCH_ITEM *aItem, bool aUpdateLibSymbol=true)
EE_RTREE & Items()
Get the full RTree, usually for iterating.
Definition sch_screen.h:122
const wxString & GetFileName() const
Definition sch_screen.h:157
const KIID & GetUuid() const
Definition sch_screen.h:594
void DeleteItem(SCH_ITEM *aItem)
Remove aItem from the linked list and deletes the object.
void SetPosition(const VECTOR2I &aPos) override
Definition sch_shape.h:87
STROKE_PARAMS GetStroke() const override
Definition sch_shape.h:57
A container for handling SCH_SHEET_PATH objects in a flattened hierarchy.
Handle access to a stack of flattened SCH_SHEET objects by way of a path for creating a flattened sch...
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:48
void SetFileName(const wxString &aFilename)
Definition sch_sheet.h:392
SCH_FIELD * GetField(FIELD_T aFieldType)
Return a mandatory field in this sheet.
wxString GetName() const
Definition sch_sheet.h:142
SCH_SCREEN * GetScreen() const
Definition sch_sheet.h:145
bool GetExcludedFromBoard(const SCH_SHEET_PATH *aInstance=nullptr, const wxString &aVariantName=wxEmptyString) const override
Definition sch_sheet.h:477
std::vector< SCH_SHEET_PIN * > & GetPins()
Definition sch_sheet.h:243
Schematic symbol object.
Definition sch_symbol.h:73
bool GetShowPinNumbers() const override
std::vector< std::unique_ptr< SCH_PIN > > & GetRawPins()
Definition sch_symbol.h:678
const std::vector< SCH_SYMBOL_INSTANCE > & GetInstances() const
Definition sch_symbol.h:133
std::vector< const SCH_PIN * > GetPins(const SCH_SHEET_PATH *aSheet) const
Retrieve a list of the SCH_PINs for the given sheet path.
bool GetShowPinNames() const override
void GetFields(std::vector< SCH_FIELD * > &aVector, bool aVisibleOnly) const override
Populate a std::vector with SCH_FIELDs, sorted in ordinal order.
VECTOR2I GetPosition() const override
Definition sch_symbol.h:895
const LIB_ID & GetLibId() const override
Definition sch_symbol.h:163
std::vector< SCH_PIN * > GetGraphicalPins(int aUnit, int aBodyStyle) const override
const wxString GetValue(const SCH_SHEET_PATH *aPath, RESOLUTION_CONTEXT aContext, const wxString &aVariantName=wxEmptyString) const override
std::unique_ptr< LIB_SYMBOL > & GetLibSymbolRef()
Definition sch_symbol.h:182
const wxString GetRef(const SCH_SHEET_PATH *aSheet, bool aIncludeUnit=false) const override
bool IsPower() const override
SCH_FIELD * GetField(FIELD_T aFieldType)
Return a mandatory field in this symbol.
VECTOR2I GetPosition() const override
Definition sch_text.h:143
Definition seg.h:38
bool Contains(const SEG &aSeg) const
Definition seg.h:321
bool LoadProject(const wxString &aFullPath, bool aSetActive=true)
Load a project or sets up a new project with a specified path.
PROJECT & Prj() const
A helper while we are not MDI-capable – return the one and only project.
SPIN Spin() const
Definition sch_label.h:69
SPIN_STYLE RotateCCW()
KIGFX::COLOR4D GetColor() const
wxString wx_str() const
Definition utf8.cpp:41
A wrapper for reporting to a wxString object.
Definition reporter.h:242
@ FOR_CANVAS
Definition common.h:90
@ RAW_VALUE
Definition common.h:96
static constexpr EDA_ANGLE ANGLE_0
Definition eda_angle.h:448
static constexpr EDA_ANGLE ANGLE_VERTICAL
Definition eda_angle.h:445
static constexpr EDA_ANGLE ANGLE_HORIZONTAL
Definition eda_angle.h:444
@ NO_FILL
Definition eda_fill.h:30
@ REVERSE_HATCH
Definition eda_fill.h:35
@ HATCH
Definition eda_fill.h:34
@ FILLED_SHAPE
Fill with object color.
Definition eda_fill.h:31
@ CROSS_HATCH
Definition eda_fill.h:36
@ RECTANGLE
Use RECTANGLE instead of RECT to avoid collision in a Windows header.
Definition eda_shape.h:57
std::string source
@ BUS
the source net is carried by a bus
@ RESOLVED
exactly one physical net carries the source net
@ LAYER_DEVICE
Definition layer_ids.h:488
@ LAYER_WIRE
Definition layer_ids.h:474
@ LAYER_BUS
Definition layer_ids.h:475
void LoadSchematic(SETTINGS_MANAGER &aSettingsManager, const wxString &aRelPath, std::unique_ptr< SCHEMATIC > &aSchematic)
VECTOR2I OleWmfRenderSize(int aNaturalWidth, int aNaturalHeight, int aMaxWidth, int aMaxHeight, double aTargetAspect)
bool OleRenderWmf(const std::vector< uint8_t > &aWmf, int aMaxWidth, int aMaxHeight, wxImage &aImage, double aTargetAspect)
OLE_IMAGE_PAYLOAD ExtractOleImageFromPayload(const std::vector< uint8_t > &aPayload)
Read the picture out of an OLE object payload that carries the 26-byte prologue.
wxString OleDescribeImagePayload(const std::vector< uint8_t > &aPayload)
Include leading bytes when the payload format is unknown.
VECTOR2I OrcadDbuToIu(int aX, int aY)
FILL_T OrcadFillType(int aFillStyle, int aHatchStyle)
int OrcadLineWidthIu(int aWidth)
std::pair< double, double > OrcadDashRatios(int aFormatVersionMajor)
int OrcadTextBaselineOffset(int aTextSize)
std::vector< SEG > OrcadHatchLines(const EDA_SHAPE &aShape, int aHatchStyle, int aPitch)
int OrcadPageOrder(wxString &aName)
Return a numeric page prefix (or -1); strip only the "N - title" convention.
int OrcadHatchPitchIu(uint32_t aModifyTimestamp)
int OrcadHatchLineWidthIu(uint32_t aModifyTimestamp)
KIGFX::COLOR4D OrcadColor(int aColorIndex)
wxString OrcadPinNameMarkup(const wxString &aName)
LINE_STYLE OrcadLineStyle(int aStyle)
int OrcadPageGraphicLineWidthIu(int aWidth)
VECTOR2I OrcadStretchedImageSize(int aWidth, int aHeight, int aBoxWidth, int aBoxHeight)
std::optional< size_t > OrcadLongPrefixCount(int aTypeId)
Registered number of long prefixes for a modern framed structure type.
@ PT_POWER_IN
power input (GND, VCC for ICs). Must be connected to a power output.
Definition pin_type.h:42
std::string OrcadNormalizeCfbName(const std::string &aName)
Definition of the SCH_SHEET_PATH and SCH_SHEET_LIST classes for Eeschema.
wxString originalName
std::vector< wxString > occurrence
wxString nameAtImport
std::vector< KIID > itemUuids
std::vector< IMPORT_NET_TERMINAL > terminals
wxString view
uint32_t sourceNetId
IMPORT_NET_STATUS status
Import provenance, held only for the lifetime of the importing SCHEMATIC.
std::vector< IMPORT_NET_MAP_ENTRY > entries
std::vector< uint8_t > data
Definition ole_image.h:48
OLE_IMAGE_TYPE type
Definition ole_image.h:47
std::unique_ptr< SCHEMATIC > m_schematic
A simple container for schematic symbol instance information.
#define ALL_UNITS
Definition symbol.h:150
#define ALL_BODY_STYLES
Definition symbol.h:151
@ DESCRIPTION
Field Description of part, i.e. "1/4W 1% Metal Film Resistor".
@ FOOTPRINT
Field Name Module PCB, i.e. "16DIP300".
@ DATASHEET
name of datasheet
@ REFERENCE
Field Reference of part, i.e. "IC21".
@ VALUE
Field Value of part, i.e. "3.3K".
BOOST_AUTO_TEST_CASE(HorizontalAlignment)
BOOST_CHECK_EQUAL_COLLECTIONS(mixed.begin(), mixed.end(), expMixed.begin(), expMixed.end())
BOOST_REQUIRE(intersection.has_value()==c.ExpectedIntersection.has_value())
BOOST_AUTO_TEST_SUITE_END()
std::string path
IbisParser parser & reporter
KIBIS top(path, &reporter)
KIBIS_PIN * pin
VECTOR3I expected(15, 30, 45)
static std::vector< std::string > splitRefs(const std::string &aCell)
static std::filesystem::path findCorpusDesign(const std::filesystem::path &aRoot, const std::string &aFileName)
static std::set< std::string > parseBomRefs(const std::string &aPath)
static std::vector< std::set< std::string > > parseNetTerminals(const std::string &aPath)
Net terminal sets from .NET.
static std::set< std::string > expectedRefsFor(const std::filesystem::path &aDsn, std::string &aSource)
Ground-truth companion beside .DSN, .NET preferred over .BOM.
static std::string trimCell(std::string aText)
static SCH_SYMBOL * findConvertedSymbol(SCH_SCREEN &aScreen, const SCH_SHEET_PATH &aPath, const wxString &aReference)
static std::pair< int, int > checkConnectivity(SCHEMATIC &aSchematic, const std::vector< std::set< std::string > > &aNets, std::vector< std::set< std::string > > *aInconsistent=nullptr)
Count ground-truth nets whose resolvable terminals all land on one KiCad net after connectivity rebui...
BOOST_AUTO_TEST_CASE(StructurePrefixDepthIsTypeOwned)
static std::set< std::string > parseNetComs(const std::string &aPath)
static std::string terminalToken(const std::string &aRef, const std::string &aPin)
static std::set< std::string > collectImportedRefs(SCHEMATIC &aSchematic)
Unique refdes of real (non-power) parts.
static wxString terminalNetName(SCHEMATIC &aSchematic, const wxString &aReference, const wxString &aPinNumber)
static std::map< std::string, std::vector< std::string > > collectImportedUuids(const SCHEMATIC &aSchematic)
VECTOR2I end
BOOST_TEST_CONTEXT("Test Clearance")
BOOST_TEST_MESSAGE("Polyline has "<< chain.PointCount()<< " points")
int actual
BOOST_CHECK_EQUAL(result, "25.4")
@ GR_TEXT_H_ALIGN_LEFT
@ SCH_TABLE_T
Definition typeinfo.h:161
@ SCH_LINE_T
Definition typeinfo.h:159
@ SCH_NO_CONNECT_T
Definition typeinfo.h:156
@ SCH_SYMBOL_T
Definition typeinfo.h:168
@ SCH_LABEL_T
Definition typeinfo.h:163
@ SCH_SHEET_T
Definition typeinfo.h:171
@ SCH_SHAPE_T
Definition typeinfo.h:145
@ SCH_HIER_LABEL_T
Definition typeinfo.h:165
@ SCH_TEXT_T
Definition typeinfo.h:147
@ SCH_BITMAP_T
Definition typeinfo.h:160
@ SCH_GLOBAL_LABEL_T
Definition typeinfo.h:164
@ SCH_JUNCTION_T
Definition typeinfo.h:155
@ SCH_PIN_T
Definition typeinfo.h:149
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:708