diff --git a/RSYNC_COMPAT.md b/RSYNC_COMPAT.md index 19a27f1..1505a23 100644 --- a/RSYNC_COMPAT.md +++ b/RSYNC_COMPAT.md @@ -6,11 +6,11 @@ This document maps rsync's full feature set to FastSync's current implementation | Status | Count | Description | |--------|-------|-------------| -| ✅ Implemented | 34 | Feature works end-to-end | +| ✅ Implemented | 35 | Feature works end-to-end | | 🔀 Alt Arg | 3 | Functionality exists but under different flag/semantics | | ⚠️ Partial | 1 | Flag parsed/stored but behavior incomplete | | ❌ Not Implemented | 98 | Flag not recognized or no behavior | -| **Total** | **136** | | +| **Total** | **137** | | --- diff --git a/src/client/client_cli.c b/src/client/client_cli.c index 0fcd804..112e77f 100644 --- a/src/client/client_cli.c +++ b/src/client/client_cli.c @@ -210,6 +210,7 @@ static int apply_table_option(Config* config, const OptionEntry* entry, const ch /* Parse CLI arguments into config. Returns 0 on success, -1 on error, 1 for help/clean-exit. */ int parse_args(Config* config, int argc, char* argv[], int* positional_args, int* positional_count) { + bool verbose = false; for (int i = 1; i < argc; i++) { const OptionEntry* entry = find_table_option(argv[i]); if (entry) { @@ -350,10 +351,9 @@ int parse_args(Config* config, int argc, char* argv[], int* positional_args, 0) return -1; } else if (opt_is(argv[i], "-v", "--verbose")) { - set_log_level(LOG_LEVEL_DEBUG); + verbose = true; } else if (opt_is(argv[i], "-q", "--quiet")) { config->quiet = true; - set_log_level(LOG_LEVEL_ERROR); } else if (opt_is(argv[i], "-T", NULL) && i + 1 < argc) { if (set_positive_int_option(&config->timeout, argv[++i], "-T") != 0) return -1; @@ -378,6 +378,7 @@ int parse_args(Config* config, int argc, char* argv[], int* positional_args, } } } + set_log_level(config->quiet ? LOG_LEVEL_ERROR : (verbose ? LOG_LEVEL_DEBUG : LOG_LEVEL_WARNING)); return 0; } diff --git a/src/client/usage.c b/src/client/usage.c index 52810cc..48f2754 100644 --- a/src/client/usage.c +++ b/src/client/usage.c @@ -37,7 +37,7 @@ void print_usage(void) { printf(" -s Enable chunk serialization\n"); printf(" -f Enable sendfile (TCP only, not with -c or -s)\n"); printf(" -v, --verbose Enable debug logging\n"); - printf(" -q, --quiet Suppress non-error output\n"); + printf(" -q, --quiet Suppress non-error output\n"); printf(" -M, --preserve Preserve file metadata\n"); printf(" --chunk-size Chunk size in bytes (default: %d)\n", DEFAULT_CHUNK_SIZE); printf(" --source-dir Source directory\n"); diff --git a/tests/integration/test_features.py b/tests/integration/test_features.py index ebd1559..4a6726d 100644 --- a/tests/integration/test_features.py +++ b/tests/integration/test_features.py @@ -52,6 +52,22 @@ class TestDryRun: assert result.returncode != 0 assert result.stderr != "" + @pytest.mark.parametrize("flags", [["-q", "-v"], ["-v", "-q"]]) + def test_quiet_successful_transfer_and_verbose_order(self, shared_server, flags): + clean_dir(DEST_DIR) + result, dur = run_client( + SOURCE_DIR, DEST_DIR, + flags=flags, + port=shared_server.port, + ) + assert result.returncode == 0, f"Exit {result.returncode}: {result.stderr[:100]}" + assert result.stdout == "" + assert result.stderr == "" + received = get_dest_received_dir(DEST_DIR, SOURCE_DIR) + mismatches, missing = verify_transfer(SOURCE_DIR, received) + assert not missing, f"Missing: {missing}" + assert not mismatches, f"Mismatch: {mismatches}" + class TestArchiveMode: def test_archive_mode(self, shared_server): diff --git a/tests/test_client_cli.c b/tests/test_client_cli.c index 5344b8b..f08969a 100644 --- a/tests/test_client_cli.c +++ b/tests/test_client_cli.c @@ -321,16 +321,17 @@ static void test_parse_args_rejects_unimplemented_options() { } } -/* Test both rsync-compatible quiet spellings. */ +/* Test both rsync-compatible quiet spellings and option ordering. */ static void test_parse_args_quiet() { - static const char* const options[] = {"-q", "--quiet"}; + static const char* const options[][2] = { + {"-q", "-v"}, {"-v", "-q"}, {"--quiet", "-v"}, {"-v", "--quiet"}}; for (size_t i = 0; i < sizeof(options) / sizeof(options[0]); i++) { Config* cfg = config_create(); - char* argv[] = {"fastsync", (char*)options[i], "/src", "/dst"}; + char* argv[] = {"fastsync", (char*)options[i][0], (char*)options[i][1], "/src", "/dst"}; int positional_args[2]; int positional_count = 0; - EXPECT_EQ_INT(parse_args(cfg, 4, argv, positional_args, &positional_count), 0); + EXPECT_EQ_INT(parse_args(cfg, 5, argv, positional_args, &positional_count), 0); EXPECT_TRUE(cfg->quiet); config_delete(cfg); }