51 evaluator.
SetVariable( std::string(
"project_name"), std::string(
"My PCB Project") );
52 evaluator.
SetVariable( std::string(
"designer"), std::string(
"John Doe") );
55 std::string expression;
56 std::string expectedPattern;
59 std::string description;
62 const std::vector<TestCase> cases = {
65 "Board area: @{${board_width} * ${board_height}} mm²",
66 "Board area: 8000 mm²",
68 "Board area calculation"
71 "Perimeter: @{2 * (${board_width} + ${board_height})} mm",
74 "Board perimeter calculation"
77 "Diagonal: @{format(sqrt(pow(${board_width}, 2) + pow(${board_height}, 2)), 1)} mm",
80 "Board diagonal calculation"
85 "Project: ${project_name} | Designer: ${designer} | Rev: @{${revision}}",
86 "Project: My PCB Project | Designer: John Doe | Rev: 3",
88 "Title block information"
91 "Components: @{${component_count}} | Density: @{format(${component_count} / (${board_width} * ${board_height} / 10000), 2)} per cm²",
92 "Components: 45 | Density: 56.25 per cm²",
94 "Component density calculation"
99 "Created: @{dateformat(today())} | Build: @{today()} days since epoch",
100 R
"(Created: \d{4}-\d{2}-\d{2} \| Build: \d+ days since epoch)",
102 "Date-based tracking"
107 "Status: @{if(${component_count} > 50, \"Complex\", \"Simple\")} design",
108 "Status: Simple design",
110 "Conditional design complexity"
113 "Status: @{if(${trace_width} >= 0.2, \"Standard\", \"Fine pitch\")} (@{${trace_width}}mm)",
114 "Status: Standard (0.2mm)",
116 "Conditional trace width description"
121 "PCB Summary:\n- Size: @{${board_width}}×@{${board_height}}mm\n- Area: @{${board_width} * ${board_height}}mm²\n- Components: @{${component_count}}",
122 "PCB Summary:\n- Size: 100×80mm\n- Area: 8000mm²\n- Components: 45",
124 "Multi-line documentation"
129 "Invalid: @{${undefined_var}} test",
130 "Invalid: @{${undefined_var}} test",
132 "Undefined variable behavior"
136 for(
const auto& testCase : cases )
138 auto result = evaluator.
Evaluate( wxString::FromUTF8( testCase.expression ) );
140 if( testCase.shouldError )
143 "Expected error for: " + testCase.description );
148 "Unexpected error for: " + testCase.description +
151 if( testCase.isRegex )
153 std::regex pattern( testCase.expectedPattern );
155 "Result '" +
result.ToStdString( wxConvUTF8 ) +
"' doesn't match pattern '" +
156 testCase.expectedPattern +
"' for: " + testCase.description );
161 "Expected '" + testCase.expectedPattern +
"' but got '" +
162 result.ToStdString( wxConvUTF8 ) +
"' for: " + testCase.description );
175 if( varName ==
"dynamic_value" )
177 else if( varName ==
"dynamic_string" )
179 else if( varName ==
"computed_value" )
188 std::string expression;
194 const std::vector<TestCase> cases = {
195 {
"@{${dynamic_value}}",
"42", 0,
false },
196 {
"Message: ${dynamic_string}",
"Message: Hello from callback", 0,
false },
197 {
"@{format(${computed_value}, 1)}",
"70.7", 0.1,
false },
198 {
"@{${dynamic_value} + ${computed_value}}",
"112.7", 0.1,
false },
199 {
"${nonexistent}",
"${nonexistent}", 0,
true },
202 for(
const auto& testCase : cases )
204 auto result = evaluator.
Evaluate( wxString::FromUTF8( testCase.expression ) );
206 if( testCase.shouldError )
214 if( testCase.tolerance > 0 )
217 std::regex numberRegex( R
"([\d.]+)" );
219 std::string resultStr = result.ToStdString( wxConvUTF8 );
220 if( std::regex_search( resultStr, match, numberRegex ) )
222 double actualValue = std::stod( match[0].str() );
223 double expectedValue = std::stod( testCase.expected );
224 BOOST_CHECK_CLOSE( actualValue, expectedValue, testCase.tolerance * 100 );
241 std::vector<std::unique_ptr<EXPRESSION_EVALUATOR>> evaluators;
243 for(
int i = 0; i < 10; ++i )
245 auto evaluator = std::make_unique<EXPRESSION_EVALUATOR>();
246 evaluator->SetVariable(
"thread_id",
static_cast<double>( i ) );
247 evaluator->SetVariable(
"multiplier", 5.0 );
248 evaluators.push_back( std::move( evaluator ) );
252 for(
int i = 0; i < 10; ++i )
254 auto result = evaluators[i]->Evaluate(
"@{${thread_id} * ${multiplier}}" );
255 BOOST_CHECK( !evaluators[i]->HasErrors() );
257 double expected =
static_cast<double>( i * 5 );
258 double actual = std::stod(
result.ToStdString( wxConvUTF8 ) );
271 std::string complexExpression =
"@{";
272 for(
int i = 0; i < 100; ++i )
274 if( i > 0 ) complexExpression +=
" + ";
275 complexExpression += std::to_string( i );
277 complexExpression +=
"}";
279 auto result = evaluator.
Evaluate( wxString::FromUTF8( complexExpression ) );
286 for(
int i = 0; i < 1000; ++i )
288 auto expr =
"@{" + std::to_string( i ) +
" * 2}";
303 std::string expression;
307 std::string description;
310 const std::vector<TestCase> cases = {
312 {
"@{ 2 + 3 }",
"5",
false, 0.0,
"Spaces in expression" },
313 {
"@{\t2\t+\t3\t}",
"5",
false, 0.0,
"Tabs in expression" },
314 {
"@{\n2\n+\n3\n}",
"5",
false, 0.0,
"Newlines in expression" },
317 {
"@{\"Hello\\\"World\\\"\"}",
"Hello\"World\"",
false, 0.0,
"Escaped quotes in string" },
318 {
"@{\"Line1\\nLine2\"}",
"Line1\nLine2",
false, 0.0,
"Newline in string" },
321 {
"A: @{1+1}, B: @{2*2}, C: @{3^2}",
"A: 2, B: 4, C: 9",
false, 0.0,
"Multiple calculations" },
324 {
"@{((((2))))}",
"2",
false, 0.0,
"Multiple nested parentheses" },
325 {
"@{(2 + 3) * (4 + 5)}",
"45",
false, 0.0,
"Grouped operations" },
328 {
"No calculations here",
"No calculations here",
false, 0.0,
"Plain text" },
329 {
"",
"",
false, 0.0,
"Empty string" },
330 {
"@{0}",
"0",
false, 0.0,
"Zero value" },
331 {
"@{-0}",
"0",
false, 0.0,
"Negative zero" },
334 {
"@{0.1 + 0.2}",
"0.3",
false, 0.01,
"Floating point precision" },
335 {
"@{1.0 / 3.0}",
"0.333333",
false, 0.01,
"Repeating decimal" },
338 {
"@{1000000 * 1000000}",
"1e+12",
false, 0.01,
"Large number result" },
341 {
"Good @{2+2} bad @{2+} good @{3+3}",
"Good 4 bad @{2+} good 6",
true, 0.0,
"Error recovery" },
344 for(
const auto& testCase : cases )
346 auto result = evaluator.
Evaluate( wxString::FromUTF8( testCase.expression ) );
348 if( testCase.shouldError )
354 if( testCase.precision > 0.0 )
357 std::regex numberRegex( R
"([\d.eE+-]+)" );
359 std::string resultStr = result.ToStdString( wxConvUTF8 );
360 if( std::regex_search( resultStr, match, numberRegex ) )
362 double actualValue = std::stod( match[0].str() );
363 double expectedValue = std::stod( testCase.expected );
364 BOOST_CHECK_CLOSE( actualValue, expectedValue, testCase.precision * 100 );
370 "Unexpected error for: " + testCase.description +
373 "Expected '" + testCase.expected +
"' but got '" +
374 result.ToStdString( wxConvUTF8 ) +
"' for: " + testCase.description );
396 std::vector<std::string> expressions = {
397 "Layer @{${board_layers}}/4",
398 "Components: @{${component_count}}",
399 "Nets: @{${net_count}}",
400 "Vias: @{${via_count}}",
401 "Area: @{${board_width} * ${board_height}} mm²",
402 "Density: @{format(${component_count} / (${board_width} * ${board_height} / 100), 1)} /cm²",
403 "Via density: @{format(${via_count} / (${board_width} * ${board_height} / 100), 1)} /cm²",
404 "Layer utilization: @{format(${net_count} / ${board_layers}, 1)} nets/layer",
405 "Design complexity: @{if(${component_count} > 100, \"High\", \"Low\")}",
406 "Board aspect ratio: @{format(${board_width} / ${board_height}, 2)}:1",
409 auto start = std::chrono::high_resolution_clock::now();
412 for(
int iteration = 0; iteration < 100; ++iteration )
414 for(
const auto& expr : expressions )
418 BOOST_CHECK( !
result.empty() );
422 auto end = std::chrono::high_resolution_clock::now();
423 auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(
end - start );
426 BOOST_CHECK_LT( duration.count(), 100 );
429 for(
auto& expr : expressions )
431 auto result1 = evaluator.
Evaluate( wxString::FromUTF8( expr ) );
432 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)
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")
BOOST_AUTO_TEST_CASE(RealWorldScenarios)
Declare the test suite.