KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_expand_text_vars.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
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 */
19
20
21#define BOOST_TEST_NO_MAIN
22#include <boost/test/unit_test.hpp>
23#include <atomic>
24#include <filesystem>
25#include <optional>
26#include <thread>
27#include <vector>
28#include <common.h>
29#include <env_paths.h>
30#include <env_vars.h>
32#include <pgm_base.h>
34#include <title_block.h>
35#include <wx/filename.h>
36#include <wx/utils.h>
37
42{
43 // Simple resolver that maps VAR->value, X->5, Y->2
44 std::function<bool( wxString* )> resolver = []( wxString* token ) -> bool
45 {
46 if( *token == wxT( "VAR" ) )
47 {
48 *token = wxT( "value" );
49 return true;
50 }
51 else if( *token == wxT( "X" ) )
52 {
53 *token = wxT( "5" );
54 return true;
55 }
56 else if( *token == wxT( "Y" ) )
57 {
58 *token = wxT( "2" );
59 return true;
60 }
61
62 return false;
63 };
64};
65
66BOOST_FIXTURE_TEST_SUITE( ExpandTextVarsTests, ExpandTextVarsFixture )
67
68// Basic variable expansion
69BOOST_AUTO_TEST_CASE( SimpleVariable )
70{
71 wxString result = ExpandTextVars( wxT( "${VAR}" ), &resolver );
72 BOOST_CHECK( result == wxT( "value" ) );
73}
74
75// Multiple variables in one string
76BOOST_AUTO_TEST_CASE( MultipleVariables )
77{
78 wxString result = ExpandTextVars( wxT( "${X}+${Y}" ), &resolver );
79 BOOST_CHECK( result == wxT( "5+2" ) );
80}
81
82// Escaped variable should produce escape marker (not expanded)
83BOOST_AUTO_TEST_CASE( EscapedVariable )
84{
85 wxString result = ExpandTextVars( wxT( "\\${VAR}" ), &resolver );
86 // The escape marker should be in the output
87 BOOST_CHECK( result.Contains( wxT( "<<<ESC_DOLLAR:" ) ) );
88}
89
90// Escaped variable followed by regular variable - both should be processed correctly
91BOOST_AUTO_TEST_CASE( EscapedThenRegularVariable )
92{
93 wxString result = ExpandTextVars( wxT( "\\${literal}${VAR}" ), &resolver );
94 // Should have escape marker for literal, and "value" for VAR
95 BOOST_CHECK( result.Contains( wxT( "<<<ESC_DOLLAR:" ) ) );
96 BOOST_CHECK( result.Contains( wxT( "value" ) ) );
97}
98
99// Regular variable followed by escaped variable
100BOOST_AUTO_TEST_CASE( RegularThenEscapedVariable )
101{
102 wxString result = ExpandTextVars( wxT( "${VAR}\\${literal}" ), &resolver );
103 // Should have "value" for VAR and escape marker for literal
104 BOOST_CHECK( result.StartsWith( wxT( "value" ) ) );
105 BOOST_CHECK( result.Contains( wxT( "<<<ESC_DOLLAR:" ) ) );
106}
107
108// Issue 22497: Escaped variable inside math expression should not prevent other expansions
109// This is the key test case for the bug fix
110BOOST_AUTO_TEST_CASE( EscapedInsideMathExpression )
111{
112 // First pass: @{\${X}+${Y}} should become @{<<<ESC_DOLLAR:X}+2}
113 // Second pass: the marker should be preserved and +2 should NOT be lost
114 wxString result = ExpandTextVars( wxT( "@{\\${X}+${Y}}" ), &resolver );
115
116 // The result should contain the escape marker
117 BOOST_CHECK_MESSAGE( result.Contains( wxT( "<<<ESC_DOLLAR:" ) ),
118 "Expected escape marker in result" );
119
120 // The result should also contain +2 (the expanded Y variable)
121 BOOST_CHECK_MESSAGE( result.Contains( wxT( "+2" ) ),
122 "Expected '+2' (from ${Y} expansion) in result" );
123
124 // The result should be @{<<<ESC_DOLLAR:X}+2}
125 BOOST_CHECK( result == wxT( "@{<<<ESC_DOLLAR:X}+2}" ) );
126}
127
128// Nested escaped variable in regular variable reference
129BOOST_AUTO_TEST_CASE( EscapedInsideVariableReference )
130{
131 // ${prefix\${suffix}} - looking up variable with literal ${suffix} in name
132 // This should try to resolve "prefix\${suffix}" which won't resolve,
133 // but the recursive expansion should convert \${suffix} to the marker
134 wxString result = ExpandTextVars( wxT( "${prefix\\${suffix}}" ), &resolver );
135
136 // The unresolved reference should be preserved with escape marker
137 BOOST_CHECK( result.Contains( wxT( "<<<ESC_DOLLAR:" ) ) );
138}
139
140// Multiple escape markers in a math expression
141BOOST_AUTO_TEST_CASE( MultipleEscapedInMathExpression )
142{
143 wxString result = ExpandTextVars( wxT( "@{\\${A}+\\${B}+${Y}}" ), &resolver );
144
145 // Should have two escape markers and the expanded Y (2)
146 BOOST_CHECK_MESSAGE( result.Contains( wxT( "+2" ) ),
147 "Expected '+2' (from ${Y} expansion) in: " + result );
148
149 // Count escape markers (should be 2)
150 int dollarCount = 0;
151 size_t pos = 0;
152
153 while( ( pos = result.find( wxT( "<<<ESC_DOLLAR:" ), pos ) ) != wxString::npos )
154 {
155 dollarCount++;
156 pos += 14;
157 }
158
159 BOOST_CHECK_EQUAL( dollarCount, 2 );
160}
161
162// Math expression with escaped @ sign
163BOOST_AUTO_TEST_CASE( EscapedAtInExpression )
164{
165 wxString result = ExpandTextVars( wxT( "${VAR}\\@{literal}" ), &resolver );
166
167 // Should have "value" for VAR and escape marker for @{literal}
168 BOOST_CHECK( result.StartsWith( wxT( "value" ) ) );
169 BOOST_CHECK( result.Contains( wxT( "<<<ESC_AT:" ) ) );
170}
171
172// Escaped followed by escaped (both should be preserved)
173BOOST_AUTO_TEST_CASE( ConsecutiveEscaped )
174{
175 wxString result = ExpandTextVars( wxT( "\\${A}\\${B}" ), &resolver );
176
177 // Should have two escape markers
178 int dollarCount = 0;
179 size_t pos = 0;
180
181 while( ( pos = result.find( wxT( "<<<ESC_DOLLAR:" ), pos ) ) != wxString::npos )
182 {
183 dollarCount++;
184 pos += 14;
185 }
186
187 BOOST_CHECK_EQUAL( dollarCount, 2 );
188}
189
190// Issue 23599: backslash path separator before text variable should NOT be treated as an escape.
191// This test documents the ExpandTextVars behavior: \${ IS treated as an escape at this level.
192// The fix is applied at call sites that deal with file paths, which normalize backslashes to
193// forward slashes before calling ExpandTextVars.
194BOOST_AUTO_TEST_CASE( BackslashBeforeVariableIsEscape )
195{
196 wxString result = ExpandTextVars( wxT( "subdir\\${VAR}_file.txt" ), &resolver );
197
198 // ExpandTextVars treats \${ as an escape, so VAR is NOT expanded
199 BOOST_CHECK( result.Contains( wxT( "<<<ESC_DOLLAR:" ) ) );
200}
201
202
203// With forward slashes the variable is expanded normally
204BOOST_AUTO_TEST_CASE( ForwardSlashBeforeVariableExpands )
205{
206 wxString result = ExpandTextVars( wxT( "subdir/${VAR}_file.txt" ), &resolver );
207
208 BOOST_CHECK( result == wxT( "subdir/value_file.txt" ) );
209}
210
212
213
214// Issue 24776: user-entered file paths with a text variable immediately after a backslash
215// separator (e.g. the Symbol Fields Table BOM export path "Output\BoM\${PROJECTNAME}.csv")
216// must expand. Callers route the path through NormalizeFilePathForTextVars before ExpandTextVars.
217BOOST_FIXTURE_TEST_SUITE( NormalizeFilePathForTextVarsTests, ExpandTextVarsFixture )
218
219BOOST_AUTO_TEST_CASE( BackslashSeparatorBeforeVarExpands )
220{
221 wxString path = NormalizeFilePathForTextVars( wxT( "Output\\BoM\\${VAR}_file.csv" ) );
222 wxString result = ExpandTextVars( path, &resolver );
223
224 // Only the backslash immediately before the variable is rewritten to a separator; the
225 // variable expands and the earlier literal backslash is preserved.
226 BOOST_CHECK_MESSAGE( !result.Contains( wxT( "<<<ESC_DOLLAR:" ) ),
227 "Variable after backslash separator should expand, not escape. Got: " + result );
228 BOOST_CHECK( result == wxT( "Output\\BoM/value_file.csv" ) );
229}
230
231
232BOOST_AUTO_TEST_CASE( MultipleVarsAfterBackslashSeparators )
233{
234 wxString path = NormalizeFilePathForTextVars( wxT( "Output\\BoM\\${X}_V${Y}.csv" ) );
235 wxString result = ExpandTextVars( path, &resolver );
236
237 BOOST_CHECK( result == wxT( "Output\\BoM/5_V2.csv" ) );
238}
239
240
241BOOST_AUTO_TEST_CASE( ForwardSlashPathUnchanged )
242{
243 wxString path = NormalizeFilePathForTextVars( wxT( "Output/BoM/${VAR}.csv" ) );
244 wxString result = ExpandTextVars( path, &resolver );
245
246 BOOST_CHECK( result == wxT( "Output/BoM/value.csv" ) );
247}
248
249
250// Backslashes that do not immediately precede a text variable are a legitimate part of the
251// filename (notably on POSIX) and must survive normalization unchanged.
252BOOST_AUTO_TEST_CASE( NonVariableBackslashesArePreserved )
253{
254 wxString path = NormalizeFilePathForTextVars( wxT( "Output\\BoM\\literal.csv" ) );
255
256 BOOST_CHECK( path == wxT( "Output\\BoM\\literal.csv" ) );
257}
258
260
261
262// Issue 23599: JOB::ResolveOutputPath must expand text variables even when preceded by backslash
263// path separators. This suite tests the fix in ResolveOutputPath that normalizes backslashes
264// before calling ExpandTextVars.
265BOOST_AUTO_TEST_SUITE( JobResolveOutputPath )
266
267BOOST_AUTO_TEST_CASE( BackslashPathSeparatorBeforeTextVar )
268{
270
271 TITLE_BLOCK titleBlock;
272 titleBlock.SetRevision( wxT( "RevA" ) );
273 job.SetTitleBlock( titleBlock );
274
275 // Simulates Windows path with text variable immediately after backslash separator
276 wxString path = wxT( "Board Stats\\${REVISION}_Stats.txt" );
277 wxString result = job.ResolveOutputPath( path, false, nullptr );
278
279 // The variable should be expanded, not escaped
280 BOOST_CHECK_MESSAGE( !result.Contains( wxT( "<<<ESC_DOLLAR:" ) ),
281 "Text variable after backslash path separator should not be escaped. Got: "
282 + result );
283 BOOST_CHECK_MESSAGE( result.Contains( wxT( "RevA" ) ),
284 "Expected resolved REVISION in path. Got: " + result );
285}
286
287
288BOOST_AUTO_TEST_CASE( BackslashPathSeparatorBeforeMultipleTextVars )
289{
291
292 TITLE_BLOCK titleBlock;
293 titleBlock.SetRevision( wxT( "B" ) );
294 titleBlock.SetComment( 0, wxT( "DWG-001" ) );
295 job.SetTitleBlock( titleBlock );
296
297 // The COMMENT1 variable maps to Comment(0) in TITLE_BLOCK
298 wxString path = wxT( "Output\\${COMMENT1}_${REVISION}_file.txt" );
299 wxString result = job.ResolveOutputPath( path, false, nullptr );
300
301 BOOST_CHECK_MESSAGE( result.Contains( wxT( "DWG-001" ) ),
302 "Expected COMMENT1 expanded in path. Got: " + result );
303 BOOST_CHECK_MESSAGE( result.Contains( wxT( "_B_" ) ),
304 "Expected REVISION expanded in path. Got: " + result );
305}
306
307
308BOOST_AUTO_TEST_CASE( TextVarNotFirstInFilename )
309{
311
312 TITLE_BLOCK titleBlock;
313 titleBlock.SetRevision( wxT( "C" ) );
314 job.SetTitleBlock( titleBlock );
315
316 // When there's literal text between the backslash and the variable, it always worked
317 wxString path = wxT( "Output\\Generated_${REVISION}_file.txt" );
318 wxString result = job.ResolveOutputPath( path, false, nullptr );
319
320 BOOST_CHECK_MESSAGE( result.Contains( wxT( "Generated_C_file.txt" ) ),
321 "Expected variable expansion with preceding literal text. Got: " + result );
322}
323
325
326
327
341{
342 wxString rootDir;
343 wxString outerDir;
344 wxString innerDir;
345 wxString targetDir;
346
348 {
349 std::filesystem::path tmp = std::filesystem::temp_directory_path() /
350 "kicad_qa_overlap_env_vars";
351 std::error_code ec;
352 std::filesystem::remove_all( tmp, ec );
353 std::filesystem::create_directories( tmp / "V10" / "symbols", ec );
354
355 rootDir = wxString::FromUTF8( tmp.string() );
357 innerDir = wxString::FromUTF8( ( tmp / "V10" ).string() );
358 targetDir = wxString::FromUTF8( ( tmp / "V10" / "symbols" ).string() );
359
360 wxSetEnv( wxS( "KICAD_QA_3RD_PARTY_OUTER" ), outerDir );
361 wxSetEnv( wxS( "KICAD_QA_USER_LIB_INNER" ), innerDir );
362 }
363
365 {
366 wxUnsetEnv( wxS( "KICAD_QA_3RD_PARTY_OUTER" ) );
367 wxUnsetEnv( wxS( "KICAD_QA_USER_LIB_INNER" ) );
368
369 std::filesystem::path tmp = std::filesystem::temp_directory_path() /
370 "kicad_qa_overlap_env_vars";
371 std::error_code ec;
372 std::filesystem::remove_all( tmp, ec );
373 }
374
376 {
377 ENV_VAR_MAP map;
378 map[wxS( "KICAD_QA_3RD_PARTY_OUTER" )] = ENV_VAR_ITEM( outerDir );
379 map[wxS( "KICAD_QA_USER_LIB_INNER" )] = ENV_VAR_ITEM( innerDir );
380 return map;
381 }
382};
383
384
385BOOST_FIXTURE_TEST_SUITE( OverlappingEnvVarPaths, OverlappingEnvVarsFixture )
386
387
388BOOST_AUTO_TEST_CASE( NormalizePicksLongestPrefix )
389{
390 wxFileName target( targetDir, wxS( "test.kicad_sym" ) );
391 ENV_VAR_MAP envMap = BuildEnvMap();
392
393 wxString normalized = NormalizePath( target, &envMap, wxEmptyString );
394
395 // NormalizePath should pick KICAD_QA_USER_LIB_INNER because it is a deeper match.
397 normalized == wxS( "${KICAD_QA_USER_LIB_INNER}/symbols/test.kicad_sym" ),
398 wxString::Format( wxS( "Expected '%s' but got '%s'" ),
399 wxS( "${KICAD_QA_USER_LIB_INNER}/symbols/test.kicad_sym" ),
400 normalized ) );
401}
402
403
404BOOST_AUTO_TEST_CASE( RoundTripPreservesAbsolutePath )
405{
406 wxFileName target( targetDir, wxS( "test.kicad_sym" ) );
407 ENV_VAR_MAP envMap = BuildEnvMap();
408
409 wxString normalized = NormalizePath( target, &envMap, wxEmptyString );
410 wxString expanded = ExpandEnvVarSubstitutions( normalized, nullptr );
411
412 wxFileName expandedFn( expanded );
413 expandedFn.Normalize( wxPATH_NORM_DOTS | wxPATH_NORM_ABSOLUTE );
414
415 wxFileName originalFn( target );
416 originalFn.Normalize( wxPATH_NORM_DOTS | wxPATH_NORM_ABSOLUTE );
417
419 expandedFn.GetFullPath() == originalFn.GetFullPath(),
420 wxString::Format(
421 wxS( "Round-trip mismatch: normalized='%s' expanded='%s' original='%s'" ),
422 normalized, expandedFn.GetFullPath(), originalFn.GetFullPath() ) );
423}
424
425
427
428
429
441BOOST_AUTO_TEST_SUITE( TextVarExpressionEvaluatorConcurrency )
442
443BOOST_AUTO_TEST_CASE( ParallelResolveTextVarsWithMathExpressions )
444{
445 std::function<bool( wxString* )> resolver = []( wxString* token ) -> bool
446 {
447 if( *token == wxT( "#" ) )
448 {
449 *token = wxT( "3" );
450 return true;
451 }
452
453 if( *token == wxT( "ROW" ) )
454 {
455 *token = wxT( "4" );
456 return true;
457 }
458
459 return false;
460 };
461
462 const std::vector<wxString> inputs = {
463 wxT( "Out@{(${#}-2)*8+0}" ),
464 wxT( "Net_@{${ROW}*2+1}" ),
465 wxT( "@{(2-2)*8+0}" ),
466 wxT( "${ROW}:@{${ROW}*${ROW}}" ),
467 wxT( "plain_label_no_expr" ),
468 wxT( "@{1+1}_@{2+2}_@{3+3}" ),
469 };
470
471 const unsigned int numThreads = std::max( 4u, std::thread::hardware_concurrency() );
472 const int iterations = 2000;
473
474 std::atomic<bool> failed{ false };
475 std::atomic<int> totalRuns{ 0 };
476 std::vector<std::thread> threads;
477 threads.reserve( numThreads );
478
479 for( unsigned int t = 0; t < numThreads; ++t )
480 {
481 threads.emplace_back(
482 [&, t]()
483 {
484 try
485 {
486 for( int i = 0; i < iterations; ++i )
487 {
488 const wxString& src = inputs[( t + i ) % inputs.size()];
489 int depth = 0;
490 wxString result = ResolveTextVars( src, &resolver, depth );
491 (void) result;
492 totalRuns.fetch_add( 1, std::memory_order_relaxed );
493 }
494 }
495 catch( ... )
496 {
497 failed.store( true, std::memory_order_relaxed );
498 }
499 } );
500 }
501
502 for( auto& th : threads )
503 th.join();
504
505 BOOST_CHECK( !failed.load() );
506 BOOST_CHECK_EQUAL( totalRuns.load(), static_cast<int>( numThreads ) * iterations );
507}
508
510
511
512
522{
523 wxString innerPath;
524 std::optional<wxString> oldInner;
525 std::optional<wxString> oldOuter;
526
528 {
529 wxString existing;
530
531 if( wxGetEnv( wxS( "KICAD_QA_INNER" ), &existing ) )
532 oldInner = existing;
533
534 if( wxGetEnv( wxS( "KICAD_QA_OUTER" ), &existing ) )
535 oldOuter = existing;
536
537 innerPath = wxString::FromUTF8(
538 ( std::filesystem::temp_directory_path() / "kicad-qa-24244" ).generic_string() );
539
540 wxSetEnv( wxS( "KICAD_QA_INNER" ), innerPath );
541 wxSetEnv( wxS( "KICAD_QA_OUTER" ), wxS( "${KICAD_QA_INNER}/templates" ) );
542 }
543
545 {
546 if( oldInner )
547 wxSetEnv( wxS( "KICAD_QA_INNER" ), *oldInner );
548 else
549 wxUnsetEnv( wxS( "KICAD_QA_INNER" ) );
550
551 if( oldOuter )
552 wxSetEnv( wxS( "KICAD_QA_OUTER" ), *oldOuter );
553 else
554 wxUnsetEnv( wxS( "KICAD_QA_OUTER" ) );
555 }
556};
557
558BOOST_FIXTURE_TEST_SUITE( EnvVarRecursiveExpansion, EnvVarRecursiveExpansionFixture )
559
560BOOST_AUTO_TEST_CASE( ExpandsNestedReferences )
561{
562 wxString rawValue;
563 BOOST_REQUIRE( wxGetEnv( wxS( "KICAD_QA_OUTER" ), &rawValue ) );
564
565 // The raw value should still contain the unexpanded reference.
566 BOOST_CHECK( rawValue.Contains( wxS( "${KICAD_QA_INNER}" ) ) );
567
568 wxString expanded = ExpandEnvVarSubstitutions( rawValue, nullptr );
569 wxString expected = innerPath + wxS( "/templates" );
570
571 // After expansion the inner reference must be resolved to its concrete path.
572 BOOST_CHECK_MESSAGE( expanded == expected,
573 wxString::Format( wxS( "Expected '%s', got '%s'" ), expected, expanded ) );
574}
575
576
577BOOST_AUTO_TEST_CASE( UndefinedReferenceLeavesLiteralMarker )
578{
579 // If a referenced variable is undefined, ExpandEnvVarSubstitutions preserves the
580 // original token. Callers that then mkdir the result would create a literal
581 // "${MISSING}" directory; production code must detect this and bail out.
582 wxUnsetEnv( wxS( "KICAD_QA_INNER" ) );
583
584 wxString rawValue;
585 BOOST_REQUIRE( wxGetEnv( wxS( "KICAD_QA_OUTER" ), &rawValue ) );
586
587 wxString expanded = ExpandEnvVarSubstitutions( rawValue, nullptr );
588 BOOST_CHECK( expanded.Contains( wxS( "${" ) ) );
589
590 // Restore so the fixture destructor sees a known state.
591 wxSetEnv( wxS( "KICAD_QA_INNER" ), innerPath );
592}
593
595
596
597
608{
610 wxString stockDir;
611 std::optional<wxString> oldVersioned;
612 std::optional<wxString> oldUser;
613 std::optional<wxString> oldLegacy;
614
616 {
617 versionedName = ENV_VAR::GetVersionedEnvVarName( wxS( "FOOTPRINT_DIR" ) );
618 stockDir = wxString::FromUTF8(
619 ( std::filesystem::temp_directory_path() / "kicad-qa-24460-stock.pretty" ).generic_string() );
620
621 wxString existing;
622
623 if( wxGetEnv( versionedName, &existing ) )
624 oldVersioned = existing;
625
626 if( wxGetEnv( wxS( "KICAD_USER_FOOTPRINT_DIR" ), &existing ) )
627 oldUser = existing;
628
629 if( wxGetEnv( wxS( "KICAD5_FOOTPRINT_DIR" ), &existing ) )
630 oldLegacy = existing;
631
632 // The current install advertises a stock footprint directory; a stale user var and an
633 // older versioned var are both absent.
634 wxSetEnv( versionedName, stockDir );
635 wxUnsetEnv( wxS( "KICAD_USER_FOOTPRINT_DIR" ) );
636 wxUnsetEnv( wxS( "KICAD5_FOOTPRINT_DIR" ) );
637 }
638
640 {
641 auto restore = [&]( const wxString& aName, const std::optional<wxString>& aOld )
642 {
643 if( aOld )
644 wxSetEnv( aName, *aOld );
645 else
646 wxUnsetEnv( aName );
647 };
648
649 restore( versionedName, oldVersioned );
650 restore( wxS( "KICAD_USER_FOOTPRINT_DIR" ), oldUser );
651 restore( wxS( "KICAD5_FOOTPRINT_DIR" ), oldLegacy );
652 }
653};
654
655BOOST_FIXTURE_TEST_SUITE( VersionedEnvVarFallback, VersionedEnvVarFallbackFixture )
656
657BOOST_AUTO_TEST_CASE( UserVarIsNotTreatedAsVersionedLibraryDir )
658{
659 const wxString uri = wxS( "${KICAD_USER_FOOTPRINT_DIR}/conn_custom.pretty" );
660
661 wxString expanded = ExpandEnvVarSubstitutions( uri, nullptr );
662
663 // An unresolved user var must stay literal, never the stock library directory.
664 BOOST_CHECK_EQUAL( expanded, uri );
665 BOOST_CHECK( !expanded.Contains( stockDir ) );
666}
667
668
669BOOST_AUTO_TEST_CASE( LegacyVersionedVarStillResolvesToCurrentDir )
670{
671 BOOST_REQUIRE( wxS( "KICAD5_FOOTPRINT_DIR" ) != versionedName );
672
673 wxString expanded =
674 ExpandEnvVarSubstitutions( wxS( "${KICAD5_FOOTPRINT_DIR}/conn_custom.pretty" ), nullptr );
675
676 BOOST_CHECK_EQUAL( expanded, stockDir + wxS( "/conn_custom.pretty" ) );
677}
678
679
680BOOST_AUTO_TEST_CASE( DeprecatedUnversionedAliasStillResolves )
681{
682 // KICAD_SYMBOL_DIR is a documented deprecated alias for the versioned symbol dir; it must
683 // still fall back to the current install even though it carries no version digits.
684 wxString symbolName = ENV_VAR::GetVersionedEnvVarName( wxS( "SYMBOL_DIR" ) );
685 std::optional<wxString> oldSymbol;
686 std::optional<wxString> oldAlias;
687 wxString existing;
688
689 if( wxGetEnv( symbolName, &existing ) )
690 oldSymbol = existing;
691
692 if( wxGetEnv( wxS( "KICAD_SYMBOL_DIR" ), &existing ) )
693 oldAlias = existing;
694
695 wxSetEnv( symbolName, stockDir );
696 wxUnsetEnv( wxS( "KICAD_SYMBOL_DIR" ) );
697
698 wxString expanded =
699 ExpandEnvVarSubstitutions( wxS( "${KICAD_SYMBOL_DIR}/Device.kicad_sym" ), nullptr );
700
701 BOOST_CHECK_EQUAL( expanded, stockDir + wxS( "/Device.kicad_sym" ) );
702
703 if( oldSymbol )
704 wxSetEnv( symbolName, *oldSymbol );
705 else
706 wxUnsetEnv( symbolName );
707
708 if( oldAlias )
709 wxSetEnv( wxS( "KICAD_SYMBOL_DIR" ), *oldAlias );
710 else
711 wxUnsetEnv( wxS( "KICAD_SYMBOL_DIR" ) );
712}
713
714
715BOOST_AUTO_TEST_CASE( IsVersionedEnvVarPredicate )
716{
717 BOOST_CHECK( ENV_VAR::IsVersionedEnvVar( wxS( "KICAD7_FOOTPRINT_DIR" ), wxS( "FOOTPRINT_DIR" ) ) );
718 BOOST_CHECK( ENV_VAR::IsVersionedEnvVar( wxS( "KICAD10_FOOTPRINT_DIR" ), wxS( "FOOTPRINT_DIR" ) ) );
719
720 BOOST_CHECK( !ENV_VAR::IsVersionedEnvVar( wxS( "KICAD_USER_FOOTPRINT_DIR" ), wxS( "FOOTPRINT_DIR" ) ) );
721 BOOST_CHECK( !ENV_VAR::IsVersionedEnvVar( wxS( "KICAD_FOOTPRINT_DIR" ), wxS( "FOOTPRINT_DIR" ) ) );
722 BOOST_CHECK( !ENV_VAR::IsVersionedEnvVar( wxS( "KICAD7_SYMBOL_DIR" ), wxS( "FOOTPRINT_DIR" ) ) );
723}
724
KiCad uses environment variables internally for determining the base paths for libraries,...
wxString ResolveOutputPath(const wxString &aPath, bool aPathIsDirectory, PROJECT *aProject) const
Definition job.cpp:100
void SetTitleBlock(const TITLE_BLOCK &aTitleBlock)
Definition job.h:204
Hold the information shown in the lower right corner of a plot, printout, or editing view.
Definition title_block.h:37
void SetRevision(const wxString &aRevision)
Definition title_block.h:77
void SetComment(int aIdx, const wxString &aComment)
Definition title_block.h:97
const wxString ExpandEnvVarSubstitutions(const wxString &aString, const PROJECT *aProject)
Replace any environment variable & text variable references with their values.
Definition common.cpp:721
wxString ExpandTextVars(const wxString &aSource, const PROJECT *aProject, int aFlags)
Definition common.cpp:59
wxString NormalizeFilePathForTextVars(const wxString &aPath)
Normalize a file path so its text variables survive ExpandTextVars.
Definition common.cpp:70
wxString ResolveTextVars(const wxString &aSource, const std::function< bool(wxString *)> *aResolver, int &aDepth)
Multi-pass text variable expansion and math expression evaluation.
Definition common.cpp:310
The common library.
wxString NormalizePath(const wxFileName &aFilePath, const ENV_VAR_MAP *aEnvVars, const wxString &aProjectPath)
Normalize a file path to an environmental variable, if possible.
Definition env_paths.cpp:73
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....
Definition env_vars.cpp:87
KICOMMON_API wxString GetVersionedEnvVarName(const wxString &aBaseName)
Construct a versioned environment variable based on this KiCad major version.
Definition env_vars.cpp:78
see class PGM_BASE
Regression test for KiCad GitLab issue #24244.
Test fixture for ExpandTextVars tests.
std::function< bool(wxString *)> resolver
Regression tests for overlapping-prefix environment variables.
Regression test for KiCad GitLab issue #24460.
std::optional< wxString > oldVersioned
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()
std::string path
VECTOR3I expected(15, 30, 45)
BOOST_CHECK_MESSAGE(totalMismatches==0, std::to_string(totalMismatches)+" board(s) with strategy disagreements")
wxString result
Test unit parsing edge cases and error handling.
BOOST_CHECK_EQUAL(result, "25.4")