22
33#include "libnetdata/libnetdata.h"
44#include "eval-internal.h"
5+ #include <pthread.h> // For multithreaded testing
56
67// Mock variable lookup function for testing
78static bool test_variable_lookup (STRING * variable , void * data __maybe_unused , NETDATA_DOUBLE * result ) {
@@ -347,20 +348,30 @@ typedef struct {
347348
348349#define ARRAY_SIZE (x ) (sizeof(x) / sizeof((x)[0]))
349350
350- static void run_test_group (TestGroup * group ) {
351- printf ("\n=== Running Test Group: %s ===\n" , group -> name );
351+ static void run_test_group (TestGroup * group , bool verbose , bool only_parsing_success ) {
352+ if (verbose ) {
353+ printf ("\n=== Running Test Group: %s ===\n" , group -> name );
354+ }
352355
353356 int passed = 0 ;
354357 int failed = 0 ;
355358
356359 for (int i = 0 ; i < group -> test_count ; i ++ ) {
357360 TestCase * tc = & group -> test_cases [i ];
361+
362+ // Skip tests that are expected to fail parsing, if requested
363+ if (only_parsing_success && !tc -> should_parse ) {
364+ continue ;
365+ }
366+
358367 const char * failed_at = NULL ;
359368 EVAL_ERROR error = EVAL_ERROR_OK ;
360369 bool test_failed = false;
361370 char error_message [1024 ] = "" ;
362371
363- printf ("Test %d: %s\n" , i + 1 , tc -> expression );
372+ if (verbose ) {
373+ printf ("Test %d: %s\n" , i + 1 , tc -> expression );
374+ }
364375
365376 // Try to parse the expression
366377 EVAL_EXPRESSION * exp = expression_parse (tc -> expression , & failed_at , & error );
@@ -428,13 +439,15 @@ static void run_test_group(TestGroup *group) {
428439 }
429440 }
430441
431- // Print additional information for debugging
432- printf (" Parsed as: %s\n" , expression_parsed_as (exp ));
433-
434- if (eval_result ) {
435- printf (" Evaluated to: %f\n" , expression_result (exp ));
436- } else {
437- printf (" Evaluation failed: %s\n" , expression_error_msg (exp ));
442+ if (verbose ) {
443+ // Print additional information for debugging
444+ printf (" Parsed as: %s\n" , expression_parsed_as (exp ));
445+
446+ if (eval_result ) {
447+ printf (" Evaluated to: %f\n" , expression_result (exp ));
448+ } else {
449+ printf (" Evaluation failed: %s\n" , expression_error_msg (exp ));
450+ }
438451 }
439452
440453 // Special handling for API function tests
@@ -454,7 +467,7 @@ static void run_test_group(TestGroup *group) {
454467 "expression_hardcode_variable failed: expected 123.456, got %f (error: %u)" ,
455468 exp -> result , exp -> error );
456469 test_failed = true;
457- } else {
470+ } else if ( verbose ) {
458471 printf (" expression_hardcode_variable test passed!\n" );
459472 }
460473 }
@@ -466,7 +479,7 @@ static void run_test_group(TestGroup *group) {
466479 "expression_source failed: expected '1 + 2', got '%s'" ,
467480 source );
468481 test_failed = true;
469- } else {
482+ } else if ( verbose ) {
470483 printf (" expression_source test passed!\n" );
471484 }
472485
@@ -476,7 +489,7 @@ static void run_test_group(TestGroup *group) {
476489 snprintf (error_message , sizeof (error_message ),
477490 "expression_parsed_as failed: got empty or NULL result" );
478491 test_failed = true;
479- } else {
492+ } else if ( verbose ) {
480493 printf (" expression_parsed_as test passed! Result: %s\n" , parsed );
481494 }
482495
@@ -487,11 +500,11 @@ static void run_test_group(TestGroup *group) {
487500 "expression_result failed: expected 3.0, got %f" ,
488501 result );
489502 test_failed = true;
490- } else {
503+ } else if ( verbose ) {
491504 printf (" expression_result test passed!\n" );
492505 }
493506 }
494- else if (strcmp (tc -> expression , "bad/syntax" ) == 0 ) {
507+ else if (strcmp (tc -> expression , "bad/syntax" ) == 0 && verbose ) {
495508 // This case is for testing expression_error_msg, but it is already tested
496509 // in the main evaluation loop when errors occur.
497510 printf (" expression_error_msg is tested during evaluation failures\n" );
@@ -501,27 +514,33 @@ static void run_test_group(TestGroup *group) {
501514 // Clean up
502515 expression_free (exp );
503516 }
504- else if (!tc -> should_parse ) {
517+ else if (!tc -> should_parse && verbose ) {
505518 printf (" Parsing failed as expected at: %s\n" ,
506519 failed_at ? ((* failed_at ) ? failed_at : "<END OF EXPRESSION>" ) : "<NONE>" );
507520 }
508521
509522 // Report test result
510523 if (test_failed ) {
524+ if (verbose ) {
511525#ifdef USE_RE2C_LEMON_PARSER
512- printf (" [RE2C_LEMON] FAILED: %s\n" , error_message );
526+ printf (" [RE2C_LEMON] FAILED: %s\n" , error_message );
513527#else
514- printf (" [RECURSIVE] FAILED: %s\n" , error_message );
528+ printf (" [RECURSIVE] FAILED: %s\n" , error_message );
515529#endif
530+ }
516531 failed ++ ;
517532 } else {
518- printf (" PASSED\n" );
533+ if (verbose ) {
534+ printf (" PASSED\n" );
535+ }
519536 passed ++ ;
520537 }
521538 }
522539
523- printf ("\nGroup Results: %d tests, %d passed, %d failed\n" ,
524- passed + failed , passed , failed );
540+ if (verbose ) {
541+ printf ("\nGroup Results: %d tests, %d passed, %d failed\n" ,
542+ passed + failed , passed , failed );
543+ }
525544}
526545
527546static TestCase arithmetic_tests [] = {
@@ -1261,29 +1280,41 @@ static TestGroup test_groups[] = {
12611280#endif
12621281};
12631282
1264- int eval_unittest (void ) {
1283+ // Run the evaluation tests and return the number of failed tests
1284+ int run_eval_unittest (bool verbose , bool only_parsing_success ) {
12651285 // Test cases for basic arithmetic operations
12661286
12671287 // Run all test groups
12681288 int total_passed = 0 ;
12691289 int total_failed = 0 ;
12701290 int total_tests = 0 ;
12711291
1292+ if (verbose ) {
12721293#ifdef USE_RE2C_LEMON_PARSER
1273- printf ("Starting comprehensive evaluation tests using RE2C/LEMON PARSER\n" );
1294+ printf ("Starting comprehensive evaluation tests using RE2C/LEMON PARSER\n" );
12741295#else
1275- printf ("Starting comprehensive evaluation tests using RECURSIVE DESCENT PARSER\n" );
1296+ printf ("Starting comprehensive evaluation tests using RECURSIVE DESCENT PARSER\n" );
12761297#endif
1298+ if (only_parsing_success ) {
1299+ printf ("Running only tests that should parse successfully\n" );
1300+ }
1301+ }
12771302
12781303 for (size_t i = 0 ; i < ARRAY_SIZE (test_groups ); i ++ ) {
1279- run_test_group (& test_groups [i ]);
1304+ run_test_group (& test_groups [i ], verbose , only_parsing_success );
12801305
12811306 int group_tests = test_groups [i ].test_count ;
12821307 int group_passed = 0 ;
12831308 int group_failed = 0 ;
12841309
12851310 for (int j = 0 ; j < group_tests ; j ++ ) {
12861311 TestCase * tc = & test_groups [i ].test_cases [j ];
1312+
1313+ // Skip tests that are expected to fail parsing, if requested
1314+ if (only_parsing_success && !tc -> should_parse ) {
1315+ continue ;
1316+ }
1317+
12871318 const char * failed_at = NULL ;
12881319 EVAL_ERROR error = EVAL_ERROR_OK ;
12891320
@@ -1350,10 +1381,122 @@ int eval_unittest(void) {
13501381 total_tests += group_tests ;
13511382 }
13521383
1353- printf ("\n========== OVERALL TEST SUMMARY ==========\n" );
1354- printf ("Total tests: %d\n" , total_tests );
1355- printf ("Passed: %d (%.1f%%)\n" , total_passed , (float )total_passed / total_tests * 100 );
1356- printf ("Failed: %d (%.1f%%)\n" , total_failed , (float )total_failed / total_tests * 100 );
1384+ if (verbose ) {
1385+ printf ("\n========== OVERALL TEST SUMMARY ==========\n" );
1386+ printf ("Total tests: %d\n" , total_tests );
1387+ printf ("Passed: %d (%.1f%%)\n" , total_passed , (float )total_passed / total_tests * 100 );
1388+ printf ("Failed: %d (%.1f%%)\n" , total_failed , (float )total_failed / total_tests * 100 );
1389+ }
13571390
13581391 return total_failed > 0 ? 1 : 0 ;
13591392}
1393+
1394+ // Thread data structure for multithreaded testing
1395+ typedef struct {
1396+ int thread_id ;
1397+ volatile int * stop_flag ;
1398+ int tests_run ;
1399+ int failed ;
1400+ } ThreadData ;
1401+
1402+ // Thread function to run tests in a loop until time expires
1403+ static void * thread_test_function (void * arg ) {
1404+ ThreadData * data = (ThreadData * )arg ;
1405+ int tests_run = 0 ;
1406+ int failed = 0 ;
1407+
1408+ // Run tests in a loop until stop_flag is set
1409+ while (* (data -> stop_flag ) == 0 ) {
1410+ // Run only tests that should parse successfully for multithreaded testing
1411+ int result = run_eval_unittest (false, true);
1412+ tests_run ++ ;
1413+ if (result != 0 ) {
1414+ failed ++ ;
1415+ }
1416+ }
1417+
1418+ data -> tests_run = tests_run ;
1419+ data -> failed = failed ;
1420+
1421+ return NULL ;
1422+ }
1423+
1424+ // The main unittest function that runs both single-threaded and multi-threaded tests
1425+ int eval_unittest (void ) {
1426+ int failed = 0 ;
1427+
1428+ // Run the standard unit tests in a single thread first
1429+ printf ("\n========== RUNNING SINGLE-THREADED TESTS ==========\n" );
1430+ failed = run_eval_unittest (true, false); // Run all tests for single-threaded case
1431+
1432+ if (failed > 0 ) {
1433+ printf ("Single-threaded tests failed, skipping multi-threaded tests\n" );
1434+ return failed ;
1435+ }
1436+
1437+ // Define constants for the multithreaded test
1438+ const int num_threads = 5 ;
1439+ const int duration_seconds = 5 ;
1440+
1441+ // Now run the same tests in parallel with 5 threads for 5 seconds
1442+ printf ("\n========== RUNNING MULTI-THREADED TESTS FOR %d SECONDS WITH %d THREADS ==========\n" ,
1443+ duration_seconds , num_threads );
1444+ printf ("Note: Running only tests that should parse successfully for multithreaded testing\n" );
1445+
1446+ pthread_t threads [num_threads ];
1447+ ThreadData thread_data [num_threads ];
1448+ volatile int stop_flag = 0 ;
1449+
1450+ // Create threads
1451+ for (int i = 0 ; i < num_threads ; i ++ ) {
1452+ thread_data [i ].thread_id = i ;
1453+ thread_data [i ].stop_flag = & stop_flag ;
1454+ thread_data [i ].tests_run = 0 ;
1455+ thread_data [i ].failed = 0 ;
1456+
1457+ int rc = pthread_create (& threads [i ], NULL , thread_test_function , & thread_data [i ]);
1458+ if (rc ) {
1459+ printf ("ERROR: Failed to create thread %d, error code: %d\n" , i , rc );
1460+ // Set stop flag to stop any threads that were created
1461+ stop_flag = 1 ;
1462+
1463+ // Wait for created threads to terminate
1464+ for (int j = 0 ; j < i ; j ++ ) {
1465+ pthread_join (threads [j ], NULL );
1466+ }
1467+
1468+ return 1 ;
1469+ }
1470+ }
1471+
1472+ // Sleep for the specified duration
1473+ sleep (duration_seconds );
1474+
1475+ // Signal threads to stop
1476+ stop_flag = 1 ;
1477+
1478+ // Wait for all threads to finish
1479+ int total_runs = 0 ;
1480+ int total_failed = 0 ;
1481+
1482+ for (int i = 0 ; i < num_threads ; i ++ ) {
1483+ pthread_join (threads [i ], NULL );
1484+
1485+ total_runs += thread_data [i ].tests_run ;
1486+ total_failed += thread_data [i ].failed ;
1487+
1488+ printf ("Thread %d: %d test runs, %d failed\n" ,
1489+ i , thread_data [i ].tests_run , thread_data [i ].failed );
1490+ }
1491+
1492+ printf ("\n========== MULTITHREADED TEST SUMMARY ==========\n" );
1493+ printf ("Total test runs: %d\n" , total_runs );
1494+
1495+ if (total_failed > 0 ) {
1496+ printf ("FAIL: %d out of %d runs failed\n" , total_failed , total_runs );
1497+ return 1 ;
1498+ } else {
1499+ printf ("SUCCESS: All %d runs passed\n" , total_runs );
1500+ return 0 ;
1501+ }
1502+ }
0 commit comments