KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_save_load_schematic.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
25#include <boost/test/unit_test.hpp>
26#include <eeschema_test_utils.h>
27
29#include <sch_shape.h>
30#include <sch_screen.h>
31#include <sch_sheet.h>
32#include <sch_symbol.h>
33#include <schematic.h>
34#include <kiid.h>
36#include <sch_file_versions.h>
38
39#include <wx/ffile.h>
40#include <wx/filename.h>
41#include <wx/stdpaths.h>
42
43
45{
48 {
49 // Create a temporary project file so we have a valid project name
50 wxString tempDir = wxStandardPaths::Get().GetTempDir();
51 wxString projectPath = tempDir + wxFileName::GetPathSeparator() + wxT("test_project.kicad_pro");
52 m_tempFiles.push_back( projectPath );
53
54 m_settingsManager.LoadProject( projectPath.ToStdString() );
55 m_schematic = std::make_unique<SCHEMATIC>( nullptr );
57 m_schematic->SetProject( m_project );
58 }
59
61 {
62 // Clean up temp files
63 for( const wxString& file : m_tempFiles )
64 {
65 if( wxFileExists( file ) )
66 wxRemoveFile( file );
67 }
68
69 m_schematic.reset();
70 }
71
72 wxString GetTempFileName( const wxString& aPrefix )
73 {
74 wxString tempDir = wxStandardPaths::Get().GetTempDir();
75 wxString fileName = wxFileName::CreateTempFileName( tempDir + wxFileName::GetPathSeparator() + aPrefix );
76 m_tempFiles.push_back( fileName );
77 return fileName;
78 }
79
81 std::unique_ptr<SCHEMATIC> m_schematic;
83 std::vector<wxString> m_tempFiles;
84};
85
86
87BOOST_FIXTURE_TEST_SUITE( SaveLoadSchematic, SAVE_LOAD_FIXTURE )
88
89
90
94BOOST_AUTO_TEST_CASE( TestSaveLoadSimpleSchematic )
95{
96 // Create a simple schematic
97 m_schematic->CreateDefaultScreens();
98
99 // Get the first (and only) top-level sheet
100 std::vector<SCH_SHEET*> topSheets = m_schematic->GetTopLevelSheets();
101 BOOST_REQUIRE( !topSheets.empty() );
102
103 SCH_SCREEN* screen = topSheets[0]->GetScreen();
104 BOOST_REQUIRE( screen != nullptr );
105
106 // Set some basic properties
107 screen->SetFileName( "test.kicad_sch" );
108 topSheets[0]->SetName( "Main Sheet" );
109
110 // Verify that currentSheet is set to a top-level sheet, not virtual root
111 SCH_SHEET_PATH& currentSheet = m_schematic->CurrentSheet();
112 BOOST_CHECK( !currentSheet.empty() );
113 if( !currentSheet.empty() )
114 {
115 BOOST_CHECK( currentSheet.Last() != &m_schematic->Root() );
116 BOOST_CHECK( currentSheet.Last()->m_Uuid != niluuid );
117 }
118
119 // Save the schematic
120 wxString fileName = GetTempFileName( "test_schematic" );
121 fileName += ".kicad_sch";
122 m_tempFiles.push_back( fileName );
123
125 BOOST_CHECK_NO_THROW( io.SaveSchematicFile( fileName, topSheets[0], m_schematic.get() ) );
126
127 // Verify file was created
128 BOOST_CHECK( wxFileExists( fileName ) );
129
130 // Now try to load it back
131 m_schematic->Reset();
132 SCH_SHEET* defaultSheet = m_schematic->GetTopLevelSheet( 0 );
133 SCH_SHEET* loadedSheet = nullptr;
134 BOOST_CHECK_NO_THROW( loadedSheet = io.LoadSchematicFile( fileName, m_schematic.get() ) );
135 BOOST_CHECK( loadedSheet != nullptr );
136
137 if( loadedSheet )
138 {
139 // Set it as root (which should create virtual root and make it a top-level sheet)
140 m_schematic->AddTopLevelSheet( loadedSheet );
141 m_schematic->RemoveTopLevelSheet( defaultSheet );
142 delete defaultSheet;
143
144 // Verify we can access the schematic
145 BOOST_CHECK( m_schematic->IsValid() );
146
147 // Verify current sheet is NOT the virtual root
148 SCH_SHEET_PATH& currentSheet2 = m_schematic->CurrentSheet();
149 BOOST_CHECK( !currentSheet2.empty() );
150 if( !currentSheet2.empty() )
151 {
152 BOOST_CHECK( currentSheet2.Last() != &m_schematic->Root() );
153 BOOST_CHECK( currentSheet2.Last()->m_Uuid != niluuid );
154 }
155
156 // Try to save again (this is where the bug occurs)
157 wxString fileName2 = GetTempFileName( "test_schematic_resave" );
158 fileName2 += ".kicad_sch";
159 m_tempFiles.push_back( fileName2 );
160
161 std::vector<SCH_SHEET*> topSheets2 = m_schematic->GetTopLevelSheets();
162 BOOST_REQUIRE( !topSheets2.empty() );
163
164 // This should not assert - this is the key test for the bug fix
165 // Symbol instances should be preserved, not dropped
166 BOOST_CHECK_NO_THROW( io.SaveSchematicFile( fileName2, topSheets2[0], m_schematic.get() ) );
167
168 // Verify the second file was created
169 BOOST_CHECK( wxFileExists( fileName2 ) );
170 }
171}
172
173
177BOOST_AUTO_TEST_CASE( TestSaveLoadHierarchicalSchematic )
178{
179 // Create a schematic with hierarchy
180 m_schematic->CreateDefaultScreens();
181
182 std::vector<SCH_SHEET*> topSheets = m_schematic->GetTopLevelSheets();
183 BOOST_REQUIRE( !topSheets.empty() );
184
185 SCH_SCREEN* screen = topSheets[0]->GetScreen();
186 BOOST_REQUIRE( screen != nullptr );
187
188 // Create a sub-sheet
189 SCH_SHEET* subSheet = new SCH_SHEET( m_schematic.get() );
190 subSheet->SetName( "SubSheet" );
191 SCH_SCREEN* subScreen = new SCH_SCREEN( m_schematic.get() );
192 subSheet->SetScreen( subScreen );
193 subSheet->SetFileName( "subsheet.kicad_sch" );
194 subScreen->SetFileName( "subsheet.kicad_sch" );
195 screen->Append( subSheet );
196
197 // Save the main sheet
198 wxString mainFileName = GetTempFileName( "test_main" );
199 mainFileName += ".kicad_sch";
200 m_tempFiles.push_back( mainFileName );
201
202 // Save the sub-sheet
203 wxString subFileName = GetTempFileName( "test_sub" );
204 subFileName += ".kicad_sch";
205 m_tempFiles.push_back( subFileName );
206
208 BOOST_CHECK_NO_THROW( io.SaveSchematicFile( mainFileName, topSheets[0], m_schematic.get() ) );
209 BOOST_CHECK_NO_THROW( io.SaveSchematicFile( subFileName, subSheet, m_schematic.get() ) );
210
211 // Load it back
212 m_schematic->Reset();
213 SCH_SHEET* defaultSheet = m_schematic->GetTopLevelSheet( 0 );
214 SCH_SHEET* loadedSheet = nullptr;
215 BOOST_CHECK_NO_THROW( loadedSheet = io.LoadSchematicFile( mainFileName, m_schematic.get() ) );
216 BOOST_CHECK( loadedSheet != nullptr );
217
218 if( loadedSheet )
219 {
220 m_schematic->AddTopLevelSheet( loadedSheet );
221 m_schematic->RemoveTopLevelSheet( defaultSheet );
222 delete defaultSheet;
223
224 // Build hierarchy
225 m_schematic->RefreshHierarchy();
226
227 // Verify hierarchy has 2 sheets (main + sub)
228 SCH_SHEET_LIST hierarchy = m_schematic->Hierarchy();
229 BOOST_CHECK_EQUAL( hierarchy.size(), 2 );
230
231 // Try to save again
232 wxString mainFileName2 = GetTempFileName( "test_main_resave" );
233 mainFileName2 += ".kicad_sch";
234 m_tempFiles.push_back( mainFileName2 );
235
236 std::vector<SCH_SHEET*> topSheets2 = m_schematic->GetTopLevelSheets();
237 BOOST_REQUIRE( !topSheets2.empty() );
238
239 // This should not assert - this is the key test for the bug fix
240 BOOST_CHECK_NO_THROW( io.SaveSchematicFile( mainFileName2, topSheets2[0], m_schematic.get() ) );
241
242 // Verify the file was created
243 BOOST_CHECK( wxFileExists( mainFileName2 ) );
244 }
245}
246
247
248BOOST_AUTO_TEST_CASE( TestSaveLoadHatchedPolygonPreservesClosingEdge )
249{
250 m_schematic->CreateDefaultScreens();
251
252 std::vector<SCH_SHEET*> topSheets = m_schematic->GetTopLevelSheets();
253 BOOST_REQUIRE( !topSheets.empty() );
254
255 SCH_SCREEN* screen = topSheets[0]->GetScreen();
256 BOOST_REQUIRE( screen != nullptr );
257
258 screen->SetFileName( "polygon_test.kicad_sch" );
259
260 std::unique_ptr<SCH_SHAPE> polygon = std::make_unique<SCH_SHAPE>( SHAPE_T::POLY );
261 polygon->SetStroke( STROKE_PARAMS( schIUScale.MilsToIU( 10 ), LINE_STYLE::SOLID ) );
262 polygon->SetFillMode( FILL_T::HATCH );
263
264 polygon->AddPoint( VECTOR2I( schIUScale.MilsToIU( 0 ), schIUScale.MilsToIU( 0 ) ) );
265 polygon->AddPoint( VECTOR2I( schIUScale.MilsToIU( 1000 ), schIUScale.MilsToIU( 0 ) ) );
266 polygon->AddPoint( VECTOR2I( schIUScale.MilsToIU( 500 ), schIUScale.MilsToIU( 1000 ) ) );
267
268 // Emulate the polygon tool's double-click closure artifact: the final point duplicates the
269 // previous one rather than the first. Reload should normalize that back into an explicit
270 // closing segment while keeping the stored outline open.
271 polygon->AddPoint( VECTOR2I( schIUScale.MilsToIU( 500 ), schIUScale.MilsToIU( 1000 ) ) );
272
273 screen->Append( polygon.release() );
274
275 wxString fileName = GetTempFileName( "test_polygon_reload" );
276 fileName += ".kicad_sch";
277 m_tempFiles.push_back( fileName );
278
280 BOOST_CHECK_NO_THROW( io.SaveSchematicFile( fileName, topSheets[0], m_schematic.get() ) );
281 BOOST_CHECK( wxFileExists( fileName ) );
282
283 m_schematic->Reset();
284
285 SCH_SHEET* loadedSheet = nullptr;
286 BOOST_CHECK_NO_THROW( loadedSheet = io.LoadSchematicFile( fileName, m_schematic.get() ) );
287 BOOST_REQUIRE( loadedSheet != nullptr );
288
289 SCH_SHAPE* loadedPolygon = nullptr;
290
291 for( SCH_ITEM* item : loadedSheet->GetScreen()->Items().OfType( SCH_SHAPE_T ) )
292 {
293 SCH_SHAPE* shape = static_cast<SCH_SHAPE*>( item );
294
295 if( shape->GetShape() == SHAPE_T::POLY )
296 {
297 loadedPolygon = shape;
298 break;
299 }
300 }
301
302 BOOST_REQUIRE( loadedPolygon != nullptr );
303
304 const SHAPE_LINE_CHAIN& outline = loadedPolygon->GetPolyShape().COutline( 0 );
305 BOOST_REQUIRE_EQUAL( outline.PointCount(), 4 );
306 BOOST_CHECK_EQUAL( outline.CLastPoint(), outline.CPoint( 0 ) );
307 BOOST_CHECK_NE( outline.CLastPoint(), outline.CPoint( outline.PointCount() - 2 ) );
308 BOOST_CHECK( !outline.IsClosed() );
309
310 loadedPolygon->UpdateHatching();
311 BOOST_CHECK( !loadedPolygon->GetHatchLines().empty() );
312
313 loadedPolygon->SetFlags( IS_MOVING );
314 loadedPolygon->SetHatchingDirty();
315 loadedPolygon->UpdateHatching();
316 BOOST_CHECK( !loadedPolygon->GetHatchLines().empty() );
317 loadedPolygon->ClearFlags( IS_MOVING );
318
319 std::vector<SHAPE*> segments = loadedPolygon->MakeEffectiveShapes( true );
320 BOOST_REQUIRE_EQUAL( segments.size(), 3 );
321
322 const SEG& closingSeg = static_cast<SHAPE_SEGMENT*>( segments.back() )->GetSeg();
323 BOOST_CHECK_EQUAL( closingSeg.A, outline.CPoint( outline.PointCount() - 2 ) );
324 BOOST_CHECK_EQUAL( closingSeg.B, outline.CPoint( 0 ) );
325
326 for( SHAPE* segment : segments )
327 delete segment;
328}
329
330
336BOOST_AUTO_TEST_CASE( TestLoadLegacyHierarchicalSchematic )
337{
338 // Load the legacy hierarchical schematic
339 wxFileName fn( KI_TEST::GetEeschemaTestDataDir() );
340 fn.AppendDir( "legacy_hierarchy" );
341 fn.SetName( "legacy_hierarchy" );
343 wxString mainFile = fn.GetFullPath();
344
345 BOOST_TEST_MESSAGE( "Loading schematic: " + mainFile.ToStdString() );
346 BOOST_REQUIRE( wxFileExists( mainFile ) );
347
349 SCH_SHEET* loadedSheet = nullptr;
350
351 // Load the schematic
352 BOOST_CHECK_NO_THROW( loadedSheet = io.LoadSchematicFile( mainFile, m_schematic.get() ) );
353 BOOST_REQUIRE( loadedSheet != nullptr );
354
355 BOOST_TEST_MESSAGE( "Loaded sheet UUID: " + loadedSheet->m_Uuid.AsString().ToStdString() );
356 BOOST_TEST_MESSAGE( "Loaded sheet name: " + loadedSheet->GetName().ToStdString() );
357
358 m_schematic->Reset();
359 SCH_SHEET* defaultSheet = m_schematic->GetTopLevelSheet( 0 );
360 m_schematic->AddTopLevelSheet( loadedSheet );
361 m_schematic->RemoveTopLevelSheet( defaultSheet );
362 delete defaultSheet;
363
364 // Build the hierarchy
365 m_schematic->RefreshHierarchy();
366 SCH_SHEET_LIST hierarchy = m_schematic->Hierarchy();
367
368 BOOST_TEST_MESSAGE( "Hierarchy size: " + std::to_string( hierarchy.size() ) );
369
370 // This schematic has 1 root + 2 sub-sheets = 3 total sheets
371 BOOST_REQUIRE_EQUAL( hierarchy.size(), 3 );
372
373 // Check each sheet path
374 for( size_t i = 0; i < hierarchy.size(); i++ )
375 {
376 const SCH_SHEET_PATH& path = hierarchy[i];
377 BOOST_TEST_MESSAGE( "\nSheet path [" + std::to_string(i) + "]: " + path.PathHumanReadable( false ).ToStdString() );
378 BOOST_TEST_MESSAGE( " Path KIID: " + path.Path().AsString().ToStdString() );
379 BOOST_TEST_MESSAGE( std::string(" Last screen: ") + (path.LastScreen() ? "YES" : "NO") );
380 BOOST_TEST_MESSAGE( " Path size: " + std::to_string( path.size() ) );
381
382 // Verify path has a screen
383 BOOST_CHECK( path.LastScreen() != nullptr );
384
385 if( path.LastScreen() )
386 {
387 // Count symbols on this sheet
388 int symbolCount = 0;
389 for( SCH_ITEM* item : path.LastScreen()->Items().OfType( SCH_SYMBOL_T ) )
390 {
391 symbolCount++;
392 }
393 BOOST_TEST_MESSAGE( " Symbol count: " + std::to_string( symbolCount ) );
394 }
395 }
396
397 // Check for missing instances (this should create them if needed)
398 BOOST_TEST_MESSAGE( "\nChecking for missing symbol instances..." );
399 hierarchy.CheckForMissingSymbolInstances( m_project->GetProjectName() );
400
401 // Now verify that symbols have proper instance data
402 std::map<wxString, int> referenceCount;
403 int totalInstances = 0;
404 int emptyPaths = 0;
405
406 for( const SCH_SHEET_PATH& path : hierarchy )
407 {
408 if( !path.LastScreen() )
409 continue;
410
411 BOOST_TEST_MESSAGE( "\nVerifying instances for path: " + path.PathHumanReadable( false ).ToStdString() );
412
413 for( SCH_ITEM* item : path.LastScreen()->Items().OfType( SCH_SYMBOL_T ) )
414 {
415 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
416 SCH_SYMBOL_INSTANCE symbolInstance;
417
418 // Check if symbol has instance for this path
419 bool hasInstance = symbol->GetInstance( symbolInstance, path.Path() );
420
421 if( hasInstance )
422 {
423 totalInstances++;
424
425 // Check that path is not empty
426 if( symbolInstance.m_Path.empty() )
427 {
428 emptyPaths++;
429 BOOST_TEST_MESSAGE( " ERROR: Symbol " + symbol->m_Uuid.AsString().ToStdString() +
430 " has EMPTY instance path!" );
431 }
432 else
433 {
434 referenceCount[symbolInstance.m_Reference]++;
435 BOOST_TEST_MESSAGE( " Symbol " + symbol->m_Uuid.AsString().ToStdString() +
436 " -> " + symbolInstance.m_Reference.ToStdString() +
437 " (path: " + symbolInstance.m_Path.AsString().ToStdString() + ")" );
438 }
439 }
440 else
441 {
442 BOOST_TEST_MESSAGE( " WARNING: Symbol " + symbol->m_Uuid.AsString().ToStdString() +
443 " has NO instance for path " + path.Path().AsString().ToStdString() );
444 }
445 }
446 }
447
448 BOOST_TEST_MESSAGE( "\nSummary:" );
449 BOOST_TEST_MESSAGE( " Total instances: " + std::to_string( totalInstances ) );
450 BOOST_TEST_MESSAGE( " Empty paths: " + std::to_string( emptyPaths ) );
451 BOOST_TEST_MESSAGE( " Unique references: " + std::to_string( referenceCount.size() ) );
452
453 // CRITICAL: No symbol instance should have an empty path
454 BOOST_CHECK_EQUAL( emptyPaths, 0 );
455
456 // The schematic should have instances for all symbols
457 BOOST_CHECK( totalInstances > 0 );
458
459 // Print reference counts for debugging
460 BOOST_TEST_MESSAGE( "\nReference designator counts:" );
461 for( const auto& pair : referenceCount )
462 {
463 BOOST_TEST_MESSAGE( " " + pair.first.ToStdString() + ": " + std::to_string( pair.second ) );
464 }
465
466 // Try to save the schematic - this should NOT crash or assert
467 BOOST_TEST_MESSAGE( "\nAttempting to save schematic..." );
468 wxString saveFile = GetTempFileName( "legacy_hierarchy_resave" );
469 saveFile += "." + FILEEXT::KiCadSchematicFileExtension;
470 m_tempFiles.push_back( saveFile );
471
472 std::vector<SCH_SHEET*> topSheets = m_schematic->GetTopLevelSheets();
473 BOOST_REQUIRE( !topSheets.empty() );
474
475 // This is the critical test - saving should not assert on empty paths
476 BOOST_CHECK_NO_THROW( io.SaveSchematicFile( saveFile, topSheets[0], m_schematic.get() ) );
477 BOOST_CHECK( wxFileExists( saveFile ) );
478
479 BOOST_TEST_MESSAGE( "Save successful!" );
480}
481
482
489BOOST_AUTO_TEST_CASE( TestEmbeddedFilesPersistedInHierarchy )
490{
491 m_schematic->CreateDefaultScreens();
492
493 std::vector<SCH_SHEET*> topSheets = m_schematic->GetTopLevelSheets();
494 BOOST_REQUIRE( !topSheets.empty() );
495
496 SCH_SHEET* topSheet = topSheets[0];
497 SCH_SCREEN* topScreen = topSheet->GetScreen();
498 BOOST_REQUIRE( topScreen );
499
500 SCH_SHEET* subSheet = new SCH_SHEET( m_schematic.get() );
501 SCH_SCREEN* subScreen = new SCH_SCREEN( m_schematic.get() );
502 subSheet->SetName( "SubSheet" );
503 subSheet->SetScreen( subScreen );
504 subSheet->SetFileName( "subsheet.kicad_sch" );
505 subScreen->SetFileName( "subsheet.kicad_sch" );
506 topScreen->Append( subSheet );
507
508 m_schematic->RefreshHierarchy();
509
510 // Drive the same code path Page Settings uses: write a real .kicad_wks and embed it via
511 // the file-based AddFile overload, which handles type detection, compression, and encoding.
512 wxString wksPath = GetTempFileName( "embed_wks" );
513 wksPath += ".kicad_wks";
514 m_tempFiles.push_back( wksPath );
515
516 {
517 wxFFile wksFile( wksPath, "wb" );
518 BOOST_REQUIRE( wksFile.IsOpened() );
519 const wxString wksBody = wxT( "(kicad_wks)" );
520 BOOST_REQUIRE( wksFile.Write( wksBody ) );
521 }
522
523 BOOST_REQUIRE( m_schematic->GetEmbeddedFiles()->AddFile( wxFileName( wksPath ), false )
524 != nullptr );
525 BOOST_REQUIRE( !m_schematic->GetEmbeddedFiles()->IsEmpty() );
526
527 wxString topFileName = GetTempFileName( "embed_main" );
528 topFileName += ".kicad_sch";
529 m_tempFiles.push_back( topFileName );
530
532 BOOST_CHECK_NO_THROW( io.SaveSchematicFile( topFileName, topSheet, m_schematic.get() ) );
533 BOOST_REQUIRE( wxFileExists( topFileName ) );
534
535 wxFFile readback( topFileName, "rb" );
536 wxString contents;
537 BOOST_REQUIRE( readback.IsOpened() && readback.ReadAll( &contents ) );
538
539 const wxString embeddedName = wxFileName( wksPath ).GetFullName();
540
541 BOOST_CHECK_MESSAGE( contents.Contains( "embedded_files" ),
542 "Top-level sheet file is missing (embedded_files) block" );
543 BOOST_CHECK_MESSAGE( contents.Contains( embeddedName ),
544 "Top-level sheet file is missing the embedded worksheet entry" );
545 BOOST_CHECK_MESSAGE( contents.Contains( "(type worksheet)" ),
546 "Top-level sheet file is missing (type worksheet) marker" );
547}
548
549
554BOOST_AUTO_TEST_CASE( TestLoadSheetFileWithTextVariable )
555{
556 m_schematic->CreateDefaultScreens();
557
558 std::vector<SCH_SHEET*> topSheets = m_schematic->GetTopLevelSheets();
559 BOOST_REQUIRE( !topSheets.empty() );
560
561 SCH_SCREEN* screen = topSheets[0]->GetScreen();
562 BOOST_REQUIRE( screen != nullptr );
563
564 m_project->GetTextVars()[wxT( "SUBSHEET_FILE" )] = wxT( "resolved_subsheet" );
565
566 SCH_SHEET* subSheet = new SCH_SHEET( m_schematic.get() );
567 SCH_SCREEN* subScreen = new SCH_SCREEN( m_schematic.get() );
568 subSheet->SetName( "SubSheet" );
569 subSheet->SetScreen( subScreen );
570 subSheet->SetFileName( "${SUBSHEET_FILE}.kicad_sch" );
571 screen->Append( subSheet );
572
573 // Save the sub-sheet at the resolved path next to the main file, where the loader looks.
574 wxString mainFileName = GetTempFileName( "tv_main" );
575 mainFileName += ".kicad_sch";
576 m_tempFiles.push_back( mainFileName );
577
578 wxFileName subOnDisk( mainFileName );
579 subOnDisk.SetFullName( "resolved_subsheet.kicad_sch" );
580 wxString subFileName = subOnDisk.GetFullPath();
581 m_tempFiles.push_back( subFileName );
582 subScreen->SetFileName( subFileName );
583
585 BOOST_REQUIRE_NO_THROW( io.SaveSchematicFile( mainFileName, topSheets[0], m_schematic.get() ) );
586 BOOST_REQUIRE_NO_THROW( io.SaveSchematicFile( subFileName, subSheet, m_schematic.get() ) );
587
588 m_schematic->Reset();
589 SCH_SHEET* defaultSheet = m_schematic->GetTopLevelSheet( 0 );
590 SCH_SHEET* loadedSheet = nullptr;
591 BOOST_REQUIRE_NO_THROW( loadedSheet = io.LoadSchematicFile( mainFileName, m_schematic.get() ) );
592 BOOST_REQUIRE( loadedSheet != nullptr );
593
594 m_schematic->AddTopLevelSheet( loadedSheet );
595 m_schematic->RemoveTopLevelSheet( defaultSheet );
596 delete defaultSheet;
597 m_schematic->RefreshHierarchy();
598
599 SCH_SHEET* loadedSub = nullptr;
600
601 for( SCH_ITEM* item : loadedSheet->GetScreen()->Items().OfType( SCH_SHEET_T ) )
602 loadedSub = static_cast<SCH_SHEET*>( item );
603
604 BOOST_REQUIRE( loadedSub != nullptr );
605
606 // Stored field keeps the raw variable for portability.
607 BOOST_CHECK_EQUAL( loadedSub->GetFileName(), wxString( "${SUBSHEET_FILE}.kicad_sch" ) );
608
609 // Screen must load from the resolved file, not the literal ${...} path.
610 BOOST_REQUIRE( loadedSub->GetScreen() != nullptr );
611 BOOST_CHECK_MESSAGE( !loadedSub->GetScreen()->GetFileName().Contains( "${" ),
612 "Sub-sheet screen filename still contains an unresolved text variable: "
613 + loadedSub->GetScreen()->GetFileName() );
614
615 wxFileName loadedScreenFn( loadedSub->GetScreen()->GetFileName() );
616 BOOST_CHECK_EQUAL( loadedScreenFn.GetFullName(), wxString( "resolved_subsheet.kicad_sch" ) );
617}
618
619
constexpr EDA_IU_SCALE schIUScale
Definition base_units.h:123
void SetFlags(EDA_ITEM_FLAGS aMask)
Definition eda_item.h:158
const KIID m_Uuid
Definition eda_item.h:597
void ClearFlags(EDA_ITEM_FLAGS aMask=EDA_ITEM_ALL_FLAGS)
Definition eda_item.h:160
SHAPE_POLY_SET & GetPolyShape()
SHAPE_T GetShape() const
Definition eda_shape.h:175
const std::vector< SEG > & GetHatchLines() const
void SetHatchingDirty()
Definition eda_shape.h:153
EE_TYPE OfType(KICAD_T aType) const
Definition sch_rtree.h:248
wxString AsString() const
Definition kiid.cpp:423
wxString AsString() const
Definition kiid.cpp:264
Container for project specific data.
Definition project.h:63
A SCH_IO derivation for loading schematic files using the new s-expression file format.
void SaveSchematicFile(const wxString &aFileName, SCH_SHEET *aSheet, SCHEMATIC *aSchematic, const std::map< std::string, UTF8 > *aProperties=nullptr) override
Write aSchematic to a storage file in a format that this SCH_IO implementation knows about,...
SCH_SHEET * LoadSchematicFile(const wxString &aFileName, SCHEMATIC *aSchematic, SCH_SHEET *aAppendToMe=nullptr, const std::map< std::string, UTF8 > *aProperties=nullptr) override
Load information from some input file format that this SCH_IO implementation knows about,...
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition sch_item.h:165
void Append(SCH_ITEM *aItem, bool aUpdateLibSymbol=true)
EE_RTREE & Items()
Get the full RTree, usually for iterating.
Definition sch_screen.h:118
const wxString & GetFileName() const
Definition sch_screen.h:153
void SetFileName(const wxString &aFileName)
Set the file name for this screen to aFileName.
std::vector< SHAPE * > MakeEffectiveShapes(bool aEdgeOnly=false) const override
Make a set of SHAPE objects representing the SCH_SHAPE.
Definition sch_shape.h:118
void UpdateHatching() const override
A container for handling SCH_SHEET_PATH objects in a flattened hierarchy.
void CheckForMissingSymbolInstances(const wxString &aProjectName)
Handle access to a stack of flattened SCH_SHEET objects by way of a path for creating a flattened sch...
bool empty() const
Forwarded method from std::vector.
SCH_SHEET * Last() const
Return a pointer to the last SCH_SHEET of the list.
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:390
wxString GetFileName() const
Return the filename corresponding to this sheet.
Definition sch_sheet.h:384
wxString GetName() const
Definition sch_sheet.h:142
void SetName(const wxString &aName)
Definition sch_sheet.h:143
SCH_SCREEN * GetScreen() const
Definition sch_sheet.h:145
void SetScreen(SCH_SCREEN *aScreen)
Set the SCH_SCREEN associated with this sheet to aScreen.
Schematic symbol object.
Definition sch_symbol.h:75
bool GetInstance(SCH_SYMBOL_INSTANCE &aInstance, const KIID_PATH &aSheetPath, bool aTestFromEnd=false) const
Definition seg.h:38
VECTOR2I A
Definition seg.h:45
VECTOR2I B
Definition seg.h:46
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
bool IsClosed() const override
int PointCount() const
Return the number of points (vertices) in this line chain.
const VECTOR2I & CPoint(int aIndex) const
Return a reference to a given point in the line chain.
const VECTOR2I & CLastPoint() const
Return the last point in the line chain.
const SHAPE_LINE_CHAIN & COutline(int aIndex) const
const SEG & GetSeg() const
An abstract shape on 2D plane.
Definition shape.h:124
Simple container to manage line stroke parameters.
@ HATCH
Definition eda_fill.h:34
#define IS_MOVING
Item being moved.
static const std::string KiCadSchematicFileExtension
KIID niluuid(0)
std::string GetEeschemaTestDataDir()
Get the configured location of Eeschema test data.
std::vector< wxString > m_tempFiles
wxString GetTempFileName(const wxString &aPrefix)
SETTINGS_MANAGER m_settingsManager
std::unique_ptr< SCHEMATIC > m_schematic
A simple container for schematic symbol instance information.
BOOST_AUTO_TEST_CASE(HorizontalAlignment)
BOOST_REQUIRE(intersection.has_value()==c.ExpectedIntersection.has_value())
BOOST_AUTO_TEST_SUITE_END()
std::string path
BOOST_AUTO_TEST_CASE(TestSaveLoadSimpleSchematic)
Test that we can save and reload a schematic with the virtual root pattern and that symbol instances ...
BOOST_TEST_MESSAGE("Polyline has "<< chain.PointCount()<< " points")
BOOST_CHECK_EQUAL(result, "25.4")
@ SCH_SYMBOL_T
Definition typeinfo.h:168
@ SCH_SHEET_T
Definition typeinfo.h:171
@ SCH_SHAPE_T
Definition typeinfo.h:145
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683