test(p6-protocol): unit + integration coverage for --protocol
This commit is contained in:
@@ -2,10 +2,11 @@
|
|||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
|
import shutil
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
sys.path.insert(0, os.path.dirname(__file__))
|
sys.path.insert(0, os.path.dirname(__file__))
|
||||||
from common import BUILD_DIR, CLIENT_CMD, SERVER_CMD
|
from common import BUILD_DIR, CLIENT_CMD, SERVER_CMD, TEST_DATA_DIR, run_client, verify_transfer
|
||||||
|
|
||||||
|
|
||||||
class TestHelp:
|
class TestHelp:
|
||||||
@@ -79,3 +80,58 @@ class TestServerPort:
|
|||||||
finally:
|
finally:
|
||||||
proc.terminate()
|
proc.terminate()
|
||||||
proc.wait(timeout=5)
|
proc.wait(timeout=5)
|
||||||
|
|
||||||
|
|
||||||
|
def _seed_protocol_source(source):
|
||||||
|
os.makedirs(source, exist_ok=True)
|
||||||
|
with open(os.path.join(source, "p.txt"), "w") as fh:
|
||||||
|
fh.write("protocol test\n")
|
||||||
|
os.makedirs(os.path.join(source, "nested"), exist_ok=True)
|
||||||
|
with open(os.path.join(source, "nested", "deep.txt"), "w") as fh:
|
||||||
|
fh.write("deep file\n")
|
||||||
|
|
||||||
|
|
||||||
|
class TestProtocol:
|
||||||
|
@pytest.mark.ci
|
||||||
|
def test_protocol_current_version_accepted(self, shared_server):
|
||||||
|
"""--protocol=2.16.0 (the current PROTOCOL_VERSION) is accepted and the
|
||||||
|
transfer completes normally."""
|
||||||
|
source = os.path.join(TEST_DATA_DIR, "proto_ok_src")
|
||||||
|
dest = os.path.join(TEST_DATA_DIR, "proto_ok_dst")
|
||||||
|
shutil.rmtree(dest, ignore_errors=True)
|
||||||
|
os.makedirs(dest)
|
||||||
|
_seed_protocol_source(source)
|
||||||
|
result, _ = run_client(source, dest, flags=["--protocol=2.16.0"],
|
||||||
|
port=shared_server.port)
|
||||||
|
assert result.returncode == 0, \
|
||||||
|
f"--protocol current run failed: {(result.stderr or result.stdout)[:400]}"
|
||||||
|
received = os.path.join(dest, os.path.abspath(source).lstrip(os.sep))
|
||||||
|
mismatches, missing = verify_transfer(source, received)
|
||||||
|
assert not mismatches and not missing, \
|
||||||
|
f"transfer mismatch: missing={missing} mismatches={mismatches}"
|
||||||
|
|
||||||
|
@pytest.mark.ci
|
||||||
|
def test_protocol_rejects_other_versions(self, shared_server):
|
||||||
|
"""Other versions are rejected up front, before connecting."""
|
||||||
|
source = os.path.join(TEST_DATA_DIR, "proto_reject_src")
|
||||||
|
dest = os.path.join(TEST_DATA_DIR, "proto_reject_dst")
|
||||||
|
shutil.rmtree(dest, ignore_errors=True)
|
||||||
|
os.makedirs(dest)
|
||||||
|
_seed_protocol_source(source)
|
||||||
|
for bad in ("2.15.0", "2.17.0", "216", "31"):
|
||||||
|
result, _ = run_client(source, dest, flags=[f"--protocol={bad}"],
|
||||||
|
port=shared_server.port)
|
||||||
|
assert result.returncode != 0, f"--protocol={bad} should be rejected"
|
||||||
|
|
||||||
|
@pytest.mark.ci
|
||||||
|
def test_protocol_rejects_garbage(self, shared_server):
|
||||||
|
"""Garbage/empty --protocol values are rejected up front."""
|
||||||
|
source = os.path.join(TEST_DATA_DIR, "proto_garbage_src")
|
||||||
|
dest = os.path.join(TEST_DATA_DIR, "proto_garbage_dst")
|
||||||
|
shutil.rmtree(dest, ignore_errors=True)
|
||||||
|
os.makedirs(dest)
|
||||||
|
_seed_protocol_source(source)
|
||||||
|
for bad in ("abc", ""):
|
||||||
|
result, _ = run_client(source, dest, flags=[f"--protocol={bad}"],
|
||||||
|
port=shared_server.port)
|
||||||
|
assert result.returncode != 0, f"--protocol={bad} should be rejected"
|
||||||
|
|||||||
@@ -210,6 +210,66 @@ static void test_parse_args_version() {
|
|||||||
config_delete(cfg);
|
config_delete(cfg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* --protocol=NUM forces the wire protocol version: the current PROTOCOL_VERSION
|
||||||
|
* is accepted (stored into config->version, which the config frame transmits),
|
||||||
|
* and any other value is rejected. Client-only: no server-side flag exists. */
|
||||||
|
static void test_parse_args_protocol_accept_current() {
|
||||||
|
Config* cfg = valid_client_config();
|
||||||
|
EXPECT_NOT_NULL(cfg);
|
||||||
|
char* argv_equals[] = {"fastsync", "--source-dir", "/src",
|
||||||
|
"--dest-dir", "/dst", "--protocol=2.16.0"};
|
||||||
|
int positional_args[2];
|
||||||
|
int positional_count = 0;
|
||||||
|
EXPECT_EQ_INT(parse_args(cfg, 6, argv_equals, positional_args, &positional_count), 0);
|
||||||
|
EXPECT_EQ_STR(cfg->version, PROTOCOL_VERSION);
|
||||||
|
config_delete(cfg);
|
||||||
|
|
||||||
|
cfg = valid_client_config();
|
||||||
|
EXPECT_NOT_NULL(cfg);
|
||||||
|
char* argv_space[] = {"fastsync", "--source-dir", "/src", "--dest-dir",
|
||||||
|
"/dst", "--protocol", "2.16.0"};
|
||||||
|
positional_count = 0;
|
||||||
|
EXPECT_EQ_INT(parse_args(cfg, 7, argv_space, positional_args, &positional_count), 0);
|
||||||
|
EXPECT_EQ_STR(cfg->version, PROTOCOL_VERSION);
|
||||||
|
config_delete(cfg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Any --protocol value other than the current PROTOCOL_VERSION must end in
|
||||||
|
* failure (parse_args simply stores it; validate_config rejects it up front). */
|
||||||
|
static void test_parse_args_protocol_rejects_other_versions() {
|
||||||
|
static const char* const bad_versions[] = {"2.16", "2.15.0", "2.17.0", "216", "31", "abc", ""};
|
||||||
|
for (size_t i = 0; i < sizeof(bad_versions) / sizeof(bad_versions[0]); i++) {
|
||||||
|
Config* cfg = valid_client_config();
|
||||||
|
EXPECT_NOT_NULL(cfg);
|
||||||
|
char arg[64];
|
||||||
|
snprintf(arg, sizeof(arg), "--protocol=%s", bad_versions[i]);
|
||||||
|
char* argv[] = {"fastsync", "--source-dir", "/src", "--dest-dir", "/dst", arg};
|
||||||
|
int positional_args[2];
|
||||||
|
int positional_count = 0;
|
||||||
|
EXPECT_EQ_INT(parse_args(cfg, 6, argv, positional_args, &positional_count), 0);
|
||||||
|
EXPECT_TRUE(strcmp(cfg->version, PROTOCOL_VERSION) != 0);
|
||||||
|
EXPECT_FALSE(validate_config(cfg));
|
||||||
|
config_delete(cfg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* validate_config accepts the current PROTOCOL_VERSION (the default) and rejects
|
||||||
|
* a version that does not equal it -- the honest post-parse enforcement. */
|
||||||
|
static void test_validate_config_protocol_version() {
|
||||||
|
Config* cfg = valid_client_config();
|
||||||
|
EXPECT_NOT_NULL(cfg);
|
||||||
|
EXPECT_EQ_STR(cfg->version, PROTOCOL_VERSION);
|
||||||
|
EXPECT_TRUE(validate_config(cfg));
|
||||||
|
config_delete(cfg);
|
||||||
|
|
||||||
|
cfg = valid_client_config();
|
||||||
|
EXPECT_NOT_NULL(cfg);
|
||||||
|
free(cfg->version);
|
||||||
|
cfg->version = str_dup("2.15.0");
|
||||||
|
EXPECT_FALSE(validate_config(cfg));
|
||||||
|
config_delete(cfg);
|
||||||
|
}
|
||||||
|
|
||||||
/* --xattrs/-X and --acls/-A preserve per-file xattrs and both imply metadata
|
/* --xattrs/-X and --acls/-A preserve per-file xattrs and both imply metadata
|
||||||
* transmission (the xattr block rides the metadata/per-file frame); each is
|
* transmission (the xattr block rides the metadata/per-file frame); each is
|
||||||
* individually negatable and the derived use_xattrs follows the flags. */
|
* individually negatable and the derived use_xattrs follows the flags. */
|
||||||
@@ -2850,6 +2910,9 @@ void test_client_cli() {
|
|||||||
test_cli_exclude_patterns();
|
test_cli_exclude_patterns();
|
||||||
test_parse_args_help();
|
test_parse_args_help();
|
||||||
test_parse_args_version();
|
test_parse_args_version();
|
||||||
|
test_parse_args_protocol_accept_current();
|
||||||
|
test_parse_args_protocol_rejects_other_versions();
|
||||||
|
test_validate_config_protocol_version();
|
||||||
test_parse_args_valid_port();
|
test_parse_args_valid_port();
|
||||||
test_parse_args_size_only();
|
test_parse_args_size_only();
|
||||||
test_parse_args_ignore_existing();
|
test_parse_args_ignore_existing();
|
||||||
|
|||||||
Reference in New Issue
Block a user