KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_issue25198_multi_top_level_plot.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
20// Regression tests for
21// https://gitlab.com/kicad/code/kicad/-/issues/25198
22// https://gitlab.com/kicad/code/kicad/-/issues/25203
23// https://gitlab.com/kicad/code/kicad/-/issues/25231
24
25#include <qa_utils/file_utils.h>
28
29#include <memory>
30#include <set>
31#include <string>
32#include <vector>
33
34#include <eeschema_helpers.h>
35#include <locale_io.h>
36#include <sch_plotter.h>
37#include <sch_render_settings.h>
38#include <sch_screen.h>
39#include <sch_sheet.h>
40#include <sch_sheet_path.h>
41#include <schematic.h>
42
43#include <wx/filefn.h>
44#include <wx/filename.h>
45#include <wx/stdpaths.h>
46
47
48namespace
49{
50
51// The issue 25231 reproduction project has three flat top-level sheets in schematic.top_level_sheets
52struct MULTI_TOP_LEVEL_PLOT_FIXTURE
53{
54 MULTI_TOP_LEVEL_PLOT_FIXTURE()
55 {
56 wxString schPath = wxString::FromUTF8( KI_TEST::GetEeschemaTestDataDir() )
57 + wxS( "issue25198/SVG-Test.kicad_sch" );
58
59 m_schematic.reset( EESCHEMA_HELPERS::LoadSchematic( schPath, true, false ) );
60
61 wxFileName outDir( wxFileName::CreateTempFileName(
62 wxStandardPaths::Get().GetTempDir() + wxFileName::GetPathSeparator()
63 + wxS( "issue25198" ) ) );
64
65 wxRemoveFile( outDir.GetFullPath() );
66 m_outputDir = outDir.GetFullPath();
67 wxFileName::Mkdir( m_outputDir, wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL );
68 }
69
70 ~MULTI_TOP_LEVEL_PLOT_FIXTURE()
71 {
72 if( !m_outputDir.IsEmpty() )
73 wxFileName::Rmdir( m_outputDir, wxPATH_RMDIR_RECURSIVE );
74 }
75
76 // Mimics the hierarchy navigator, which sets only the screen filename and leaves SHEET_FILENAME empty
77 void AddTopLevelSheet( const wxString& aName, const wxString& aFileName )
78 {
79 SCH_SHEET* sheet = new SCH_SHEET( m_schematic.get() );
80 SCH_SCREEN* screen = new SCH_SCREEN( m_schematic.get() );
81
82 sheet->SetScreen( screen );
83 sheet->GetField( FIELD_T::SHEET_NAME )->SetText( aName );
84 screen->SetFileName( aFileName );
85
86 m_schematic->AddTopLevelSheet( sheet );
87 }
88
89 std::vector<wxString> PlotAll( PLOT_FORMAT aFormat )
90 {
91 SCH_RENDER_SETTINGS renderSettings;
92
93 SCH_PLOT_OPTS opts;
94 opts.m_plotAll = true;
95 opts.m_plotDrawingSheet = true;
96 opts.m_outputDirectory = m_outputDir;
97
98 SCH_PLOTTER plotter( m_schematic.get() );
99 plotter.Plot( aFormat, opts, &renderSettings, nullptr );
100
101 return plotter.GetOutputFilePaths();
102 }
103
104 std::unique_ptr<SCHEMATIC> m_schematic;
105 wxString m_outputDir;
106};
107
108
109int CountPdfPages( const wxString& aPath )
110{
111 std::string contents;
112
113 if( !ReadPdfWithDecompressedStreams( aPath, contents ) )
114 return 0;
115
116 // The plotter writes the page tree node as "/Type /Pages", so the newline keeps them apart
117 return CountOccurrences( contents, "/Type /Page\n" );
118}
119
120} // namespace
121
122
123BOOST_FIXTURE_TEST_SUITE( Issue25198MultiTopLevelPlot, MULTI_TOP_LEVEL_PLOT_FIXTURE )
124
125
126// kicad-cli used to load only the named file, dropping every sibling top-level sheet
127BOOST_AUTO_TEST_CASE( LoadsEveryTopLevelSheet )
128{
130
131 BOOST_REQUIRE( m_schematic != nullptr );
132 BOOST_REQUIRE_EQUAL( m_schematic->GetTopLevelSheets().size(), 3u );
133 BOOST_CHECK_EQUAL( m_schematic->Hierarchy().size(), 3u );
134
135 std::set<wxString> names;
136
137 for( const SCH_SHEET* sheet : m_schematic->GetTopLevelSheets() )
138 names.insert( sheet->GetName() );
139
140 BOOST_CHECK_EQUAL( names.count( wxS( "Toplevel1" ) ), 1u );
141 BOOST_CHECK_EQUAL( names.count( wxS( "Toplevel2" ) ), 1u );
142 BOOST_CHECK_EQUAL( names.count( wxS( "Toplevel3" ) ), 1u );
143}
144
145
146BOOST_AUTO_TEST_CASE( PlotsEveryTopLevelSheetToPdf )
147{
149
150 BOOST_REQUIRE( m_schematic != nullptr );
151
152 std::vector<wxString> outputs = PlotAll( PLOT_FORMAT::PDF );
153
154 BOOST_REQUIRE_EQUAL( outputs.size(), 1u );
155 BOOST_REQUIRE( wxFileName::FileExists( outputs[0] ) );
156 BOOST_CHECK_EQUAL( CountPdfPages( outputs[0] ), 3 );
157}
158
159
160// SVG, DXF and Postscript write one file per page, so each top-level sheet needs a unique name
161BOOST_AUTO_TEST_CASE( PlotsEveryTopLevelSheetToItsOwnSvg )
162{
164
165 BOOST_REQUIRE( m_schematic != nullptr );
166
167 std::vector<wxString> outputs = PlotAll( PLOT_FORMAT::SVG );
168
169 BOOST_REQUIRE_EQUAL( outputs.size(), 3u );
170
171 std::set<wxString> unique( outputs.begin(), outputs.end() );
172 BOOST_REQUIRE_EQUAL( unique.size(), 3u );
173
174 std::set<wxString> baseNames;
175
176 for( const wxString& path : outputs )
177 {
178 BOOST_REQUIRE( wxFileName::FileExists( path ) );
179
180 // An empty file is not a plot
181 std::string contents = KI_TEST::LoadStringData( path );
182 BOOST_CHECK_MESSAGE( contents.find( "<svg" ) != std::string::npos,
183 path.ToStdString() + " is not an SVG" );
184 BOOST_CHECK_MESSAGE( contents.find( "</svg>" ) != std::string::npos,
185 path.ToStdString() + " is truncated" );
186
187 baseNames.insert( wxFileName( path ).GetName() );
188 }
189
190 BOOST_CHECK_EQUAL( baseNames.count( wxS( "SVG-Test" ) ), 1u );
191 BOOST_CHECK_EQUAL( baseNames.count( wxS( "toplevel2" ) ), 1u );
192 BOOST_CHECK_EQUAL( baseNames.count( wxS( "toplevel3" ) ), 1u );
193}
194
195
196// Screen-only sheets used to fall back to the same default plot name and overwrite each other
197BOOST_AUTO_TEST_CASE( PlotsNewTopLevelSheetsToDistinctFiles )
198{
200
201 BOOST_REQUIRE( m_schematic != nullptr );
202
203 AddTopLevelSheet( wxS( "Toplevel4" ), wxS( "toplevel4.kicad_sch" ) );
204 AddTopLevelSheet( wxS( "Toplevel5" ), wxS( "toplevel5.kicad_sch" ) );
205
206 BOOST_REQUIRE_EQUAL( m_schematic->GetTopLevelSheets().size(), 5u );
207
208 std::vector<wxString> outputs = PlotAll( PLOT_FORMAT::SVG );
209
210 BOOST_REQUIRE_EQUAL( outputs.size(), 5u );
211
212 std::set<wxString> unique( outputs.begin(), outputs.end() );
213 BOOST_CHECK_EQUAL( unique.size(), 5u );
214
215 std::set<wxString> baseNames;
216
217 for( const wxString& path : outputs )
218 {
219 BOOST_CHECK_MESSAGE( wxFileName::FileExists( path ),
220 path.ToStdString() + " was not written" );
221 baseNames.insert( wxFileName( path ).GetName() );
222 }
223
224 BOOST_CHECK_EQUAL( baseNames.count( wxS( "toplevel4" ) ), 1u );
225 BOOST_CHECK_EQUAL( baseNames.count( wxS( "toplevel5" ) ), 1u );
226}
227
228
static SCHEMATIC * LoadSchematic(const wxString &aFileName, bool aSetActive, bool aForceDefaultProject, PROJECT *aProject=nullptr, bool aCalculateConnectivity=true, REPORTER *aRootReporter=nullptr)
Instantiate the current locale within a scope in which you are expecting exceptions to be thrown.
Definition locale_io.h:37
void SetText(const wxString &aText) override
void SetFileName(const wxString &aFileName)
Set the file name for this screen to aFileName.
Sheet symbol placed in a schematic, and is the entry point for a sub schematic.
Definition sch_sheet.h:48
SCH_FIELD * GetField(FIELD_T aFieldType)
Return a mandatory field in this sheet.
wxString GetName() const
Definition sch_sheet.h:142
void SetScreen(SCH_SCREEN *aScreen)
Set the SCH_SCREEN associated with this sheet to aScreen.
std::string LoadStringData(const wxString &aPath)
Load the contents of a file into a string.
std::string GetEeschemaTestDataDir()
Get the configured location of Eeschema test data.
int CountOccurrences(const std::string &aHaystack, const std::string &aNeedle)
Count occurrences of a substring in a string (overlapping allowed).
bool ReadPdfWithDecompressedStreams(const wxString &aPdfPath, std::string &aOutBuffer)
Read a PDF file and append best-effort decompressed contents of any Flate streams to the returned buf...
PLOT_FORMAT
The set of supported output plot formats.
Definition plotter.h:63
Definition of the SCH_SHEET_PATH and SCH_SHEET_LIST classes for Eeschema.
std::vector< FAB_LAYER_COLOR > dummy
wxString m_outputDirectory
Definition sch_plotter.h:67
bool m_plotDrawingSheet
Definition sch_plotter.h:55
BOOST_AUTO_TEST_CASE(HorizontalAlignment)
BOOST_REQUIRE(intersection.has_value()==c.ExpectedIntersection.has_value())
BOOST_AUTO_TEST_SUITE_END()
BOOST_AUTO_TEST_CASE(LoadsEveryTopLevelSheet)
std::string path
BOOST_CHECK_EQUAL(result, "25.4")