From d8f1f7b761a41fc027c53dcd85c2b07dd73c6d1b Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Tue, 17 May 2022 11:21:20 +0200 Subject: [PATCH] integrate purse expiration into test, bugfixes --- debian/taler-exchange.install | 4 + src/exchange/taler-exchange-expire.c | 41 ++++- src/exchange/taler-exchange-httpd.h | 4 +- .../taler-exchange-httpd_purses_get.c | 31 ++++ src/exchangedb/common-0001.sql | 8 + src/exchangedb/exchange-0001-part.sql | 29 ++-- src/include/taler_testing_lib.h | 24 +++ src/lib/exchange_api_purses_get.c | 5 + src/testing/Makefile.am | 2 + src/testing/test_exchange_api-cs.conf | 8 + src/testing/test_exchange_api-rsa.conf | 9 + src/testing/test_exchange_p2p.c | 58 +++++++ src/testing/testing_api_cmd_exec_expire.c | 162 ++++++++++++++++++ src/testing/testing_api_cmd_exec_router.c | 161 +++++++++++++++++ src/testing/testing_api_cmd_purse_get.c | 1 + 15 files changed, 521 insertions(+), 26 deletions(-) create mode 100644 src/testing/testing_api_cmd_exec_expire.c create mode 100644 src/testing/testing_api_cmd_exec_router.c diff --git a/debian/taler-exchange.install b/debian/taler-exchange.install index ca77c3d73..0af0788ae 100644 --- a/debian/taler-exchange.install +++ b/debian/taler-exchange.install @@ -1,7 +1,9 @@ usr/bin/taler-exchange-aggregator usr/bin/taler-exchange-closer usr/bin/taler-exchange-dbinit +usr/bin/taler-exchange-expire usr/bin/taler-exchange-httpd +usr/bin/taler-exchange-router usr/bin/taler-exchange-secmod-cs usr/bin/taler-exchange-secmod-eddsa usr/bin/taler-exchange-secmod-rsa @@ -11,7 +13,9 @@ usr/bin/taler-exchange-wire-gateway-client usr/share/man/man1/taler-exchange-aggregator* usr/share/man/man1/taler-exchange-closer* usr/share/man/man1/taler-exchange-dbinit* +usr/share/man/man1/taler-exchange-expire* usr/share/man/man1/taler-exchange-httpd* +usr/share/man/man1/taler-exchange-router* usr/share/man/man1/taler-exchange-secmod-eddsa* usr/share/man/man1/taler-exchange-secmod-rsa* usr/share/man/man1/taler-exchange-secmod-cs* diff --git a/src/exchange/taler-exchange-expire.c b/src/exchange/taler-exchange-expire.c index c7691930b..b5df64a77 100644 --- a/src/exchange/taler-exchange-expire.c +++ b/src/exchange/taler-exchange-expire.c @@ -95,6 +95,12 @@ static int global_ret; */ static int test_mode; +/** + * If this is a first-time run, we immediately + * try to catch up with the present. + */ +static bool jump_mode; + /** * Select a shard to work on. @@ -188,6 +194,7 @@ static void release_shard (struct Shard *s) { enum GNUNET_DB_QueryStatus qs; + unsigned long long wc = (unsigned long long) s->work_counter; qs = db_plugin->complete_shard ( db_plugin->cls, @@ -209,10 +216,14 @@ release_shard (struct Shard *s) case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Purse expiration shard completed with %llu purses\n", - (unsigned long long) s->work_counter); + wc); /* normal case */ break; } + if ( (0 == wc) && + (test_mode) && + (! jump_mode) ) + GNUNET_SCHEDULER_shutdown (); } @@ -262,13 +273,16 @@ run_expire (void *cls) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to obtain database connection!\n"); + abort_shard (s); global_ret = EXIT_FAILURE; GNUNET_SCHEDULER_shutdown (); return; } - if (db_plugin->start (db_plugin->cls, + if (GNUNET_OK != + db_plugin->start (db_plugin->cls, "expire-purse")) { + GNUNET_break (0); global_ret = EXIT_FAILURE; db_plugin->rollback (db_plugin->cls); abort_shard (s); @@ -290,6 +304,7 @@ run_expire (void *cls) case GNUNET_DB_STATUS_SOFT_ERROR: db_plugin->rollback (db_plugin->cls); abort_shard (s); + GNUNET_assert (NULL == task); task = GNUNET_SCHEDULER_add_now (&run_shard, NULL); return; @@ -303,6 +318,7 @@ run_expire (void *cls) { release_shard (s); } + GNUNET_assert (NULL == task); task = GNUNET_SCHEDULER_add_now (&run_shard, NULL); return; @@ -310,6 +326,7 @@ run_expire (void *cls) /* commit, and go again immediately */ s->work_counter++; (void) commit_or_warn (); + GNUNET_assert (NULL == task); task = GNUNET_SCHEDULER_add_now (&run_expire, s); } @@ -343,9 +360,15 @@ run_shard (void *cls) qs = db_plugin->begin_shard (db_plugin->cls, "expire", shard_size, - shard_size.rel_value_us, + jump_mode + ? GNUNET_TIME_absolute_subtract ( + GNUNET_TIME_absolute_get (), + shard_size). + abs_value_us + : shard_size.rel_value_us, &s->shard_start.abs_value_us, &s->shard_end.abs_value_us); + jump_mode = false; if (0 >= qs) { if (GNUNET_DB_STATUS_SOFT_ERROR == qs) @@ -355,6 +378,7 @@ run_shard (void *cls) GNUNET_free (s); delay = GNUNET_TIME_randomized_backoff (delay, GNUNET_TIME_UNIT_SECONDS); + GNUNET_assert (NULL == task); task = GNUNET_SCHEDULER_add_delayed (delay, &run_shard, NULL); @@ -368,9 +392,10 @@ run_shard (void *cls) GNUNET_SCHEDULER_shutdown (); return; } - if (GNUNET_TIME_absolute_is_future (s->shard_end)) + if (GNUNET_TIME_absolute_is_future (s->shard_start)) { - task = GNUNET_SCHEDULER_add_at (s->shard_end, + GNUNET_assert (NULL == task); + task = GNUNET_SCHEDULER_add_at (s->shard_start, &run_shard, NULL); abort_shard (s); @@ -379,12 +404,12 @@ run_shard (void *cls) /* If this is a first-time run, we immediately try to catch up with the present */ if (GNUNET_TIME_absolute_is_zero (s->shard_start)) - s->shard_end = GNUNET_TIME_absolute_get (); - + jump_mode = true; GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Starting shard [%llu:%llu]!\n", + "Starting shard [%llu:%llu)!\n", (unsigned long long) s->shard_start.abs_value_us, (unsigned long long) s->shard_end.abs_value_us); + GNUNET_assert (NULL == task); task = GNUNET_SCHEDULER_add_now (&run_expire, s); } diff --git a/src/exchange/taler-exchange-httpd.h b/src/exchange/taler-exchange-httpd.h index bb387696d..278a05be9 100644 --- a/src/exchange/taler-exchange-httpd.h +++ b/src/exchange/taler-exchange-httpd.h @@ -309,8 +309,8 @@ struct TEH_RequestHandler * @return MHD result code */ MHD_RESULT - (*get)(struct TEH_RequestContext *rc, - const char *const args[]); + (*get)(struct TEH_RequestContext *rc, + const char *const args[]); /** diff --git a/src/exchange/taler-exchange-httpd_purses_get.c b/src/exchange/taler-exchange-httpd_purses_get.c index 656a34dbc..12a244897 100644 --- a/src/exchange/taler-exchange-httpd_purses_get.c +++ b/src/exchange/taler-exchange-httpd_purses_get.c @@ -333,6 +333,37 @@ TEH_handler_purses_get (struct TEH_RequestContext *rc, case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: break; /* handled below */ } + if (GNUNET_TIME_absolute_cmp (gc->timeout, + >, + gc->purse_expiration.abs_time)) + { + /* Timeout too high, need to replace event handler */ + struct TALER_PurseEventP rep = { + .header.size = htons (sizeof (rep)), + .header.type = htons ( + gc->wait_for_merge + ? TALER_DBEVENT_EXCHANGE_PURSE_MERGED + : TALER_DBEVENT_EXCHANGE_PURSE_DEPOSITED), + .purse_pub = gc->purse_pub + }; + struct GNUNET_DB_EventHandler *eh2; + + gc->timeout = gc->purse_expiration.abs_time; + eh2 = TEH_plugin->event_listen ( + TEH_plugin->cls, + GNUNET_TIME_absolute_get_remaining (gc->timeout), + &rep.header, + &db_event_cb, + rc); + if (NULL == eh2) + { + GNUNET_break (0); + gc->timeout = GNUNET_TIME_UNIT_ZERO_ABS; + } + TEH_plugin->event_listen_cancel (TEH_plugin->cls, + gc->eh); + gc->eh = eh2; + } } if (GNUNET_TIME_absolute_is_past (gc->purse_expiration.abs_time)) { diff --git a/src/exchangedb/common-0001.sql b/src/exchangedb/common-0001.sql index 05394246c..cb64f446e 100644 --- a/src/exchangedb/common-0001.sql +++ b/src/exchangedb/common-0001.sql @@ -1193,6 +1193,14 @@ BEGIN '(merge_pub);' ); + -- FIXME: drop index on master (crosses shards)? + -- Or use materialized index? (needed?) + EXECUTE FORMAT ( + 'CREATE INDEX IF NOT EXISTS ' || table_name || '_purse_expiration ' + 'ON ' || table_name || ' ' + '(purse_expiration);' + ); + END $$; diff --git a/src/exchangedb/exchange-0001-part.sql b/src/exchangedb/exchange-0001-part.sql index 97c26cb05..1b1d2a38e 100644 --- a/src/exchangedb/exchange-0001-part.sql +++ b/src/exchangedb/exchange-0001-part.sql @@ -2505,8 +2505,7 @@ UPDATE known_coins THEN 1 ELSE 0 END - WHERE coin_pub=in_coin_pub - LIMIT 1; -- just to be extra safe + WHERE coin_pub=in_coin_pub; out_conflict=FALSE; @@ -3363,7 +3362,6 @@ END $$; CREATE OR REPLACE FUNCTION exchange_do_expire_purse( - IN in_partner_id INT8, IN in_start_time INT8, IN in_end_time INT8, OUT out_found BOOLEAN) @@ -3375,25 +3373,26 @@ DECLARE my_deposit record; BEGIN -UPDATE purse_requests - SET refunded=TRUE, - finished=TRUE +SELECT purse_pub + INTO my_purse_pub + FROM purse_requests WHERE (purse_expiration >= in_start_time) AND (purse_expiration < in_end_time) AND (NOT finished) AND (NOT refunded) - RETURNING purse_pub - ,in_reserve_quota - ,flags - INTO my_purse_pub - ,my_rq - ,my_flags; + ORDER BY purse_expiration ASC + LIMIT 1; out_found = FOUND; IF NOT FOUND THEN RETURN; END IF; +UPDATE purse_requests + SET refunded=TRUE, + finished=TRUE + WHERE purse_pub=my_purse_pub; + -- restore balance to each coin deposited into the purse FOR my_deposit IN SELECT coin_pub @@ -3402,7 +3401,7 @@ FOR my_deposit IN FROM purse_deposits WHERE purse_pub = my_purse_pub LOOP - UPDATE + UPDATE known_coins SET remaining_frac=remaining_frac+my_deposit.amount_with_fee_frac - CASE WHEN remaining_frac+my_deposit.amount_with_fee_frac >= 100000000 @@ -3415,9 +3414,7 @@ LOOP THEN 1 ELSE 0 END - FROM known_coins - WHERE coin_pub = my_deposit.coin_pub - LIMIT 1; -- just to be extra safe + WHERE coin_pub = my_deposit.coin_pub; END LOOP; END $$; diff --git a/src/include/taler_testing_lib.h b/src/include/taler_testing_lib.h index 930a8e9a6..b0dee023f 100644 --- a/src/include/taler_testing_lib.h +++ b/src/include/taler_testing_lib.h @@ -1211,6 +1211,30 @@ TALER_TESTING_cmd_exec_wirewatch (const char *label, const char *config_filename); +/** + * Make a "expire" CMD. + * + * @param label command label. + * @param config_filename configuration filename. + * @return the command. + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_exec_expire (const char *label, + const char *config_filename); + + +/** + * Make a "router" CMD. + * + * @param label command label. + * @param config_filename configuration filename. + * @return the command. + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_exec_router (const char *label, + const char *config_filename); + + /** * Run a "taler-exchange-aggregator" CMD. * diff --git a/src/lib/exchange_api_purses_get.c b/src/lib/exchange_api_purses_get.c index db5111287..021954c2d 100644 --- a/src/lib/exchange_api_purses_get.c +++ b/src/lib/exchange_api_purses_get.c @@ -169,6 +169,11 @@ handle_purse_get_finished (void *cls, /* Exchange does not know about transaction; we should pass the reply to the application */ break; + case MHD_HTTP_GONE: + /* purse expired */ + dr.hr.ec = TALER_JSON_get_error_code (j); + dr.hr.hint = TALER_JSON_get_error_hint (j); + break; case MHD_HTTP_INTERNAL_SERVER_ERROR: dr.hr.ec = TALER_JSON_get_error_code (j); dr.hr.hint = TALER_JSON_get_error_hint (j); diff --git a/src/testing/Makefile.am b/src/testing/Makefile.am index 4d3c6061a..42c4d8d6b 100644 --- a/src/testing/Makefile.am +++ b/src/testing/Makefile.am @@ -61,6 +61,8 @@ libtalertesting_la_SOURCES = \ testing_api_cmd_exec_aggregator.c \ testing_api_cmd_exec_auditor-offline.c \ testing_api_cmd_exec_closer.c \ + testing_api_cmd_exec_expire.c \ + testing_api_cmd_exec_router.c \ testing_api_cmd_exec_transfer.c \ testing_api_cmd_exec_wirewatch.c \ testing_api_cmd_insert_deposit.c \ diff --git a/src/testing/test_exchange_api-cs.conf b/src/testing/test_exchange_api-cs.conf index 79332d648..204718120 100644 --- a/src/testing/test_exchange_api-cs.conf +++ b/src/testing/test_exchange_api-cs.conf @@ -45,6 +45,14 @@ DB = postgres # exchange (or the twister) is actually listening. BASE_URL = "http://localhost:8081/" +# How big is an individual shard to be processed +# by taler-exchange-expire (in time). It may take +# this much time for an expired purse to be really +# cleaned up and the coins refunded. +EXPIRE_SHARD_SIZE = 300 ms + +EXPIRE_IDLE_SLEEP_INTERVAL = 1 s + [exchangedb-postgres] CONFIG = "postgres:///talercheck" diff --git a/src/testing/test_exchange_api-rsa.conf b/src/testing/test_exchange_api-rsa.conf index 1d4456623..be0e4bade 100644 --- a/src/testing/test_exchange_api-rsa.conf +++ b/src/testing/test_exchange_api-rsa.conf @@ -45,6 +45,15 @@ DB = postgres # exchange (or the twister) is actually listening. BASE_URL = "http://localhost:8081/" +# How big is an individual shard to be processed +# by taler-exchange-expire (in time). It may take +# this much time for an expired purse to be really +# cleaned up and the coins refunded. +EXPIRE_SHARD_SIZE = 300 ms + +EXPIRE_IDLE_SLEEP_INTERVAL = 1 s + + [exchangedb-postgres] CONFIG = "postgres:///talercheck" diff --git a/src/testing/test_exchange_p2p.c b/src/testing/test_exchange_p2p.c index b3e98daa4..ec3c660c4 100644 --- a/src/testing/test_exchange_p2p.c +++ b/src/testing/test_exchange_p2p.c @@ -277,6 +277,62 @@ run (void *cls, TALER_TESTING_cmd_end () }; + struct TALER_TESTING_Command expire[] = { + TALER_TESTING_cmd_purse_create_with_reserve ( + "purse-create-with-reserve-expire", + MHD_HTTP_OK, + "{\"amount\":\"EUR:1\",\"summary\":\"ice cream\"}", + true /* upload contract */, + GNUNET_TIME_relative_multiply ( + GNUNET_TIME_UNIT_SECONDS, + 1), /* expiration */ + "create-reserve-1"), + TALER_TESTING_cmd_purse_poll ( + "pull-poll-purse-before-expire", + MHD_HTTP_GONE, + "purse-create-with-reserve-expire", + "EUR:1", + false, + GNUNET_TIME_UNIT_MINUTES), + TALER_TESTING_cmd_purse_create_with_deposit ( + "purse-with-deposit-expire", + MHD_HTTP_OK, + "{\"amount\":\"EUR:1\",\"summary\":\"ice cream\"}", + true, /* upload contract */ + GNUNET_TIME_relative_multiply ( + GNUNET_TIME_UNIT_SECONDS, + 1), /* expiration */ + "withdraw-coin-1", + "EUR:1.01", + NULL), + TALER_TESTING_cmd_purse_poll ( + "push-poll-purse-before-expire", + MHD_HTTP_GONE, + "purse-with-deposit-expire", + "EUR:1", + true, + GNUNET_TIME_UNIT_MINUTES), + TALER_TESTING_cmd_sleep ("sleep", + 2 /* seconds */), + TALER_TESTING_cmd_exec_expire ("exec-expire", + config_file), + TALER_TESTING_cmd_purse_poll_finish ( + "push-merge-purse-poll-finish-expire", + GNUNET_TIME_relative_multiply ( + GNUNET_TIME_UNIT_SECONDS, + 15), + "push-poll-purse-before-expire"), + TALER_TESTING_cmd_purse_poll_finish ( + "pull-deposit-purse-poll-expire-finish", + GNUNET_TIME_relative_multiply ( + GNUNET_TIME_UNIT_SECONDS, + 15), + "pull-poll-purse-before-expire"), + // FIXME: check coin was refunded + // FIXME: check reserve purse capacity is back up! + TALER_TESTING_cmd_end () + }; + struct TALER_TESTING_Command commands[] = { /* setup exchange */ TALER_TESTING_cmd_auditor_add ("add-auditor-OK", @@ -313,6 +369,8 @@ run (void *cls, push), TALER_TESTING_cmd_batch ("pull", pull), + TALER_TESTING_cmd_batch ("expire", + expire), /* End the suite. */ TALER_TESTING_cmd_end () }; diff --git a/src/testing/testing_api_cmd_exec_expire.c b/src/testing/testing_api_cmd_exec_expire.c new file mode 100644 index 000000000..aabf37361 --- /dev/null +++ b/src/testing/testing_api_cmd_exec_expire.c @@ -0,0 +1,162 @@ +/* + This file is part of TALER + Copyright (C) 2022 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 3, + or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public + License along with TALER; see the file COPYING. If not, see + +*/ +/** + * @file testing/testing_api_cmd_exec_expire.c + * @brief run the taler-exchange-expire command + * @author Christian Grothoff + * @author Marcello Stanisci + */ +#include "platform.h" +#include "taler_json_lib.h" +#include +#include "taler_signatures.h" +#include "taler_testing_lib.h" + + +/** + * State for a "expire" CMD. + */ +struct ExpireState +{ + + /** + * Process for the expireer. + */ + struct GNUNET_OS_Process *expire_proc; + + /** + * Configuration file used by the expireer. + */ + const char *config_filename; +}; + + +/** + * Run the command; use the `taler-exchange-expire' program. + * + * @param cls closure. + * @param cmd command currently being executed. + * @param is interpreter state. + */ +static void +expire_run (void *cls, + const struct TALER_TESTING_Command *cmd, + struct TALER_TESTING_Interpreter *is) +{ + struct ExpireState *ws = cls; + + (void) cmd; + ws->expire_proc + = GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_ALL, + NULL, NULL, NULL, + "taler-exchange-expire", + "taler-exchange-expire", + "-L", "INFO", + "-c", ws->config_filename, + "-t", /* exit when done */ + NULL); + if (NULL == ws->expire_proc) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + TALER_TESTING_wait_for_sigchld (is); +} + + +/** + * Free the state of a "expire" CMD, and possibly + * kills its process if it did not terminate regularly. + * + * @param cls closure. + * @param cmd the command being freed. + */ +static void +expire_cleanup (void *cls, + const struct TALER_TESTING_Command *cmd) +{ + struct ExpireState *ws = cls; + + (void) cmd; + if (NULL != ws->expire_proc) + { + GNUNET_break (0 == + GNUNET_OS_process_kill (ws->expire_proc, + SIGKILL)); + GNUNET_OS_process_wait (ws->expire_proc); + GNUNET_OS_process_destroy (ws->expire_proc); + ws->expire_proc = NULL; + } + GNUNET_free (ws); +} + + +/** + * Offer "expire" CMD internal data to other commands. + * + * @param cls closure. + * @param[out] ret result. + * @param trait name of the trait. + * @param index index number of the object to offer. + * @return #GNUNET_OK on success. + */ +static enum GNUNET_GenericReturnValue +expire_traits (void *cls, + const void **ret, + const char *trait, + unsigned int index) +{ + struct ExpireState *ws = cls; + struct TALER_TESTING_Trait traits[] = { + TALER_TESTING_make_trait_process (&ws->expire_proc), + TALER_TESTING_trait_end () + }; + + return TALER_TESTING_get_trait (traits, + ret, + trait, + index); +} + + +struct TALER_TESTING_Command +TALER_TESTING_cmd_exec_expire (const char *label, + const char *config_filename) +{ + struct ExpireState *ws; + + ws = GNUNET_new (struct ExpireState); + ws->config_filename = config_filename; + + { + struct TALER_TESTING_Command cmd = { + .cls = ws, + .label = label, + .run = &expire_run, + .cleanup = &expire_cleanup, + .traits = &expire_traits + }; + + return cmd; + } +} + + +/* end of testing_api_cmd_exec_expire.c */ diff --git a/src/testing/testing_api_cmd_exec_router.c b/src/testing/testing_api_cmd_exec_router.c new file mode 100644 index 000000000..d7f3fe265 --- /dev/null +++ b/src/testing/testing_api_cmd_exec_router.c @@ -0,0 +1,161 @@ +/* + This file is part of TALER + Copyright (C) 2022 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 3, + or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public + License along with TALER; see the file COPYING. If not, see + +*/ +/** + * @file testing/testing_api_cmd_exec_router.c + * @brief run the taler-exchange-router command + * @author Christian Grothoff + * @author Marcello Stanisci + */ +#include "platform.h" +#include "taler_json_lib.h" +#include +#include "taler_signatures.h" +#include "taler_testing_lib.h" + + +/** + * State for a "router" CMD. + */ +struct RouterState +{ + + /** + * Process for the routerer. + */ + struct GNUNET_OS_Process *router_proc; + + /** + * Configuration file used by the routerer. + */ + const char *config_filename; +}; + + +/** + * Run the command; use the `taler-exchange-router' program. + * + * @param cls closure. + * @param cmd command currently being executed. + * @param is interpreter state. + */ +static void +router_run (void *cls, + const struct TALER_TESTING_Command *cmd, + struct TALER_TESTING_Interpreter *is) +{ + struct RouterState *ws = cls; + + (void) cmd; + ws->router_proc + = GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_ALL, + NULL, NULL, NULL, + "taler-exchange-router", + "taler-exchange-router", + "-c", ws->config_filename, + "-t", /* exit when done */ + NULL); + if (NULL == ws->router_proc) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + TALER_TESTING_wait_for_sigchld (is); +} + + +/** + * Free the state of a "router" CMD, and possibly + * kills its process if it did not terminate regularly. + * + * @param cls closure. + * @param cmd the command being freed. + */ +static void +router_cleanup (void *cls, + const struct TALER_TESTING_Command *cmd) +{ + struct RouterState *ws = cls; + + (void) cmd; + if (NULL != ws->router_proc) + { + GNUNET_break (0 == + GNUNET_OS_process_kill (ws->router_proc, + SIGKILL)); + GNUNET_OS_process_wait (ws->router_proc); + GNUNET_OS_process_destroy (ws->router_proc); + ws->router_proc = NULL; + } + GNUNET_free (ws); +} + + +/** + * Offer "router" CMD internal data to other commands. + * + * @param cls closure. + * @param[out] ret result. + * @param trait name of the trait. + * @param index index number of the object to offer. + * @return #GNUNET_OK on success. + */ +static enum GNUNET_GenericReturnValue +router_traits (void *cls, + const void **ret, + const char *trait, + unsigned int index) +{ + struct RouterState *ws = cls; + struct TALER_TESTING_Trait traits[] = { + TALER_TESTING_make_trait_process (&ws->router_proc), + TALER_TESTING_trait_end () + }; + + return TALER_TESTING_get_trait (traits, + ret, + trait, + index); +} + + +struct TALER_TESTING_Command +TALER_TESTING_cmd_exec_router (const char *label, + const char *config_filename) +{ + struct RouterState *ws; + + ws = GNUNET_new (struct RouterState); + ws->config_filename = config_filename; + + { + struct TALER_TESTING_Command cmd = { + .cls = ws, + .label = label, + .run = &router_run, + .cleanup = &router_cleanup, + .traits = &router_traits + }; + + return cmd; + } +} + + +/* end of testing_api_cmd_exec_router.c */ diff --git a/src/testing/testing_api_cmd_purse_get.c b/src/testing/testing_api_cmd_purse_get.c index 3fc576912..61873721b 100644 --- a/src/testing/testing_api_cmd_purse_get.c +++ b/src/testing/testing_api_cmd_purse_get.c @@ -261,6 +261,7 @@ TALER_TESTING_cmd_purse_poll ( ss->expected_balance = expected_balance; ss->expected_response_code = expected_http_status; ss->timeout = timeout; + ss->wait_for_merge = wait_for_merge; { struct TALER_TESTING_Command cmd = { .cls = ss,