KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_text_eval_numeric_compat.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, you may find one here:
18 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
19 * or you may search the http://www.gnu.org website for the version 2 license,
20 * or you may write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22 */
23
28
30
31// Code under test
33#include <wx/wxcrt.h>
34
35// Make EDA_UNITS printable for Boost.Test
36std::ostream& operator<<( std::ostream& aStream, EDA_UNITS aUnits )
37{
38 wxString unitStr = EDA_UNIT_UTILS::GetText( aUnits );
39 return aStream << unitStr.ToStdString();
40}
41
45BOOST_AUTO_TEST_SUITE( TextEvalNumericCompat )
46
47
51{
52 wxString input; // Input expression wrapped in @{} for text_eval
53 wxString exp_result; // Expected result as string
54 bool shouldError; // Whether this case should produce an error
55};
56
61{
62 EXPRESSION_EVALUATOR evaluator;
63
64 wxString result = evaluator.Evaluate("@{1}");
66 BOOST_CHECK( !evaluator.HasErrors() );
67}
68
73{
74 EXPRESSION_EVALUATOR evaluator;
75
76 // Set variable and test usage
77 evaluator.SetVariable( "MoL", 42.0 );
78 wxString result = evaluator.Evaluate( "@{1 + ${MoL}}" );
80 BOOST_CHECK( !evaluator.HasErrors() );
81
82 // Change variable value
83 evaluator.SetVariable( "MoL", 422.0 );
84 result = evaluator.Evaluate( "@{1 + ${MoL}}" );
85 BOOST_CHECK_EQUAL( result, "423" );
86 BOOST_CHECK( !evaluator.HasErrors() );
87
88 // Add another variable
89 evaluator.SetVariable( "pi", 3.14 );
90 BOOST_CHECK( evaluator.HasVariable( "pi" ) );
91
92 // Remove one variable
93 BOOST_CHECK( evaluator.RemoveVariable( "pi" ) );
94 BOOST_CHECK( !evaluator.HasVariable( "pi" ) );
95
96 // Other variable should still be there
97 BOOST_CHECK( evaluator.HasVariable( "MoL" ) );
98
99 // Add another variable back
100 evaluator.SetVariable( "piish", 3.1 );
101
102 // Test multiple variables
103 result = evaluator.Evaluate( "@{1 + ${MoL} + ${piish}}" );
104 BOOST_CHECK_EQUAL( result, "426.1" );
105 BOOST_CHECK( !evaluator.HasErrors() );
106
107 // Clear all variables
108 evaluator.ClearVariables();
109 BOOST_CHECK( !evaluator.HasVariable( "MoL" ) );
110 BOOST_CHECK( !evaluator.HasVariable( "piish" ) );
111}
112
117static const std::vector<TEXT_EVAL_CASE> eval_cases_valid = {
118 // Empty case - text_eval handles this differently than numeric evaluator
119 { "@{}", "@{}", true }, // Empty expressions should error in text_eval
120
121 // Trivial eval
122 { "@{1}", "1", false },
123
124 // Decimal separators (text_eval may handle differently)
125 { "@{1.5}", "1.5", false },
126 // Note: comma as decimal separator might not work in text_eval
127
128 // Simple arithmetic
129 { "@{1+2}", "3", false },
130 { "@{1 + 2}", "3", false },
131 { "@{1.5 + 0.2 + 0.1}", "1.8", false },
132 { "@{3 - 10}", "-7", false },
133 { "@{1 + 2 + 10 + 1000.05}", "1013.05", false },
134
135 // Operator precedence
136 { "@{1 + 2 - 4 * 20 / 2}", "-37", false },
137
138 // Parentheses
139 { "@{(1)}", "1", false },
140 { "@{-(1 + (2 - 4)) * 20.8 / 2}", "10.4", false },
141
142 // Unary operators
143 { "@{+2 - 1}", "1", false },
144};
145
149static const std::vector<TEXT_EVAL_CASE> eval_cases_invalid = {
150 // Trailing operator
151 { "@{1+}", "", true },
152
153 // Leading operator (except unary)
154 { "@{*2 + 1}", "", true },
155
156 // Division by zero
157 { "@{1 / 0}", "", true },
158
159 // Unknown variables should preserve the original expression
160 { "@{1 + ${unknown}}", "@{1 + ${unknown}}", true },
161
162 // Mismatched parentheses
163 { "@{(1 + 2}", "", true },
164 { "@{1 + 2)}", "", true },
165
166 // Invalid syntax
167 { "@{1 $ 2}", "", true },
168};
169
173BOOST_AUTO_TEST_CASE( ValidResults )
174{
175 EXPRESSION_EVALUATOR evaluator;
176
177 for( const auto& testCase : eval_cases_valid )
178 {
179 BOOST_TEST_CONTEXT( testCase.input + " -> " + testCase.exp_result )
180 {
181 wxString result = evaluator.Evaluate( testCase.input );
182
183 if( testCase.shouldError )
184 {
185 BOOST_CHECK( evaluator.HasErrors() );
186 }
187 else
188 {
189 BOOST_CHECK( !evaluator.HasErrors() );
190 BOOST_CHECK_EQUAL( result, testCase.exp_result );
191 }
192 }
193 }
194}
195
199BOOST_AUTO_TEST_CASE( InvalidResults )
200{
201 EXPRESSION_EVALUATOR evaluator;
202
203 for( const auto& testCase : eval_cases_invalid )
204 {
205 BOOST_TEST_CONTEXT( testCase.input )
206 {
207 wxString result = evaluator.Evaluate( testCase.input );
208
209 // All these cases should produce errors
210 BOOST_CHECK( evaluator.HasErrors() );
211
212 // For undefined variables, result should be the original expression
213 if( testCase.input.Contains( "${unknown}" ) )
214 {
215 BOOST_CHECK_EQUAL( result, testCase.input );
216 }
217 }
218 }
219}
220
224BOOST_AUTO_TEST_CASE( VariableExpressions )
225{
226 EXPRESSION_EVALUATOR evaluator;
227
228 // Set up variables similar to numeric evaluator tests
229 evaluator.SetVariable( "x", 10.0 );
230 evaluator.SetVariable( "y", 5.0 );
231
232 struct VarTestCase {
233 wxString input;
234 wxString expected;
235 bool shouldError;
236 };
237
238 const std::vector<VarTestCase> varCases = {
239 { "@{${x}}", "10", false },
240 { "@{${y}}", "5", false },
241 { "@{${x} + ${y}}", "15", false },
242 { "@{${x} * ${y}}", "50", false },
243 { "@{${x} - ${y}}", "5", false },
244 { "@{${x} / ${y}}", "2", false },
245 { "@{(${x} + ${y}) * 2}", "30", false },
246
247 // Undefined variable should preserve expression
248 { "@{${undefined}}", "@{${undefined}}", true },
249
250 // Mixed defined and undefined
251 { "@{${x} + ${undefined}}", "@{${x} + ${undefined}}", true },
252 };
253
254 for( const auto& testCase : varCases )
255 {
256 BOOST_TEST_CONTEXT( testCase.input + " -> " + testCase.expected )
257 {
258 wxString result = evaluator.Evaluate( testCase.input );
259
260 if( testCase.shouldError )
261 {
262 BOOST_CHECK( evaluator.HasErrors() );
263 BOOST_CHECK_EQUAL( result, testCase.input ); // Original expression preserved
264 }
265 else
266 {
267 BOOST_CHECK( !evaluator.HasErrors() );
268 BOOST_CHECK_EQUAL( result, testCase.expected );
269 }
270 }
271 }
272}
273
277BOOST_AUTO_TEST_CASE( MathFunctions )
278{
279 EXPRESSION_EVALUATOR evaluator;
280
281 struct MathTestCase {
282 wxString input;
283 wxString expected;
284 bool shouldError;
285 };
286
287 const std::vector<MathTestCase> mathCases = {
288 // Basic math functions that are confirmed to work
289 { "@{abs(-5)}", "5", false },
290 { "@{min(3, 7)}", "3", false },
291 { "@{max(3, 7)}", "7", false },
292 { "@{sqrt(16)}", "4", false },
293 { "@{ceil(3.2)}", "4", false },
294 { "@{floor(3.8)}", "3", false },
295 { "@{round(3.6)}", "4", false },
296 { "@{pow(2, 3)}", "8", false },
297
298 // Sum and average functions
299 { "@{sum(1, 2, 3)}", "6", false },
300 { "@{avg(2, 4, 6)}", "4", false },
301 };
302
303 for( const auto& testCase : mathCases )
304 {
305 BOOST_TEST_CONTEXT( testCase.input + " -> " + testCase.expected )
306 {
307 wxString result = evaluator.Evaluate( testCase.input );
308
309 if( testCase.shouldError )
310 {
311 BOOST_CHECK( evaluator.HasErrors() );
312 }
313 else
314 {
315 BOOST_CHECK( !evaluator.HasErrors() );
316 BOOST_CHECK_EQUAL( result, testCase.expected );
317 }
318 }
319 }
320}
321
326{
327 // Test basic unit constructor
328 EXPRESSION_EVALUATOR evaluator_mm( EDA_UNITS::MM );
329 EXPRESSION_EVALUATOR evaluator_inch( EDA_UNITS::INCH );
330 EXPRESSION_EVALUATOR evaluator_mil( EDA_UNITS::MILS );
331
332 // Test unit setting and getting
336
337 // Test unit change
338 evaluator_mm.SetDefaultUnits( EDA_UNITS::INCH );
340
341 // Test basic expressions work with unit-aware evaluator
342 wxString result = evaluator_mm.Evaluate( "@{1 + 2}" );
344 BOOST_CHECK( !evaluator_mm.HasErrors() );
345
346 // Test unit constructor with variable callback
347 auto callback = [](const std::string& varName) -> calc_parser::Result<calc_parser::Value> {
348 if (varName == "width") {
350 }
351 return calc_parser::MakeError<calc_parser::Value>("Variable not found: " + varName);
352 };
353
354 EXPRESSION_EVALUATOR evaluator_callback( EDA_UNITS::MM, callback, false );
355 BOOST_CHECK_EQUAL( evaluator_callback.GetDefaultUnits(), EDA_UNITS::MM );
356 BOOST_CHECK( evaluator_callback.HasVariableCallback() );
357
358 result = evaluator_callback.Evaluate( "@{${width} * 2}" );
359 BOOST_CHECK_EQUAL( result, "20" );
360 BOOST_CHECK( !evaluator_callback.HasErrors() );
361}
362
367BOOST_AUTO_TEST_CASE( UnitInfrastructureReadiness )
368{
369 // Test that different unit types can be set and retrieved
370 EXPRESSION_EVALUATOR evaluator_mm( EDA_UNITS::MM );
371 EXPRESSION_EVALUATOR evaluator_inch( EDA_UNITS::INCH );
372 EXPRESSION_EVALUATOR evaluator_mil( EDA_UNITS::MILS );
373 EXPRESSION_EVALUATOR evaluator_cm( EDA_UNITS::CM );
374 EXPRESSION_EVALUATOR evaluator_um( EDA_UNITS::UM );
375
376 // Verify unit storage works for all supported units
382
383 // Test unit changes
384 evaluator_mm.SetDefaultUnits( EDA_UNITS::INCH );
386
387 evaluator_inch.SetDefaultUnits( EDA_UNITS::MILS );
389
390 // Verify expressions still work with all unit types
391 wxString result;
392
393 result = evaluator_mm.Evaluate( "@{5 * 2}" );
394 BOOST_CHECK_EQUAL( result, "10" );
395
396 result = evaluator_inch.Evaluate( "@{3.5 + 1.5}" );
398
399 result = evaluator_mil.Evaluate( "@{100 / 4}" );
400 BOOST_CHECK_EQUAL( result, "25" );
401
402 // Test complex expressions work with unit-aware evaluators
403 result = evaluator_cm.Evaluate( "@{(10 + 5) * 2 - 1}" );
404 BOOST_CHECK_EQUAL( result, "29" );
405
406 // Test variable support with units
407 evaluator_um.SetVariable( "length", 25.4 );
408 result = evaluator_um.Evaluate( "@{${length} * 2}" );
409 BOOST_CHECK_EQUAL( result, "50.8" );
410
411 // Test that unit-aware evaluator preserves its unit setting across operations
412 EXPRESSION_EVALUATOR persistent_eval( EDA_UNITS::MILS );
414
415 persistent_eval.Evaluate( "@{1 + 1}" );
417
418 persistent_eval.SetVariable( "test", 42 );
420
421 persistent_eval.Evaluate( "@{${test} + 8}" );
423 BOOST_CHECK_EQUAL( persistent_eval.Evaluate( "@{${test} + 8}" ), "50" );
424}
425
430BOOST_AUTO_TEST_CASE( UnitMixingExpectations )
431{
432 EXPRESSION_EVALUATOR evaluator_mm( EDA_UNITS::MM );
433
434 // Verify basic functionality works before discussing unit mixing
436
437 wxString result = evaluator_mm.Evaluate( "@{2 + 3}" );
439
440 // Test complex arithmetic
441 result = evaluator_mm.Evaluate( "@{(1 + 2) * 3}" );
443
444 // Test with decimals (important for unit conversions)
445 result = evaluator_mm.Evaluate( "@{25.4 + 12.7}" );
446 // Use close comparison for floating point
447 double numeric_result = wxAtof( result );
448 BOOST_CHECK_CLOSE( numeric_result, 38.1, 0.01 );
449
450 // Test with variables that could represent converted values
451 evaluator_mm.SetVariable( "inch_in_mm", 25.4 ); // 1 inch = 25.4 mm
452 evaluator_mm.SetVariable( "mil_in_mm", 0.0254 ); // 1 mil = 0.0254 mm
453
454 result = evaluator_mm.Evaluate( "@{${inch_in_mm} + ${mil_in_mm}}" );
455 BOOST_CHECK_EQUAL( result, "25.4254" );
456
457 // Simulate what "1mm + 1in" should become when units are parsed
458 evaluator_mm.SetVariable( "mm_part", 1.0 );
459 evaluator_mm.SetVariable( "in_part", 25.4 ); // 1in converted to mm
460 result = evaluator_mm.Evaluate( "@{${mm_part} + ${in_part}}" );
461 BOOST_CHECK_EQUAL( result, "26.4" );
462
463 // Simulate what "1in + 1000mil" should become
464 evaluator_mm.SetVariable( "one_inch", 25.4 );
465 evaluator_mm.SetVariable( "thousand_mils", 25.4 ); // 1000 mils = 1 inch = 25.4 mm
466 result = evaluator_mm.Evaluate( "@{${one_inch} + ${thousand_mils}}" );
467 BOOST_CHECK_EQUAL( result, "50.8" );
468
469 // Test expressions that will be possible once unit parsing is integrated:
470 // These would parse "1mm", "1in", "1mil" etc. and convert to default units
471
472
473 // Basic unit expressions
474 BOOST_CHECK_EQUAL( evaluator_mm.Evaluate( "@{1mm}" ), "1" );
475 BOOST_CHECK_EQUAL( evaluator_mm.Evaluate( "@{1in}" ), "25.4" );
476 BOOST_CHECK_EQUAL( evaluator_mm.Evaluate( "@{1000mil}" ), "25.4" );
477
478 // Mixed unit arithmetic
479 BOOST_CHECK_EQUAL( evaluator_mm.Evaluate( "@{1mm + 1in}" ), "26.4" );
480 BOOST_CHECK_EQUAL( evaluator_mm.Evaluate( "@{1in + 1000mil}" ), "50.8" );
481 BOOST_CHECK_EQUAL( evaluator_mm.Evaluate( "@{10mm + 0.5in + 500mil}" ), "35.4" );
482
483 // Unit expressions with whitespace
484 BOOST_CHECK_EQUAL( evaluator_mm.Evaluate( "@{1 mm}" ), "1" );
485 BOOST_CHECK_EQUAL( evaluator_mm.Evaluate( "@{1 in}" ), "25.4" );
486
487 // Complex mixed unit expressions with variables
488 evaluator_mm.SetVariable( "width", 10 ); // 10mm
489 BOOST_CHECK_EQUAL( evaluator_mm.Evaluate( "@{${width}mm + 1in}" ), "35.4" );
490 // These two should both work the same
491 BOOST_CHECK_EQUAL( evaluator_mm.Evaluate( "@{${width} * 1mm + 1in}" ), "35.4" );
492
493 // Different evaluator units should convert appropriately
494 EXPRESSION_EVALUATOR evaluator_inch( EDA_UNITS::INCH );
495 BOOST_CHECK_EQUAL( evaluator_inch.Evaluate( "@{1in}" ), "1" );
496 BOOST_CHECK_EQUAL( evaluator_inch.Evaluate( "@{25.4mm}" ), "1" );
497}
498
502BOOST_AUTO_TEST_CASE( ActualUnitParsing )
503{
504 // Test MM evaluator with unit expressions
505 EXPRESSION_EVALUATOR evaluator_mm( EDA_UNITS::MM );
507
508 // Test basic unit expressions (these should now work!)
509 wxString result;
510
511 // Debug: Test basic arithmetic first
512 result = evaluator_mm.Evaluate( "@{2+3}" );
514
515 // Debug: Test just number
516 result = evaluator_mm.Evaluate( "@{1}" );
518
519 // Debug: Test the working case
520 result = evaluator_mm.Evaluate( "@{1in}" );
521 if (result != "25.4") {
522 std::cout << "DEBUG: @{1in} returned '" << result.ToStdString() << "'" << std::endl;
523 if (evaluator_mm.HasErrors()) {
524 std::cout << "DEBUG: @{1in} Errors: " << evaluator_mm.GetErrorSummary().ToStdString() << std::endl;
525 }
526 }
527 BOOST_CHECK_EQUAL( result, "25.4" );
528
529 result = evaluator_mm.Evaluate( "@{1mil}" );
530 BOOST_CHECK_EQUAL( result, "0.0254" );
531
532 result = evaluator_mm.Evaluate( "@{1mm}" );
534
535 // 1 inch should convert to 25.4 mm
536 result = evaluator_mm.Evaluate( "@{1in}" );
537 BOOST_CHECK_EQUAL( result, "25.4" );
538
539 // 1000 mils should convert to 25.4 mm (1000 mils = 1 inch)
540 result = evaluator_mm.Evaluate( "@{1000mil}" );
541 BOOST_CHECK_EQUAL( result, "25.4" );
542
543 // Test mixed unit arithmetic
544 result = evaluator_mm.Evaluate( "@{1mm + 1in}" );
545 BOOST_CHECK_EQUAL( result, "26.4" );
546
547 result = evaluator_mm.Evaluate( "@{1in + 1000mil}" );
548 BOOST_CHECK_EQUAL( result, "50.8" );
549
550 // Test more complex expressions
551 result = evaluator_mm.Evaluate( "@{10mm + 0.5in + 500mil}" );
552 BOOST_CHECK_EQUAL( result, "35.4" );
553
554 // Test unit expressions with spaces (if supported)
555 result = evaluator_mm.Evaluate( "@{1 mm}" );
557
558 // Test with different default units
559 EXPRESSION_EVALUATOR evaluator_inch( EDA_UNITS::INCH );
560
561 // 1 inch should be 1 when default unit is inches
562 result = evaluator_inch.Evaluate( "@{1in}" );
564
565 // 25.4mm should convert to 1 inch (with floating point tolerance)
566 result = evaluator_inch.Evaluate( "@{25.4mm}" );
567 // Use approximate comparison for floating point
568 double result_val = wxAtof(result);
569 BOOST_CHECK( std::abs(result_val - 1.0) < 0.001 );
570
571 // Test arithmetic with inch evaluator
572 result = evaluator_inch.Evaluate( "@{1in + 1000mil}" );
573 BOOST_CHECK_EQUAL( result, "2" ); // 1 inch + 1 inch = 2 inches
574
575 // Test centimeters
576 result = evaluator_mm.Evaluate( "@{1cm}" );
577 BOOST_CHECK_EQUAL( result, "10" ); // 1 cm = 10 mm
578
579 // Test micrometers
580 result = evaluator_mm.Evaluate( "@{1000um}" );
581 BOOST_CHECK_EQUAL( result, "1" ); // 1000 um = 1 mm
582
583 // Test quotes for inches
584 result = evaluator_mm.Evaluate( "@{1\"}" );
585 BOOST_CHECK_EQUAL( result, "25.4" ); // 1" = 25.4 mm
586
587 // Test complex mixed expressions with parentheses
588 result = evaluator_mm.Evaluate( "@{(1in + 500mil) * 2}" );
589 // Expected: (25.4 + 12.7) * 2 = 38.1 * 2 = 76.2mm
590 double result_val2 = wxAtof(result);
591 BOOST_CHECK( std::abs(result_val2 - 76.2) < 0.001 );
592}
593
597BOOST_AUTO_TEST_CASE( UnitParsingEdgeCases, * boost::unit_test::enabled() )
598{
599 EXPRESSION_EVALUATOR evaluator_mm( EDA_UNITS::MM );
600
601 // Test invalid units (should be treated as plain numbers)
602 wxString result = evaluator_mm.Evaluate( "@{1xyz}" );
603 // Should parse as "1" followed by identifier "xyz", might be an error or treat as 1
604 // This behavior depends on implementation details
605
606 // Test numbers without units (should work normally)
607 result = evaluator_mm.Evaluate( "@{25.4}" );
609
610 // Test zero with units
611 result = evaluator_mm.Evaluate( "@{0mm}" );
613
614 result = evaluator_mm.Evaluate( "@{0in}" );
616
617 // Test decimal values with units
618 result = evaluator_mm.Evaluate( "@{2.54cm}" );
619 BOOST_CHECK_EQUAL( result, "25.4" ); // 2.54 cm = 25.4 mm
620
621 result = evaluator_mm.Evaluate( "@{0.5in}" );
622 BOOST_CHECK_EQUAL( result, "12.7" ); // 0.5 inch = 12.7 mm
623
624 // Test very small values
625 result = evaluator_mm.Evaluate( "@{1um}" );
626 BOOST_CHECK_EQUAL( result, "0.001" ); // 1 um = 0.001 mm
627}
628
629BOOST_AUTO_TEST_CASE( NumericEvaluatorCompatibility )
630{
631 // Test the NUMERIC_EVALUATOR_COMPAT wrapper class that provides
632 // a drop-in replacement for NUMERIC_EVALUATOR using EXPRESSION_EVALUATOR backend
633
635
636 // Test basic arithmetic
637 BOOST_CHECK( eval.Process( "1 + 2" ) );
638 BOOST_CHECK( eval.IsValid() );
639 BOOST_CHECK_EQUAL( eval.Result(), "3" );
640 BOOST_CHECK_EQUAL( eval.OriginalText(), "1 + 2" );
641
642 // Test variables
643 eval.SetVar( "x", 5.0 );
644 eval.SetVar( "y", 3.0 );
645
646 BOOST_CHECK( eval.Process( "x + y" ) );
647 BOOST_CHECK( eval.IsValid() );
648 BOOST_CHECK_EQUAL( eval.Result(), "8" );
649
650 // Test GetVar
651 BOOST_CHECK_CLOSE( eval.GetVar( "x" ), 5.0, 0.001 );
652 BOOST_CHECK_CLOSE( eval.GetVar( "y" ), 3.0, 0.001 );
653 BOOST_CHECK_CLOSE( eval.GetVar( "undefined" ), 0.0, 0.001 );
654
655 // Test units (should work seamlessly)
656 BOOST_CHECK( eval.Process( "1in + 1mm" ) );
657 BOOST_CHECK( eval.IsValid() );
658 BOOST_CHECK_EQUAL( eval.Result(), "26.4" ); // 1 inch + 1mm in mm
659
660 // Test mathematical functions
661 BOOST_CHECK( eval.Process( "sqrt(16)" ) );
662 BOOST_CHECK( eval.IsValid() );
663 BOOST_CHECK_EQUAL( eval.Result(), "4" );
664
665 // Test invalid expression - use something clearly invalid
666 BOOST_CHECK( !eval.Process( "1 + * 2" ) ); // Clearly invalid: two operators in a row
667 BOOST_CHECK( !eval.IsValid() );
668
669 // Test Clear() - should reset state but keep variables
670 eval.Clear();
671 BOOST_CHECK_CLOSE( eval.GetVar( "x" ), 5.0, 0.001 ); // Variables should still be there
672
673 BOOST_CHECK( eval.Process( "x * 2" ) );
674 BOOST_CHECK( eval.IsValid() );
675 BOOST_CHECK_EQUAL( eval.Result(), "10" );
676
677 // Test variable removal
678 eval.RemoveVar( "x" );
679 BOOST_CHECK_CLOSE( eval.GetVar( "x" ), 0.0, 0.001 ); // Should be 0.0 for undefined
680 BOOST_CHECK_CLOSE( eval.GetVar( "y" ), 3.0, 0.001 ); // y should still exist
681
682 // Test ClearVar()
683 eval.ClearVar();
684 BOOST_CHECK_CLOSE( eval.GetVar( "y" ), 0.0, 0.001 ); // All variables should be gone
685
686 // Test that we can still use the evaluator after clearing
687 BOOST_CHECK( eval.Process( "42" ) );
688 BOOST_CHECK( eval.IsValid() );
689 BOOST_CHECK_EQUAL( eval.Result(), "42" );
690
691 // Test LocaleChanged() - should be a no-op but not crash
692 eval.LocaleChanged();
693 BOOST_CHECK( eval.Process( "3.14" ) );
694 BOOST_CHECK( eval.IsValid() );
695 BOOST_CHECK_EQUAL( eval.Result(), "3.14" );
696}
697
High-level wrapper for evaluating mathematical and string expressions in wxString format.
EDA_UNITS GetDefaultUnits() const
Get the current default units.
bool HasVariableCallback() const
Check if a custom variable callback is set.
wxString Evaluate(const wxString &aInput)
Main evaluation function - processes input string and evaluates all} expressions.
bool RemoveVariable(const wxString &aName)
Remove a variable from the evaluator.
bool HasErrors() const
Check if the last evaluation had errors.
wxString GetErrorSummary() const
Get detailed error information from the last evaluation.
void SetDefaultUnits(EDA_UNITS aUnits)
Set the default units for expressions.
void ClearVariables()
Clear all stored variables.
bool HasVariable(const wxString &aName) const
Check if a variable exists in stored variables.
void SetVariable(const wxString &aName, double aValue)
Set a numeric variable for use in expressions.
NUMERIC_EVALUATOR compatible wrapper around EXPRESSION_EVALUATOR.
void LocaleChanged()
Handle locale changes (for decimal separator)
bool Process(const wxString &aString)
Process and evaluate an expression.
wxString Result() const
Get the result of the last evaluation.
void RemoveVar(const wxString &aString)
Remove a single variable.
void SetVar(const wxString &aString, double aValue)
Set a variable value.
void ClearVar()
Remove all variables.
double GetVar(const wxString &aString)
Get a variable value.
void Clear()
Clear parser state but retain variables.
bool IsValid() const
Check if the last evaluation was successful.
wxString OriginalText() const
Get the original input text.
EDA_UNITS
Definition eda_units.h:48
KICOMMON_API wxString GetText(EDA_UNITS aUnits, EDA_DATA_TYPE aType=EDA_DATA_TYPE::DISTANCE)
Get the units string for a given units type.
auto MakeValue(T aVal) -> Result< T >
auto MakeError(std::string aMsg) -> Result< T >
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition eda_angle.h:400
Declare the test suite.
BOOST_AUTO_TEST_SUITE(CadstarPartParser)
BOOST_AUTO_TEST_SUITE_END()
VECTOR3I expected(15, 30, 45)
static const std::vector< EVAL_INVALID_CASE > eval_cases_invalid
A list of invalid test strings.
static const std::vector< EVAL_CASE > eval_cases_valid
A list of valid test strings and the expected results.
BOOST_TEST_CONTEXT("Test Clearance")
BOOST_AUTO_TEST_CASE(Basic)
Basic functionality test.
std::ostream & operator<<(std::ostream &aStream, EDA_UNITS aUnits)
wxString result
Test unit parsing edge cases and error handling.
BOOST_CHECK_EQUAL(result, "25.4")