From 9ba12e5496c49f48f40ad52e816641dfdbb0474e Mon Sep 17 00:00:00 2001 From: Egor Tensin Date: Fri, 7 Jul 2023 22:13:00 +0200 Subject: test: add some basic command-line usage tests --- src/client.c | 24 ++++++++++++------- src/client.h | 2 +- src/client_main.c | 6 +++-- test/py/conftest.py | 51 ++++++++++++++++++++++++++++++---------- test/py/lib/process.py | 10 ++++++++ test/py/test_basic.py | 64 +++++++++++++++++++++++++++++++++++++++++++++----- 6 files changed, 127 insertions(+), 30 deletions(-) diff --git a/src/client.c b/src/client.c index b783afe..5259d52 100644 --- a/src/client.c +++ b/src/client.c @@ -6,6 +6,7 @@ */ #include "client.h" +#include "cmd_line.h" #include "compiler.h" #include "log.h" #include "msg.h" @@ -35,25 +36,30 @@ void client_destroy(struct client *client) free(client); } -int client_main(UNUSED const struct client *client, const struct settings *settings, +int client_main(UNUSED const struct client *client, const struct settings *settings, int argc, const char **argv) { struct msg *response = NULL; int ret = 0; - ret = msg_connect_and_talk_argv(settings->host, settings->port, argv, &response); - if (ret < 0) - return ret; + if (argc < 1) { + exit_with_usage_err("no message to send to the server"); + return -1; + } - if (!msg_is_success(response)) { - log_err("Server failed to process the request\n"); - msg_dump(response); - ret = -1; + ret = msg_connect_and_talk_argv(settings->host, settings->port, argv, &response); + if (ret < 0 || !response || !msg_is_success(response)) { + log_err("Failed to connect to server or it couldn't process the request\n"); + if (response) + msg_dump(response); + if (!ret) + ret = -1; goto free_response; } free_response: - msg_free(response); + if (response) + msg_free(response); return ret; } diff --git a/src/client.h b/src/client.h index 471a7be..ff91170 100644 --- a/src/client.h +++ b/src/client.h @@ -18,6 +18,6 @@ struct client; int client_create(struct client **); void client_destroy(struct client *); -int client_main(const struct client *, const struct settings *, const char **argv); +int client_main(const struct client *, const struct settings *, int argc, const char **argv); #endif diff --git a/src/client_main.c b/src/client_main.c index 0a68a55..ac092d1 100644 --- a/src/client_main.c +++ b/src/client_main.c @@ -24,7 +24,9 @@ static struct settings default_settings(void) const char *get_usage_string(void) { - return "[-h|--help] [-V|--version] [-v|--verbose] [-H|--host HOST] [-p|--port PORT]"; + /* clang-format off */ + return "[-h|--help] [-V|--version] [-v|--verbose] [-H|--host HOST] [-p|--port PORT] ACTION [ARG...]"; + /* clang-format on */ } static int parse_settings(struct settings *settings, int argc, char *argv[]) @@ -84,7 +86,7 @@ int main(int argc, char *argv[]) if (ret < 0) return ret; - ret = client_main(client, &settings, (const char **)argv + optind); + ret = client_main(client, &settings, argc - optind, (const char **)argv + optind); if (ret < 0) goto destroy_client; diff --git a/test/py/conftest.py b/test/py/conftest.py index 9551210..750e4e8 100644 --- a/test/py/conftest.py +++ b/test/py/conftest.py @@ -129,20 +129,49 @@ class CmdLineWorker(CmdLine): @fixture -def server(base_cmd_line, paths, server_port, sqlite_path): +def server_exe(paths): + return CmdLineServer(paths.server_binary) + + +@fixture +def worker_exe(paths): + return CmdLineWorker(paths.worker_binary) + + +@fixture +def client_exe(paths): + return CmdLine(paths.client_binary) + + +@fixture +def server_cmd(base_cmd_line, paths, server_port, sqlite_path): args = ['--port', server_port, '--sqlite', sqlite_path] - cmd_line = CmdLineServer.wrap(base_cmd_line, CmdLine(paths.server_binary, *args)) - with cmd_line.run_async() as server: + return CmdLineServer.wrap(base_cmd_line, CmdLine(paths.server_binary, *args)) + + +@fixture +def worker_cmd(base_cmd_line, paths, server_port): + args = ['--host', '127.0.0.1', '--port', server_port] + return CmdLineWorker.wrap(base_cmd_line, CmdLine(paths.worker_binary, *args)) + + +@fixture +def client_cmd(base_cmd_line, paths, server_port): + args = ['--host', '127.0.0.1', '--port', server_port] + return CmdLine.wrap(base_cmd_line, CmdLine(paths.client_binary, *args)) + + +@fixture +def server(server_cmd): + with server_cmd.run_async() as server: yield server assert server.returncode == 0 @fixture -def workers(base_cmd_line, paths, server_port): - args = ['--host', '127.0.0.1', '--port', server_port] - cmd_line = CmdLineWorker.wrap(base_cmd_line, CmdLine(paths.worker_binary, *args)) - with cmd_line.run_async() as worker1, \ - cmd_line.run_async() as worker2: +def workers(worker_cmd): + with worker_cmd.run_async() as worker1, \ + worker_cmd.run_async() as worker2: yield [worker1, worker2] assert worker1.returncode == 0 assert worker2.returncode == 0 @@ -154,10 +183,8 @@ def server_and_workers(server, workers): @fixture -def client(base_cmd_line, paths, server_port): - args = ['--host', '127.0.0.1', '--port', server_port] - cmd_line = CmdLine.wrap(base_cmd_line, CmdLine(paths.client_binary, *args)) - return cmd_line +def client(client_cmd): + return client_cmd @fixture diff --git a/test/py/lib/process.py b/test/py/lib/process.py index 3436280..d478c32 100644 --- a/test/py/lib/process.py +++ b/test/py/lib/process.py @@ -89,6 +89,9 @@ class CmdLine: def run(self, *argv): return Process.run(*self.argv, *argv) + def try_run(self, *argv): + return Process.try_run(*self.argv, *argv) + @contextmanager def run_async(self, *argv): with Process(self, *argv) as process: @@ -146,6 +149,13 @@ class Process(subprocess.Popen): Process._log_process_end(argv, ec, output) raise + @staticmethod + def try_run(*args, **kwargs): + try: + return 0, Process.run(*args, **kwargs) + except subprocess.CalledProcessError as e: + return e.returncode, e.stdout + def __init__(self, cmd_line, *args): self.cmd_line = cmd_line diff --git a/test/py/test_basic.py b/test/py/test_basic.py index ab062e5..9737552 100644 --- a/test/py/test_basic.py +++ b/test/py/test_basic.py @@ -6,12 +6,64 @@ import re -def test_server_and_workers_run(server_and_workers): +def _test_cmd_line_version_internal(cmd_line, name, version): + for flag in ('--version', '-V'): + output = cmd_line.run(flag).removesuffix('\n') + match = re.match(r'^cimple-(\w+) v(\d+\.\d+\.\d+) \([0-9a-f]{40,}\)$', output) + assert match, f'Invalid {flag} output:\n{output}' + assert match.group(1) == name + assert match.group(2) == version + + +def test_cmd_line_version(server_exe, worker_exe, client_exe, version): + _test_cmd_line_version_internal(server_exe, 'server', version) + _test_cmd_line_version_internal(worker_exe, 'worker', version) + _test_cmd_line_version_internal(client_exe, 'client', version) + + +def _test_cmd_line_help_internal(cmd_line, name): + for flag in ('--help', '-h'): + output = cmd_line.run(flag).removesuffix('\n') + match = re.match(r'^usage: cimple-(\w+) ', output) + assert match, f'Invalid {flag} output:\n{output}' + assert match.group(1) == name + + +def test_cmd_line_help(server_exe, worker_exe, client_exe): + _test_cmd_line_help_internal(server_exe, 'server') + _test_cmd_line_help_internal(worker_exe, 'worker') + _test_cmd_line_help_internal(client_exe, 'client') + + +def _test_cmd_line_invalid_option_internal(cmd_line, name): + for args in (['-x'], ['--invalid', 'value']): + ec, output = cmd_line.try_run(*args) + assert ec != 0, f'Invalid exit code {ec}, output:\n{output}' + + +def test_cmd_line_invalid_option(server_exe, worker_exe, client_exe): + _test_cmd_line_invalid_option_internal(server_exe, 'server') + _test_cmd_line_invalid_option_internal(worker_exe, 'worker') + _test_cmd_line_invalid_option_internal(client_exe, 'client') + + +def test_run_client_no_msg(client): + ec, output = client.try_run() + assert ec != 0, f'Invalid exit code {ec}, output:\n{output}' + prefix = 'usage error: no message to send to the server\n' + assert output.startswith(prefix), f'Invalid output:\n{output}' + + +def test_run_client_invalid_msg(server, client): + ec, output = client.try_run('hello') + assert ec != 0, f'Invalid exit code {ec}, output:\n{output}' + suffix = "Failed to connect to server or it couldn't process the request\n" + assert output.endswith(suffix), f'Invalid output:\n{output}' + + +def test_run_noop_server(server): pass -def test_client_version(client, version): - output = client.run('--version').removesuffix('\n') - match = re.match(r'^cimple-client v(\d+\.\d+\.\d+) \([0-9a-f]{40,}\)$', output) - assert match, f'Invalid --version output: {output}' - assert match.group(1) == version +def test_run_noop_server_and_workers(server_and_workers): + pass -- cgit v1.2.3