20#include <boost/test/unit_test.hpp>
47#include <wx/filename.h>
62static void appendLe32( std::vector<uint8_t>& aBytes, uint32_t aValue )
64 for(
int shift = 0; shift < 32; shift += 8 )
65 aBytes.push_back(
static_cast<uint8_t
>( aValue >> shift ) );
69static void appendLe16( std::vector<uint8_t>& aBytes, uint16_t aValue )
71 aBytes.push_back(
static_cast<uint8_t
>( aValue ) );
72 aBytes.push_back(
static_cast<uint8_t
>( aValue >> 8 ) );
76static void appendLzt( std::vector<uint8_t>& aBytes,
const std::string& aValue )
78 appendLe16( aBytes,
static_cast<uint16_t
>( aValue.size() ) );
79 aBytes.insert( aBytes.end(), aValue.begin(), aValue.end() );
80 aBytes.push_back( 0 );
84static void writeLe16( std::vector<uint8_t>& aBytes,
size_t aOffset, uint16_t aValue )
86 aBytes[aOffset] =
static_cast<uint8_t
>( aValue );
87 aBytes[aOffset + 1] =
static_cast<uint8_t
>( aValue >> 8 );
91static void writeLe32( std::vector<uint8_t>& aBytes,
size_t aOffset, uint32_t aValue )
93 for(
int shift = 0; shift < 32; shift += 8 )
94 aBytes[aOffset++] =
static_cast<uint8_t
>( aValue >> shift );
98static std::vector<uint8_t> makeOlePreviewCfb(
const std::vector<uint16_t>& aName,
const std::vector<uint8_t>& aStream )
100 constexpr uint32_t FREE_SECTOR = 0xFFFFFFFF;
101 constexpr uint32_t END_OF_CHAIN = 0xFFFFFFFE;
102 constexpr uint32_t FAT_SECTOR = 0xFFFFFFFD;
103 constexpr size_t SECTOR_SIZE = 512;
104 constexpr size_t STREAM_SECTORS = 8;
106 std::vector<uint8_t> cfb( SECTOR_SIZE * ( 3 + STREAM_SECTORS ), 0 );
107 const uint8_t magic[] = { 0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1 };
108 std::copy( std::begin( magic ), std::end( magic ), cfb.begin() );
109 writeLe16( cfb, 24, 0x003E );
110 writeLe16( cfb, 26, 3 );
111 writeLe16( cfb, 28, 0xFFFE );
112 writeLe16( cfb, 30, 9 );
113 writeLe16( cfb, 32, 6 );
114 writeLe32( cfb, 44, 1 );
115 writeLe32( cfb, 48, 1 );
116 writeLe32( cfb, 56, 4096 );
117 writeLe32( cfb, 60, END_OF_CHAIN );
118 writeLe32( cfb, 68, END_OF_CHAIN );
120 for(
size_t i = 0; i < 109; ++i )
121 writeLe32( cfb, 76 + 4 * i, i == 0 ? 0 : FREE_SECTOR );
123 size_t fat = SECTOR_SIZE;
124 writeLe32( cfb, fat, FAT_SECTOR );
125 writeLe32( cfb, fat + 4, END_OF_CHAIN );
127 for(
size_t i = 0; i < STREAM_SECTORS; ++i )
128 writeLe32( cfb, fat + 4 * ( 2 + i ), i + 1 == STREAM_SECTORS ? END_OF_CHAIN : 3 + i );
130 for(
size_t i = 2 + STREAM_SECTORS; i < SECTOR_SIZE / 4; ++i )
131 writeLe32( cfb, fat + 4 * i, FREE_SECTOR );
133 size_t root = 2 * SECTOR_SIZE;
134 writeLe16( cfb, root,
'R' );
135 writeLe16( cfb, root + 2, 0 );
136 writeLe16( cfb, root + 64, 4 );
138 writeLe32( cfb, root + 68, FREE_SECTOR );
139 writeLe32( cfb, root + 72, FREE_SECTOR );
140 writeLe32( cfb, root + 76, 1 );
141 writeLe32( cfb, root + 116, END_OF_CHAIN );
143 size_t entry = root + 128;
145 for(
size_t i = 0; i < aName.size(); ++i )
146 writeLe16( cfb, entry + 2 * i, aName[i] );
148 writeLe16( cfb, entry + 64,
static_cast<uint16_t
>( 2 * aName.size() ) );
150 writeLe32( cfb, entry + 68, FREE_SECTOR );
151 writeLe32( cfb, entry + 72, FREE_SECTOR );
152 writeLe32( cfb, entry + 76, FREE_SECTOR );
153 writeLe32( cfb, entry + 116, 2 );
154 writeLe32( cfb, entry + 120, STREAM_SECTORS * SECTOR_SIZE );
156 std::copy_n( aStream.begin(), std::min( aStream.size(), STREAM_SECTORS * SECTOR_SIZE ),
157 cfb.begin() + 3 * SECTOR_SIZE );
164 TEMP_TEST_FILE(
const wxString& aFileName,
const wxString& aContents ) :
165 m_path( wxFileName( wxFileName::GetTempDir(), aFileName ).GetFullPath() )
167 wxFFile file( m_path, wxS(
"w" ) );
169 if( file.IsOpened() )
170 file.Write( aContents );
173 ~TEMP_TEST_FILE() { wxRemoveFile( m_path ); }
197 std::string
dataPath(
const std::string& aRelPath )
const
219 appendLe32( bytes, 32 );
220 appendLe32( bytes, 0 );
221 appendLe32( bytes, 10 );
222 appendLe32( bytes, 20 );
223 appendLe32( bytes, 30 );
224 appendLe32( bytes, 40 );
225 appendLe32( bytes, 1 );
226 appendLe32( bytes, 2 );
252 "Sch 2: PCI Connector" );
277 wxString dashed = wxS(
"03 - CAN" );
278 wxString dotted = wxS(
"02.uC" );
279 wxString colon = wxS(
"13:IMU" );
280 wxString folder = wxS(
"Sch 7: CAN Drivers" );
281 wxString pager = wxS(
"PAGER 8" );
282 wxString plain = wxS(
"Overview" );
302 appendLe32( bytes, 40 );
303 appendLe32( bytes, 0 );
304 appendLe32( bytes, 10 );
305 appendLe32( bytes, 20 );
306 appendLe32( bytes, 30 );
307 appendLe32( bytes, 40 );
308 appendLe32( bytes, 2 );
309 appendLe32( bytes, 3 );
310 appendLe32( bytes, 2 );
311 appendLe32( bytes, 5 );
327 appendLe32( bytes, 34 );
328 appendLe32( bytes, 0 );
329 appendLe32( bytes, 1 );
330 appendLe32( bytes, 2 );
331 appendLe32( bytes, 2 );
332 appendLe32( bytes, 4 );
333 appendLe16( bytes, 2 );
334 appendLe16( bytes, 20 );
335 appendLe16( bytes, 10 );
336 appendLe16( bytes, 40 );
337 appendLe16( bytes, 30 );
347 BOOST_REQUIRE_EQUAL( primitive->points.size(), 2u );
348 BOOST_CHECK( ( primitive->points[0] ==
ORCAD_POINT{ 10, 20 } ) );
349 BOOST_CHECK( ( primitive->points[1] ==
ORCAD_POINT{ 30, 40 } ) );
356 appendLe32( bytes, 63 );
357 appendLe32( bytes, 0 );
359 appendLe16( bytes, 0 );
361 appendLe32( bytes, 0 );
362 appendLe16( bytes, 12 );
363 appendLe16( bytes, 26 );
364 appendLe16( bytes, 1 );
365 bytes.insert( bytes.end(), { ORCAD_PRIM_POLYLINE, 0, ORCAD_PRIM_POLYLINE } );
366 appendLe32( bytes, 30 );
367 appendLe32( bytes, 0 );
368 appendLe32( bytes, 0 );
369 appendLe32( bytes, 0 );
370 appendLe16( bytes, 3 );
371 appendLe16( bytes, 8 );
372 appendLe16( bytes, 4 );
373 appendLe16( bytes, 0 );
374 appendLe16( bytes, 4 );
375 appendLe16( bytes, 0 );
376 appendLe16( bytes, 16 );
377 appendLe16( bytes, 10 );
378 bytes.insert( bytes.end(), {
'H',
'y',
's',
't',
'e',
'r',
'e',
's',
'i',
's', 0 } );
387 BOOST_REQUIRE_EQUAL( primitive->children.size(), 1u );
389 BOOST_REQUIRE_EQUAL( primitive->children[0].points.size(), 3u );
390 BOOST_CHECK( ( primitive->children[0].points[2] ==
ORCAD_POINT{ 16, 0 } ) );
396 std::vector<uint8_t> presentation( 4096, 0 );
397 writeLe32( presentation, 4, 14 );
398 presentation[40] = 1;
399 presentation[41] = 0;
400 presentation[42] = 9;
401 presentation[43] = 0;
403 std::vector<uint16_t>
name = { 2,
'O',
'l',
'e',
'P',
'r',
'e',
's',
'0',
'0',
'0', 0 };
407 BOOST_REQUIRE_EQUAL( preview.
data.size(), presentation.size() - 40 );
415 std::vector<uint8_t> native( 4096, 0 );
416 writeLe32( native, 0, 58 );
419 writeLe32( native, 6, 58 );
421 std::vector<uint16_t>
name = { 1,
'O',
'l',
'e',
'1',
'0',
'N',
'a',
't',
'i',
'v',
'e', 0 };
425 BOOST_REQUIRE_EQUAL( preview.
data.size(), native.size() - 4 );
434 appendLzt( bytes,
"PAGE" );
435 appendLzt( bytes,
"C" );
436 bytes.resize( bytes.size() + 156 );
437 appendLe16( bytes, 0 );
438 appendLe16( bytes, 0 );
439 appendLe16( bytes, 1 );
440 appendLe32( bytes, 0x12345678 );
441 appendLzt( bytes,
"BUS[1:0]" );
442 appendLe16( bytes, 2 );
443 appendLe32( bytes, 0x11111111 );
444 appendLe32( bytes, 0x22222222 );
446 for(
int i = 0; i < 10; ++i )
447 appendLe16( bytes, 0 );
449 std::vector<char> data( bytes.begin(), bytes.end() );
451 [](
const wxString& )
455 BOOST_REQUIRE_EQUAL( page.
netGroups.size(), 1u );
458 BOOST_REQUIRE_EQUAL( page.
netGroups[0].members.size(), 2u );
473 TEMP_TEST_FILE specctra( wxS(
"qa_orcad_specctra_impostor.dsn" ),
474 wxS(
"(pcb \"impostor.dsn\"\n (parser\n (string_quote \")\n )\n)\n" ) );
477 BOOST_CHECK( !m_plugin.CanReadSchematicFile( specctra.m_path ) );
483 wxFileName missing( wxFileName::GetTempDir(), wxS(
"qa_orcad_does_not_exist.dsn" ) );
486 BOOST_CHECK( !m_plugin.CanReadSchematicFile( missing.GetFullPath() ) );
492 TEMP_TEST_FILE textFile( wxS(
"qa_orcad_impostor.txt" ),
493 wxS(
"Just some text, not a schematic.\n" ) );
496 BOOST_CHECK( !m_plugin.CanReadSchematicFile( textFile.m_path ) );
515 auto notSpace = [](
unsigned char c ) {
return !std::isspace( c ); };
516 aText.erase( aText.begin(), std::find_if( aText.begin(), aText.end(), notSpace ) );
517 aText.erase( std::find_if( aText.rbegin(), aText.rend(), notSpace ).base(), aText.end() );
519 if( aText.size() >= 2 && aText.front() ==
'"' && aText.back() ==
'"' )
520 aText = aText.substr( 1, aText.size() - 2 );
526static std::vector<std::string>
splitRefs(
const std::string& aCell )
528 std::vector<std::string> refs;
531 for(
char c : aCell )
559 std::set<std::string> refs;
560 std::ifstream in( aPath );
564 while( std::getline( in, line ) )
566 if( !line.empty() && line.back() ==
'\r' )
569 std::vector<std::string> cols;
574 if( c ==
'\t' ) { cols.push_back( cell ); cell.clear(); }
578 cols.push_back( cell );
583 for(
size_t i = 0; i < cols.size(); ++i )
585 if(
trimCell( cols[i] ) ==
"Reference" )
587 refCol =
static_cast<int>( i );
595 if( refCol <
static_cast<int>( cols.size() ) )
608 std::set<std::string> refs;
609 std::ifstream in( aPath );
612 while( std::getline( in, line ) )
614 if( line.rfind(
".ADD_COM", 0 ) != 0 )
618 std::string rest =
trimCell( line.substr( 8 ) );
623 if( std::isspace(
static_cast<unsigned char>( c ) ) )
641 std::set<std::string> refs;
654 wxString ref = symbol->
GetRef( &
path,
false );
657 if( ref.IsEmpty() || ref.StartsWith( wxS(
"#" ) ) || ref.EndsWith( wxS(
"?" ) ) )
660 refs.insert( std::string( ref.ToUTF8() ) );
668static std::string
terminalToken(
const std::string& aRef,
const std::string& aPin )
678 std::vector<std::set<std::string>> nets;
679 std::set<std::string> current;
680 std::ifstream in( aPath );
685 if( current.size() >= 2 )
686 nets.push_back( current );
691 while( std::getline( in, line ) )
693 if( !line.empty() && line.back() ==
'\r' )
696 bool addTer = line.rfind(
".ADD_TER", 0 ) == 0;
697 bool ter = line.rfind(
".TER", 0 ) == 0;
698 bool cont = !line.empty() && std::isspace(
static_cast<unsigned char>( line[0] ) );
700 if( line.rfind(
".END", 0 ) == 0 )
706 if( addTer || ter || cont )
708 std::istringstream ss( addTer ? line.substr( 8 ) : ter ? line.substr( 4 ) : line );
709 std::string ref,
pin;
711 if( ss >> ref >>
pin )
724 std::vector<std::set<std::string>>* aInconsistent =
nullptr )
729 std::map<std::string, int> pinNet;
736 for(
SCH_ITEM* item : subgraph->GetItems() )
747 wxString ref = symbol->
GetRef( &subgraph->GetSheet(),
false );
749 if( ref.IsEmpty() || ref.StartsWith( wxS(
"#" ) ) || ref.EndsWith( wxS(
"?" ) ) )
753 std::string(
pin->GetNumber().ToUTF8() ) )] = netId;
762 const std::set<std::string>* terminals;
766 std::vector<CHECKED_NET> checkedNets;
767 std::map<int, int> sourceNetsPerImportedNet;
769 for(
const std::set<std::string>& net : aNets )
774 for(
const std::string& term : net )
776 auto it = pinNet.find( term );
778 if( it != pinNet.end() )
780 ids.insert( it->second );
787 if( ids.size() == 1 )
788 sourceNetsPerImportedNet[*ids.begin()]++;
790 checkedNets.push_back( { &net, std::move( ids ) } );
796 for(
const CHECKED_NET& net : checkedNets )
798 bool exact = net.ids.size() == 1 && sourceNetsPerImportedNet[*net.ids.begin()] == 1;
802 else if( aInconsistent )
803 aInconsistent->push_back( *net.terminals );
806 return { consistent,
static_cast<int>( checkedNets.size() ) };
812 std::string& aSource )
814 for(
const char* ext : {
".NET",
".net",
".BOM",
".bom" } )
816 std::filesystem::path candidate = aDsn;
817 candidate.replace_extension( ext );
819 if( std::filesystem::exists( candidate ) )
821 aSource = candidate.filename().string();
823 bool isNet = std::string( ext ) ==
".NET" || std::string( ext ) ==
".net";
836 const char* corpusEnv = std::getenv(
"KICAD_ORCAD_CORPUS" );
838 if( !corpusEnv || !*corpusEnv )
840 BOOST_TEST_MESSAGE(
"KICAD_ORCAD_CORPUS not set; skipping OrCAD corpus validation." );
844 namespace fs = std::filesystem;
845 fs::path root( corpusEnv );
847 BOOST_REQUIRE_MESSAGE( fs::exists( root ),
"KICAD_ORCAD_CORPUS path does not exist." );
849 std::vector<fs::path> designs;
851 for(
auto it = fs::recursive_directory_iterator( root,
852 fs::directory_options::skip_permission_denied );
853 it != fs::recursive_directory_iterator(); ++it )
855 if( !it->is_regular_file() )
858 std::string ext = it->path().extension().string();
859 std::transform( ext.begin(), ext.end(), ext.begin(),
860 [](
unsigned char c ) { return std::tolower( c ); } );
863 designs.push_back( it->path() );
866 std::sort( designs.begin(), designs.end() );
868 BOOST_TEST_MESSAGE(
"OrCAD corpus: " << designs.size() <<
" .DSN files under " << root );
870 int imported = 0, crashed = 0, unsupported = 0, rejected = 0, checked = 0;
871 unsigned int totalExpected = 0, totalMatched = 0, totalMissing = 0, totalExtra = 0;
872 int netConsistent = 0, netCheckable = 0, netTotal = 0;
873 uint64_t importedPages = 0, importedComponents = 0, importedPowerSymbols = 0;
874 uint64_t importedPins = 0, importedWires = 0, importedBuses = 0;
875 uint64_t importedLabels = 0, importedShapes = 0, importedTexts = 0, importedBitmaps = 0;
877 const char* debugEnv = std::getenv(
"KICAD_ORCAD_DEBUG" );
878 std::string debugFilter = debugEnv ? debugEnv :
"";
879 const char* filterEnv = std::getenv(
"KICAD_ORCAD_FILTER" );
880 std::string designFilter = filterEnv ? filterEnv :
"";
882 for(
const fs::path& dsn : designs )
884 std::string rel = fs::relative( dsn, root ).string();
886 if( !designFilter.empty() && rel.find( designFilter ) == std::string::npos )
890 uint64_t perDesignPages = 0, perDesignComponents = 0, perDesignPowerSymbols = 0;
891 uint64_t perDesignPins = 0, perDesignWires = 0, perDesignBuses = 0;
892 uint64_t perDesignLabels = 0, perDesignShapes = 0, perDesignTexts = 0;
893 uint64_t perDesignBitmaps = 0;
894 uint64_t perDesignRedWires = 0;
895 uint64_t perDesignVddmPowerSymbols = 0;
897 bool debug = !debugFilter.empty() && rel.find( debugFilter ) != std::string::npos;
905 std::unique_ptr<SCHEMATIC> schematic(
new SCHEMATIC(
nullptr ) );
908 schematic->SetProject( &manager.
Prj() );
909 schematic->CurrentSheet().clear();
910 schematic->CurrentSheet().push_back( &schematic->Root() );
918 schematic->CurrentSheet().UpdateAllScreenReferences();
920 catch(
const std::exception& e )
923 if( std::string( e.what() ).find(
"pre-2003" ) != std::string::npos )
946 switch( item->Type() )
951 wxString ref = symbol->
GetRef( &
path,
false );
953 if( ref.StartsWith( wxS(
"#" ) ) )
955 ++importedPowerSymbols;
956 ++perDesignPowerSymbols;
958 if( symbol->
GetValue(
false, &
path,
false ) == wxS(
"VDDM" ) )
959 ++perDesignVddmPowerSymbols;
963 ++importedComponents;
964 ++perDesignComponents;
968 importedPins += pinCount;
969 perDesignPins += pinCount;
1021 BOOST_TEST_MESSAGE(
" AUDIT " << rel <<
"|pages=" << perDesignPages <<
"|components=" << perDesignComponents
1022 <<
"|power=" << perDesignPowerSymbols <<
"|pins=" << perDesignPins
1023 <<
"|wires=" << perDesignWires <<
"|buses=" << perDesignBuses
1024 <<
"|labels=" << perDesignLabels <<
"|shapes=" << perDesignShapes
1025 <<
"|texts=" << perDesignTexts <<
"|bitmaps=" << perDesignBitmaps );
1027 if( rel ==
"allegro/beagleboard-xm/SCH/BeagleBoard-xM_ORCAD.DSN" )
1035 ==
"allegro/OpenCellular-LED/Rev-C/schematic/"
1036 "OpenCellular_Connect-1_LED_Life-3_Schematic.DSN" )
1042 ==
"orcad/OpenCellular-GBC-Elgon_ARM/Rev-A/schematics/"
1043 "CN81XX_GBCV2_sch_0530.DSN" )
1050 << std::string(
reporter.GetMessages().ToUTF8() ) );
1062 std::set<std::string> missing, extra;
1063 std::set_difference(
expected.begin(),
expected.end(), got.begin(), got.end(),
1064 std::inserter( missing, missing.begin() ) );
1065 std::set_difference( got.begin(), got.end(),
expected.begin(),
expected.end(),
1066 std::inserter( extra, extra.begin() ) );
1068 unsigned int matched =
static_cast<unsigned int>(
expected.size() - missing.size() );
1072 totalMatched += matched;
1073 totalMissing += missing.size();
1074 totalExtra += extra.size();
1077 <<
int( 100.0 * matched /
expected.size() ) <<
"%), extra " << extra.size()
1078 <<
" [" << source <<
"]" );
1082 for(
const std::string& ref : missing )
1085 for(
const std::string& ref : extra )
1090 std::filesystem::path net = dsn;
1091 net.replace_extension( source.size() >= 4 && source.substr( source.size() - 4 ) ==
".net"
1095 if( std::filesystem::exists( net ) )
1101 std::vector<std::set<std::string>> inconsistent;
1102 auto [consistent, checkableNets] =
checkConnectivity( *schematic, nets, &inconsistent );
1103 netConsistent += consistent;
1104 netCheckable += checkableNets;
1105 netTotal +=
static_cast<int>( nets.size() );
1108 <<
" nets consistent" );
1112 for(
const std::set<std::string>& terminals : inconsistent )
1116 for(
const std::string& terminal : terminals )
1118 if( !joined.empty() )
1132 BOOST_TEST_MESSAGE(
" designs: " << designs.size() <<
" imported: " << imported <<
" crashed: " << crashed
1133 <<
" unsupported: " << unsupported <<
" rejected: " << rejected );
1135 << importedPages <<
" components " << importedComponents <<
" power " << importedPowerSymbols
1136 <<
" pins " << importedPins <<
" wires " << importedWires <<
" buses " << importedBuses
1137 <<
" labels " << importedLabels <<
" shapes " << importedShapes <<
" texts " << importedTexts
1138 <<
" bitmaps " << importedBitmaps );
1142 BOOST_TEST_MESSAGE(
" refdes coverage: " << totalMatched <<
"/" << totalExpected <<
" ("
1143 <<
int( 100.0 * totalMatched / totalExpected )
1144 <<
"%) missing: " << totalMissing
1145 <<
" extra: " << totalExtra );
1150 BOOST_TEST_MESSAGE(
" net connectivity: " << netConsistent <<
"/" << netCheckable <<
" ("
1151 <<
int( 100.0 * netConsistent / netCheckable )
1158 const char* snapshotEnv = std::getenv(
"KICAD_ORCAD_CORPUS_SNAPSHOT" );
1161 if( designFilter.empty() )
1162 BOOST_REQUIRE_MESSAGE( checked > 0,
"No ground-truth .BOM/.NET companions were validated." );
1164 if( designFilter.empty() && snapshotEnv && *snapshotEnv )
1174 BOOST_CHECK_GE( 100.0 * totalMatched / totalExpected, 95.0 );
1180 BOOST_CHECK_GE( 100.0 * netCheckable / netTotal, 80.0 );
1187 const std::string& aFileName )
1189 for(
const std::filesystem::directory_entry& entry :
1190 std::filesystem::recursive_directory_iterator( aRoot ) )
1192 if( entry.is_regular_file() && entry.path().filename() == aFileName )
1193 return entry.path();
1202 const char* corpusEnv = std::getenv(
"KICAD_ORCAD_CORPUS" );
1204 if( !corpusEnv || !*corpusEnv )
1218 std::unique_ptr<SCHEMATIC> schematic(
new SCHEMATIC(
nullptr ) );
1221 schematic->SetProject( &manager.
Prj() );
1222 schematic->CurrentSheet().clear();
1223 schematic->CurrentSheet().push_back( &schematic->Root() );
1228 std::vector<SCH_SHEET*> topSheets = schematic->GetTopLevelSheets();
1229 BOOST_REQUIRE_EQUAL( topSheets.size(), 1u );
1231 SCH_SCREEN* rootScreen = topSheets.front()->GetScreen();
1233 size_t sheetPins = 0;
1239 sheetPins += sheet->
GetPins().size();
1242 const std::vector<wxString> expectedNames = { wxS(
"PAG_2" ), wxS(
"PAG_3" ), wxS(
"PAG_4" ),
1243 wxS(
"PAG_5" ), wxS(
"PAG_6" ), wxS(
"PAG_7" ),
1244 wxS(
"PAG_8" ), wxS(
"PAG_9" ) };
1245 const std::vector<size_t> expectedPinCounts = { 31, 24, 39, 47, 40, 33, 26, 10 };
1246 SCH_SHEET_LIST hierarchy = schematic->BuildSheetListSortedByPageNumbers();
1247 std::vector<wxString> sheetNames;
1248 std::vector<size_t> pinCounts;
1250 for(
auto it = std::next( hierarchy.begin() ); it != hierarchy.end(); ++it )
1253 std::set<wxString> sheetPinNames;
1254 std::set<wxString> hierarchicalLabelNames;
1257 pinCounts.push_back( sheet->
GetPins().size() );
1260 sheetPinNames.insert(
pin->GetText() );
1263 hierarchicalLabelNames.insert(
static_cast<SCH_HIERLABEL*
>( item )->GetText() );
1265 BOOST_CHECK_EQUAL_COLLECTIONS( sheetPinNames.begin(), sheetPinNames.end(),
1266 hierarchicalLabelNames.begin(), hierarchicalLabelNames.end() );
1269 schematic->ConnectionGraph()->Recalculate( hierarchy,
true );
1274 BOOST_CHECK_EQUAL_COLLECTIONS( sheetNames.begin(), sheetNames.end(), expectedNames.begin(),
1275 expectedNames.end() );
1276 BOOST_CHECK_EQUAL_COLLECTIONS( pinCounts.begin(), pinCounts.end(), expectedPinCounts.begin(),
1277 expectedPinCounts.end() );
1283 const char* corpusEnv = std::getenv(
"KICAD_ORCAD_CORPUS" );
1285 if( !corpusEnv || !*corpusEnv )
1291 std::filesystem::path dsn =
findCorpusDesign( corpusEnv,
"SE_NGFOC-L_01.DSN" );
1295 BOOST_TEST_MESSAGE(
"SE_NGFOC-L_01.DSN not present in corpus; skipping issue 25009." );
1299 std::unique_ptr<SCHEMATIC> schematic(
new SCHEMATIC(
nullptr ) );
1302 schematic->SetProject( &manager.
Prj() );
1303 schematic->CurrentSheet().clear();
1304 schematic->CurrentSheet().push_back( &schematic->Root() );
1309 const std::vector<wxString> expectedNames = {
1310 wxS(
"01.REV.HISTORY" ), wxS(
"02.uC" ), wxS(
"03.CAN" ),
1311 wxS(
"04. Ethercat" ), wxS(
"05.EtherSynch" ), wxS(
"06.RS-485" ),
1312 wxS(
"11.GPIO" ), wxS(
"12.Analog" ), wxS(
"13:IMU" ),
1313 wxS(
"14.Bridge" ), wxS(
"15.Encoder" ), wxS(
"29.uCPower" ),
1314 wxS(
"30.PowerSupply" ), wxS(
"31.Expansion" )
1317 std::vector<SCH_SHEET*> sheets = schematic->GetTopLevelSheets();
1318 BOOST_REQUIRE_EQUAL( sheets.size(), expectedNames.size() );
1325 for(
size_t i = 0; i < sheets.size(); ++i )
1329 for(
SCH_ITEM* item : sheets[i]->GetScreen()->Items() )
1373 const char* corpusEnv = std::getenv(
"KICAD_ORCAD_CORPUS" );
1375 if( !corpusEnv || !*corpusEnv )
1377 BOOST_TEST_MESSAGE(
"KICAD_ORCAD_CORPUS not set; skipping OrCAD OLB library import." );
1381 namespace fs = std::filesystem;
1382 std::vector<fs::path> libs;
1384 for(
auto it = fs::recursive_directory_iterator( fs::path( corpusEnv ),
1385 fs::directory_options::skip_permission_denied );
1386 it != fs::recursive_directory_iterator(); ++it )
1388 if( !it->is_regular_file() )
1391 std::string ext = it->path().extension().string();
1392 std::transform( ext.begin(), ext.end(), ext.begin(),
1393 [](
unsigned char c ) { return std::tolower( c ); } );
1396 libs.push_back( it->path() );
1399 std::sort( libs.begin(), libs.end() );
1402 int totalSymbols = 0, emptySymbols = 0, checkedLibs = 0, crashedLibs = 0;
1404 for(
const fs::path& lib : libs )
1407 std::vector<LIB_SYMBOL*> symbols;
1414 catch(
const std::exception& e )
1422 int withGeometry = 0;
1429 if( symbol->GetPinCount() > 0 || !symbol->GetDrawItems().empty() )
1436 <<
" symbols, " << withGeometry <<
" with pins/graphics" );
1440 <<
" crashed, " << totalSymbols <<
" symbols, "
1441 << emptySymbols <<
" empty" );
1445 BOOST_CHECK_GT( totalSymbols, 0 );
1448 BOOST_CHECK_LT( emptySymbols, totalSymbols / 2 );
1456 const char* corpusEnv = std::getenv(
"KICAD_ORCAD_CORPUS" );
1458 if( !corpusEnv || !*corpusEnv )
1460 BOOST_TEST_MESSAGE(
"KICAD_ORCAD_CORPUS not set; skipping OrCAD multi-page import." );
1464 namespace fs = std::filesystem;
1465 fs::path dsn = fs::path( corpusEnv ) /
"cutiepi-board" /
"CutiePi_V2.3-20210409.DSN";
1467 if( !fs::exists( dsn ) )
1473 std::unique_ptr<SCHEMATIC> schematic(
new SCHEMATIC(
nullptr ) );
1476 schematic->SetProject( &manager.
Prj() );
1477 schematic->CurrentSheet().clear();
1478 schematic->CurrentSheet().push_back( &schematic->Root() );
1484 std::vector<SCH_SHEET*> tops = schematic->GetTopLevelSheets();
1485 BOOST_REQUIRE_EQUAL( tops.size(), 3u );
1489 wxS(
"CM4,USB HUB,AUDIO,MIC" ) );
1491 wxS(
"CSI, DSI, HDMI, MCU" ) );
1493 std::set<wxString> globalLabels;
1498 globalLabels.insert(
static_cast<SCH_LABEL_BASE*
>( item )->GetText() );
1502 BOOST_CHECK( globalLabels.count( wxS(
"CAM0_IO1" ) ) );
1503 BOOST_CHECK( globalLabels.count( wxS(
"AMP_SHUTDOWN" ) ) );
1506 bool checkedField =
false;
1514 if( sym->
GetRef( &
path,
false ) == wxS(
"R3197" ) )
1518 checkedField =
true;
1523 BOOST_CHECK( checkedField );
1531 const char* corpusEnv = std::getenv(
"KICAD_ORCAD_CORPUS" );
1533 if( !corpusEnv || !*corpusEnv )
1535 BOOST_TEST_MESSAGE(
"KICAD_ORCAD_CORPUS not set; skipping OrCAD component detail." );
1539 namespace fs = std::filesystem;
1540 fs::path dsn = fs::path( corpusEnv ) /
"cutiepi-board" /
"CutiePi_V2.3-20210409.DSN";
1542 if( !fs::exists( dsn ) )
1548 std::unique_ptr<SCHEMATIC> schematic(
new SCHEMATIC(
nullptr ) );
1551 schematic->SetProject( &manager.
Prj() );
1552 schematic->CurrentSheet().clear();
1553 schematic->CurrentSheet().push_back( &schematic->Root() );
1558 std::map<wxString, SCH_SYMBOL*> symbols;
1559 std::multimap<wxString, int> labelSpins;
1588 BOOST_CHECK( symbols[wxS(
"U3" )]->GetShowPinNumbers() );
1589 BOOST_CHECK( symbols[wxS(
"U3" )]->GetShowPinNames() );
1591 BOOST_CHECK( !symbols[wxS(
"R3174" )]->GetShowPinNumbers() );
1592 BOOST_CHECK( !symbols[wxS(
"R3174" )]->GetShowPinNames() );
1596 BOOST_CHECK( !symbols[wxS(
"FB8" )]->GetField(
FIELD_T::VALUE )->IsVisible() );
1602 BOOST_CHECK_GT( fb8Ref->
GetPosition().
x, symbols[wxS(
"FB8" )]->GetPosition().x );
1609 for(
const wxString& ref : { wxS(
"R3186" ), wxS(
"C2517" ), wxS(
"R3189" ),
1610 wxS(
"FB13" ), wxS(
"FB9" ) } )
1612 BOOST_REQUIRE_MESSAGE( symbols.count( ref ), ref );
1618 BOOST_CHECK( symbols[wxS(
"FB9" )]->GetField(
FIELD_T::VALUE )->GetDrawRotation()
1620 BOOST_CHECK( symbols[wxS(
"C2517" )]->GetField(
FIELD_T::VALUE )->GetDrawRotation()
1624 bool checkedPower =
false;
1633 if( val == wxS(
"REG1V8" ) || val == wxS(
"REG3V3" ) )
1637 checkedPower =
true;
1642 BOOST_CHECK( checkedPower );
1645 BOOST_CHECK_GE( noConnects, 17 );
1650 auto range = labelSpins.equal_range( aText );
1652 for(
auto it = range.first; it != range.second; ++it )
1654 if( it->second == (
int) aSpin )
constexpr EDA_IU_SCALE schIUScale
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.
FILL_T GetFillMode() const
virtual const wxString & GetText() const
Return the string associated with the text object.
GR_TEXT_H_ALIGN_T GetHorizJustify() const
EE_TYPE OfType(KICAD_T aType) const
virtual void SetReporter(REPORTER *aReporter)
Set an optional reporter for warnings/errors.
A color representation with 4 components: red, green, blue, alpha.
static const COLOR4D UNSPECIFIED
For legacy support; used as a value to indicate color hasn't been set yet.
Define a library symbol object.
Little-endian byte cursor over one OrCAD Capture DSN stream.
static constexpr uint8_t PREAMBLE[4]
Magic preceding every framed structure body: FF E4 5C 39.
Holds all the data relating to one schematic.
SCH_SHEET_LIST BuildSheetListSortedByPageNumbers() const
CONNECTION_GRAPH * ConnectionGraph() const
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.
A SCH_IO derivation for loading OrCAD Capture schematic designs (.dsn).
bool CanReadSchematicFile(const wxString &aFileName) const override
The .dsn extension collides with SPECCTRA PCB session files (plain text), so beyond the extension gat...
SCH_SHEET * LoadSchematicFile(const wxString &aFileName, SCHEMATIC *aSchematic, SCH_SHEET *aAppendToMe=nullptr, const std::map< std::string, UTF8 > *aProperties=nullptr) override
Orchestration: root sheet/screen boilerplate (honoring aAppendToMe), CFB open, Library parse + versio...
void EnumerateSymbolLib(wxArrayString &aSymbolNameList, const wxString &aLibraryPath, const std::map< std::string, UTF8 > *aProperties=nullptr) override
Enumerate the symbols of an OrCAD .OLB library (an OLE2/CFB compound document with the same Library/C...
Base class for any item which can be embedded within the SCHEMATIC container class,...
SCH_LAYER_ID GetLayer() const
Return the layer this item is on.
SPIN_STYLE GetSpinStyle() const
Segment description base class to describe items which have 2 end points (track, wire,...
COLOR4D GetLineColor() const
Return COLOR4D::UNSPECIFIED if a custom color hasn't been set for this line.
EE_RTREE & Items()
Get the full RTree, usually for iterating.
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.
SCH_FIELD * GetField(FIELD_T aFieldType)
Return a mandatory field in this sheet.
SCH_SCREEN * GetScreen() const
std::vector< SCH_SHEET_PIN * > & GetPins()
std::vector< const SCH_PIN * > GetPins(const SCH_SHEET_PATH *aSheet) const
Retrieve a list of the SCH_PINs for the given sheet path.
const wxString GetValue(bool aResolve, const SCH_SHEET_PATH *aPath, bool aAllowExtraText, const wxString &aVariantName=wxEmptyString) const override
VECTOR2I GetPosition() const override
const wxString GetRef(const SCH_SHEET_PATH *aSheet, bool aIncludeUnit=false) const override
SCH_FIELD * GetField(FIELD_T aFieldType)
Return a mandatory field in this symbol.
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.
A wrapper for reporting to a wxString object.
static constexpr EDA_ANGLE ANGLE_VERTICAL
static constexpr EDA_ANGLE ANGLE_HORIZONTAL
@ FILLED_WITH_BG_BODYCOLOR
std::string GetEeschemaTestDataDir()
Get the configured location of Eeschema test data.
std::optional< ORCAD_PRIMITIVE > OrcadReadPrimitive(ORCAD_STREAM &aStream)
Read one graphic primitive including its doubled u8 type-pair prefix, at the current cursor.
Parsers for the DSN 'Cache' stream and the 'Packages/<name>' streams: symbol definitions with graphic...
ORCAD_CONVERTER turns a parsed ORCAD_DESIGN into KiCad schematic objects.
FILL_T OrcadFillType(int aFillStyle, int aHatchStyle)
int OrcadLineWidthIu(int aWidth)
int OrcadPageOrder(wxString &aName)
Return a numeric page prefix (or -1); strip only the "N - title" convention.
KIGFX::COLOR4D OrcadColor(int aColorIndex)
LINE_STYLE OrcadLineStyle(int aStyle)
ORCAD_OLE_PREVIEW OrcadExtractOlePreview(const std::vector< uint8_t > &aPayload)
ORCAD_RAW_PAGE OrcadParsePageV2(const std::vector< char > &aData, const std::vector< std::string > &aStrings, const ORCAD_WARN_FN &)
Parse one v2.0 (pre-2003) 'Views/<folder>/Pages/<page>' stream.
Parsers for the per-page streams 'Views/<folder>/Pages/<page>', the per-folder page-order stream 'Vie...
@ ORCAD_PRIM_SYMBOL_VECTOR
nested prefix-framed vector graphic
std::string OrcadNormalizeCfbName(const std::string &aName)
Definition of the SCH_SHEET_PATH and SCH_SHEET_LIST classes for Eeschema.
std::vector< uint8_t > data
ORCAD_OLE_PREVIEW_TYPE type
Integer point in OrCAD DBU.
One parsed 'Views/<folder>/Pages/<page>' stream, raw structure lists in stream order.
std::vector< ORCAD_NET_GROUP > netGroups
bus net id -> member net ids
ORCAD_SCH_IMPORT_FIXTURE()
SCH_SHEET * LoadOrcadSchematic(const std::string &aRelPath)
std::unique_ptr< SCHEMATIC > m_schematic
std::string dataPath(const std::string &aRelPath) const
SETTINGS_MANAGER m_manager
~ORCAD_SCH_IMPORT_FIXTURE()
@ REFERENCE
Field Reference of part, i.e. "IC21".
@ VALUE
Field Value of part, i.e. "3.3K".
BOOST_AUTO_TEST_CASE(HorizontalAlignment)
BOOST_REQUIRE(intersection.has_value()==c.ExpectedIntersection.has_value())
BOOST_AUTO_TEST_SUITE_END()
IbisParser parser & reporter
KIBIS top(path, &reporter)
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 std::set< std::string > parseNetComs(const std::string &aPath)
static std::string terminalToken(const std::string &aRef, const std::string &aPin)
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...
static std::set< std::string > collectImportedRefs(SCHEMATIC &aSchematic)
Unique refdes of real (non-power) parts.
BOOST_AUTO_TEST_CASE(PrimitiveLineWidth)
BOOST_CHECK_MESSAGE(totalMismatches==0, std::to_string(totalMismatches)+" board(s) with strategy disagreements")
BOOST_TEST_MESSAGE("\n=== Real-World Polygon PIP Benchmark ===\n"<< formatTable(table))
BOOST_CHECK_EQUAL(result, "25.4")