55 evaluator.
SetVariable( std::string(
"project_name"), std::string(
"My PCB Project") );
56 evaluator.
SetVariable( std::string(
"designer"), std::string(
"John Doe") );
59 std::string expression;
60 std::string expectedPattern;
63 std::string description;
66 const std::vector<TestCase> cases = {
69 "Board area: @{${board_width} * ${board_height}} mm²",
70 "Board area: 8000 mm²",
72 "Board area calculation"
75 "Perimeter: @{2 * (${board_width} + ${board_height})} mm",
78 "Board perimeter calculation"
81 "Diagonal: @{format(sqrt(pow(${board_width}, 2) + pow(${board_height}, 2)), 1)} mm",
84 "Board diagonal calculation"
89 "Project: ${project_name} | Designer: ${designer} | Rev: @{${revision}}",
90 "Project: My PCB Project | Designer: John Doe | Rev: 3",
92 "Title block information"
95 "Components: @{${component_count}} | Density: @{format(${component_count} / (${board_width} * ${board_height} / 10000), 2)} per cm²",
96 "Components: 45 | Density: 56.25 per cm²",
98 "Component density calculation"
103 "Created: @{dateformat(today())} | Build: @{today()} days since epoch",
104 R
"(Created: \d{4}-\d{2}-\d{2} \| Build: \d+ days since epoch)",
106 "Date-based tracking"
111 "Status: @{if(${component_count} > 50, \"Complex\", \"Simple\")} design",
112 "Status: Simple design",
114 "Conditional design complexity"
117 "Status: @{if(${trace_width} >= 0.2, \"Standard\", \"Fine pitch\")} (@{${trace_width}}mm)",
118 "Status: Standard (0.2mm)",
120 "Conditional trace width description"
125 "PCB Summary:\n- Size: @{${board_width}}×@{${board_height}}mm\n- Area: @{${board_width} * ${board_height}}mm²\n- Components: @{${component_count}}",
126 "PCB Summary:\n- Size: 100×80mm\n- Area: 8000mm²\n- Components: 45",
128 "Multi-line documentation"
133 "Invalid: @{${undefined_var}} test",
134 "Invalid: @{${undefined_var}} test",
136 "Undefined variable behavior"
140 for(
const auto& testCase : cases )
142 auto result = evaluator.
Evaluate( wxString::FromUTF8( testCase.expression ) );
144 if( testCase.shouldError )
146 BOOST_CHECK_MESSAGE( evaluator.
HasErrors(),
147 "Expected error for: " + testCase.description );
151 BOOST_CHECK_MESSAGE( !evaluator.
HasErrors(),
152 "Unexpected error for: " + testCase.description +
155 if( testCase.isRegex )
157 std::regex pattern( testCase.expectedPattern );
158 BOOST_CHECK_MESSAGE( std::regex_match(
result.ToStdString( wxConvUTF8 ), pattern ),
159 "Result '" +
result.ToStdString( wxConvUTF8 ) +
"' doesn't match pattern '" +
160 testCase.expectedPattern +
"' for: " + testCase.description );
164 BOOST_CHECK_MESSAGE(
result.ToStdString( wxConvUTF8 ) == testCase.expectedPattern,
165 "Expected '" + testCase.expectedPattern +
"' but got '" +
166 result.ToStdString( wxConvUTF8 ) +
"' for: " + testCase.description );
179 if( varName ==
"dynamic_value" )
181 else if( varName ==
"dynamic_string" )
183 else if( varName ==
"computed_value" )
192 std::string expression;
198 const std::vector<TestCase> cases = {
199 {
"@{${dynamic_value}}",
"42", 0,
false },
200 {
"Message: ${dynamic_string}",
"Message: Hello from callback", 0,
false },
201 {
"@{format(${computed_value}, 1)}",
"70.7", 0.1,
false },
202 {
"@{${dynamic_value} + ${computed_value}}",
"112.7", 0.1,
false },
203 {
"${nonexistent}",
"${nonexistent}", 0,
true },
206 for(
const auto& testCase : cases )
208 auto result = evaluator.
Evaluate( wxString::FromUTF8( testCase.expression ) );
210 if( testCase.shouldError )
218 if( testCase.tolerance > 0 )
221 std::regex numberRegex( R
"([\d.]+)" );
223 std::string resultStr = result.ToStdString( wxConvUTF8 );
224 if( std::regex_search( resultStr, match, numberRegex ) )
226 double actualValue = std::stod( match[0].str() );
227 double expectedValue = std::stod( testCase.expected );
228 BOOST_CHECK_CLOSE( actualValue, expectedValue, testCase.tolerance * 100 );
245 std::vector<std::unique_ptr<EXPRESSION_EVALUATOR>> evaluators;
247 for(
int i = 0; i < 10; ++i )
249 auto evaluator = std::make_unique<EXPRESSION_EVALUATOR>();
250 evaluator->SetVariable(
"thread_id",
static_cast<double>( i ) );
251 evaluator->SetVariable(
"multiplier", 5.0 );
252 evaluators.push_back( std::move( evaluator ) );
256 for(
int i = 0; i < 10; ++i )
258 auto result = evaluators[i]->Evaluate(
"@{${thread_id} * ${multiplier}}" );
259 BOOST_CHECK( !evaluators[i]->HasErrors() );
261 double expected =
static_cast<double>( i * 5 );
262 double actual = std::stod(
result.ToStdString( wxConvUTF8 ) );
275 std::string complexExpression =
"@{";
276 for(
int i = 0; i < 100; ++i )
278 if( i > 0 ) complexExpression +=
" + ";
279 complexExpression += std::to_string( i );
281 complexExpression +=
"}";
283 auto result = evaluator.
Evaluate( wxString::FromUTF8( complexExpression ) );
290 for(
int i = 0; i < 1000; ++i )
292 auto expr =
"@{" + std::to_string( i ) +
" * 2}";
307 std::string expression;
311 std::string description;
314 const std::vector<TestCase> cases = {
316 {
"@{ 2 + 3 }",
"5",
false, 0.0,
"Spaces in expression" },
317 {
"@{\t2\t+\t3\t}",
"5",
false, 0.0,
"Tabs in expression" },
318 {
"@{\n2\n+\n3\n}",
"5",
false, 0.0,
"Newlines in expression" },
321 {
"@{\"Hello\\\"World\\\"\"}",
"Hello\"World\"",
false, 0.0,
"Escaped quotes in string" },
322 {
"@{\"Line1\\nLine2\"}",
"Line1\nLine2",
false, 0.0,
"Newline in string" },
325 {
"A: @{1+1}, B: @{2*2}, C: @{3^2}",
"A: 2, B: 4, C: 9",
false, 0.0,
"Multiple calculations" },
328 {
"@{((((2))))}",
"2",
false, 0.0,
"Multiple nested parentheses" },
329 {
"@{(2 + 3) * (4 + 5)}",
"45",
false, 0.0,
"Grouped operations" },
332 {
"No calculations here",
"No calculations here",
false, 0.0,
"Plain text" },
333 {
"",
"",
false, 0.0,
"Empty string" },
334 {
"@{0}",
"0",
false, 0.0,
"Zero value" },
335 {
"@{-0}",
"0",
false, 0.0,
"Negative zero" },
338 {
"@{0.1 + 0.2}",
"0.3",
false, 0.01,
"Floating point precision" },
339 {
"@{1.0 / 3.0}",
"0.333333",
false, 0.01,
"Repeating decimal" },
342 {
"@{1000000 * 1000000}",
"1e+12",
false, 0.01,
"Large number result" },
345 {
"Good @{2+2} bad @{2+} good @{3+3}",
"Good 4 bad @{2+} good 6",
true, 0.0,
"Error recovery" },
348 for(
const auto& testCase : cases )
350 auto result = evaluator.
Evaluate( wxString::FromUTF8( testCase.expression ) );
352 if( testCase.shouldError )
354 BOOST_CHECK_MESSAGE( evaluator.
HasErrors(),
"Expected error for: " + testCase.description );
358 if( testCase.precision > 0.0 )
361 std::regex numberRegex( R
"([\d.eE+-]+)" );
363 std::string resultStr = result.ToStdString( wxConvUTF8 );
364 if( std::regex_search( resultStr, match, numberRegex ) )
366 double actualValue = std::stod( match[0].str() );
367 double expectedValue = std::stod( testCase.expected );
368 BOOST_CHECK_CLOSE( actualValue, expectedValue, testCase.precision * 100 );
373 BOOST_CHECK_MESSAGE( !evaluator.
HasErrors(),
374 "Unexpected error for: " + testCase.description +
376 BOOST_CHECK_MESSAGE(
result.ToStdString( wxConvUTF8 ) == testCase.expected,
377 "Expected '" + testCase.expected +
"' but got '" +
378 result.ToStdString( wxConvUTF8 ) +
"' for: " + testCase.description );
400 std::vector<std::string> expressions = {
401 "Layer @{${board_layers}}/4",
402 "Components: @{${component_count}}",
403 "Nets: @{${net_count}}",
404 "Vias: @{${via_count}}",
405 "Area: @{${board_width} * ${board_height}} mm²",
406 "Density: @{format(${component_count} / (${board_width} * ${board_height} / 100), 1)} /cm²",
407 "Via density: @{format(${via_count} / (${board_width} * ${board_height} / 100), 1)} /cm²",
408 "Layer utilization: @{format(${net_count} / ${board_layers}, 1)} nets/layer",
409 "Design complexity: @{if(${component_count} > 100, \"High\", \"Low\")}",
410 "Board aspect ratio: @{format(${board_width} / ${board_height}, 2)}:1",
413 auto start = std::chrono::high_resolution_clock::now();
416 for(
int iteration = 0; iteration < 100; ++iteration )
418 for(
const auto& expr : expressions )
422 BOOST_CHECK( !
result.empty() );
426 auto end = std::chrono::high_resolution_clock::now();
427 auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(
end - start );
430 BOOST_CHECK_LT( duration.count(), 100 );
433 for(
auto& expr : expressions )
435 auto result1 = evaluator.
Evaluate( wxString::FromUTF8( expr ) );
436 auto result2 = evaluator.
Evaluate( wxString::FromUTF8( expr ) );
High-level wrapper for evaluating mathematical and string expressions in wxString format.
wxString Evaluate(const wxString &aInput)
Main evaluation function - processes input string and evaluates all} expressions.
bool HasErrors() const
Check if the last evaluation had errors.
wxString GetErrorSummary() const
Get detailed error information from the last evaluation.
void SetVariable(const wxString &aName, double aValue)
Set a numeric variable for use in expressions.
auto MakeValue(T aVal) -> Result< T >
auto MakeError(std::string aMsg) -> Result< T >
BOOST_AUTO_TEST_CASE(HorizontalAlignment)
BOOST_AUTO_TEST_SUITE(CadstarPartParser)
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")
BOOST_AUTO_TEST_CASE(RealWorldScenarios)
Declare the test suite.