KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_saveas_copy_subsheets.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#include "eeschema_test_utils.h"
27
30#include <schematic.h>
31#include <sch_sheet.h>
32#include <sch_sheet_path.h>
33#include <sch_symbol.h>
35#include <wx/filename.h>
36#include <wx/ffile.h>
37#include <wx/filefn.h>
38
40{
41public:
43
44 wxFileName GetSchematicPath( const wxString& aRelativePath );
45
46 wxFileName m_baseDir;
47 wxFileName m_srcDir;
48 wxFileName m_externalDir;
49};
50
52{
53 wxFileName tmp( wxFileName::CreateTempFileName( wxS( "saveas" ) ) );
54 wxString dirPath = tmp.GetPath(); // /tmp
55 wxString dirName = tmp.GetName(); // saveasXXXX
56 wxRemoveFile( tmp.GetFullPath() );
57
58 wxFileName base( dirPath, wxEmptyString ); // represents /tmp
59 base.AppendDir( dirName ); // now /tmp/saveasXXXX
60 base.Mkdir(); // create the directory
61 m_baseDir = base;
62
64 m_srcDir.AppendDir( wxS( "src" ) );
65 m_srcDir.Mkdir();
66
68 m_externalDir.AppendDir( wxS( "external" ) );
69 m_externalDir.Mkdir();
70
71 wxFileName dataDir( KI_TEST::GetEeschemaTestDataDir() );
72
73 wxCopyFile( dataDir.GetFullPath() + wxS( "/issue13212.kicad_sch" ),
74 m_srcDir.GetFullPath() + wxS( "/issue13212.kicad_sch" ) );
75 wxCopyFile( dataDir.GetFullPath() + wxS( "/issue13212_subsheet_1.kicad_sch" ),
76 m_srcDir.GetFullPath() + wxS( "/issue13212_subsheet_1.kicad_sch" ) );
77 wxCopyFile( dataDir.GetFullPath() + wxS( "/issue13212_subsheet_2.kicad_sch" ),
78 m_externalDir.GetFullPath() + wxS( "/issue13212_subsheet_2.kicad_sch" ) );
79
80 wxFileName rootFile( m_srcDir );
81 rootFile.SetFullName( wxS( "issue13212.kicad_sch" ) );
82
83 wxFFile file( rootFile.GetFullPath(), wxS( "rb" ) );
84 wxString content;
85 file.ReadAll( &content );
86 file.Close();
87 content.Replace( wxS( "issue13212_subsheet_2.kicad_sch" ),
88 wxS( "../external/issue13212_subsheet_2.kicad_sch" ) );
89 wxFFile outFile( rootFile.GetFullPath(), wxS( "wb" ) );
90 outFile.Write( content );
91 outFile.Close();
92}
93
94wxFileName SAVEAS_SUBSHEET_FIXTURE::GetSchematicPath( const wxString& aRelativePath )
95{
96 wxFileName fn( m_srcDir );
97 fn.SetName( aRelativePath );
99 return fn;
100}
101
102static bool ScreensContain( SCH_SCREENS& aScreens, SCH_SCREEN* aScreen )
103{
104 for( size_t i = 0; i < aScreens.GetCount(); i++ )
105 {
106 if( aScreens.GetScreen( i ) == aScreen )
107 return true;
108 }
109
110 return false;
111}
112
113BOOST_FIXTURE_TEST_SUITE( SaveAsSubsheetCopy, SAVEAS_SUBSHEET_FIXTURE )
114
115BOOST_AUTO_TEST_CASE( CopyInternalReferenceExternal )
116{
117 LoadSchematic( GetSchematicPath( wxS( "issue13212" ) ) );
118
119 SCH_SCREENS screens( m_schematic->Root() );
120 wxFileName srcRoot = GetSchematicPath( wxS( "issue13212" ) );
121
122 wxFileName destRoot( m_baseDir );
123 destRoot.AppendDir( wxS( "new" ) );
124 destRoot.AppendDir( wxS( "location" ) );
125 destRoot.SetName( wxS( "issue13212" ) );
126 destRoot.SetExt( FILEEXT::KiCadSchematicFileExtension );
127 { wxFileName destDir( destRoot ); destDir.SetFullName( wxEmptyString ); if( !destDir.DirExists() ) destDir.Mkdir( 0777, wxPATH_MKDIR_FULL ); }
128
129 std::unordered_map<SCH_SCREEN*, wxString> filenameMap;
130 wxString msg;
131
132 m_schematic->Root().SetFileName( destRoot.GetFullName() );
133 m_schematic->RootScreen()->SetFileName( destRoot.GetFullPath() );
134
135 bool ok = PrepareSaveAsFiles( *m_schematic, screens, srcRoot, destRoot, false, true,
136 false, filenameMap, msg );
137 BOOST_CHECK( ok );
138
139 SCH_SCREEN* internal = nullptr;
140 SCH_SCREEN* external = nullptr;
141
142 for( size_t i = 0; i < screens.GetCount(); i++ )
143 {
144 SCH_SCREEN* s = screens.GetScreen( i );
145
146 if( wxString( s->GetFileName() ).EndsWith( wxS( "issue13212_subsheet_1.kicad_sch" ) ) )
147 internal = s;
148 else if( wxString( s->GetFileName() ).EndsWith( wxS( "issue13212_subsheet_2.kicad_sch" ) ) )
149 external = s;
150 }
151
152 wxFileName internalExpected( destRoot.GetPath(), wxS( "issue13212_subsheet_1.kicad_sch" ) );
153 BOOST_CHECK_EQUAL( internal->GetFileName(), internalExpected.GetFullPath() );
154
155 wxFileName externalExpected( m_externalDir.GetFullPath(), wxS( "issue13212_subsheet_2.kicad_sch" ) );
156 BOOST_CHECK_EQUAL( external->GetFileName(), externalExpected.GetFullPath() );
157
158 SCH_SHEET_LIST sheetList = m_schematic->BuildSheetListSortedByPageNumbers();
159
160 wxString externalSheetPath;
161
162 for( const SCH_SHEET_PATH& path : sheetList )
163 {
164 if( path.Last()->GetFileName().Contains( wxS( "issue13212_subsheet_2" ) ) )
165 externalSheetPath = path.Last()->GetFileName();
166 }
167
168 BOOST_CHECK_EQUAL( externalSheetPath, wxS( "../../external/issue13212_subsheet_2.kicad_sch" ) );
169}
170
171BOOST_AUTO_TEST_CASE( CopyIncludingExternal )
172{
173 LoadSchematic( GetSchematicPath( wxS( "issue13212" ) ) );
174
175 SCH_SCREENS screens( m_schematic->Root() );
176 wxFileName srcRoot = GetSchematicPath( wxS( "issue13212" ) );
177
178 wxFileName destRoot( m_baseDir );
179 destRoot.AppendDir( wxS( "destall" ) );
180 destRoot.SetName( wxS( "issue13212" ) );
181 destRoot.SetExt( FILEEXT::KiCadSchematicFileExtension );
182 { wxFileName destDir( destRoot ); destDir.SetFullName( wxEmptyString ); if( !destDir.DirExists() ) destDir.Mkdir( 0777, wxPATH_MKDIR_FULL ); }
183
184 std::unordered_map<SCH_SCREEN*, wxString> filenameMap;
185 wxString msg;
186
187 m_schematic->Root().SetFileName( destRoot.GetFullName() );
188 m_schematic->RootScreen()->SetFileName( destRoot.GetFullPath() );
189
190 bool ok = PrepareSaveAsFiles( *m_schematic, screens, srcRoot, destRoot, false, true,
191 true, filenameMap, msg );
192 BOOST_CHECK( ok );
193
194 SCH_SCREEN* external = nullptr;
195
196 for( size_t i = 0; i < screens.GetCount(); i++ )
197 {
198 SCH_SCREEN* s = screens.GetScreen( i );
199
200 if( wxString( s->GetFileName() ).EndsWith( wxS( "issue13212_subsheet_2.kicad_sch" ) ) )
201 external = s;
202 }
203
204 wxFileName externalExpected( destRoot.GetPath(), wxS( "issue13212_subsheet_2.kicad_sch" ) );
205 BOOST_CHECK_EQUAL( external->GetFileName(), externalExpected.GetFullPath() );
206}
207
208BOOST_AUTO_TEST_CASE( NoCopyKeepsOriginalPaths )
209{
210 LoadSchematic( GetSchematicPath( wxS( "issue13212" ) ) );
211
212 SCH_SCREENS screens( m_schematic->Root() );
213 wxFileName srcRoot = GetSchematicPath( wxS( "issue13212" ) );
214
215 wxFileName destRoot( m_baseDir );
216 destRoot.AppendDir( wxS( "nocopy" ) );
217 destRoot.SetName( wxS( "issue13212" ) );
218 destRoot.SetExt( FILEEXT::KiCadSchematicFileExtension );
219 { wxFileName destDir( destRoot ); destDir.SetFullName( wxEmptyString ); if( !destDir.DirExists() ) destDir.Mkdir( 0777, wxPATH_MKDIR_FULL ); }
220
221 std::unordered_map<SCH_SCREEN*, wxString> filenameMap;
222 wxString msg;
223
224 m_schematic->Root().SetFileName( destRoot.GetFullName() );
225 m_schematic->RootScreen()->SetFileName( destRoot.GetFullPath() );
226
227 bool ok = PrepareSaveAsFiles( *m_schematic, screens, srcRoot, destRoot, false, false,
228 false, filenameMap, msg );
229 BOOST_CHECK( ok );
230
231 SCH_SCREEN* internal = nullptr;
232
233 for( size_t i = 0; i < screens.GetCount(); i++ )
234 {
235 SCH_SCREEN* s = screens.GetScreen( i );
236
237 if( wxString( s->GetFileName() ).EndsWith( wxS( "issue13212_subsheet_1.kicad_sch" ) ) )
238 internal = s;
239 }
240
241 wxFileName internalExpected( m_srcDir.GetFullPath(), wxS( "issue13212_subsheet_1.kicad_sch" ) );
242 BOOST_CHECK_EQUAL( internal->GetFileName(), internalExpected.GetFullPath() );
243}
244
245// A destination on the virtual root's screen becomes a nameless path that the callers stamp
246// an extension onto, writing a stray ".kicad_sch" beside the real schematic
247BOOST_AUTO_TEST_CASE( VirtualRootScreenGetsNoDestination )
248{
249 LoadSchematic( GetSchematicPath( wxS( "issue13212" ) ) );
250
251 SCH_SCREENS screens( m_schematic->Root() );
252 wxFileName srcRoot = GetSchematicPath( wxS( "issue13212" ) );
253
254 wxFileName destRoot( m_baseDir );
255 destRoot.AppendDir( wxS( "virtualroot" ) );
256 destRoot.SetName( wxS( "issue13212" ) );
257 destRoot.SetExt( FILEEXT::KiCadSchematicFileExtension );
258
259 wxFileName destDir( destRoot );
260 destDir.SetFullName( wxEmptyString );
261 BOOST_REQUIRE( destDir.DirExists() || destDir.Mkdir( 0777, wxPATH_MKDIR_FULL ) );
262
263 SCH_SCREEN* virtualRootScreen = m_schematic->Root().GetScreen();
264 BOOST_REQUIRE( virtualRootScreen );
265 BOOST_REQUIRE( virtualRootScreen != m_schematic->RootScreen() );
266 BOOST_REQUIRE( ScreensContain( screens, virtualRootScreen ) );
267
268 std::unordered_map<SCH_SCREEN*, wxString> filenameMap;
269 wxString msg;
270
271 m_schematic->Root().SetFileName( destRoot.GetFullName() );
272 m_schematic->RootScreen()->SetFileName( destRoot.GetFullPath() );
273
274 BOOST_CHECK( PrepareSaveAsFiles( *m_schematic, screens, srcRoot, destRoot, false, true,
275 false, filenameMap, msg ) );
276
277 BOOST_CHECK_EQUAL( virtualRootScreen->GetFileName(), wxString() );
278}
279
280BOOST_AUTO_TEST_CASE( VirtualRootScreenGetsNoDestinationOnCopy )
281{
282 LoadSchematic( GetSchematicPath( wxS( "issue13212" ) ) );
283
284 SCH_SCREENS screens( m_schematic->Root() );
285 wxFileName srcRoot = GetSchematicPath( wxS( "issue13212" ) );
286
287 wxFileName destRoot( m_baseDir );
288 destRoot.AppendDir( wxS( "virtualrootcopy" ) );
289 destRoot.SetName( wxS( "issue13212" ) );
290 destRoot.SetExt( FILEEXT::KiCadSchematicFileExtension );
291
292 wxFileName destDir( destRoot );
293 destDir.SetFullName( wxEmptyString );
294 BOOST_REQUIRE( destDir.DirExists() || destDir.Mkdir( 0777, wxPATH_MKDIR_FULL ) );
295
296 SCH_SCREEN* virtualRootScreen = m_schematic->Root().GetScreen();
297 BOOST_REQUIRE( virtualRootScreen );
298 BOOST_REQUIRE( virtualRootScreen != m_schematic->RootScreen() );
299 BOOST_REQUIRE( ScreensContain( screens, virtualRootScreen ) );
300
301 std::unordered_map<SCH_SCREEN*, wxString> filenameMap;
302 wxString msg;
303
304 filenameMap[m_schematic->RootScreen()] = destRoot.GetFullPath();
305
306 BOOST_CHECK( PrepareSaveAsFiles( *m_schematic, screens, srcRoot, destRoot, true, true,
307 false, filenameMap, msg ) );
308
309 BOOST_CHECK( !filenameMap.contains( virtualRootScreen ) );
310}
311
312// The API save-copy took Root() as the sheet to write, so it emitted the virtual root's
313// container screen and the copy came out valid but empty
314BOOST_AUTO_TEST_CASE( ApiSaveCopyWritesTheTopLevelSheet )
315{
316 LoadSchematic( GetSchematicPath( wxS( "issue13212" ) ) );
317
318 std::vector<wxString> references;
319
320 for( SCH_ITEM* item : m_schematic->RootScreen()->Items().OfType( SCH_SYMBOL_T ) )
321 references.push_back( static_cast<SCH_SYMBOL*>( item )->GetField( FIELD_T::REFERENCE )->GetText() );
322
323 BOOST_REQUIRE( !references.empty() );
324
325 wxFileName destFile( m_baseDir );
326 destFile.AppendDir( wxS( "apicopy" ) );
327 destFile.SetName( wxS( "copy" ) );
328 destFile.SetExt( FILEEXT::KiCadSchematicFileExtension );
329
330 wxFileName destDir( destFile );
331 destDir.SetFullName( wxEmptyString );
332 BOOST_REQUIRE( destDir.DirExists() || destDir.Mkdir( 0777, wxPATH_MKDIR_FULL ) );
333
334 BOOST_REQUIRE( SCH_API_SAVE::SaveSchematicCopy( *m_schematic, m_schematic->Project(),
335 destFile.GetFullPath(), false ) );
336
337 wxFFile written( destFile.GetFullPath(), wxS( "rb" ) );
338 BOOST_REQUIRE( written.IsOpened() );
339
340 wxString content;
341 written.ReadAll( &content );
342
343 for( const wxString& reference : references )
344 BOOST_CHECK( content.Contains( wxS( "\"" ) + reference + wxS( "\"" ) ) );
345}
346
A generic fixture for loading schematics and associated settings for qa tests.
wxFileName GetSchematicPath(const wxString &aRelativePath)
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition sch_item.h:165
Container class that holds multiple SCH_SCREEN objects in a hierarchy.
Definition sch_screen.h:758
SCH_SCREEN * GetScreen(unsigned int aIndex) const
size_t GetCount() const
Definition sch_screen.h:763
const wxString & GetFileName() const
Definition sch_screen.h:153
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...
Schematic symbol object.
Definition sch_symbol.h:75
bool PrepareSaveAsFiles(SCHEMATIC &aSchematic, SCH_SCREENS &aScreens, const wxFileName &aOldRoot, const wxFileName &aNewRoot, bool aSaveCopy, bool aCopySubsheets, bool aIncludeExternSheets, std::unordered_map< SCH_SCREEN *, wxString > &aFilenameMap, wxString &aErrorMsg)
static const std::string KiCadSchematicFileExtension
std::string GetEeschemaTestDataDir()
Get the configured location of Eeschema test data.
bool SaveSchematicCopy(SCHEMATIC &aSchematic, PROJECT &aProject, const wxString &aFileName, bool aCreateProject)
Save the root schematic to aFileName without changing the open document.
static void LoadSchematic(SCHEMATIC *aSchematic, SCH_SHEET *aRootSheet, const wxString &aFileName)
Definition of the SCH_SHEET_PATH and SCH_SHEET_LIST classes for Eeschema.
@ REFERENCE
Field Reference of part, i.e. "IC21".
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(CopyInternalReferenceExternal)
static bool ScreensContain(SCH_SCREENS &aScreens, SCH_SCREEN *aScreen)
BOOST_CHECK_EQUAL(result, "25.4")
@ SCH_SYMBOL_T
Definition typeinfo.h:168
Definition of file extensions used in Kicad.