Merge latest dev into triage remediation
CI / lint (pull_request) Successful in 25s
CI / sanitizers (address) (pull_request) Successful in 35s
CI / sanitizers (undefined) (pull_request) Successful in 36s
CI / fuzz-build (pull_request) Successful in 14s
CI / coverage (pull_request) Successful in 31s
CI / build-and-test (pull_request) Successful in 1m15s
CI / valgrind (pull_request) Successful in 33s
CI / lint (pull_request) Successful in 25s
CI / sanitizers (address) (pull_request) Successful in 35s
CI / sanitizers (undefined) (pull_request) Successful in 36s
CI / fuzz-build (pull_request) Successful in 14s
CI / coverage (pull_request) Successful in 31s
CI / build-and-test (pull_request) Successful in 1m15s
CI / valgrind (pull_request) Successful in 33s
This commit is contained in:
@@ -258,7 +258,7 @@ class TestProgress:
|
||||
)
|
||||
assert result.returncode == 0, f"Exit {result.returncode}: {result.stderr[:100]}"
|
||||
output = result.stdout + result.stderr
|
||||
assert len(output) >= 0
|
||||
assert output, "--progress produced no output"
|
||||
|
||||
|
||||
class TestBandwidthLimit:
|
||||
|
||||
@@ -52,9 +52,11 @@ def _check_ssh():
|
||||
pass
|
||||
|
||||
|
||||
_check_ssh()
|
||||
|
||||
|
||||
@pytest.fixture(scope="module", autouse=True)
|
||||
def setup_test_data():
|
||||
_check_ssh()
|
||||
if SSH_AVAILABLE:
|
||||
generate_test_files(SOURCE_DIR, full=False)
|
||||
clean_dir(DEST_DIR)
|
||||
@@ -84,8 +86,12 @@ def _run_ssh_test(name, flags, expected_missing=None):
|
||||
return make_result(name, True, duration)
|
||||
|
||||
|
||||
@pytest.mark.skipif(not SSH_AVAILABLE, reason="SSH to localhost not available")
|
||||
class TestSSHStandard:
|
||||
@pytest.fixture(autouse=True)
|
||||
def require_ssh(self):
|
||||
if not SSH_AVAILABLE:
|
||||
pytest.skip("SSH to localhost not available")
|
||||
|
||||
def test_standard(self):
|
||||
r = _run_ssh_test("SSH (localhost)", [])
|
||||
assert r["status"] == "Success", r["error"]
|
||||
@@ -119,8 +125,12 @@ class TestSSHStandard:
|
||||
assert r["status"] == "Success", r["error"]
|
||||
|
||||
|
||||
@pytest.mark.skipif(not SSH_AVAILABLE, reason="SSH to localhost not available")
|
||||
class TestSSHFeatures:
|
||||
@pytest.fixture(autouse=True)
|
||||
def require_ssh(self):
|
||||
if not SSH_AVAILABLE:
|
||||
pytest.skip("SSH to localhost not available")
|
||||
|
||||
def test_archive(self):
|
||||
r = _run_ssh_test("SSH Archive (-a)", ["-a"])
|
||||
assert r["status"] == "Success", r["error"]
|
||||
|
||||
@@ -6,6 +6,9 @@
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/* Declaration of parse_args from client_cli.c */
|
||||
int parse_args(Config* config, int argc, char* argv[], int* positional_args, int* positional_count);
|
||||
|
||||
/* Test main() with --help flag (early return path, no server connection needed) */
|
||||
static void test_cli_help() {
|
||||
/* We can't easily call main() because it calls send_files which needs a server.
|
||||
@@ -80,10 +83,160 @@ static void test_cli_exclude_patterns() {
|
||||
config_delete(cfg);
|
||||
}
|
||||
|
||||
/* Test parse_args with --help returns 1 (clean exit) */
|
||||
static void test_parse_args_help() {
|
||||
Config* cfg = config_create();
|
||||
char* argv[] = {"fastsync", "--help"};
|
||||
int positional_args[2];
|
||||
int positional_count = 0;
|
||||
|
||||
int ret = parse_args(cfg, 2, argv, positional_args, &positional_count);
|
||||
EXPECT_EQ_INT(ret, 1);
|
||||
|
||||
config_delete(cfg);
|
||||
}
|
||||
|
||||
/* Test parse_args with -V/--version returns 1 */
|
||||
static void test_parse_args_version() {
|
||||
Config* cfg = config_create();
|
||||
char* argv_short[] = {"fastsync", "-V"};
|
||||
char* argv_long[] = {"fastsync", "--version"};
|
||||
int positional_args[2];
|
||||
int positional_count = 0;
|
||||
|
||||
int ret = parse_args(cfg, 2, argv_short, positional_args, &positional_count);
|
||||
EXPECT_EQ_INT(ret, 1);
|
||||
|
||||
ret = parse_args(cfg, 2, argv_long, positional_args, &positional_count);
|
||||
EXPECT_EQ_INT(ret, 1);
|
||||
|
||||
config_delete(cfg);
|
||||
}
|
||||
|
||||
/* Test parse_args with valid port */
|
||||
static void test_parse_args_valid_port() {
|
||||
Config* cfg = config_create();
|
||||
char* argv[] = {"fastsync", "-p", "2222", "/src", "/dst"};
|
||||
int positional_args[2];
|
||||
int positional_count = 0;
|
||||
|
||||
int ret = parse_args(cfg, 5, argv, positional_args, &positional_count);
|
||||
EXPECT_EQ_INT(ret, 0);
|
||||
EXPECT_EQ_INT(cfg->ssh_port, 2222);
|
||||
EXPECT_EQ_INT(positional_count, 2);
|
||||
|
||||
config_delete(cfg);
|
||||
}
|
||||
|
||||
/* Test parse_args rejects port > 65535 */
|
||||
static void test_parse_args_invalid_port() {
|
||||
Config* cfg = config_create();
|
||||
char* argv[] = {"fastsync", "-p", "99999", "/src", "/dst"};
|
||||
int positional_args[2];
|
||||
int positional_count = 0;
|
||||
|
||||
int ret = parse_args(cfg, 5, argv, positional_args, &positional_count);
|
||||
EXPECT_EQ_INT(ret, -1);
|
||||
|
||||
config_delete(cfg);
|
||||
}
|
||||
|
||||
/* Test parse_args rejects non-numeric port */
|
||||
static void test_parse_args_non_numeric_port() {
|
||||
Config* cfg = config_create();
|
||||
char* argv[] = {"fastsync", "-p", "abc", "/src", "/dst"};
|
||||
int positional_args[2];
|
||||
int positional_count = 0;
|
||||
|
||||
int ret = parse_args(cfg, 5, argv, positional_args, &positional_count);
|
||||
EXPECT_EQ_INT(ret, -1);
|
||||
|
||||
config_delete(cfg);
|
||||
}
|
||||
|
||||
/* Test parse_args rejects server port > 65535 */
|
||||
static void test_parse_args_invalid_server_port() {
|
||||
Config* cfg = config_create();
|
||||
char* argv[] = {"fastsync", "--server-port", "70000", "/src", "/dst"};
|
||||
int positional_args[2];
|
||||
int positional_count = 0;
|
||||
|
||||
int ret = parse_args(cfg, 5, argv, positional_args, &positional_count);
|
||||
EXPECT_EQ_INT(ret, -1);
|
||||
|
||||
config_delete(cfg);
|
||||
}
|
||||
|
||||
/* Test parse_args rejects invalid compression level */
|
||||
static void test_parse_args_invalid_compression_level() {
|
||||
Config* cfg = config_create();
|
||||
char* argv[] = {"fastsync", "-c", "25", "/src", "/dst"};
|
||||
int positional_args[2];
|
||||
int positional_count = 0;
|
||||
|
||||
int ret = parse_args(cfg, 5, argv, positional_args, &positional_count);
|
||||
EXPECT_EQ_INT(ret, -1);
|
||||
|
||||
config_delete(cfg);
|
||||
}
|
||||
|
||||
/* Test parse_args accepts valid compression level */
|
||||
static void test_parse_args_valid_compression_level() {
|
||||
Config* cfg = config_create();
|
||||
char* argv[] = {"fastsync", "-c", "10", "/src", "/dst"};
|
||||
int positional_args[2];
|
||||
int positional_count = 0;
|
||||
|
||||
int ret = parse_args(cfg, 5, argv, positional_args, &positional_count);
|
||||
EXPECT_EQ_INT(ret, 0);
|
||||
EXPECT_EQ_INT(cfg->compression_level, 10);
|
||||
|
||||
config_delete(cfg);
|
||||
}
|
||||
|
||||
/* Test parse_args unknown option returns error */
|
||||
static void test_parse_args_unknown_option() {
|
||||
Config* cfg = config_create();
|
||||
char* argv[] = {"fastsync", "--nonexistent", "/src", "/dst"};
|
||||
int positional_args[2];
|
||||
int positional_count = 0;
|
||||
|
||||
int ret = parse_args(cfg, 4, argv, positional_args, &positional_count);
|
||||
EXPECT_EQ_INT(ret, -1);
|
||||
|
||||
config_delete(cfg);
|
||||
}
|
||||
|
||||
/* Test parse_args with --archive flag */
|
||||
static void test_parse_args_archive() {
|
||||
Config* cfg = config_create();
|
||||
char* argv[] = {"fastsync", "--archive", "/src", "/dst"};
|
||||
int positional_args[2];
|
||||
int positional_count = 0;
|
||||
|
||||
int ret = parse_args(cfg, 4, argv, positional_args, &positional_count);
|
||||
EXPECT_EQ_INT(ret, 0);
|
||||
EXPECT_TRUE(cfg->use_compression);
|
||||
EXPECT_TRUE(cfg->use_multithreading);
|
||||
EXPECT_TRUE(cfg->use_metadata);
|
||||
|
||||
config_delete(cfg);
|
||||
}
|
||||
|
||||
void test_client_cli() {
|
||||
test_cli_help();
|
||||
test_cli_archive_flags();
|
||||
test_cli_dry_run();
|
||||
test_cli_delete_flag();
|
||||
test_cli_exclude_patterns();
|
||||
test_parse_args_help();
|
||||
test_parse_args_version();
|
||||
test_parse_args_valid_port();
|
||||
test_parse_args_invalid_port();
|
||||
test_parse_args_non_numeric_port();
|
||||
test_parse_args_invalid_server_port();
|
||||
test_parse_args_invalid_compression_level();
|
||||
test_parse_args_valid_compression_level();
|
||||
test_parse_args_unknown_option();
|
||||
test_parse_args_archive();
|
||||
}
|
||||
|
||||
@@ -327,6 +327,22 @@ static void test_large_file_delta() {
|
||||
free(new_data);
|
||||
}
|
||||
|
||||
static void test_delta_apply_rejects_output_overflow() {
|
||||
uint8_t old_data[8] = {0};
|
||||
uint8_t literal_data[2] = {'x', 'y'};
|
||||
DeltaInstruction instruction = {
|
||||
.type = DELTA_INSTR_LITERAL,
|
||||
.literal = {.data = literal_data, .length = sizeof(literal_data)},
|
||||
};
|
||||
Delta delta = {
|
||||
.new_file_size = 1,
|
||||
.instruction_count = 1,
|
||||
.instructions = &instruction,
|
||||
};
|
||||
|
||||
EXPECT_TRUE(delta_apply(old_data, sizeof(old_data), &delta, 1) == NULL);
|
||||
}
|
||||
|
||||
void test_delta() {
|
||||
test_adler32_basic();
|
||||
test_adler32_different_data();
|
||||
@@ -343,4 +359,5 @@ void test_delta() {
|
||||
test_should_attempt();
|
||||
test_is_worthwhile();
|
||||
test_large_file_delta();
|
||||
test_delta_apply_rejects_output_overflow();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user