Skip to content

Commit 3f9f6e5

Browse files
committed
add multi-threaded tests to eval
1 parent 360901c commit 3f9f6e5

File tree

1 file changed

+172
-29
lines changed

1 file changed

+172
-29
lines changed

src/libnetdata/eval/eval-unittest.c

Lines changed: 172 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
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
78
static 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

527546
static 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

Comments
 (0)