CI / lint (pull_request) Successful in 1m41s
CI / sanitizers (address) (pull_request) Skipped
CI / sanitizers (undefined) (pull_request) Skipped
CI / fuzz-build (pull_request) Skipped
CI / coverage (pull_request) Skipped
CI / valgrind (pull_request) Skipped
CI / build-and-test (pull_request) Successful in 46s
The post-merge valgrind job on dev hangs. Root cause: the CI valgrind step exports FASTSYNC_UNDER_VALGRIND=1, but nothing read it, and the /proc/self/maps "vgpreload" probe is unreliable on valgrind 3.22 (the guest's maps no longer list the tool's own libraries). So the fork-based unit tests ran under valgrind anyway; tests that call io_set_fds() left the thread-local read/write descriptors pointing at a closed test pipe, and a later send_n_data()/receive_n_data() call was silently redirected to those stale fds (legacy_session() prefers the globals, which the stdin/stdout SSH server requires). Later tests only worked by fd-reuse luck; under valgrind the fd numbers no longer coincide, so the read blocked forever on an empty pipe. - test_utils.h: honor FASTSYNC_UNDER_VALGRIND (already set by ci.yaml) and keep the maps scan as a best-effort fallback. Reset io_set_fds(-1, -1) at the start of every RUN_TEST so one suite cannot leak descriptor redirection into the next. - test_iconv.c: skip the forking wire-string roundtrip under valgrind like the other fork-based tests. - config.c: initialize per_dir_filter_count in config_set_defaults. The field was never initialized, so -F/-FF counting read uninitialized heap (valgrind: conditional jump on uninitialised value at client_cli.c:1464) and could count from garbage. Verified with the CI-equivalent command (FASTSYNC_UNDER_VALGRIND=1 valgrind --leak-check=full --show-leak-kinds=definite --error-exitcode=1): completes with 0 errors (previously hung >80 min). Unit 43/43; full integration 729 passed; cppcheck and clang-format clean.
137 lines
9.2 KiB
C
137 lines
9.2 KiB
C
#ifndef TEST_UTILS_H
|
|
#define TEST_UTILS_H
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdbool.h>
|
|
|
|
// Reset the thread-local protocol descriptor redirection installed by
|
|
// io_set_fds(), so a suite that leaks a test pipe's fds cannot redirect a later
|
|
// suite's raw send_n_data()/receive_n_data() to the wrong descriptor.
|
|
void io_set_fds(int read_fd, int write_fd);
|
|
|
|
// Detect if running under valgrind. The CI valgrind step exports
|
|
// FASTSYNC_UNDER_VALGRIND=1; a /proc/self/maps scan is the fallback for a local
|
|
// valgrind run (newer valgrind versions can hide their own mappings from the
|
|
// guest, so the "vgpreload" match is not reliable on every version -- set
|
|
// FASTSYNC_UNDER_VALGRIND=1 when invoking valgrind by hand). Used to skip
|
|
// fork-based tests incompatible with valgrind, whose instrumented parent runs
|
|
// too slowly and causes pipe timeouts.
|
|
static inline bool is_running_under_valgrind(void) {
|
|
const char* env = getenv("FASTSYNC_UNDER_VALGRIND");
|
|
if (env && env[0] != '\0' && strcmp(env, "0") != 0)
|
|
return true;
|
|
FILE* f = fopen("/proc/self/maps", "r");
|
|
if (!f)
|
|
return false;
|
|
char buf[4096];
|
|
size_t n = fread(buf, 1, sizeof(buf) - 1, f);
|
|
fclose(f);
|
|
buf[n] = '\0';
|
|
return strstr(buf, "vgpreload") != NULL;
|
|
}
|
|
|
|
// Global test suite status
|
|
extern int tests_run;
|
|
extern int tests_failed;
|
|
extern bool current_test_failed;
|
|
|
|
// Helper to run a test function
|
|
#define RUN_TEST(test_func) \
|
|
do { \
|
|
printf("Running %s...\n", #test_func); \
|
|
tests_run++; \
|
|
current_test_failed = false; \
|
|
io_set_fds(-1, -1); \
|
|
test_func(); \
|
|
if (current_test_failed) { \
|
|
tests_failed++; \
|
|
printf(" \033[1;31m[FAILED]\033[0m %s\n", #test_func); \
|
|
} else { \
|
|
printf(" \033[1;32m[PASSED]\033[0m %s\n", #test_func); \
|
|
} \
|
|
} while (0)
|
|
|
|
// Assertion macros
|
|
#define EXPECT_TRUE(condition) \
|
|
do { \
|
|
if (!(condition)) { \
|
|
printf(" \033[1;31m[FAIL]\033[0m %s:%d: Assertion failed: %s is false\n", __FILE__, \
|
|
__LINE__, #condition); \
|
|
current_test_failed = true; \
|
|
return; \
|
|
} \
|
|
} while (0)
|
|
|
|
#define EXPECT_FALSE(condition) \
|
|
do { \
|
|
if (condition) { \
|
|
printf(" \033[1;31m[FAIL]\033[0m %s:%d: Assertion failed: %s is true\n", __FILE__, \
|
|
__LINE__, #condition); \
|
|
current_test_failed = true; \
|
|
return; \
|
|
} \
|
|
} while (0)
|
|
|
|
#define EXPECT_EQ_INT(actual, expected) \
|
|
do { \
|
|
int act = (actual); \
|
|
int exp = (expected); \
|
|
if (act != exp) { \
|
|
printf(" \033[1;31m[FAIL]\033[0m %s:%d: Expected %d, got %d\n", __FILE__, __LINE__, exp, \
|
|
act); \
|
|
current_test_failed = true; \
|
|
return; \
|
|
} \
|
|
} while (0)
|
|
|
|
#define EXPECT_EQ_STR(actual, expected) \
|
|
do { \
|
|
const char* act = (actual); \
|
|
const char* exp = (expected); \
|
|
if (act == NULL || exp == NULL) { \
|
|
if (act != exp) { \
|
|
printf(" \033[1;31m[FAIL]\033[0m %s:%d: Expected %s, got %s\n", __FILE__, __LINE__, \
|
|
exp ? exp : "NULL", act ? act : "NULL"); \
|
|
current_test_failed = true; \
|
|
return; \
|
|
} \
|
|
} else if (strcmp(act, exp) != 0) { \
|
|
printf(" \033[1;31m[FAIL]\033[0m %s:%d: Expected \"%s\", got \"%s\"\n", __FILE__, \
|
|
__LINE__, exp, act); \
|
|
current_test_failed = true; \
|
|
return; \
|
|
} \
|
|
} while (0)
|
|
|
|
#define EXPECT_NOT_NULL(ptr) \
|
|
do { \
|
|
if ((ptr) == NULL) { \
|
|
printf(" \033[1;31m[FAIL]\033[0m %s:%d: Expected non-null pointer, got NULL\n", __FILE__, \
|
|
__LINE__); \
|
|
current_test_failed = true; \
|
|
return; \
|
|
} \
|
|
} while (0)
|
|
|
|
#define EXPECT_NULL(ptr) \
|
|
do { \
|
|
if ((ptr) != NULL) { \
|
|
printf(" \033[1;31m[FAIL]\033[0m %s:%d: Expected NULL, got %p\n", __FILE__, __LINE__, \
|
|
(void*)(ptr)); \
|
|
current_test_failed = true; \
|
|
return; \
|
|
} \
|
|
} while (0)
|
|
|
|
/* Unconditional test failure carrying an explanatory message. */
|
|
#define EXPECT_FAIL(message) \
|
|
do { \
|
|
printf(" \033[1;31m[FAIL]\033[0m %s:%d: %s\n", __FILE__, __LINE__, (message)); \
|
|
current_test_failed = true; \
|
|
return; \
|
|
} while (0)
|
|
|
|
#endif
|