diff --git a/CMakeLists.txt b/CMakeLists.txt index 2b4db14ec..3d562f411 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -290,7 +290,6 @@ if(UNIX OR MSVC) add_executable(z_keyexpr_test ${PROJECT_SOURCE_DIR}/tests/z_keyexpr_test.c) add_executable(z_api_null_drop_test ${PROJECT_SOURCE_DIR}/tests/z_api_null_drop_test.c) add_executable(z_api_double_drop_test ${PROJECT_SOURCE_DIR}/tests/z_api_double_drop_test.c) - add_executable(z_modular_test ${PROJECT_SOURCE_DIR}/tests/z_modular_test.c) target_link_libraries(z_data_struct_test ${Libname}) target_link_libraries(z_endpoint_test ${Libname}) @@ -299,7 +298,8 @@ if(UNIX OR MSVC) target_link_libraries(z_keyexpr_test ${Libname}) target_link_libraries(z_api_null_drop_test ${Libname}) target_link_libraries(z_api_double_drop_test ${Libname}) - target_link_libraries(z_modular_test ${Libname}) + + configure_file(${PROJECT_SOURCE_DIR}/tests/modularity.py ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/modularity.py COPYONLY) enable_testing() add_test(z_data_struct_test ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/z_data_struct_test) diff --git a/tests/modularity.py b/tests/modularity.py new file mode 100644 index 000000000..2b3f50551 --- /dev/null +++ b/tests/modularity.py @@ -0,0 +1,266 @@ +import argparse +import subprocess +import sys +import time + +# Specify the directory for the binaries +DIR_EXAMPLES = "build/examples" +exit_status = 0 + +def set_err_status(): + exit_status = 1 + +def pub_and_sub(args): + print("*** Pub & sub test ***") + + # Expected z_pub output & status + if args.pub == 1: + z_pub_expected_status = 0 + z_pub_expected_output = '''Opening session... +Declaring publisher for 'demo/example/zenoh-pico-pub'... +Putting Data ('demo/example/zenoh-pico-pub': 'Pub from Pico!')... +Putting Data ('demo/example/zenoh-pico-pub': 'Pub from Pico!')... +Putting Data ('demo/example/zenoh-pico-pub': 'Pub from Pico!')... +Putting Data ('demo/example/zenoh-pico-pub': 'Pub from Pico!')... +Putting Data ('demo/example/zenoh-pico-pub': 'Pub from Pico!')... +Putting Data ('demo/example/zenoh-pico-pub': 'Pub from Pico!')... +Putting Data ('demo/example/zenoh-pico-pub': 'Pub from Pico!')... +Putting Data ('demo/example/zenoh-pico-pub': 'Pub from Pico!')... +Putting Data ('demo/example/zenoh-pico-pub': 'Pub from Pico!')... +Putting Data ('demo/example/zenoh-pico-pub': 'Pub from Pico!')...''' + else : + z_pub_expected_status = 254 + z_pub_expected_output = "ERROR: Zenoh pico was compiled without Z_FEATURE_PUBLICATION but this example requires it." + + # Expected z_sub output & status + if args.sub == 1: + z_sub_expected_status = 0 + if args.pub == 1: + z_sub_expected_output = '''Opening session... +Declaring Subscriber on 'demo/example/**'... +Enter 'q' to quit... +>> [Subscriber] Received ('demo/example/zenoh-pico-pub': 'Pub from Pico!') +>> [Subscriber] Received ('demo/example/zenoh-pico-pub': 'Pub from Pico!') +>> [Subscriber] Received ('demo/example/zenoh-pico-pub': 'Pub from Pico!') +>> [Subscriber] Received ('demo/example/zenoh-pico-pub': 'Pub from Pico!') +>> [Subscriber] Received ('demo/example/zenoh-pico-pub': 'Pub from Pico!') +>> [Subscriber] Received ('demo/example/zenoh-pico-pub': 'Pub from Pico!') +>> [Subscriber] Received ('demo/example/zenoh-pico-pub': 'Pub from Pico!') +>> [Subscriber] Received ('demo/example/zenoh-pico-pub': 'Pub from Pico!') +>> [Subscriber] Received ('demo/example/zenoh-pico-pub': 'Pub from Pico!') +>> [Subscriber] Received ('demo/example/zenoh-pico-pub': 'Pub from Pico!')''' + else: + z_sub_expected_output = '''Opening session... +Declaring Subscriber on 'demo/example/**'... +Enter 'q' to quit...''' + else : + z_sub_expected_status = 254 + z_sub_expected_output = "ERROR: Zenoh pico was compiled without Z_FEATURE_SUBSCRIPTION but this example requires it." + + print("Start subscriber") + # Start z_sub in the background + z_sub_command = f"./{DIR_EXAMPLES}/z_sub" + z_sub_process = subprocess.Popen(z_sub_command, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) + + # Introduce a delay to ensure z_sub starts + time.sleep(2) + + print("Start publisher") + # Start z_pub + z_pub_command = f"./{DIR_EXAMPLES}/z_pub" + z_pub_process = subprocess.Popen(z_pub_command, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) + + # Wait for z_pub to finish + z_pub_process.wait() + + print("Stop subscriber") + if z_pub_process.poll() is None: + # Send "q" command to z_sub to stop it + z_sub_process.stdin.write("q\n") + z_sub_process.stdin.flush() + + # Wait for z_sub to finish + z_sub_process.wait() + + print("Check publisher status & output") + # Check the exit status of z_pub + z_pub_status = z_pub_process.returncode + if z_pub_status == z_pub_expected_status: + print("z_pub status valid") + else: + print(f"z_pub status invalid, expected: {z_pub_expected_status}, received: {z_pub_status}") + set_err_status() + + # Check output of z_pub + z_pub_output = z_pub_process.stdout.read() + if z_pub_output.__contains__(z_pub_expected_output): + print("z_pub output valid") + else: + print("z_pub output invalid:") + print(f"Expected: \"{z_pub_expected_output}\"") + print(f"Received: \"{z_pub_output}\"") + set_err_status() + + print("Check subscriber status & output") + # Check the exit status of z_sub + z_sub_status = z_sub_process.returncode + if z_sub_status == z_sub_expected_status: + print("z_sub status valid") + else: + print(f"z_sub status invalid, expected: {z_sub_expected_status}, received: {z_sub_status}") + set_err_status() + + # Check output of z_sub + z_sub_output = z_sub_process.stdout.read() + if z_sub_output.__contains__(z_sub_expected_output): + print("z_sub output valid") + else: + print("z_sub output invalid:") + print(f"Expected: \"{z_sub_expected_output}\"") + print(f"Received: \"{z_sub_output}\"") + set_err_status() + +def query_and_queryable(args): + print("*** Query & queryable test ***") + + # Expected z_query output & status + if args.query == 1: + z_query_expected_status = 0 + if args.queryable == 1: + z_query_expected_output = '''Opening session... +Enter any key to pull data or 'q' to quit... +Sending Query 'demo/example/**'... +>> Received ('demo/example/zenoh-pico-queryable': 'Queryable from Pico!') +>> Received query final notification +Sending Query 'demo/example/**'... +>> Received ('demo/example/zenoh-pico-queryable': 'Queryable from Pico!') +>> Received query final notification +Sending Query 'demo/example/**'... +>> Received ('demo/example/zenoh-pico-queryable': 'Queryable from Pico!') +>> Received query final notification''' + else: + z_query_expected_output = '''Opening session... +Enter any key to pull data or 'q' to quit... +Sending Query 'demo/example/**'... +>> Received query final notification +Sending Query 'demo/example/**'...\" +>> Received query final notification +Sending Query 'demo/example/**'... +>> Received query final notification''' + else : + z_query_expected_status = 254 + z_query_expected_output = "ERROR: Zenoh pico was compiled without Z_FEATURE_QUERY but this example requires it." + + # Expected z_queryable output & status + if args.queryable == 1: + z_queryable_expected_status = 0 + if args.query == 1: + z_queryable_expected_output = '''Opening session... +Creating Queryable on 'demo/example/zenoh-pico-queryable'... +Enter 'q' to quit... + >> [Queryable handler] Received Query 'demo/example/**?' + >> [Queryable handler] Received Query 'demo/example/**?' + >> [Queryable handler] Received Query 'demo/example/**?''' + else: + z_queryable_expected_output = '''Opening session... +Creating Queryable on 'demo/example/zenoh-pico-queryable'... +Enter 'q' to quit...''' + else : + z_queryable_expected_status = 254 + z_queryable_expected_output = "ERROR: Zenoh pico was compiled without Z_FEATURE_QUERYABLE but this example requires it." + + print("Start queryable") + # Start z_queryable in the background + z_queryable_command = f"./{DIR_EXAMPLES}/z_queryable" + z_queryable_process = subprocess.Popen(z_queryable_command, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) + + # Introduce a delay to ensure z_queryable starts + time.sleep(2) + + print("Start query") + # Start z_query + z_query_command = f"./{DIR_EXAMPLES}/z_get" + z_query_process = subprocess.Popen(z_query_command, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) + + # Introduce a delay to ensure z_query starts + time.sleep(2) + + print("Send requests") + if z_query_process.poll() is None: + z_query_process.stdin.write("\n") + z_query_process.stdin.flush() + time.sleep(1) + z_query_process.stdin.write("\n") + z_query_process.stdin.flush() + time.sleep(1) + z_query_process.stdin.write("\n") + z_query_process.stdin.flush() + time.sleep(1) + + print("Stop query") + if z_query_process.poll() is None: + z_query_process.stdin.write("q\n") + z_query_process.stdin.flush() + + # Wait for z_query to finish + z_query_process.wait() + + print("Stop queryable") + if z_queryable_process.poll() is None: + # Send "q" command to z_sub to stop it + z_queryable_process.stdin.write("q\n") + z_queryable_process.stdin.flush() + + # Wait for z_queryable to finish + z_queryable_process.wait() + + print("Check query status & output") + # Check the exit status of z_query + z_query_status = z_query_process.returncode + if z_query_status == z_query_expected_status: + print("z_query status valid") + else: + print(f"z_query status invalid, expected: {z_query_expected_status}, received: {z_query_status}") + set_err_status() + + # Check output of z_query + z_query_output = z_query_process.stdout.read() + if z_query_output.__contains__(z_query_expected_output): + print("z_query output valid") + else: + print("z_query output invalid:") + print(f"Expected: \"{z_query_expected_output}\"") + print(f"Received: \"{z_query_output}\"") + set_err_status() + + print("Check queryable status & output") + # Check the exit status of z_queryable + z_queryable_status = z_queryable_process.returncode + if z_queryable_status == z_queryable_expected_status: + print("z_queryable status valid") + else: + print(f"z_queryable status invalid, expected: {z_queryable_expected_status}, received: {z_queryable_status}") + set_err_status() + + # Check output of z_queryable + z_queryable_output = z_queryable_process.stdout.read() + if z_queryable_output.__contains__(z_queryable_expected_output): + print("z_queryable output valid") + else: + print("z_queryable output invalid:") + print(f"Expected: \"{z_queryable_expected_output}\"") + print(f"Received: \"{z_queryable_output}\"") + set_err_status() + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description="This script runs zenoh-pico examples and checks them according to the given configuration") + parser.add_argument("--pub", type=int, choices=[0, 1], help="Z_FEATURE_PUBLICATION (0 or 1)") + parser.add_argument("--sub", type=int, choices=[0, 1], help="Z_FEATURE_SUBSCRIPTION (0 or 1)") + parser.add_argument("--queryable", type=int, choices=[0, 1], help="Z_FEATURE_QUERYABLE (0 or 1)") + parser.add_argument("--query", type=int, choices=[0, 1], help="Z_FEATURE_QUERY (0 or 1)") + + args = parser.parse_args() + print(f"Args value, pub:{args.pub}, sub:{args.sub}, queryable:{args.queryable}, query:{args.query}") + pub_and_sub(args) + query_and_queryable(args) + sys.exit(exit_status) \ No newline at end of file diff --git a/tests/z_modular_test.c b/tests/z_modular_test.c deleted file mode 100644 index e5ea32b26..000000000 --- a/tests/z_modular_test.c +++ /dev/null @@ -1,285 +0,0 @@ -// -// Copyright (c) 2022 ZettaScale Technology -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// -// Contributors: -// ZettaScale Zenoh Team, - -#include -#include -#include -#include - -#include "zenoh-pico.h" - -#undef NDEBUG -#include - -static const char *ARG_LIST[] = {"Z_FEATURE_PUBLICATION", "Z_FEATURE_SUBSCRIPTION", "Z_FEATURE_QUERYABLE", - "Z_FEATURE_QUERY"}; -#define ARG_NB (sizeof(ARG_LIST) / sizeof(ARG_LIST[0])) - -int test_publication(void) { -#if Z_FEATURE_PUBLICATION == 1 - const char *keyexpr = "demo/example/zenoh-pico-pub"; - const char *value = "Pub from Pico!"; - const char *mode = "client"; - - // Set up config - z_owned_config_t config = z_config_default(); - zp_config_insert(z_loan(config), Z_CONFIG_MODE_KEY, z_string_make(mode)); - // Open session - printf("Opening session...\n"); - z_owned_session_t s = z_open(z_move(config)); - if (!z_check(s)) { - printf("Unable to open session!\n"); - return -1; - } - // Start read and lease tasks for zenoh-pico - if (zp_start_read_task(z_loan(s), NULL) < 0 || zp_start_lease_task(z_loan(s), NULL) < 0) { - printf("Unable to start read and lease tasks"); - return -1; - } - // Declare publisher - printf("Declaring publisher for '%s'...\n", keyexpr); - z_owned_publisher_t pub = z_declare_publisher(z_loan(s), z_keyexpr(keyexpr), NULL); - if (!z_check(pub)) { - printf("Unable to declare publisher for key expression!\n"); - return -1; - } - // Put data - printf("Putting Data ('%s': '%s')...\n", keyexpr, value); - z_publisher_put_options_t options = z_publisher_put_options_default(); - options.encoding = z_encoding(Z_ENCODING_PREFIX_TEXT_PLAIN, NULL); - z_publisher_put(z_loan(pub), (const uint8_t *)value, strlen(value), &options); - - // Clean-up - z_undeclare_publisher(z_move(pub)); - zp_stop_read_task(z_loan(s)); - zp_stop_lease_task(z_loan(s)); - z_close(z_move(s)); - return 1; -#else - return 0; -#endif -} - -#if Z_FEATURE_SUBSCRIPTION == 1 -static void subscription_data_handler(const z_sample_t *sample, void *ctx) { - (void)(ctx); - z_owned_str_t keystr = z_keyexpr_to_string(sample->keyexpr); - printf(">> [Subscriber] Received ('%s': '%.*s')\n", z_loan(keystr), (int)sample->payload.len, - sample->payload.start); - z_drop(z_move(keystr)); -} -#endif - -int test_subscription(void) { -#if Z_FEATURE_SUBSCRIPTION == 1 - const char *keyexpr = "demo/example/**"; - const char *mode = "client"; - - // Set up config - z_owned_config_t config = z_config_default(); - zp_config_insert(z_loan(config), Z_CONFIG_MODE_KEY, z_string_make(mode)); - // Open session - printf("Opening session...\n"); - z_owned_session_t s = z_open(z_move(config)); - if (!z_check(s)) { - printf("Unable to open session!\n"); - return -1; - } - // Start read and lease tasks for zenoh-pico - if (zp_start_read_task(z_loan(s), NULL) < 0 || zp_start_lease_task(z_loan(s), NULL) < 0) { - printf("Unable to start read and lease tasks"); - return -1; - } - // Declare subscriber - z_owned_closure_sample_t callback = z_closure(subscription_data_handler); - printf("Declaring Subscriber on '%s'...\n", keyexpr); - z_owned_subscriber_t sub = z_declare_subscriber(z_loan(s), z_keyexpr(keyexpr), z_move(callback), NULL); - if (!z_check(sub)) { - printf("Unable to declare subscriber.\n"); - return -1; - } - // Clean-up - z_undeclare_subscriber(z_move(sub)); - zp_stop_read_task(z_loan(s)); - zp_stop_lease_task(z_loan(s)); - z_close(z_move(s)); - return 1; -#else - return 0; -#endif -} - -#if Z_FEATURE_QUERYABLE == 1 -static const char *queryable_keyexpr = "demo/example/zenoh-pico-queryable"; -static const char *queryable_value = "Queryable from Pico!"; - -void query_handler(const z_query_t *query, void *ctx) { - (void)(ctx); - z_owned_str_t keystr = z_keyexpr_to_string(z_query_keyexpr(query)); - z_bytes_t pred = z_query_parameters(query); - z_value_t payload_value = z_query_value(query); - printf(" >> [Queryable handler] Received Query '%s?%.*s'\n", z_loan(keystr), (int)pred.len, pred.start); - if (payload_value.payload.len > 0) { - printf(" with value '%.*s'\n", (int)payload_value.payload.len, payload_value.payload.start); - } - z_query_reply_options_t options = z_query_reply_options_default(); - options.encoding = z_encoding(Z_ENCODING_PREFIX_TEXT_PLAIN, NULL); - z_query_reply(query, z_keyexpr(queryable_keyexpr), (const unsigned char *)queryable_value, strlen(queryable_value), &options); - z_drop(z_move(keystr)); -} -#endif - -int test_queryable(void) { -#if Z_FEATURE_QUERYABLE == 1 - const char *mode = "client"; - - z_keyexpr_t ke = z_keyexpr(queryable_keyexpr); - if (!z_check(ke)) { - printf("%s is not a valid key expression", queryable_keyexpr); - return -1; - } - // Set up config - z_owned_config_t config = z_config_default(); - zp_config_insert(z_loan(config), Z_CONFIG_MODE_KEY, z_string_make(mode)); - // Open session - printf("Opening session...\n"); - z_owned_session_t s = z_open(z_move(config)); - if (!z_check(s)) { - printf("Unable to open session!\n"); - return -1; - } - // Start read and lease tasks for zenoh-pico - if (zp_start_read_task(z_loan(s), NULL) < 0 || zp_start_lease_task(z_loan(s), NULL) < 0) { - printf("Unable to start read and lease tasks"); - return -1; - } - // Declare queryable - printf("Creating Queryable on '%s'...\n", queryable_keyexpr); - z_owned_closure_query_t callback = z_closure(query_handler); - z_owned_queryable_t qable = z_declare_queryable(z_loan(s), ke, z_move(callback), NULL); - if (!z_check(qable)) { - printf("Unable to create queryable.\n"); - return -1; - } - // Clean-up - z_undeclare_queryable(z_move(qable)); - zp_stop_read_task(z_loan(s)); - zp_stop_lease_task(z_loan(s)); - z_close(z_move(s)); - - return 1; -#else - return 0; -#endif -} - -#if Z_FEATURE_QUERY == 1 -void reply_dropper(void *ctx) { - (void)(ctx); - printf(">> Received query final notification\n"); -} - -void reply_handler(z_owned_reply_t *reply, void *ctx) { - (void)(ctx); - if (z_reply_is_ok(reply)) { - z_sample_t sample = z_reply_ok(reply); - z_owned_str_t keystr = z_keyexpr_to_string(sample.keyexpr); - printf(">> Received ('%s': '%.*s')\n", z_loan(keystr), (int)sample.payload.len, sample.payload.start); - z_drop(z_move(keystr)); - } else { - printf(">> Received an error\n"); - } -} -#endif - -int test_query(void) { -#if Z_FEATURE_QUERY == 1 - const char *keyexpr = "demo/example/**"; - const char *mode = "client"; - - z_keyexpr_t ke = z_keyexpr(keyexpr); - if (!z_check(ke)) { - printf("%s is not a valid key expression", keyexpr); - return -1; - } - // Set up config - z_owned_config_t config = z_config_default(); - zp_config_insert(z_loan(config), Z_CONFIG_MODE_KEY, z_string_make(mode)); - // Open session - printf("Opening session...\n"); - z_owned_session_t s = z_open(z_move(config)); - if (!z_check(s)) { - printf("Unable to open session!\n"); - return -1; - } - // Start read and lease tasks for zenoh-pico - if (zp_start_read_task(z_loan(s), NULL) < 0 || zp_start_lease_task(z_loan(s), NULL) < 0) { - printf("Unable to start read and lease tasks"); - return -1; - } - // Send query - printf("Sending Query '%s'...\n", keyexpr); - z_get_options_t opts = z_get_options_default(); - z_owned_closure_reply_t callback = z_closure(reply_handler, reply_dropper); - if (z_get(z_loan(s), ke, "", z_move(callback), &opts) < 0) { - printf("Unable to send query.\n"); - return -1; - } - // Clean-up - zp_stop_read_task(z_loan(s)); - zp_stop_lease_task(z_loan(s)); - z_close(z_move(s)); - - return 1; -#else - return 0; -#endif -} - -// Send feature config as int list, and compare with compiled feature -int main(int argc, char **argv) { - if (argc < (int)(ARG_NB + 1)) { - printf("To start this test you must give the state of the feature config as argument\n"); - printf("Arg order: "); - for (size_t i = 0; i < ARG_NB; i++) { - printf("%s ", ARG_LIST[i]); - } - printf("\n"); - return -1; - } - if (test_publication() != atoi(argv[1])) { - printf("Problem during publication testing\n"); - return -1; - } else { - printf("Publication status ok\n"); - } - if (test_subscription() != atoi(argv[2])) { - printf("Problem during subscription testing\n"); - return -1; - } else { - printf("Subscription status ok\n"); - } - if (test_queryable() != atoi(argv[3])) { - printf("Problem during queryable testing\n"); - return -1; - } else { - printf("Queryable status ok\n"); - } - if (test_query() != atoi(argv[4])) { - printf("Problem during query testing\n"); - return -1; - } else { - printf("Query status ok\n"); - } - return 0; -}