21#define BOOST_TEST_NO_MAIN
22#include <boost/test/unit_test.hpp>
35#include <wx/filename.h>
45 []( wxString* token ) ->
bool
47 if( *token == wxT(
"VAR" ) )
49 *token = wxT(
"value" );
52 else if( *token == wxT(
"X" ) )
57 else if( *token == wxT(
"Y" ) )
73 BOOST_CHECK(
result == wxT(
"value" ) );
80 BOOST_CHECK(
result == wxT(
"5+2" ) );
88 BOOST_CHECK(
result.Contains( wxT(
"<<<ESC_DOLLAR:" ) ) );
96 BOOST_CHECK(
result.Contains( wxT(
"<<<ESC_DOLLAR:" ) ) );
97 BOOST_CHECK(
result.Contains( wxT(
"value" ) ) );
105 BOOST_CHECK(
result.StartsWith( wxT(
"value" ) ) );
106 BOOST_CHECK(
result.Contains( wxT(
"<<<ESC_DOLLAR:" ) ) );
118 BOOST_CHECK_MESSAGE(
result.Contains( wxT(
"<<<ESC_DOLLAR:" ) ),
119 "Expected escape marker in result" );
122 BOOST_CHECK_MESSAGE(
result.Contains( wxT(
"+2" ) ),
123 "Expected '+2' (from ${Y} expansion) in result" );
126 BOOST_CHECK(
result == wxT(
"@{<<<ESC_DOLLAR:X}+2}" ) );
138 BOOST_CHECK(
result.Contains( wxT(
"<<<ESC_DOLLAR:" ) ) );
147 BOOST_CHECK_MESSAGE(
result.Contains( wxT(
"+2" ) ),
148 "Expected '+2' (from ${Y} expansion) in: " +
result );
154 while( ( pos =
result.find( wxT(
"<<<ESC_DOLLAR:" ), pos ) ) != wxString::npos )
169 BOOST_CHECK(
result.StartsWith( wxT(
"value" ) ) );
170 BOOST_CHECK(
result.Contains( wxT(
"<<<ESC_AT:" ) ) );
182 while( ( pos =
result.find( wxT(
"<<<ESC_DOLLAR:" ), pos ) ) != wxString::npos )
200 BOOST_CHECK(
result.Contains( wxT(
"<<<ESC_DOLLAR:" ) ) );
209 BOOST_CHECK(
result == wxT(
"subdir/value_file.txt" ) );
227 BOOST_CHECK_MESSAGE( !
result.Contains( wxT(
"<<<ESC_DOLLAR:" ) ),
228 "Variable after backslash separator should expand, not escape. Got: " +
result );
229 BOOST_CHECK(
result == wxT(
"Output\\BoM/value_file.csv" ) );
238 BOOST_CHECK(
result == wxT(
"Output\\BoM/5_V2.csv" ) );
247 BOOST_CHECK(
result == wxT(
"Output/BoM/value.csv" ) );
257 BOOST_CHECK(
path == wxT(
"Output\\BoM\\literal.csv" ) );
277 wxString
path = wxT(
"Board Stats\\${REVISION}_Stats.txt" );
281 BOOST_CHECK_MESSAGE( !
result.Contains( wxT(
"<<<ESC_DOLLAR:" ) ),
282 "Text variable after backslash path separator should not be escaped. Got: "
284 BOOST_CHECK_MESSAGE(
result.Contains( wxT(
"RevA" ) ),
285 "Expected resolved REVISION in path. Got: " +
result );
299 wxString
path = wxT(
"Output\\${COMMENT1}_${REVISION}_file.txt" );
302 BOOST_CHECK_MESSAGE(
result.Contains( wxT(
"DWG-001" ) ),
303 "Expected COMMENT1 expanded in path. Got: " +
result );
304 BOOST_CHECK_MESSAGE(
result.Contains( wxT(
"_B_" ) ),
305 "Expected REVISION expanded in path. Got: " +
result );
318 wxString
path = wxT(
"Output\\Generated_${REVISION}_file.txt" );
321 BOOST_CHECK_MESSAGE(
result.Contains( wxT(
"Generated_C_file.txt" ) ),
322 "Expected variable expansion with preceding literal text. Got: " +
result );
350 std::filesystem::path tmp = std::filesystem::temp_directory_path() /
351 "kicad_qa_overlap_env_vars";
353 std::filesystem::remove_all( tmp, ec );
354 std::filesystem::create_directories( tmp /
"V10" /
"symbols", ec );
356 rootDir = wxString::FromUTF8( tmp.string() );
358 innerDir = wxString::FromUTF8( ( tmp /
"V10" ).
string() );
359 targetDir = wxString::FromUTF8( ( tmp /
"V10" /
"symbols" ).
string() );
361 wxSetEnv( wxS(
"KICAD_QA_3RD_PARTY_OUTER" ),
outerDir );
362 wxSetEnv( wxS(
"KICAD_QA_USER_LIB_INNER" ),
innerDir );
367 wxUnsetEnv( wxS(
"KICAD_QA_3RD_PARTY_OUTER" ) );
368 wxUnsetEnv( wxS(
"KICAD_QA_USER_LIB_INNER" ) );
370 std::filesystem::path tmp = std::filesystem::temp_directory_path() /
371 "kicad_qa_overlap_env_vars";
373 std::filesystem::remove_all( tmp, ec );
391 wxFileName target( targetDir, wxS(
"test.kicad_sym" ) );
394 wxString normalized =
NormalizePath( target, &envMap, wxEmptyString );
398 normalized == wxS(
"${KICAD_QA_USER_LIB_INNER}/symbols/test.kicad_sym" ),
399 wxString::Format( wxS(
"Expected '%s' but got '%s'" ),
400 wxS(
"${KICAD_QA_USER_LIB_INNER}/symbols/test.kicad_sym" ),
407 wxFileName target( targetDir, wxS(
"test.kicad_sym" ) );
410 wxString normalized =
NormalizePath( target, &envMap, wxEmptyString );
413 wxFileName expandedFn( expanded );
414 expandedFn.Normalize( wxPATH_NORM_DOTS | wxPATH_NORM_ABSOLUTE );
416 wxFileName originalFn( target );
417 originalFn.Normalize( wxPATH_NORM_DOTS | wxPATH_NORM_ABSOLUTE );
420 expandedFn.GetFullPath() == originalFn.GetFullPath(),
422 wxS(
"Round-trip mismatch: normalized='%s' expanded='%s' original='%s'" ),
423 normalized, expandedFn.GetFullPath(), originalFn.GetFullPath() ) );
446 std::function<bool( wxString* )>
resolver =
447 []( wxString* token ) ->
bool
449 if( *token == wxT(
"#" ) )
455 if( *token == wxT(
"ROW" ) )
464 const std::vector<wxString> inputs = {
465 wxT(
"Out@{(${#}-2)*8+0}" ),
466 wxT(
"Net_@{${ROW}*2+1}" ),
467 wxT(
"@{(2-2)*8+0}" ),
468 wxT(
"${ROW}:@{${ROW}*${ROW}}" ),
469 wxT(
"plain_label_no_expr" ),
470 wxT(
"@{1+1}_@{2+2}_@{3+3}" ),
473 const unsigned int numThreads = std::max( 4u, std::thread::hardware_concurrency() );
474 const int iterations = 2000;
476 std::atomic<bool> failed{
false };
477 std::atomic<int> totalRuns{ 0 };
478 std::vector<std::thread> threads;
479 threads.reserve( numThreads );
481 for(
unsigned int t = 0; t < numThreads; ++t )
483 threads.emplace_back(
488 for(
int i = 0; i < iterations; ++i )
490 const wxString& src = inputs[( t + i ) % inputs.size()];
494 totalRuns.fetch_add( 1, std::memory_order_relaxed );
499 failed.store(
true, std::memory_order_relaxed );
504 for(
auto& th : threads )
507 BOOST_CHECK( !failed.load() );
508 BOOST_CHECK_EQUAL( totalRuns.load(),
static_cast<int>( numThreads ) * iterations );
533 if( wxGetEnv( wxS(
"KICAD_QA_INNER" ), &existing ) )
536 if( wxGetEnv( wxS(
"KICAD_QA_OUTER" ), &existing ) )
540 ( std::filesystem::temp_directory_path() /
"kicad-qa-24244" ).generic_string() );
542 wxSetEnv( wxS(
"KICAD_QA_INNER" ),
innerPath );
543 wxSetEnv( wxS(
"KICAD_QA_OUTER" ), wxS(
"${KICAD_QA_INNER}/templates" ) );
549 wxSetEnv( wxS(
"KICAD_QA_INNER" ), *
oldInner );
551 wxUnsetEnv( wxS(
"KICAD_QA_INNER" ) );
554 wxSetEnv( wxS(
"KICAD_QA_OUTER" ), *
oldOuter );
556 wxUnsetEnv( wxS(
"KICAD_QA_OUTER" ) );
565 BOOST_REQUIRE( wxGetEnv( wxS(
"KICAD_QA_OUTER" ), &rawValue ) );
568 BOOST_CHECK( rawValue.Contains( wxS(
"${KICAD_QA_INNER}" ) ) );
571 wxString
expected = innerPath + wxS(
"/templates" );
574 BOOST_CHECK_MESSAGE( expanded ==
expected,
575 wxString::Format( wxS(
"Expected '%s', got '%s'" ),
expected, expanded ) );
584 wxUnsetEnv( wxS(
"KICAD_QA_INNER" ) );
587 BOOST_REQUIRE( wxGetEnv( wxS(
"KICAD_QA_OUTER" ), &rawValue ) );
590 BOOST_CHECK( expanded.Contains( wxS(
"${" ) ) );
593 wxSetEnv( wxS(
"KICAD_QA_INNER" ), innerPath );
621 ( std::filesystem::temp_directory_path() /
"kicad-qa-24460-stock.pretty" ).generic_string() );
628 if( wxGetEnv( wxS(
"KICAD_USER_FOOTPRINT_DIR" ), &existing ) )
631 if( wxGetEnv( wxS(
"KICAD5_FOOTPRINT_DIR" ), &existing ) )
637 wxUnsetEnv( wxS(
"KICAD_USER_FOOTPRINT_DIR" ) );
638 wxUnsetEnv( wxS(
"KICAD5_FOOTPRINT_DIR" ) );
643 auto restore = [&](
const wxString& aName,
const std::optional<wxString>& aOld )
646 wxSetEnv( aName, *aOld );
652 restore( wxS(
"KICAD_USER_FOOTPRINT_DIR" ),
oldUser );
653 restore( wxS(
"KICAD5_FOOTPRINT_DIR" ),
oldLegacy );
661 const wxString uri = wxS(
"${KICAD_USER_FOOTPRINT_DIR}/conn_custom.pretty" );
667 BOOST_CHECK( !expanded.Contains( stockDir ) );
673 BOOST_REQUIRE( wxS(
"KICAD5_FOOTPRINT_DIR" ) != versionedName );
687 std::optional<wxString> oldSymbol;
688 std::optional<wxString> oldAlias;
691 if( wxGetEnv( symbolName, &existing ) )
692 oldSymbol = existing;
694 if( wxGetEnv( wxS(
"KICAD_SYMBOL_DIR" ), &existing ) )
697 wxSetEnv( symbolName, stockDir );
698 wxUnsetEnv( wxS(
"KICAD_SYMBOL_DIR" ) );
706 wxSetEnv( symbolName, *oldSymbol );
708 wxUnsetEnv( symbolName );
711 wxSetEnv( wxS(
"KICAD_SYMBOL_DIR" ), *oldAlias );
713 wxUnsetEnv( wxS(
"KICAD_SYMBOL_DIR" ) );
KiCad uses environment variables internally for determining the base paths for libraries,...
wxString ResolveOutputPath(const wxString &aPath, bool aPathIsDirectory, PROJECT *aProject) const
void SetTitleBlock(const TITLE_BLOCK &aTitleBlock)
Hold the information shown in the lower right corner of a plot, printout, or editing view.
void SetRevision(const wxString &aRevision)
void SetComment(int aIdx, const wxString &aComment)
const wxString ExpandEnvVarSubstitutions(const wxString &aString, const PROJECT *aProject)
Replace any environment variable & text variable references with their values.
wxString ExpandTextVars(const wxString &aSource, const PROJECT *aProject, int aFlags)
wxString NormalizeFilePathForTextVars(const wxString &aPath)
Normalize a file path so its text variables survive ExpandTextVars.
wxString ResolveTextVars(const wxString &aSource, const std::function< bool(wxString *)> *aResolver, int &aDepth)
Multi-pass text variable expansion and math expression evaluation.
wxString NormalizePath(const wxFileName &aFilePath, const ENV_VAR_MAP *aEnvVars, const wxString &aProjectPath)
Normalize a file path to an environmental variable, if possible.
Helper functions to substitute paths with environmental variables.
Functions related to environment variables, including help functions.
static FILENAME_RESOLVER * resolver
std::map< wxString, ENV_VAR_ITEM > ENV_VAR_MAP
KICOMMON_API bool IsVersionedEnvVar(const wxString &aName, const wxString &aBaseName)
Test whether a name is a versioned KiCad environment variable for the given base, i....
KICOMMON_API wxString GetVersionedEnvVarName(const wxString &aBaseName)
Construct a versioned environment variable based on this KiCad major version.
Regression test for KiCad GitLab issue #24244.
~EnvVarRecursiveExpansionFixture()
std::optional< wxString > oldInner
EnvVarRecursiveExpansionFixture()
std::optional< wxString > oldOuter
Test fixture for ExpandTextVars tests.
std::function< bool(wxString *)> resolver
Regression tests for overlapping-prefix environment variables.
OverlappingEnvVarsFixture()
ENV_VAR_MAP BuildEnvMap() const
~OverlappingEnvVarsFixture()
Regression test for KiCad GitLab issue #24460.
std::optional< wxString > oldUser
VersionedEnvVarFallbackFixture()
std::optional< wxString > oldVersioned
std::optional< wxString > oldLegacy
~VersionedEnvVarFallbackFixture()
BOOST_AUTO_TEST_CASE(HorizontalAlignment)
BOOST_AUTO_TEST_SUITE(CadstarPartParser)
BOOST_AUTO_TEST_CASE(SimpleVariable)
BOOST_REQUIRE(intersection.has_value()==c.ExpectedIntersection.has_value())
BOOST_AUTO_TEST_SUITE_END()
VECTOR3I expected(15, 30, 45)
wxString result
Test unit parsing edge cases and error handling.
BOOST_CHECK_EQUAL(result, "25.4")