KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_api_jobs.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 modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation, either version 3 of the License, or (at your
9 * option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#include <fstream>
21#include <sstream>
22
23#include <boost/test/unit_test.hpp>
24#include <wx/dir.h>
25#include <wx/filename.h>
26
27#include "api_e2e_utils.h"
28
29#include <api/board/board_jobs.pb.h>
30#include <api/board/board_types.pb.h>
31#include <api/schematic/schematic_jobs.pb.h>
32
33
40bool textFilesMatch( const wxString& aGoldenPath, const wxString& aGeneratedPath, int aSkipLines )
41{
42 std::ifstream goldenStream( aGoldenPath.ToStdString() );
43 std::ifstream generatedStream( aGeneratedPath.ToStdString() );
44
45 if( !goldenStream.is_open() )
46 {
47 BOOST_TEST_MESSAGE( "Cannot open golden file: " + aGoldenPath );
48 return false;
49 }
50
51 if( !generatedStream.is_open() )
52 {
53 BOOST_TEST_MESSAGE( "Cannot open generated file: " + aGeneratedPath );
54 return false;
55 }
56
57 std::string goldenLine, generatedLine;
58
59 for( int i = 0; i < aSkipLines; ++i )
60 {
61 std::getline( goldenStream, goldenLine );
62 std::getline( generatedStream, generatedLine );
63 }
64
65 int lineNum = aSkipLines + 1;
66
67 while( std::getline( goldenStream, goldenLine ) )
68 {
69 if( !std::getline( generatedStream, generatedLine ) )
70 {
71 BOOST_TEST_MESSAGE( "Generated file is shorter than golden at line " << lineNum );
72 return false;
73 }
74
75 if( goldenLine != generatedLine )
76 {
77 BOOST_TEST_MESSAGE( "Mismatch at line " << lineNum << ":\n"
78 << " golden: " << goldenLine << "\n"
79 << " generated: " << generatedLine );
80 return false;
81 }
82
83 ++lineNum;
84 }
85
86 if( std::getline( generatedStream, generatedLine ) )
87 {
88 BOOST_TEST_MESSAGE( "Generated file is longer than golden after line " << lineNum );
89 return false;
90 }
91
92 return true;
93}
94
95
96BOOST_AUTO_TEST_SUITE( ApiJobs )
97
98
100{
101 BOOST_REQUIRE_MESSAGE( Start(), LastError() );
102
103 wxString testDataDir =
104 wxString::FromUTF8( KI_TEST::GetTestDataRootDir() ) + wxS( "cli/artwork_generation_regressions/" );
105
106 wxFileName boardPath( testDataDir, wxS( "ZoneFill-4.0.7.kicad_pcb" ) );
107
108 kiapi::common::types::DocumentSpecifier document;
109
110 BOOST_REQUIRE_MESSAGE( Client().OpenDocument( boardPath.GetFullPath(), &document ),
111 "OpenDocument failed: " + Client().LastError() );
112
113 wxFileName outputPath = wxFileName::CreateTempFileName( wxS( "api_job_svg_" ) );
114 outputPath.SetExt( wxS( "svg" ) );
115
116 kiapi::board::jobs::RunBoardJobExportSvg request;
117 *request.mutable_job_settings()->mutable_document() = document;
118 request.mutable_job_settings()->set_output_path( outputPath.GetFullPath().ToUTF8().data() );
119
120 request.mutable_plot_settings()->add_layers( kiapi::board::types::BL_F_Cu );
121 request.mutable_plot_settings()->set_black_and_white( true );
122 request.mutable_plot_settings()->set_plot_drawing_sheet( false );
123 request.mutable_plot_settings()->set_drill_marks( kiapi::board::jobs::PDM_FULL );
124 request.set_page_mode( kiapi::board::jobs::BJPM_EACH_LAYER_OWN_FILE );
125
126 kiapi::common::types::RunJobResponse response;
127 BOOST_REQUIRE_MESSAGE( Client().RunJob( request, &response ), "RunJob failed: " + Client().LastError() );
128
129 BOOST_REQUIRE_MESSAGE( response.status() == kiapi::common::types::JS_SUCCESS,
130 "Job failed: " + wxString::FromUTF8( response.message() ) );
131
132 BOOST_REQUIRE_MESSAGE( response.output_path_size() > 0, "Job returned no output paths" );
133
134 wxString generatedPath = wxString::FromUTF8( response.output_path( 0 ) );
135 BOOST_REQUIRE_MESSAGE( wxFileName::FileExists( generatedPath ), "Generated SVG does not exist: " + generatedPath );
136
137 // Image comparison in c++ would be hard; so for now just check size is close
138 constexpr long target_size = 41839;
139 wxFileName generatedFn( generatedPath );
140 BOOST_CHECK_LT( std::abs( target_size - static_cast<long>( generatedFn.GetSize().GetValue() ) ), 10 );
141
142 if( wxFileName::FileExists( generatedPath ) )
143 wxRemoveFile( generatedPath );
144
145 if( wxFileName::FileExists( outputPath.GetFullPath() ) )
146 wxRemoveFile( outputPath.GetFullPath() );
147}
148
149
151{
152 BOOST_REQUIRE_MESSAGE( Start(), LastError() );
153
154 wxString testDataDir = wxString::FromUTF8( KI_TEST::GetTestDataRootDir() ) + wxS( "cli/basic_test/" );
155
156 wxFileName boardPath( testDataDir, wxS( "basic_test.kicad_pcb" ) );
157
158 kiapi::common::types::DocumentSpecifier document;
159
160 BOOST_REQUIRE_MESSAGE( Client().OpenDocument( boardPath.GetFullPath(), &document ),
161 "OpenDocument failed: " + Client().LastError() );
162
163 wxString outputDir = wxFileName::GetTempDir() + wxFileName::GetPathSeparator() + wxS( "api_job_drill_" )
164 + wxString::Format( "%ld", wxGetProcessId() ) + wxFileName::GetPathSeparator();
165 wxFileName::Mkdir( outputDir, wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL );
166
167 kiapi::board::jobs::RunBoardJobExportDrill request;
168 *request.mutable_job_settings()->mutable_document() = document;
169 request.mutable_job_settings()->set_output_path( outputDir.ToUTF8().data() );
170 request.set_format( kiapi::board::jobs::DF_EXCELLON );
171
172 kiapi::common::types::RunJobResponse response;
173 BOOST_REQUIRE_MESSAGE( Client().RunJob( request, &response ), "RunJob failed: " + Client().LastError() );
174
175 BOOST_REQUIRE_MESSAGE( response.status() == kiapi::common::types::JS_SUCCESS,
176 "Job failed: " + wxString::FromUTF8( response.message() ) );
177
178 BOOST_REQUIRE_MESSAGE( response.output_path_size() > 0, "Job returned no output paths" );
179
180 // Find the generated .drl file. The API may return either explicit file paths or an output
181 // directory (for jobs that emit multiple files).
182 wxString generatedOutputDir = wxString::FromUTF8( response.output_path( 0 ) );
183 wxString generatedDrillPath;
184
185 if( !generatedOutputDir.IsEmpty() )
186 {
187 wxDir dir( generatedOutputDir );
188 wxString filename;
189
190 if( dir.IsOpened() && dir.GetFirst( &filename, wxS( "*.drl" ), wxDIR_FILES ) )
191 {
192 wxFileName fn( generatedOutputDir, filename );
193 generatedDrillPath = fn.GetFullPath();
194 }
195 }
196
197 BOOST_REQUIRE_MESSAGE( !generatedDrillPath.IsEmpty(), "No .drl file found in job output" );
198 BOOST_REQUIRE_MESSAGE( wxFileName::FileExists( generatedDrillPath ),
199 "Generated drill file does not exist: " + generatedDrillPath );
200
201 wxString goldenPath = testDataDir + wxS( "basic_test_excellon_inches.drl" );
202 BOOST_CHECK_MESSAGE( textFilesMatch( goldenPath, generatedDrillPath, 5 ),
203 "Drill output does not match golden file" );
204
205 wxFileName::Rmdir( outputDir, wxPATH_RMDIR_RECURSIVE );
206}
207
208
210{
211 BOOST_REQUIRE_MESSAGE( Start(), LastError() );
212
213 wxString testDataDir = wxString::FromUTF8( KI_TEST::GetTestDataRootDir() ) + wxS( "cli/basic_test/" );
214
215 wxFileName schPath( testDataDir, wxS( "basic_test.kicad_sch" ) );
216
217 kiapi::common::types::DocumentSpecifier document;
218
219 BOOST_REQUIRE_MESSAGE(
220 Client().OpenDocument( schPath.GetFullPath(), kiapi::common::types::DOCTYPE_SCHEMATIC, &document ),
221 "OpenDocument failed: " + Client().LastError() );
222
223 wxFileName outputPath = wxFileName::CreateTempFileName( wxS( "api_job_netlist_" ) );
224 outputPath.SetExt( wxS( "cadstar" ) );
225
226 kiapi::schematic::jobs::RunSchematicJobExportNetlist request;
227 *request.mutable_job_settings()->mutable_document() = document;
228 request.mutable_job_settings()->set_output_path( outputPath.GetFullPath().ToUTF8().data() );
229 request.set_format( kiapi::schematic::jobs::SNF_CADSTAR );
230
231 kiapi::common::types::RunJobResponse response;
232 BOOST_REQUIRE_MESSAGE( Client().RunJob( request, &response ), "RunJob failed: " + Client().LastError() );
233
234 BOOST_REQUIRE_MESSAGE( response.status() == kiapi::common::types::JS_SUCCESS,
235 "Job failed: " + wxString::FromUTF8( response.message() ) );
236
237 BOOST_REQUIRE_MESSAGE( response.output_path_size() > 0, "Job returned no output paths" );
238
239 wxString generatedPath = wxString::FromUTF8( response.output_path( 0 ) );
240 BOOST_REQUIRE_MESSAGE( wxFileName::FileExists( generatedPath ),
241 "Generated netlist does not exist: " + generatedPath );
242
243 // 3 header lines to skip (contain timestamp/version)
244 wxString goldenPath = testDataDir + wxS( "basic_test.netlist.cadstar" );
245 BOOST_CHECK_MESSAGE( textFilesMatch( goldenPath, generatedPath, 3 ), "Netlist output does not match golden file" );
246
247 // Clean up
248 if( wxFileName::FileExists( generatedPath ) )
249 wxRemoveFile( generatedPath );
250
251 if( wxFileName::FileExists( outputPath.GetFullPath() ) )
252 wxRemoveFile( outputPath.GetFullPath() );
253}
254
255
257{
258 BOOST_REQUIRE_MESSAGE( Start(), LastError() );
259
260 wxString testDataDir = wxString::FromUTF8( KI_TEST::GetTestDataRootDir() ) + wxS( "cli/variants/" );
261
262 wxFileName schPath( testDataDir, wxS( "variants.kicad_sch" ) );
263
264 kiapi::common::types::DocumentSpecifier document;
265
266 BOOST_REQUIRE_MESSAGE(
267 Client().OpenDocument( schPath.GetFullPath(), kiapi::common::types::DOCTYPE_SCHEMATIC, &document ),
268 "OpenDocument failed: " + Client().LastError() );
269
270 wxFileName outputPath = wxFileName::CreateTempFileName( wxS( "api_job_bom_" ) );
271 outputPath.SetExt( wxS( "csv" ) );
272
273 kiapi::schematic::jobs::RunSchematicJobExportBOM request;
274 *request.mutable_job_settings()->mutable_document() = document;
275 request.mutable_job_settings()->set_output_path( outputPath.GetFullPath().ToUTF8().data() );
276 request.set_exclude_dnp( true );
277
278 request.mutable_format()->set_preset_name( "CSV" );
279
280 auto* refField = request.mutable_fields()->add_fields();
281 refField->set_name( "Reference" );
282 refField->set_label( "Refs" );
283 refField->set_group_by( false );
284
285 auto* valField = request.mutable_fields()->add_fields();
286 valField->set_name( "Value" );
287 valField->set_label( "Value" );
288 valField->set_group_by( false );
289
290 kiapi::common::types::RunJobResponse response;
291 BOOST_REQUIRE_MESSAGE( Client().RunJob( request, &response ), "RunJob failed: " + Client().LastError() );
292
293 BOOST_REQUIRE_MESSAGE( response.status() == kiapi::common::types::JS_SUCCESS,
294 "Job failed: " + wxString::FromUTF8( response.message() ) );
295
296 BOOST_REQUIRE_MESSAGE( response.output_path_size() > 0, "Job returned no output paths" );
297
298 wxString generatedPath = wxString::FromUTF8( response.output_path( 0 ) );
299 BOOST_REQUIRE_MESSAGE( wxFileName::FileExists( generatedPath ), "Generated BOM does not exist: " + generatedPath );
300
301 wxString goldenPath = testDataDir + wxS( "variants_default.bom.csv" );
302 BOOST_CHECK_MESSAGE( textFilesMatch( goldenPath, generatedPath, 0 ), "BOM output does not match golden file" );
303
304 if( wxFileName::FileExists( generatedPath ) )
305 wxRemoveFile( generatedPath );
306
307 if( wxFileName::FileExists( outputPath.GetFullPath() ) )
308 wxRemoveFile( outputPath.GetFullPath() );
309}
310
311
std::string GetTestDataRootDir()
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition eda_angle.h:400
BOOST_FIXTURE_TEST_CASE(ServerStartsAndResponds, API_SERVER_E2E_FIXTURE)
bool textFilesMatch(const wxString &aGoldenPath, const wxString &aGeneratedPath, int aSkipLines)
Compare two text files line-by-line, optionally skipping the first aSkipLines lines of each file (to ...
BOOST_FIXTURE_TEST_CASE(ExportBoardSvg, API_SERVER_E2E_FIXTURE)
BOOST_AUTO_TEST_SUITE(CadstarPartParser)
BOOST_AUTO_TEST_SUITE_END()
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))