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