24#include <boost/test/unit_test.hpp>
31#include <wx/filename.h>
32#include <wx/process.h>
33#include <wx/stdpaths.h>
34#include <wx/txtstrm.h>
41#ifndef QA_KICAD_CLI_PATH
42#define QA_KICAD_CLI_PATH "kicad-cli"
53 static int counter = 0;
54 m_path = wxFileName::GetTempDir() + wxFILE_SEP_PATH
55 + wxString::Format( wxS(
"kicad_cli_visual_diff_%ld_%d" ),
56 static_cast<long>( wxGetProcessId() ), ++counter );
58 BOOST_REQUIRE( wxFileName::Mkdir( m_path, wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL ) );
63 if( !m_path.IsEmpty() && wxFileName::DirExists( m_path ) )
64 wxFileName::Rmdir( m_path, wxPATH_RMDIR_RECURSIVE );
67 wxString Path(
const wxString& aName )
const
69 return m_path + wxFILE_SEP_PATH + aName;
84wxString readStream( wxInputStream* aStream )
91 wxTextInputStream textStream( *aStream );
93 while( !aStream->Eof() )
95 wxString line = textStream.ReadLine();
97 if( !line.IsEmpty() || !aStream->Eof() )
99 if( !output.IsEmpty() )
100 output += wxS(
"\n" );
110COMMAND_RESULT runCli(
const std::vector<wxString>& aArgs )
115 std::vector<const wchar_t*> argv;
116 argv.reserve( aArgs.size() + 2 );
119 for(
const wxString& arg : aArgs )
120 argv.push_back( arg.wc_str() );
122 argv.push_back(
nullptr );
125 result.exitCode =
static_cast<int>( wxExecute(
const_cast<wchar_t**
>( argv.data() ), wxEXEC_SYNC, &
process ) );
135void copyFixture(
const wxString& aName,
const wxString& aDest )
138 + wxS(
"diff_merge/visual_diff/" ) + aName;
140 BOOST_REQUIRE_MESSAGE( wxCopyFile( src, aDest ),
"Could not copy " << src );
144void expectCleanExit(
const wxString& aName,
const COMMAND_RESULT& aResult,
int aExpectedExitCode )
149 BOOST_CHECK_MESSAGE( aResult.output.IsEmpty(),
"Unexpected stdout: " << aResult.output );
150 BOOST_CHECK_MESSAGE( aResult.error.IsEmpty(),
"Unexpected stderr: " << aResult.error );
155void expectInvalidExit(
const wxString& aName,
const COMMAND_RESULT& aResult,
int aExpectedExitCode )
164void expectSvg(
const wxString& aName,
const wxString& aPath )
170 BOOST_CHECK( bytes.find(
"<svg" ) != std::string::npos );
175void expectPng(
const wxString& aName,
const wxString& aPath )
177 static constexpr std::array<unsigned char, 8> PNG_HEADER = { 0x89,
'P',
'N',
'G',
'\r',
'\n', 0x1a,
'\n' };
182 BOOST_REQUIRE_GE( bytes.size(), PNG_HEADER.size() );
184 for(
size_t i = 0; i < PNG_HEADER.size(); ++i )
190void expectFilesDiffer(
const wxString& aName,
const wxString& aPathA,
const wxString& aPathB )
201 explicit CLI_FIXTURES( TEMP_DIR& aDir )
203 pcbA = aDir.Path( wxS(
"pcb_a.kicad_pcb" ) );
204 pcbB = aDir.Path( wxS(
"pcb_b.kicad_pcb" ) );
205 schA = aDir.Path( wxS(
"sch_a.kicad_sch" ) );
206 schB = aDir.Path( wxS(
"sch_b.kicad_sch" ) );
207 fpA = aDir.Path( wxS(
"fp_a.pretty" ) );
208 fpB = aDir.Path( wxS(
"fp_b.pretty" ) );
209 symA = aDir.Path( wxS(
"sym_a.kicad_sym" ) );
210 symB = aDir.Path( wxS(
"sym_b.kicad_sym" ) );
218 void writePcbFixtures()
220 copyFixture( wxS(
"pcb_a.kicad_pcb" ), pcbA );
221 copyFixture( wxS(
"pcb_b.kicad_pcb" ), pcbB );
224 void writeSchFixtures()
226 copyFixture( wxS(
"sch_a.kicad_sch" ), schA );
227 copyFixture( wxS(
"sch_b.kicad_sch" ), schB );
230 void writeFpFixtures()
232 BOOST_REQUIRE( wxFileName::Mkdir( fpA, wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL ) );
233 BOOST_REQUIRE( wxFileName::Mkdir( fpB, wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL ) );
236 copyFixture( wxS(
"fp_b.pretty/VisualDiff.kicad_mod" ),
237 fpB + wxFILE_SEP_PATH + wxS(
"VisualDiff.kicad_mod" ) );
240 void writeSymFixtures()
242 copyFixture( wxS(
"sym_a.kicad_sym" ), symA );
243 copyFixture( wxS(
"sym_b.kicad_sym" ), symB );
259 wxString commandGroup;
260 wxString commandName;
262 wxString changedPath;
266void expectVisualDifference( TEMP_DIR& aDir,
const CLI_CASE& aCase,
const wxString& aFormat )
268 const wxString caseName = aCase.commandGroup + wxS(
" " ) + aFormat;
269 const wxString sameOut = aDir.Path( aCase.commandGroup + wxS(
"_same." ) + aFormat );
270 const wxString diffOut = aDir.Path( aCase.commandGroup + wxS(
"_diff." ) + aFormat );
272 std::vector<wxString> sameArgs = { aCase.commandGroup, aCase.commandName, aCase.refPath, aCase.refPath,
273 wxS(
"--format" ), aFormat, wxS(
"--output" ), sameOut };
274 std::vector<wxString> diffArgs = { aCase.commandGroup, aCase.commandName, aCase.refPath, aCase.changedPath,
275 wxS(
"--format" ), aFormat, wxS(
"--output" ), diffOut };
277 expectCleanExit( caseName + wxS(
" identical" ), runCli( sameArgs ), 0 );
278 expectCleanExit( caseName + wxS(
" changed" ), runCli( diffArgs ), 5 );
280 if( aFormat == wxS(
"svg" ) )
282 expectSvg( caseName + wxS(
" identical" ), sameOut );
283 expectSvg( caseName + wxS(
" changed" ), diffOut );
287 expectPng( caseName + wxS(
" identical" ), sameOut );
288 expectPng( caseName + wxS(
" changed" ), diffOut );
291 expectFilesDiffer( caseName, sameOut, diffOut );
313 BOOST_CHECK( j.contains(
"output_filename" ) );
314 BOOST_CHECK_EQUAL( j[
"output_filename"].get<wxString>(), wxString( wxS(
"diff-out.json" ) ) );
315 BOOST_CHECK( !j.contains(
"output" ) );
328 CLI_FIXTURES fixtures( dir );
330 const std::vector<CLI_CASE> cases = {
331 { wxS(
"pcb" ), wxS(
"diff" ), fixtures.pcbA, fixtures.pcbB },
332 { wxS(
"sch" ), wxS(
"diff" ), fixtures.schA, fixtures.schB },
333 { wxS(
"fp" ), wxS(
"diff" ), fixtures.fpA, fixtures.fpB },
334 { wxS(
"sym" ), wxS(
"diff" ), fixtures.symA, fixtures.symB },
337 for(
const CLI_CASE& c : cases )
341 expectInvalidExit( wxS(
"invalid format" ),
342 runCli( { c.commandGroup, c.commandName, c.refPath, c.refPath, wxS(
"--format" ),
346 expectInvalidExit( wxS(
"svg requires output" ),
347 runCli( { c.commandGroup, c.commandName, c.refPath, c.refPath, wxS(
"--format" ),
351 expectInvalidExit( wxS(
"png requires output" ),
352 runCli( { c.commandGroup, c.commandName, c.refPath, c.refPath, wxS(
"--format" ),
363 CLI_FIXTURES fixtures( dir );
365 const std::vector<CLI_CASE> cases = {
366 { wxS(
"pcb" ), wxS(
"diff" ), fixtures.pcbA, fixtures.pcbB },
367 { wxS(
"sch" ), wxS(
"diff" ), fixtures.schA, fixtures.schB },
368 { wxS(
"fp" ), wxS(
"diff" ), fixtures.fpA, fixtures.fpB },
369 { wxS(
"sym" ), wxS(
"diff" ), fixtures.symA, fixtures.symB },
372 for(
const CLI_CASE& c : cases )
374 expectVisualDifference( dir, c, wxS(
"svg" ) );
375 expectVisualDifference( dir, c, wxS(
"png" ) );
#define QA_KICAD_CLI_PATH
Job: diff two PCB files end-to-end via PCB_DIFFER.
void SetConfiguredOutputPath(const wxString &aPath)
Sets the configured output path for the job, this path is always saved to file.
wxString GetConfiguredOutputPath() const
Returns the configured output path for the job.
virtual void ToJson(nlohmann::json &j) const
std::string GetTestDataRootDir()
std::string LoadStringData(const wxString &aPath)
Load the contents of a file into a string.
static PGM_BASE * process
BOOST_AUTO_TEST_CASE(HorizontalAlignment)
BOOST_AUTO_TEST_SUITE(CadstarPartParser)
BOOST_AUTO_TEST_CASE(DiffJobSerializesSingleOutputKey)
BOOST_REQUIRE(intersection.has_value()==c.ExpectedIntersection.has_value())
BOOST_AUTO_TEST_SUITE_END()
BOOST_TEST_CONTEXT("Test Clearance")
wxString result
Test unit parsing edge cases and error handling.
BOOST_CHECK_EQUAL(result, "25.4")