KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_diptrace_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
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 */
19
24
26
27
28BOOST_FIXTURE_TEST_SUITE( DipTraceSchImport, DIPTRACE_SCH_IMPORT_FIXTURE )
29
30
31
35BOOST_AUTO_TEST_CASE( CanReadSchematic )
36{
37 BOOST_CHECK( m_plugin.CanReadSchematicFile( GetTestDataDir() + "z80_board.dch" ) );
38 BOOST_CHECK( m_plugin.CanReadSchematicFile( GetTestDataDir() + "power_supply.dch" ) );
39 BOOST_CHECK( m_plugin.CanReadSchematicFile( GetTestDataDir() + "pppp.dch" ) );
40}
41
42
48BOOST_AUTO_TEST_CASE( V37Utf16SchematicLoadsWithContent )
49{
50 const std::string path = GetTestDataDir() + "power_supply.dch";
51
52 SCH_SHEET* root = LoadDipTraceSchematic( path );
53 BOOST_REQUIRE( root != nullptr );
54
55 BOOST_CHECK_GT( CountItemsOfType( SCH_SYMBOL_T ), 0 );
56 BOOST_CHECK_GT( CountItemsOfType( SCH_LINE_T ), 0 );
57
58 RemoveGeneratedLibrary( path );
59}
60
61
62BOOST_AUTO_TEST_CASE( InvalidComponentCountFailsDeterministically )
63{
64 const std::string sourcePath = GetTestDataDir() + "z80_board.dch";
65 wxString tempBase = wxFileName::CreateTempFileName( wxS( "kicad_diptrace_bad_count_" ) );
66 wxRemoveFile( tempBase );
67 wxString tempPath = tempBase + wxS( ".dch" );
68
69 BOOST_REQUIRE( wxCopyFile( sourcePath, tempPath ) );
70
71 {
72 static constexpr wxFileOffset COMPONENT_COUNT_OFFSET = 0xEE;
73 const uint8_t invalidCount[] = { 0x12, 0x4F, 0x81 }; // int3 value 200001
74
75 wxFFile file( tempPath, wxS( "r+b" ) );
76 BOOST_REQUIRE( file.IsOpened() );
77 BOOST_REQUIRE( file.Seek( COMPONENT_COUNT_OFFSET ) );
78 BOOST_REQUIRE_EQUAL( file.Write( invalidCount, sizeof( invalidCount ) ), sizeof( invalidCount ) );
79 }
80
81 BOOST_CHECK_THROW( LoadDipTraceSchematic( tempPath.ToStdString() ), IO_ERROR );
82
83 RemoveGeneratedLibrary( tempPath.ToStdString() );
84 wxRemoveFile( tempPath );
85}
86
87
88BOOST_AUTO_TEST_CASE( ComponentRecordsAreParsedSequentially )
89{
90 const std::string path = GetTestDataDir() + "z80_board.dch";
91
92 SCH_SHEET* rootSheet = new SCH_SHEET( m_schematic.get() );
93 const_cast<KIID&>( rootSheet->m_Uuid ) = niluuid;
94
95 wxFileName newFilename( path );
96 newFilename.SetExt( FILEEXT::KiCadSchematicFileExtension );
97 rootSheet->SetFileName( newFilename.GetFullPath() );
98 m_schematic->SetTopLevelSheets( { rootSheet } );
99
100 SCH_SCREEN* screen = new SCH_SCREEN( m_schematic.get() );
101 screen->SetFileName( newFilename.GetFullPath() );
102 rootSheet->SetScreen( screen );
103
104 DIPTRACE::SCH_PARSER parser( wxString::FromUTF8( path ), m_schematic.get(), rootSheet, nullptr, &m_reporter );
105 parser.Parse();
106
108
109 RemoveGeneratedLibrary( path );
110}
111
112
117BOOST_AUTO_TEST_CASE( NoSymbolLibraryFileIsGenerated )
118{
119 const std::string path = GetTestDataDir() + "z80_board.dch";
120
121 wxFileName genLib( path );
122 genLib.SetName( genLib.GetName() + wxT( "-diptrace-import" ) );
123 genLib.SetExt( wxT( "kicad_sym" ) );
124
125 if( genLib.FileExists() )
126 wxRemoveFile( genLib.GetFullPath() );
127
128 SCH_SHEET* root = LoadDipTraceSchematic( path );
129 BOOST_REQUIRE( root != nullptr );
130
131 BOOST_CHECK_MESSAGE( !genLib.FileExists(), "DipTrace import must not create a symbol library file: "
132 << genLib.GetFullPath().ToStdString() );
133
134 // The imported symbols stay usable because their definitions are embedded in the schematic.
135 std::set<SCH_SCREEN*> seenScreens;
136 std::vector<SCH_SCREEN*> pendingScreens;
137 bool foundEmbeddedSymbol = false;
138
139 if( root->GetScreen() )
140 pendingScreens.push_back( root->GetScreen() );
141
142 while( !pendingScreens.empty() && !foundEmbeddedSymbol )
143 {
144 SCH_SCREEN* screen = pendingScreens.back();
145 pendingScreens.pop_back();
146
147 if( !screen || !seenScreens.insert( screen ).second )
148 continue;
149
150 for( SCH_ITEM* item : screen->Items() )
151 {
152 if( item->Type() == SCH_SYMBOL_T )
153 {
154 if( static_cast<SCH_SYMBOL*>( item )->GetLibSymbolRef() )
155 {
156 foundEmbeddedSymbol = true;
157 break;
158 }
159 }
160 else if( item->Type() == SCH_SHEET_T )
161 {
162 SCH_SHEET* subSheet = static_cast<SCH_SHEET*>( item );
163
164 if( subSheet->GetScreen() )
165 pendingScreens.push_back( subSheet->GetScreen() );
166 }
167 }
168 }
169
170 BOOST_CHECK_MESSAGE( foundEmbeddedSymbol, "Imported schematic must carry embedded symbol definitions." );
171
172 if( genLib.FileExists() )
173 wxRemoveFile( genLib.GetFullPath() );
174}
175
176
182BOOST_AUTO_TEST_CASE( ImportedRootIsTheSchematicTopLevelSheet )
183{
184 const std::string path = GetTestDataDir() + "z80_board.dch";
185
186 SCH_SHEET* root = LoadDipTraceSchematic( path );
187 BOOST_REQUIRE( root != nullptr );
188 BOOST_REQUIRE( root->GetScreen() != nullptr );
189
190 BOOST_CHECK( root->m_Uuid != niluuid );
191 BOOST_CHECK_EQUAL( m_schematic->RootScreen(), root->GetScreen() );
192
193 SCH_SHEET_LIST hierarchy = m_schematic->BuildUnorderedSheetList();
194 BOOST_REQUIRE_GE( hierarchy.size(), 1u );
195
196 bool contentReachable = false;
197
198 for( const SCH_SHEET_PATH& sheetPath : hierarchy )
199 {
200 if( sheetPath.LastScreen() == root->GetScreen() )
201 {
202 contentReachable = true;
203 break;
204 }
205 }
206
207 BOOST_CHECK( contentReachable );
208
209 RemoveGeneratedLibrary( path );
210}
211
212
213BOOST_AUTO_TEST_CASE( ViewerExampleComponentRecordsAreParsedSequentiallyOptional )
214{
215 const std::string examplesDir = GetViewerExamplesDir();
216 const std::vector<std::string> paths = {
217 examplesDir + "/CNC_controller.dch",
218 examplesDir + "/Schematic_2.dch",
219 examplesDir + "/Schematic_4.dch",
220 examplesDir + "/Schematic_6.dch",
221 };
222
223 bool foundAny = false;
224
225 for( const std::string& path : paths )
226 {
227 if( !wxFileExists( path ) )
228 continue;
229
230 foundAny = true;
231
232 SCH_SHEET* rootSheet = new SCH_SHEET( m_schematic.get() );
233 const_cast<KIID&>( rootSheet->m_Uuid ) = niluuid;
234
235 wxFileName newFilename( path );
236 newFilename.SetExt( FILEEXT::KiCadSchematicFileExtension );
237 rootSheet->SetFileName( newFilename.GetFullPath() );
238 m_schematic->SetTopLevelSheets( { rootSheet } );
239
240 SCH_SCREEN* screen = new SCH_SCREEN( m_schematic.get() );
241 screen->SetFileName( newFilename.GetFullPath() );
242 rootSheet->SetScreen( screen );
243
244 DIPTRACE::SCH_PARSER parser( wxString::FromUTF8( path ), m_schematic.get(), rootSheet, nullptr, &m_reporter );
245 parser.Parse();
246
248 path + ": component boundary scan fallback used" );
249
250 RemoveGeneratedLibrary( path );
251 }
252
253 if( !foundAny )
254 {
255 BOOST_TEST_MESSAGE( "Skipping sequential component record check; no viewer examples found at "
256 << examplesDir );
257 }
258}
259
260
261BOOST_AUTO_TEST_CASE( ComponentCountMismatchFailsDeterministically )
262{
263 const std::string sourcePath = GetTestDataDir() + "z80_board.dch";
264 wxString tempBase = wxFileName::CreateTempFileName( wxS( "kicad_diptrace_count_mismatch_" ) );
265 wxRemoveFile( tempBase );
266 wxString tempPath = tempBase + wxS( ".dch" );
267
268 BOOST_REQUIRE( wxCopyFile( sourcePath, tempPath ) );
269
270 {
271 static constexpr wxFileOffset COMPONENT_COUNT_OFFSET = 0xEE;
272 const uint8_t mismatchedCount[] = { 0x0F, 0x42, 0xC5 }; // int3 value 133
273
274 wxFFile file( tempPath, wxS( "r+b" ) );
275 BOOST_REQUIRE( file.IsOpened() );
276 BOOST_REQUIRE( file.Seek( COMPONENT_COUNT_OFFSET ) );
277 BOOST_REQUIRE_EQUAL( file.Write( mismatchedCount, sizeof( mismatchedCount ) ), sizeof( mismatchedCount ) );
278 }
279
280 BOOST_CHECK_THROW( LoadDipTraceSchematic( tempPath.ToStdString() ), IO_ERROR );
281
282 RemoveGeneratedLibrary( tempPath.ToStdString() );
283 wxRemoveFile( tempPath );
284}
285
286
287BOOST_AUTO_TEST_CASE( InvalidComponentPinCountFailsDeterministically )
288{
289 const std::string sourcePath = GetTestDataDir() + "z80_board.dch";
290 wxString tempBase = wxFileName::CreateTempFileName( wxS( "kicad_diptrace_bad_pin_count_" ) );
291 wxRemoveFile( tempBase );
292 wxString tempPath = tempBase + wxS( ".dch" );
293
294 BOOST_REQUIRE( wxCopyFile( sourcePath, tempPath ) );
295
296 {
297 static constexpr wxFileOffset FIRST_COMPONENT_PIN_COUNT_OFFSET = 0x20A;
298 const uint8_t invalidPinCount[] = { 0x12, 0x4F, 0x81 }; // int3 value 200001
299
300 wxFFile file( tempPath, wxS( "r+b" ) );
301 BOOST_REQUIRE( file.IsOpened() );
302 BOOST_REQUIRE( file.Seek( FIRST_COMPONENT_PIN_COUNT_OFFSET ) );
303 BOOST_REQUIRE_EQUAL( file.Write( invalidPinCount, sizeof( invalidPinCount ) ), sizeof( invalidPinCount ) );
304 }
305
306 BOOST_CHECK_THROW( LoadDipTraceSchematic( tempPath.ToStdString() ), IO_ERROR );
307
308 RemoveGeneratedLibrary( tempPath.ToStdString() );
309 wxRemoveFile( tempPath );
310}
311
312
313BOOST_AUTO_TEST_CASE( InvalidComponentExtraTailLengthFailsDeterministically )
314{
315 const std::string sourcePath = GetTestDataDir() + "z80_board.dch";
316 wxString tempBase = wxFileName::CreateTempFileName( wxS( "kicad_diptrace_bad_extra_tail_" ) );
317 wxRemoveFile( tempBase );
318 wxString tempPath = tempBase + wxS( ".dch" );
319
320 BOOST_REQUIRE( wxCopyFile( sourcePath, tempPath ) );
321
322 {
323 static constexpr wxFileOffset FIRST_COMPONENT_EXTRA_TAIL_COUNT_OFFSET = 0x206;
324 const uint8_t invalidExtraTailCount[] = { 0x00, 0x00, 0x27, 0x10 }; // u4 count 10000
325
326 wxFFile file( tempPath, wxS( "r+b" ) );
327 BOOST_REQUIRE( file.IsOpened() );
328 BOOST_REQUIRE( file.Seek( FIRST_COMPONENT_EXTRA_TAIL_COUNT_OFFSET ) );
329 BOOST_REQUIRE_EQUAL( file.Write( invalidExtraTailCount, sizeof( invalidExtraTailCount ) ),
330 sizeof( invalidExtraTailCount ) );
331 }
332
333 BOOST_CHECK_THROW( LoadDipTraceSchematic( tempPath.ToStdString() ), IO_ERROR );
334
335 RemoveGeneratedLibrary( tempPath.ToStdString() );
336 wxRemoveFile( tempPath );
337}
338
339
340BOOST_AUTO_TEST_CASE( InvalidComponentPinRecordFailsDeterministically )
341{
342 const std::string sourcePath = GetTestDataDir() + "z80_board.dch";
343 wxString tempBase = wxFileName::CreateTempFileName( wxS( "kicad_diptrace_bad_pin_" ) );
344 wxRemoveFile( tempBase );
345 wxString tempPath = tempBase + wxS( ".dch" );
346
347 BOOST_REQUIRE( wxCopyFile( sourcePath, tempPath ) );
348
349 {
350 static constexpr wxFileOffset FIRST_COMPONENT_FIRST_PIN_NAME_LEN_OFFSET = 0x226;
351 const uint8_t invalidNameLength[] = { 0xFF, 0xFF };
352
353 wxFFile file( tempPath, wxS( "r+b" ) );
354 BOOST_REQUIRE( file.IsOpened() );
355 BOOST_REQUIRE( file.Seek( FIRST_COMPONENT_FIRST_PIN_NAME_LEN_OFFSET ) );
356 BOOST_REQUIRE_EQUAL( file.Write( invalidNameLength, sizeof( invalidNameLength ) ),
357 sizeof( invalidNameLength ) );
358 }
359
360 BOOST_CHECK_THROW( LoadDipTraceSchematic( tempPath.ToStdString() ), IO_ERROR );
361
362 RemoveGeneratedLibrary( tempPath.ToStdString() );
363 wxRemoveFile( tempPath );
364}
365
366
367BOOST_AUTO_TEST_CASE( InvalidLaterComponentPinRecordFailsDeterministically )
368{
369 const std::string sourcePath = GetTestDataDir() + "z80_board.dch";
370 wxString tempBase = wxFileName::CreateTempFileName( wxS( "kicad_diptrace_bad_later_pin_" ) );
371 wxRemoveFile( tempBase );
372 wxString tempPath = tempBase + wxS( ".dch" );
373
374 BOOST_REQUIRE( wxCopyFile( sourcePath, tempPath ) );
375
376 {
377 static constexpr wxFileOffset FIRST_COMPONENT_SECOND_PIN_NAME_LEN_OFFSET = 0x272;
378 const uint8_t invalidNameLength[] = { 0xFF, 0xFF };
379
380 wxFFile file( tempPath, wxS( "r+b" ) );
381 BOOST_REQUIRE( file.IsOpened() );
382 BOOST_REQUIRE( file.Seek( FIRST_COMPONENT_SECOND_PIN_NAME_LEN_OFFSET ) );
383 BOOST_REQUIRE_EQUAL( file.Write( invalidNameLength, sizeof( invalidNameLength ) ),
384 sizeof( invalidNameLength ) );
385 }
386
387 BOOST_CHECK_THROW( LoadDipTraceSchematic( tempPath.ToStdString() ), IO_ERROR );
388
389 RemoveGeneratedLibrary( tempPath.ToStdString() );
390 wxRemoveFile( tempPath );
391}
392
393
394BOOST_AUTO_TEST_CASE( InvalidComponentShapePointCountFailsDeterministically )
395{
396 const std::string sourcePath = GetTestDataDir() + "z80_board.dch";
397 wxString tempBase = wxFileName::CreateTempFileName( wxS( "kicad_diptrace_bad_shape_points_" ) );
398 wxRemoveFile( tempBase );
399 wxString tempPath = tempBase + wxS( ".dch" );
400
401 BOOST_REQUIRE( wxCopyFile( sourcePath, tempPath ) );
402
403 {
404 static constexpr wxFileOffset FIRST_COMPONENT_SHAPE_POINT_COUNT_OFFSET = 0x2C2;
405 const uint8_t invalidPointCount[] = { 0x0F, 0x42, 0xA5 }; // int3 value 101
406
407 wxFFile file( tempPath, wxS( "r+b" ) );
408 BOOST_REQUIRE( file.IsOpened() );
409 BOOST_REQUIRE( file.Seek( FIRST_COMPONENT_SHAPE_POINT_COUNT_OFFSET ) );
410 BOOST_REQUIRE_EQUAL( file.Write( invalidPointCount, sizeof( invalidPointCount ) ),
411 sizeof( invalidPointCount ) );
412 }
413
414 BOOST_CHECK_THROW( LoadDipTraceSchematic( tempPath.ToStdString() ), IO_ERROR );
415
416 RemoveGeneratedLibrary( tempPath.ToStdString() );
417 wxRemoveFile( tempPath );
418}
419
420
421BOOST_AUTO_TEST_CASE( ZeroComponentShapePointCountFailsDeterministically )
422{
423 const std::string sourcePath = GetTestDataDir() + "z80_board.dch";
424 wxString tempBase = wxFileName::CreateTempFileName( wxS( "kicad_diptrace_zero_shape_points_" ) );
425 wxRemoveFile( tempBase );
426 wxString tempPath = tempBase + wxS( ".dch" );
427
428 BOOST_REQUIRE( wxCopyFile( sourcePath, tempPath ) );
429
430 {
431 static constexpr wxFileOffset FIRST_COMPONENT_SHAPE_POINT_COUNT_OFFSET = 0x2C2;
432 const uint8_t zeroPointCount[] = { 0x0F, 0x42, 0x40 }; // int3 value 0
433
434 wxFFile file( tempPath, wxS( "r+b" ) );
435 BOOST_REQUIRE( file.IsOpened() );
436 BOOST_REQUIRE( file.Seek( FIRST_COMPONENT_SHAPE_POINT_COUNT_OFFSET ) );
437 BOOST_REQUIRE_EQUAL( file.Write( zeroPointCount, sizeof( zeroPointCount ) ), sizeof( zeroPointCount ) );
438 }
439
440 BOOST_CHECK_THROW( LoadDipTraceSchematic( tempPath.ToStdString() ), IO_ERROR );
441
442 RemoveGeneratedLibrary( tempPath.ToStdString() );
443 wxRemoveFile( tempPath );
444}
445
446
447BOOST_AUTO_TEST_CASE( InvalidBusEntryTerminatorFailsDeterministically )
448{
449 const std::string sourcePath = GetTestDataDir() + "z80_board.dch";
450 wxString tempBase = wxFileName::CreateTempFileName( wxS( "kicad_diptrace_bad_bus_" ) );
451 wxRemoveFile( tempBase );
452 wxString tempPath = tempBase + wxS( ".dch" );
453
454 BOOST_REQUIRE( wxCopyFile( sourcePath, tempPath ) );
455
456 {
457 static constexpr wxFileOffset FIRST_BUS_TERMINATOR_OFFSET = 0x39484;
458 const uint8_t invalidTerminator[] = { 0x0F, 0x42, 0x40 }; // int3 value 0
459
460 wxFFile file( tempPath, wxS( "r+b" ) );
461 BOOST_REQUIRE( file.IsOpened() );
462 BOOST_REQUIRE( file.Seek( FIRST_BUS_TERMINATOR_OFFSET ) );
463 BOOST_REQUIRE_EQUAL( file.Write( invalidTerminator, sizeof( invalidTerminator ) ),
464 sizeof( invalidTerminator ) );
465 }
466
467 BOOST_CHECK_THROW( LoadDipTraceSchematic( tempPath.ToStdString() ), IO_ERROR );
468
469 RemoveGeneratedLibrary( tempPath.ToStdString() );
470 wxRemoveFile( tempPath );
471}
472
473
474BOOST_AUTO_TEST_CASE( InvalidBusSectionCountFailsDeterministically )
475{
476 const std::string sourcePath = GetTestDataDir() + "z80_board.dch";
477 wxString tempBase = wxFileName::CreateTempFileName( wxS( "kicad_diptrace_bad_bus_count_" ) );
478 wxRemoveFile( tempBase );
479 wxString tempPath = tempBase + wxS( ".dch" );
480
481 BOOST_REQUIRE( wxCopyFile( sourcePath, tempPath ) );
482
483 {
484 static constexpr wxFileOffset BUS_SECTION_COUNT_OFFSET = 0x3946A;
485 const uint8_t invalidCount[] = { 0x0F, 0x46, 0x29 }; // int3 value 1001
486
487 wxFFile file( tempPath, wxS( "r+b" ) );
488 BOOST_REQUIRE( file.IsOpened() );
489 BOOST_REQUIRE( file.Seek( BUS_SECTION_COUNT_OFFSET ) );
490 BOOST_REQUIRE_EQUAL( file.Write( invalidCount, sizeof( invalidCount ) ), sizeof( invalidCount ) );
491 }
492
493 BOOST_CHECK_THROW( LoadDipTraceSchematic( tempPath.ToStdString() ), IO_ERROR );
494
495 RemoveGeneratedLibrary( tempPath.ToStdString() );
496 wxRemoveFile( tempPath );
497}
498
499
500BOOST_AUTO_TEST_CASE( InvalidWireNetPinCountFailsDeterministically )
501{
502 const std::string sourcePath = GetTestDataDir() + "z80_board.dch";
503 wxString tempBase = wxFileName::CreateTempFileName( wxS( "kicad_diptrace_bad_wire_pin_count_" ) );
504 wxRemoveFile( tempBase );
505 wxString tempPath = tempBase + wxS( ".dch" );
506
507 BOOST_REQUIRE( wxCopyFile( sourcePath, tempPath ) );
508
509 {
510 static constexpr wxFileOffset FIRST_WIRE_NET_PIN_COUNT_OFFSET = 0x3988E;
511 const uint8_t invalidPinCount[] = { 0x0F, 0x51, 0xE9 }; // int3 value 4001
512
513 wxFFile file( tempPath, wxS( "r+b" ) );
514 BOOST_REQUIRE( file.IsOpened() );
515 BOOST_REQUIRE( file.Seek( FIRST_WIRE_NET_PIN_COUNT_OFFSET ) );
516 BOOST_REQUIRE_EQUAL( file.Write( invalidPinCount, sizeof( invalidPinCount ) ), sizeof( invalidPinCount ) );
517 }
518
519 BOOST_CHECK_THROW( LoadDipTraceSchematic( tempPath.ToStdString() ), IO_ERROR );
520
521 RemoveGeneratedLibrary( tempPath.ToStdString() );
522 wxRemoveFile( tempPath );
523}
524
525
526BOOST_AUTO_TEST_CASE( InvalidWireNetNameLengthFailsDeterministically )
527{
528 const std::string sourcePath = GetTestDataDir() + "z80_board.dch";
529 wxString tempBase = wxFileName::CreateTempFileName( wxS( "kicad_diptrace_bad_wire_name_" ) );
530 wxRemoveFile( tempBase );
531 wxString tempPath = tempBase + wxS( ".dch" );
532
533 BOOST_REQUIRE( wxCopyFile( sourcePath, tempPath ) );
534
535 {
536 static constexpr wxFileOffset FIRST_WIRE_NET_NAME_LENGTH_OFFSET = 0x3987A;
537 const uint8_t invalidNameLength[] = { 0xFF, 0xFF };
538
539 wxFFile file( tempPath, wxS( "r+b" ) );
540 BOOST_REQUIRE( file.IsOpened() );
541 BOOST_REQUIRE( file.Seek( FIRST_WIRE_NET_NAME_LENGTH_OFFSET ) );
542 BOOST_REQUIRE_EQUAL( file.Write( invalidNameLength, sizeof( invalidNameLength ) ),
543 sizeof( invalidNameLength ) );
544 }
545
546 BOOST_CHECK_THROW( LoadDipTraceSchematic( tempPath.ToStdString() ), IO_ERROR );
547
548 RemoveGeneratedLibrary( tempPath.ToStdString() );
549 wxRemoveFile( tempPath );
550}
551
552
553BOOST_AUTO_TEST_CASE( InvalidWirePointCountFailsDeterministically )
554{
555 const std::string sourcePath = GetTestDataDir() + "z80_board.dch";
556 wxString tempBase = wxFileName::CreateTempFileName( wxS( "kicad_diptrace_bad_wire_point_count_" ) );
557 wxRemoveFile( tempBase );
558 wxString tempPath = tempBase + wxS( ".dch" );
559
560 BOOST_REQUIRE( wxCopyFile( sourcePath, tempPath ) );
561
562 {
563 static constexpr wxFileOffset FIRST_WIRE_POINT_COUNT_OFFSET = 0x39913;
564 const uint8_t invalidPointCount[] = { 0x0F, 0x51, 0xE9 }; // int3 value 4001
565
566 wxFFile file( tempPath, wxS( "r+b" ) );
567 BOOST_REQUIRE( file.IsOpened() );
568 BOOST_REQUIRE( file.Seek( FIRST_WIRE_POINT_COUNT_OFFSET ) );
569 BOOST_REQUIRE_EQUAL( file.Write( invalidPointCount, sizeof( invalidPointCount ) ),
570 sizeof( invalidPointCount ) );
571 }
572
573 BOOST_CHECK_THROW( LoadDipTraceSchematic( tempPath.ToStdString() ), IO_ERROR );
574
575 RemoveGeneratedLibrary( tempPath.ToStdString() );
576 wxRemoveFile( tempPath );
577}
578
579
589BOOST_AUTO_TEST_CASE( ImportChainIsClean )
590{
591 for( const char* file : { "z80_board.dch", "power_supply.dch", "pppp.dch" } )
592 {
593 const std::string path = GetTestDataDir() + file;
594
595 if( !wxFileExists( path ) )
596 continue;
597
598 SCH_SHEET* root = nullptr;
599 BOOST_REQUIRE_NO_THROW( root = LoadDipTraceSchematic( path ) );
600 BOOST_REQUIRE( root );
601
602 BOOST_CHECK_MESSAGE( m_reporter.CountOfSeverity( RPT_SEVERITY_ERROR ) == 0,
603 "Import of " << file << " reported errors" );
604 BOOST_CHECK_MESSAGE( m_reporter.CountOfSeverity( RPT_SEVERITY_WARNING ) == 0,
605 "Import of " << file << " reported warnings" );
606
607 RemoveGeneratedLibrary( path );
608 }
609}
610
611
612BOOST_AUTO_TEST_CASE( CanReadLegacyHeaderOptional )
613{
614 const std::string examplesDir = GetViewerExamplesDir();
615 const std::string sch2 = examplesDir + "/Schematic_2.dch";
616 const std::string sch4 = examplesDir + "/Schematic_4.dch";
617
618 if( !wxFileExists( sch2 ) || !wxFileExists( sch4 ) )
619 {
620 BOOST_TEST_MESSAGE( "Skipping legacy header check; viewer examples not found at " << examplesDir );
621 return;
622 }
623
624 BOOST_CHECK( m_plugin.CanReadSchematicFile( sch2 ) );
625 BOOST_CHECK( m_plugin.CanReadSchematicFile( sch4 ) );
626}
627
628
629BOOST_AUTO_TEST_CASE( ViewerExamplesLoadOptional )
630{
631 const std::string examplesDir = GetViewerExamplesDir();
632
633 struct EXPECTED
634 {
635 const char* fileName;
636 int minSymbols;
637 int minScreens;
638 };
639
640 const std::vector<EXPECTED> expected = {
641 { "CNC_controller.dch", 100, 3 },
642 { "Schematic_2.dch", 50, 1 },
643 { "Schematic_4.dch", 70, 1 },
644 { "Schematic_6.dch", 120, 1 },
645 };
646
647 bool foundAny = false;
648
649 for( const EXPECTED& e : expected )
650 {
651 const std::string path = examplesDir + "/" + e.fileName;
652
653 if( !wxFileExists( path ) )
654 continue;
655
656 foundAny = true;
657
658 SCH_SHEET* root = nullptr;
659
660 try
661 {
662 root = LoadDipTraceSchematic( path );
663 }
664 catch( const std::exception& eex )
665 {
666 BOOST_FAIL( "Failed to load " << path << ": " << eex.what() );
667 }
668 catch( ... )
669 {
670 BOOST_FAIL( "Failed to load " << path << ": unknown exception" );
671 }
672
673 BOOST_REQUIRE_MESSAGE( root != nullptr, "Failed to load " << path );
674 BOOST_REQUIRE( root->GetScreen() );
675
676 // DipTrace net ports import as global labels rather than symbols, so count both as
677 // placement-equivalent items (the minSymbols thresholds predate the net-port split).
678 const int symbolCount = CountItemsOfType( SCH_SYMBOL_T ) + CountItemsOfType( SCH_GLOBAL_LABEL_T );
679 const int screenCount = CountImportedScreens();
680
681 BOOST_CHECK_GE( symbolCount, e.minSymbols );
682 BOOST_CHECK_GE( screenCount, e.minScreens );
683 }
684
685 if( !foundAny )
686 {
687 BOOST_TEST_MESSAGE( "Skipping external example load test; no .dch files found at " << examplesDir );
688 }
689}
690
691
692BOOST_AUTO_TEST_CASE( ExternalCorpusLoadOptional )
693{
694 const char* corpusEnv = std::getenv( "DIPTRACE_EXTERNAL_CORPUS_DIR" );
695
696 if( !corpusEnv || !*corpusEnv )
697 {
698 BOOST_TEST_MESSAGE( "DIPTRACE_EXTERNAL_CORPUS_DIR not set; skipping external schematic corpus sweep" );
699 return;
700 }
701
702 std::filesystem::path corpusRoot( corpusEnv );
703
704 if( !std::filesystem::exists( corpusRoot ) )
705 {
706 BOOST_TEST_MESSAGE( "External corpus path does not exist; skipping external schematic corpus sweep" );
707 return;
708 }
709
710 std::vector<std::filesystem::path> dchFiles;
711
712 for( const auto& entry : std::filesystem::recursive_directory_iterator( corpusRoot ) )
713 {
714 if( entry.is_regular_file() && entry.path().extension() == ".dch" )
715 dchFiles.push_back( entry.path() );
716 }
717
718 std::sort( dchFiles.begin(), dchFiles.end() );
719 BOOST_REQUIRE_MESSAGE( !dchFiles.empty(), "No .dch files found under: " + corpusRoot.string() );
720
721 int loaded = 0;
722
723 for( const std::filesystem::path& path : dchFiles )
724 {
725 SCH_SHEET* root = nullptr;
726
727 try
728 {
729 root = LoadDipTraceSchematic( path.string() );
730 }
731 catch( const std::exception& e )
732 {
733 BOOST_ERROR( path.string() + ": exception: " + std::string( e.what() ) );
734 continue;
735 }
736
737 BOOST_REQUIRE_MESSAGE( root != nullptr, "Failed to load " + path.string() );
738 BOOST_REQUIRE( root->GetScreen() );
739 loaded++;
740
742 m_reporter.CountOfSeverity( RPT_SEVERITY_ERROR ) == 0,
743 path.string() + ": import reported errors: " + m_reporter.MessagesOfSeverity( RPT_SEVERITY_ERROR ) );
744 BOOST_CHECK_MESSAGE( m_reporter.CountOfSeverity( RPT_SEVERITY_WARNING ) == 0,
745 path.string() + ": import reported warnings: "
746 + m_reporter.MessagesOfSeverity( RPT_SEVERITY_WARNING ) );
747
748 RemoveGeneratedLibrary( path.string() );
749 }
750
751 BOOST_CHECK_MESSAGE( loaded > 0, "External schematic corpus sweep loaded zero schematics" );
752}
753
754
755BOOST_AUTO_TEST_CASE( ViewerExamplesDchXmlParityOptional )
756{
757 const std::string examplesDir = GetViewerExamplesDir();
758
759 struct EXPECTED
760 {
761 const char* dchFile;
762 const char* dchXmlFile;
763 double minPartCoverage;
764 };
765
766 // Coverage is measured against real (non-net-port) parts. CNC_controller relies more on the
767 // heuristic boundary scanner (its count-guided header desyncs), which recovers ~80% of real
768 // symbols there; the other examples decode cleanly at >=95%.
769 const std::vector<EXPECTED> expected = {
770 { "CNC_controller.dch", "CNC_controller.dchxml", 0.78 },
771 { "Schematic_2.dch", "PCB_2.dchxml", 0.95 },
772 { "Schematic_4.dch", "PCB_4.dchxml", 0.95 },
773 { "Schematic_6.dch", "PCB_6.dchxml", 0.95 },
774 };
775
776 bool foundAny = false;
777
778 for( const EXPECTED& e : expected )
779 {
780 const std::string dchPath = examplesDir + "/" + e.dchFile;
781 const std::string xmlPath = examplesDir + "/" + e.dchXmlFile;
782
783 if( !wxFileExists( dchPath ) || !wxFileExists( xmlPath ) )
784 continue;
785
786 foundAny = true;
787
788 DCH_XML_COUNTS xmlCounts;
789 BOOST_REQUIRE_MESSAGE( LoadDchXmlCounts( xmlPath, xmlCounts ), "Failed to parse " << xmlPath );
790 BOOST_REQUIRE_GT( xmlCounts.partCount, 0 );
791 BOOST_REQUIRE_GT( xmlCounts.sheetCount, 0 );
792
793 SCH_SHEET* root = nullptr;
794
795 try
796 {
797 root = LoadDipTraceSchematic( dchPath );
798 }
799 catch( const std::exception& eex )
800 {
801 BOOST_FAIL( "Failed to load " << dchPath << ": " << eex.what() );
802 }
803 catch( ... )
804 {
805 BOOST_FAIL( "Failed to load " << dchPath << ": unknown exception" );
806 }
807
808 BOOST_REQUIRE( root != nullptr );
809 BOOST_REQUIRE( root->GetScreen() );
810
811 // Net ports import as global labels, not symbols, so compare real symbols against the
812 // non-net-port part count, and verify the net ports are represented as labels.
813 const int importedSymbols = CountItemsOfType( SCH_SYMBOL_T );
814 const int importedLabels = CountItemsOfType( SCH_GLOBAL_LABEL_T );
815 const int importedScreens = CountImportedScreens();
816 const int realParts = xmlCounts.partCount - xmlCounts.netPortCount;
817 const int minParts = static_cast<int>( std::floor( realParts * e.minPartCoverage ) );
818
819 BOOST_CHECK_EQUAL( importedScreens, xmlCounts.sheetCount );
820 BOOST_CHECK_LE( importedSymbols, realParts + 2 );
821 BOOST_CHECK_GE( importedSymbols, minParts );
822
823 if( xmlCounts.netPortCount > 0 )
824 BOOST_CHECK_GE( importedLabels, 1 );
825 }
826
827 if( !foundAny )
828 {
829 BOOST_TEST_MESSAGE( "Skipping DCH/XML parity test; no matched files found at " << examplesDir );
830 }
831}
832
833
834BOOST_AUTO_TEST_CASE( ViewerExamplesPinCountsOptional )
835{
836 const std::string examplesDir = GetViewerExamplesDir();
837 const std::string cncPath = examplesDir + "/CNC_controller.dch";
838 const std::string sch6Path = examplesDir + "/Schematic_6.dch";
839
840 if( !wxFileExists( cncPath ) || !wxFileExists( sch6Path ) )
841 {
842 BOOST_TEST_MESSAGE( "Skipping pin-count checks; required viewer examples not found at " << examplesDir );
843 return;
844 }
845
846 {
847 SCH_SHEET* root = LoadDipTraceSchematic( cncPath );
848 BOOST_REQUIRE( root != nullptr );
849 BOOST_REQUIRE( root->GetScreen() );
850 BOOST_CHECK_EQUAL( MaxPinCountForRefdes( wxT( "C17" ) ), 2 );
851 }
852
853 {
854 SCH_SHEET* root = LoadDipTraceSchematic( sch6Path );
855 BOOST_REQUIRE( root != nullptr );
856 BOOST_REQUIRE( root->GetScreen() );
857 BOOST_CHECK_EQUAL( MaxPinCountForRefdes( wxT( "U1" ) ), 34 );
858 BOOST_CHECK_EQUAL( MaxPinCountForRefdes( wxT( "U5" ) ), 72 );
859 BOOST_CHECK_EQUAL( MaxPinCountForRefdes( wxT( "U10" ) ), 9 );
860 }
861}
862
863
864BOOST_AUTO_TEST_CASE( ViewerExamplesFootprintFieldOptional )
865{
866 const std::string examplesDir = GetViewerExamplesDir();
867 const std::string sch6Path = examplesDir + "/Schematic_6.dch";
868
869 if( !wxFileExists( sch6Path ) )
870 {
871 BOOST_TEST_MESSAGE( "Skipping footprint field checks; Schematic_6.dch not found at " << examplesDir );
872 return;
873 }
874
875 SCH_SHEET* root = LoadDipTraceSchematic( sch6Path );
876 BOOST_REQUIRE( root != nullptr );
877 BOOST_REQUIRE( root->GetScreen() );
878
879 // Schematic_6 (v41) contains resistors and capacitors with embedded patterns.
880 // Pattern names confirmed against PCB_6.dchxml library definitions.
881 BOOST_CHECK_EQUAL( GetFootprintForRefdes( wxT( "C1" ) ), wxString( wxT( "CAP_2012_N" ) ) );
882 BOOST_CHECK_EQUAL( GetFootprintForRefdes( wxT( "R1" ) ), wxString( wxT( "RES_2012_N" ) ) );
883
884 // Verify several more to confirm consistency
885 BOOST_CHECK_EQUAL( GetFootprintForRefdes( wxT( "C10" ) ), wxString( wxT( "CAP_2012_N" ) ) );
886 BOOST_CHECK_EQUAL( GetFootprintForRefdes( wxT( "R10" ) ), wxString( wxT( "RES_2012_N" ) ) );
887}
888
889
896BOOST_AUTO_TEST_CASE( ImportedNetConnectivityIsConsistent )
897{
898 const std::string path = GetTestDataDir() + "z80_board.dch";
899
900 SCH_SHEET* root = LoadDipTraceSchematic( path );
901 BOOST_REQUIRE( root != nullptr );
902
903 SCH_SHEET_LIST sheets = m_schematic->BuildSheetListSortedByPageNumbers();
904 m_schematic->ConnectionGraph()->Recalculate( sheets, true );
905
906 const NET_MAP& netMap = m_schematic->ConnectionGraph()->GetNetMap();
907
908 int multiPinNets = 0;
909
910 for( const auto& [key, subgraphs] : netMap )
911 {
912 for( CONNECTION_SUBGRAPH* sg : subgraphs )
913 {
914 int pinsOnNet = 0;
915
916 for( SCH_ITEM* item : sg->GetItems() )
917 {
918 if( item->Type() != SCH_PIN_T )
919 continue;
920
921 SCH_PIN* pin = static_cast<SCH_PIN*>( item );
922 SCH_CONNECTION* conn = pin->Connection( &sg->GetSheet() );
923
924 BOOST_REQUIRE( conn != nullptr );
925 BOOST_CHECK_EQUAL( conn->NetCode(), key.Netcode );
926
927 pinsOnNet++;
928 }
929
930 if( pinsOnNet >= 2 )
931 multiPinNets++;
932 }
933 }
934
935 BOOST_CHECK_MESSAGE( multiPinNets > 0, "Import produced no net connecting two or more pins" );
936
937 RemoveGeneratedLibrary( path );
938}
939
940
947BOOST_AUTO_TEST_CASE( AllSymbolsWithinPageBounds )
948{
949 const std::string path = GetTestDataDir() + "z80_board.dch";
950
951 SCH_SHEET* root = LoadDipTraceSchematic( path );
952 BOOST_REQUIRE( root != nullptr );
953
954 // KiCad default sheet size in schematic IU (matches getOrCreateSheet placement size).
955 static constexpr int PAGE_WIDTH_IU = 40640000;
956 static constexpr int PAGE_HEIGHT_IU = 30480000;
957
958 // Allow a generous margin for symbols drawn outside the visible page area.
959 static constexpr int MARGIN_FACTOR = 4;
960 const int maxX = PAGE_WIDTH_IU * MARGIN_FACTOR;
961 const int maxY = PAGE_HEIGHT_IU * MARGIN_FACTOR;
962
963 int symbolsChecked = 0;
964
965 for( const SCH_SHEET_PATH& sheetPath : m_schematic->BuildUnorderedSheetList() )
966 {
967 SCH_SCREEN* screen = sheetPath.LastScreen();
968
969 if( !screen )
970 continue;
971
972 for( SCH_ITEM* item : screen->Items().OfType( SCH_SYMBOL_T ) )
973 {
974 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
975 BOX2I bbox = symbol->GetBoundingBox();
976
977 BOOST_CHECK_MESSAGE( std::abs( bbox.GetLeft() ) <= maxX && std::abs( bbox.GetRight() ) <= maxX,
978 "Symbol X extent exceeds page bounds: [" << bbox.GetLeft() << ", " << bbox.GetRight()
979 << "]" );
980 BOOST_CHECK_MESSAGE( std::abs( bbox.GetTop() ) <= maxY && std::abs( bbox.GetBottom() ) <= maxY,
981 "Symbol Y extent exceeds page bounds: [" << bbox.GetTop() << ", " << bbox.GetBottom()
982 << "]" );
983
984 symbolsChecked++;
985 }
986 }
987
988 BOOST_CHECK_GT( symbolsChecked, 0 );
989
990 RemoveGeneratedLibrary( path );
991}
992
993
1001BOOST_AUTO_TEST_CASE( SymbolGraphicWidthMatchesPinWidth )
1002{
1003 const std::string path = GetTestDataDir() + "z80_board.dch";
1004
1005 SCH_SHEET* root = LoadDipTraceSchematic( path );
1006 BOOST_REQUIRE( root != nullptr );
1007
1008 // Pins draw at the schematic default line width. Real DipTrace strokes can be several times
1009 // that, so cap at 100 mil; the 100x scaling bug overshot this same shape by roughly 50x.
1010 const int defaultWidth = schIUScale.MilsToIU( DEFAULT_LINE_WIDTH_MILS );
1011 const int maxShapeWidth = schIUScale.MilsToIU( 100 );
1012
1013 int shapesChecked = 0;
1014
1015 for( const SCH_SHEET_PATH& sheetPath : m_schematic->BuildUnorderedSheetList() )
1016 {
1017 SCH_SCREEN* screen = sheetPath.LastScreen();
1018
1019 if( !screen )
1020 continue;
1021
1022 for( SCH_ITEM* item : screen->Items().OfType( SCH_SYMBOL_T ) )
1023 {
1024 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
1025 const std::unique_ptr<LIB_SYMBOL>& libSymbol = symbol->GetLibSymbolRef();
1026
1027 if( !libSymbol )
1028 continue;
1029
1030 for( SCH_ITEM& drawItem : libSymbol->GetDrawItems() )
1031 {
1032 if( drawItem.Type() != SCH_SHAPE_T )
1033 continue;
1034
1035 int width = static_cast<SCH_SHAPE&>( drawItem ).GetStroke().GetWidth();
1036
1037 // Zero means "use the default", which is exactly the pin width, so accept it.
1038 BOOST_CHECK_MESSAGE( width >= 0 && width <= maxShapeWidth,
1039 "Symbol graphic width " << width << " is far from pin width " << defaultWidth );
1040
1041 shapesChecked++;
1042 }
1043 }
1044 }
1045
1046 BOOST_CHECK_GT( shapesChecked, 0 );
1047
1048 RemoveGeneratedLibrary( path );
1049}
1050
1051
1058BOOST_AUTO_TEST_CASE( ShapeKindDiscriminatorProducesRectanglesAndLines )
1059{
1060 const std::string path = GetTestDataDir() + "z80_board.dch";
1061
1062 SCH_SHEET* root = LoadDipTraceSchematic( path );
1063 BOOST_REQUIRE( root != nullptr );
1064
1065 int rectangles = 0;
1066 int diagonalPolylines = 0;
1067
1068 for( const SCH_SHEET_PATH& sheetPath : m_schematic->BuildUnorderedSheetList() )
1069 {
1070 SCH_SCREEN* screen = sheetPath.LastScreen();
1071
1072 if( !screen )
1073 continue;
1074
1075 for( SCH_ITEM* item : screen->Items().OfType( SCH_SYMBOL_T ) )
1076 {
1077 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
1078 const std::unique_ptr<LIB_SYMBOL>& libSymbol = symbol->GetLibSymbolRef();
1079
1080 if( !libSymbol )
1081 continue;
1082
1083 for( SCH_ITEM& drawItem : libSymbol->GetDrawItems() )
1084 {
1085 if( drawItem.Type() != SCH_SHAPE_T )
1086 continue;
1087
1088 SCH_SHAPE& shape = static_cast<SCH_SHAPE&>( drawItem );
1089
1090 if( shape.GetShape() == SHAPE_T::RECTANGLE )
1091 {
1092 rectangles++;
1093 continue;
1094 }
1095
1096 if( shape.GetShape() != SHAPE_T::POLY || shape.GetPolyShape().OutlineCount() == 0 )
1097 continue;
1098
1099 const std::vector<VECTOR2I>& pts = shape.GetPolyShape().Outline( 0 ).CPoints();
1100
1101 if( pts.size() == 2 && pts[1].x != pts[0].x && pts[1].y != pts[0].y )
1102 diagonalPolylines++;
1103 }
1104 }
1105 }
1106
1107 BOOST_TEST_MESSAGE( "z80 rectangles=" << rectangles << " diagonalPolylines=" << diagonalPolylines );
1108
1109 // z80_board draws its symbol boxes as line edges rather than rectangle primitives, so the kind
1110 // discriminator yields no rectangles here. The point of the test is that the two point diagonal
1111 // lines stay polylines instead of being promoted to rectangles as the geometry guess did.
1112 BOOST_CHECK_GT( diagonalPolylines, 0 );
1113
1114 RemoveGeneratedLibrary( path );
1115}
1116
1117
1123BOOST_AUTO_TEST_CASE( PinsCarryNonDefaultOrientations )
1124{
1125 const std::string path = GetTestDataDir() + "z80_board.dch";
1126
1127 SCH_SHEET* root = LoadDipTraceSchematic( path );
1128 BOOST_REQUIRE( root != nullptr );
1129
1130 std::set<PIN_ORIENTATION> orientations;
1131 int pinsChecked = 0;
1132
1133 for( const SCH_SHEET_PATH& sheetPath : m_schematic->BuildUnorderedSheetList() )
1134 {
1135 SCH_SCREEN* screen = sheetPath.LastScreen();
1136
1137 if( !screen )
1138 continue;
1139
1140 for( SCH_ITEM* item : screen->Items().OfType( SCH_SYMBOL_T ) )
1141 {
1142 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
1143 const std::unique_ptr<LIB_SYMBOL>& libSymbol = symbol->GetLibSymbolRef();
1144
1145 if( !libSymbol )
1146 continue;
1147
1148 for( SCH_ITEM& drawItem : libSymbol->GetDrawItems() )
1149 {
1150 if( drawItem.Type() != SCH_PIN_T )
1151 continue;
1152
1153 orientations.insert( static_cast<SCH_PIN&>( drawItem ).GetOrientation() );
1154 pinsChecked++;
1155 }
1156 }
1157 }
1158
1159 BOOST_CHECK_GT( pinsChecked, 0 );
1160 BOOST_CHECK_MESSAGE( orientations.size() > 1,
1161 "All imported pins share a single orientation; orientation was not applied" );
1162
1163 RemoveGeneratedLibrary( path );
1164}
1165
1166
1173BOOST_AUTO_TEST_CASE( PageSizeIsAppliedWhenStored )
1174{
1175 const std::string path = GetTestDataDir() + "power_supply.dch";
1176
1177 SCH_SHEET* root = LoadDipTraceSchematic( path );
1178 BOOST_REQUIRE( root != nullptr );
1179 BOOST_REQUIRE( root->GetScreen() != nullptr );
1180
1181 const PAGE_INFO& page = root->GetScreen()->GetPageSettings();
1182
1183 // A4 is 297 x 210 mm; allow a small tolerance for the mm -> mils -> mm round trip.
1184 BOOST_CHECK_CLOSE( page.GetWidthMM(), 297.0, 0.5 );
1185 BOOST_CHECK_CLOSE( page.GetHeightMM(), 210.0, 0.5 );
1186
1187 RemoveGeneratedLibrary( path );
1188}
1189
1190
1199BOOST_AUTO_TEST_CASE( MultiLineValueComponentIsNotSkipped )
1200{
1201 const std::string path = GetTestDataDir() + "re-arm_pub.dch";
1202
1203 SCH_SHEET* root = LoadDipTraceSchematic( path );
1204 BOOST_REQUIRE( root != nullptr );
1205
1206 BOOST_CHECK_EQUAL( CountSymbolsForRefdesOnSheet( wxT( "U3" ), GetSheetNameForRefdes( wxT( "U3" ) ) ), 1 );
1207 BOOST_CHECK_GT( CountItemsOfType( SCH_SYMBOL_T ), 0 );
1208 BOOST_CHECK_EQUAL( m_reporter.CountOfSeverity( RPT_SEVERITY_ERROR ), 0 );
1209
1210 RemoveGeneratedLibrary( path );
1211}
1212
1213
constexpr EDA_IU_SCALE schIUScale
Definition base_units.h:123
BOX2< VECTOR2I > BOX2I
Definition box2.h:918
constexpr coord_type GetLeft() const
Definition box2.h:224
constexpr coord_type GetRight() const
Definition box2.h:213
constexpr coord_type GetTop() const
Definition box2.h:225
constexpr coord_type GetBottom() const
Definition box2.h:218
A subgraph is a set of items that are electrically connected on a single sheet.
Parser for DipTrace .dch schematic binary files.
void Parse()
Parse the .dch file and populate the schematic with KiCad objects.
int ComponentBoundaryScanCount() const
const KIID m_Uuid
Definition eda_item.h:531
SHAPE_POLY_SET & GetPolyShape()
SHAPE_T GetShape() const
Definition eda_shape.h:185
virtual int GetWidth() const
Definition eda_shape.h:173
EE_TYPE OfType(KICAD_T aType) const
Definition sch_rtree.h:221
Hold an error message and may be used when throwing exceptions containing meaningful error messages.
Definition kiid.h:44
Describe the page size and margins of a paper page on which to eventually print or plot.
Definition page_info.h:75
double GetHeightMM() const
Definition page_info.h:144
double GetWidthMM() const
Definition page_info.h:139
Each graphical item can have a SCH_CONNECTION describing its logical connection (to a bus or net).
int NetCode() const
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition sch_item.h:162
const PAGE_INFO & GetPageSettings() const
Definition sch_screen.h:137
EE_RTREE & Items()
Get the full RTree, usually for iterating.
Definition sch_screen.h:115
void SetFileName(const wxString &aFileName)
Set the file name for this screen to aFileName.
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...
Sheet symbol placed in a schematic, and is the entry point for a sub schematic.
Definition sch_sheet.h:44
void SetFileName(const wxString &aFilename)
Definition sch_sheet.h:376
SCH_SCREEN * GetScreen() const
Definition sch_sheet.h:139
void SetScreen(SCH_SCREEN *aScreen)
Set the SCH_SCREEN associated with this sheet to aScreen.
Schematic symbol object.
Definition sch_symbol.h:69
const BOX2I GetBoundingBox() const override
Return the orthogonal bounding box of this object for display purposes.
std::unique_ptr< LIB_SYMBOL > & GetLibSymbolRef()
Definition sch_symbol.h:177
const std::vector< VECTOR2I > & CPoints() const
SHAPE_LINE_CHAIN & Outline(int aIndex)
Return the reference to aIndex-th outline in the set.
int OutlineCount() const
Return the number of outlines in the set.
std::unordered_map< NET_NAME_CODE_CACHE_KEY, std::vector< CONNECTION_SUBGRAPH * > > NET_MAP
Associate a #NET_CODE_NAME with all the subgraphs in that net.
#define DEFAULT_LINE_WIDTH_MILS
The default wire width in mils. (can be changed in preference menu)
@ RECTANGLE
Use RECTANGLE instead of RECT to avoid collision in a Windows header.
Definition eda_shape.h:47
static const std::string KiCadSchematicFileExtension
KIID niluuid(0)
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition eda_angle.h:400
@ RPT_SEVERITY_WARNING
@ RPT_SEVERITY_ERROR
BOOST_AUTO_TEST_CASE(HorizontalAlignment)
BOOST_AUTO_TEST_CASE(CanReadSchematic)
Test that CanReadSchematicFile correctly identifies DipTrace .dch files by their magic header bytes.
Shared fixture, reporter and helpers for the DipTrace schematic import test suite.
BOOST_REQUIRE(intersection.has_value()==c.ExpectedIntersection.has_value())
BOOST_AUTO_TEST_SUITE_END()
std::string path
KIBIS_PIN * pin
VECTOR3I expected(15, 30, 45)
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")
@ SCH_LINE_T
Definition typeinfo.h:160
@ SCH_SYMBOL_T
Definition typeinfo.h:169
@ SCH_SHEET_T
Definition typeinfo.h:172
@ SCH_SHAPE_T
Definition typeinfo.h:146
@ SCH_GLOBAL_LABEL_T
Definition typeinfo.h:165
@ SCH_PIN_T
Definition typeinfo.h:150