Uplift status.c unit test coverage with new test cases and macros.

A new set of unit test cases are provided, as well as introducing
an alternative paradigm and macros to support it. Primarily, each test
case is given its own namespace, in order to isolate tests from each
other. We do this by in the usual fashion, by appending module and
submodule names to our symbols. New macros assist by reducing friction
for this and other tasks, like overriding a function in the global
namespace with one in the current namespace, or declaring integer
variables to assist tracking how many times a mock has been called.

A set of tests for a small-scale module has been included in this
commit, in order to highlight how the paradigm can be used. This
suite gives 100% coverage to status.c in test execution.
This commit is contained in:
dana koch 2014-04-15 22:20:34 +10:00 committed by Nick Mathewson
parent 2704441e7f
commit 3ce3984772
23 changed files with 1274 additions and 50 deletions

View File

@ -147,9 +147,6 @@ static INLINE char *format_msg(char *buf, size_t buf_len,
const char *suffix, const char *suffix,
const char *format, va_list ap, size_t *msg_len_out) const char *format, va_list ap, size_t *msg_len_out)
CHECK_PRINTF(7,0); CHECK_PRINTF(7,0);
static void logv(int severity, log_domain_mask_t domain, const char *funcname,
const char *suffix, const char *format, va_list ap)
CHECK_PRINTF(5,0);
/** Name of the application: used to generate the message we write at the /** Name of the application: used to generate the message we write at the
* start of each new log. */ * start of each new log. */
@ -336,9 +333,9 @@ format_msg(char *buf, size_t buf_len,
* <b>severity</b>. If provided, <b>funcname</b> is prepended to the * <b>severity</b>. If provided, <b>funcname</b> is prepended to the
* message. The actual message is derived as from tor_snprintf(format,ap). * message. The actual message is derived as from tor_snprintf(format,ap).
*/ */
static void MOCK_IMPL(STATIC void,
logv(int severity, log_domain_mask_t domain, const char *funcname, logv,(int severity, log_domain_mask_t domain, const char *funcname,
const char *suffix, const char *format, va_list ap) const char *suffix, const char *format, va_list ap))
{ {
char buf[10024]; char buf[10024];
size_t msg_len = 0; size_t msg_len = 0;

View File

@ -13,6 +13,7 @@
#ifndef TOR_TORLOG_H #ifndef TOR_TORLOG_H
#include "compat.h" #include "compat.h"
#include "testsupport.h"
#ifdef HAVE_SYSLOG_H #ifdef HAVE_SYSLOG_H
#include <syslog.h> #include <syslog.h>
@ -228,6 +229,12 @@ extern const char *log_fn_function_name_;
#endif /* !GNUC */ #endif /* !GNUC */
#ifdef LOG_PRIVATE
MOCK_DECL(STATIC void, logv, (int severity, log_domain_mask_t domain,
const char *funcname, const char *suffix, const char *format,
va_list ap) CHECK_PRINTF(5,0));
#endif
# define TOR_TORLOG_H # define TOR_TORLOG_H
#endif #endif

View File

@ -2579,8 +2579,8 @@ tor_tls_get_n_raw_bytes(tor_tls_t *tls, size_t *n_read, size_t *n_written)
/** Return a ratio of the bytes that TLS has sent to the bytes that we've told /** Return a ratio of the bytes that TLS has sent to the bytes that we've told
* it to send. Used to track whether our TLS records are getting too tiny. */ * it to send. Used to track whether our TLS records are getting too tiny. */
double MOCK_IMPL(double,
tls_get_write_overhead_ratio(void) tls_get_write_overhead_ratio,(void))
{ {
if (total_bytes_written_over_tls == 0) if (total_bytes_written_over_tls == 0)
return 1.0; return 1.0;

View File

@ -13,6 +13,7 @@
#include "crypto.h" #include "crypto.h"
#include "compat.h" #include "compat.h"
#include "testsupport.h"
/* Opaque structure to hold a TLS connection. */ /* Opaque structure to hold a TLS connection. */
typedef struct tor_tls_t tor_tls_t; typedef struct tor_tls_t tor_tls_t;
@ -95,7 +96,7 @@ void tor_tls_get_buffer_sizes(tor_tls_t *tls,
size_t *rbuf_capacity, size_t *rbuf_bytes, size_t *rbuf_capacity, size_t *rbuf_bytes,
size_t *wbuf_capacity, size_t *wbuf_bytes); size_t *wbuf_capacity, size_t *wbuf_bytes);
double tls_get_write_overhead_ratio(void); MOCK_DECL(double, tls_get_write_overhead_ratio, (void));
int tor_tls_used_v1_handshake(tor_tls_t *tls); int tor_tls_used_v1_handshake(tor_tls_t *tls);
int tor_tls_received_v3_certificate(tor_tls_t *tls); int tor_tls_received_v3_certificate(tor_tls_t *tls);

View File

@ -438,8 +438,8 @@ circuit_close_all_marked(void)
} }
/** Return the head of the global linked list of circuits. */ /** Return the head of the global linked list of circuits. */
struct global_circuitlist_s * MOCK_IMPL(struct global_circuitlist_s *,
circuit_get_global_list(void) circuit_get_global_list,(void))
{ {
return &global_circuitlist; return &global_circuitlist;
} }

View File

@ -16,7 +16,7 @@
TOR_LIST_HEAD(global_circuitlist_s, circuit_t); TOR_LIST_HEAD(global_circuitlist_s, circuit_t);
struct global_circuitlist_s* circuit_get_global_list(void); MOCK_DECL(struct global_circuitlist_s*, circuit_get_global_list, (void));
const char *circuit_state_to_string(int state); const char *circuit_state_to_string(int state);
const char *circuit_purpose_to_controller_string(uint8_t purpose); const char *circuit_purpose_to_controller_string(uint8_t purpose);
const char *circuit_purpose_to_controller_hs_state_string(uint8_t purpose); const char *circuit_purpose_to_controller_hs_state_string(uint8_t purpose);

View File

@ -620,8 +620,8 @@ get_options_mutable(void)
} }
/** Returns the currently configured options */ /** Returns the currently configured options */
const or_options_t * MOCK_IMPL(const or_options_t *,
get_options(void) get_options,(void))
{ {
return get_options_mutable(); return get_options_mutable();
} }

View File

@ -12,8 +12,10 @@
#ifndef TOR_CONFIG_H #ifndef TOR_CONFIG_H
#define TOR_CONFIG_H #define TOR_CONFIG_H
#include "testsupport.h"
const char *get_dirportfrontpage(void); const char *get_dirportfrontpage(void);
const or_options_t *get_options(void); MOCK_DECL(const or_options_t *,get_options,(void));
or_options_t *get_options_mutable(void); or_options_t *get_options_mutable(void);
int set_options(or_options_t *new_val, char **msg); int set_options(or_options_t *new_val, char **msg);
void config_free_all(void); void config_free_all(void);

View File

@ -239,8 +239,8 @@ accounting_parse_options(const or_options_t *options, int validate_only)
/** If we want to manage the accounting system and potentially /** If we want to manage the accounting system and potentially
* hibernate, return 1, else return 0. * hibernate, return 1, else return 0.
*/ */
int MOCK_IMPL(int,
accounting_is_enabled(const or_options_t *options) accounting_is_enabled,(const or_options_t *options))
{ {
if (options->AccountingMax) if (options->AccountingMax)
return 1; return 1;
@ -256,8 +256,8 @@ accounting_get_interval_length(void)
} }
/** Return the time at which the current accounting interval will end. */ /** Return the time at which the current accounting interval will end. */
time_t MOCK_IMPL(time_t,
accounting_get_end_time(void) accounting_get_end_time,(void))
{ {
return interval_end_time; return interval_end_time;
} }
@ -823,8 +823,8 @@ hibernate_begin_shutdown(void)
} }
/** Return true iff we are currently hibernating. */ /** Return true iff we are currently hibernating. */
int MOCK_IMPL(int,
we_are_hibernating(void) we_are_hibernating,(void))
{ {
return hibernate_state != HIBERNATE_STATE_LIVE; return hibernate_state != HIBERNATE_STATE_LIVE;
} }

View File

@ -12,16 +12,18 @@
#ifndef TOR_HIBERNATE_H #ifndef TOR_HIBERNATE_H
#define TOR_HIBERNATE_H #define TOR_HIBERNATE_H
#include "testsupport.h"
int accounting_parse_options(const or_options_t *options, int validate_only); int accounting_parse_options(const or_options_t *options, int validate_only);
int accounting_is_enabled(const or_options_t *options); MOCK_DECL(int, accounting_is_enabled, (const or_options_t *options));
int accounting_get_interval_length(void); int accounting_get_interval_length(void);
time_t accounting_get_end_time(void); MOCK_DECL(time_t, accounting_get_end_time, (void));
void configure_accounting(time_t now); void configure_accounting(time_t now);
void accounting_run_housekeeping(time_t now); void accounting_run_housekeeping(time_t now);
void accounting_add_bytes(size_t n_read, size_t n_written, int seconds); void accounting_add_bytes(size_t n_read, size_t n_written, int seconds);
int accounting_record_bandwidth_usage(time_t now, or_state_t *state); int accounting_record_bandwidth_usage(time_t now, or_state_t *state);
void hibernate_begin_shutdown(void); void hibernate_begin_shutdown(void);
int we_are_hibernating(void); MOCK_DECL(int, we_are_hibernating, (void));
void consider_hibernation(time_t now); void consider_hibernation(time_t now);
int getinfo_helper_accounting(control_connection_t *conn, int getinfo_helper_accounting(control_connection_t *conn,
const char *question, char **answer, const char *question, char **answer,

View File

@ -469,15 +469,15 @@ get_connection_array(void)
/** Provides the traffic read and written over the life of the process. */ /** Provides the traffic read and written over the life of the process. */
uint64_t MOCK_IMPL(uint64_t,
get_bytes_read(void) get_bytes_read,(void))
{ {
return stats_n_bytes_read; return stats_n_bytes_read;
} }
/* DOCDOC get_bytes_written */ /* DOCDOC get_bytes_written */
uint64_t MOCK_IMPL(uint64_t,
get_bytes_written(void) get_bytes_written,(void))
{ {
return stats_n_bytes_written; return stats_n_bytes_written;
} }
@ -2121,8 +2121,8 @@ process_signal(uintptr_t sig)
} }
/** Returns Tor's uptime. */ /** Returns Tor's uptime. */
long MOCK_IMPL(long,
get_uptime(void) get_uptime,(void))
{ {
return stats_n_seconds_working; return stats_n_seconds_working;
} }

View File

@ -24,8 +24,8 @@ void add_connection_to_closeable_list(connection_t *conn);
int connection_is_on_closeable_list(connection_t *conn); int connection_is_on_closeable_list(connection_t *conn);
smartlist_t *get_connection_array(void); smartlist_t *get_connection_array(void);
uint64_t get_bytes_read(void); MOCK_DECL(uint64_t,get_bytes_read,(void));
uint64_t get_bytes_written(void); MOCK_DECL(uint64_t,get_bytes_written,(void));
/** Bitmask for events that we can turn on and off with /** Bitmask for events that we can turn on and off with
* connection_watch_events. */ * connection_watch_events. */
@ -52,7 +52,8 @@ void ip_address_changed(int at_interface);
void dns_servers_relaunch_checks(void); void dns_servers_relaunch_checks(void);
void reschedule_descriptor_update_check(void); void reschedule_descriptor_update_check(void);
long get_uptime(void); MOCK_DECL(long,get_uptime,(void));
unsigned get_signewnym_epoch(void); unsigned get_signewnym_epoch(void);
void handle_signals(int is_parent); void handle_signals(int is_parent);

View File

@ -85,8 +85,8 @@ node_get_mutable_by_id(const char *identity_digest)
/** Return the node_t whose identity is <b>identity_digest</b>, or NULL /** Return the node_t whose identity is <b>identity_digest</b>, or NULL
* if no such node exists. */ * if no such node exists. */
const node_t * MOCK_IMPL(const node_t *,
node_get_by_id(const char *identity_digest) node_get_by_id,(const char *identity_digest))
{ {
return node_get_mutable_by_id(identity_digest); return node_get_mutable_by_id(identity_digest);
} }

View File

@ -17,7 +17,7 @@
} STMT_END } STMT_END
node_t *node_get_mutable_by_id(const char *identity_digest); node_t *node_get_mutable_by_id(const char *identity_digest);
const node_t *node_get_by_id(const char *identity_digest); MOCK_DECL(const node_t *, node_get_by_id, (const char *identity_digest));
const node_t *node_get_by_hex_id(const char *identity_digest); const node_t *node_get_by_hex_id(const char *identity_digest);
node_t *nodelist_set_routerinfo(routerinfo_t *ri, routerinfo_t **ri_old_out); node_t *nodelist_set_routerinfo(routerinfo_t *ri, routerinfo_t **ri_old_out);
node_t *nodelist_add_microdesc(microdesc_t *md); node_t *nodelist_add_microdesc(microdesc_t *md);

View File

@ -2995,8 +2995,8 @@ rep_hist_conn_stats_write(time_t now)
* handshake we've received, and how many we've assigned to cpuworkers. * handshake we've received, and how many we've assigned to cpuworkers.
* Useful for seeing trends in cpu load. * Useful for seeing trends in cpu load.
* @{ */ * @{ */
static int onion_handshakes_requested[MAX_ONION_HANDSHAKE_TYPE+1] = {0}; STATIC int onion_handshakes_requested[MAX_ONION_HANDSHAKE_TYPE+1] = {0};
static int onion_handshakes_assigned[MAX_ONION_HANDSHAKE_TYPE+1] = {0}; STATIC int onion_handshakes_assigned[MAX_ONION_HANDSHAKE_TYPE+1] = {0};
/**@}*/ /**@}*/
/** A new onionskin (using the <b>type</b> handshake) has arrived. */ /** A new onionskin (using the <b>type</b> handshake) has arrived. */

View File

@ -1348,8 +1348,8 @@ authdir_mode_bridge(const or_options_t *options)
/** Return true iff we are trying to be a server. /** Return true iff we are trying to be a server.
*/ */
int MOCK_IMPL(int,
server_mode(const or_options_t *options) server_mode,(const or_options_t *options))
{ {
if (options->ClientOnly) return 0; if (options->ClientOnly) return 0;
/* XXXX024 I believe we can kill off ORListenAddress here.*/ /* XXXX024 I believe we can kill off ORListenAddress here.*/
@ -1358,8 +1358,8 @@ server_mode(const or_options_t *options)
/** Return true iff we are trying to be a non-bridge server. /** Return true iff we are trying to be a non-bridge server.
*/ */
int MOCK_IMPL(int,
public_server_mode(const or_options_t *options) public_server_mode,(const or_options_t *options))
{ {
if (!server_mode(options)) return 0; if (!server_mode(options)) return 0;
return (!options->BridgeRelay); return (!options->BridgeRelay);
@ -1689,8 +1689,8 @@ router_is_me(const routerinfo_t *router)
/** Return a routerinfo for this OR, rebuilding a fresh one if /** Return a routerinfo for this OR, rebuilding a fresh one if
* necessary. Return NULL on error, or if called on an OP. */ * necessary. Return NULL on error, or if called on an OP. */
const routerinfo_t * MOCK_IMPL(const routerinfo_t *,
router_get_my_routerinfo(void) router_get_my_routerinfo,(void))
{ {
if (!server_mode(get_options())) if (!server_mode(get_options()))
return NULL; return NULL;

View File

@ -66,8 +66,8 @@ uint16_t router_get_advertised_or_port_by_af(const or_options_t *options,
uint16_t router_get_advertised_dir_port(const or_options_t *options, uint16_t router_get_advertised_dir_port(const or_options_t *options,
uint16_t dirport); uint16_t dirport);
int server_mode(const or_options_t *options); MOCK_DECL(int, server_mode, (const or_options_t *options));
int public_server_mode(const or_options_t *options); MOCK_DECL(int, public_server_mode, (const or_options_t *options));
int advertised_server_mode(void); int advertised_server_mode(void);
int proxy_mode(const or_options_t *options); int proxy_mode(const or_options_t *options);
void consider_publishable_server(int force); void consider_publishable_server(int force);
@ -82,7 +82,7 @@ void router_new_address_suggestion(const char *suggestion,
const dir_connection_t *d_conn); const dir_connection_t *d_conn);
int router_compare_to_my_exit_policy(const tor_addr_t *addr, uint16_t port); int router_compare_to_my_exit_policy(const tor_addr_t *addr, uint16_t port);
int router_my_exit_policy_is_reject_star(void); int router_my_exit_policy_is_reject_star(void);
const routerinfo_t *router_get_my_routerinfo(void); MOCK_DECL(const routerinfo_t *, router_get_my_routerinfo, (void));
extrainfo_t *router_get_my_extrainfo(void); extrainfo_t *router_get_my_extrainfo(void);
const char *router_get_my_descriptor(void); const char *router_get_my_descriptor(void);
const char *router_get_descriptor_gen_reason(void); const char *router_get_descriptor_gen_reason(void);

View File

@ -6,6 +6,8 @@
* \brief Keep status information and log the heartbeat messages. * \brief Keep status information and log the heartbeat messages.
**/ **/
#define STATUS_PRIVATE
#include "or.h" #include "or.h"
#include "config.h" #include "config.h"
#include "status.h" #include "status.h"
@ -22,7 +24,7 @@
static void log_accounting(const time_t now, const or_options_t *options); static void log_accounting(const time_t now, const or_options_t *options);
/** Return the total number of circuits. */ /** Return the total number of circuits. */
static int STATIC int
count_circuits(void) count_circuits(void)
{ {
circuit_t *circ; circuit_t *circ;
@ -36,7 +38,7 @@ count_circuits(void)
/** Take seconds <b>secs</b> and return a newly allocated human-readable /** Take seconds <b>secs</b> and return a newly allocated human-readable
* uptime string */ * uptime string */
static char * STATIC char *
secs_to_uptime(long secs) secs_to_uptime(long secs)
{ {
long int days = secs / 86400; long int days = secs / 86400;
@ -63,7 +65,7 @@ secs_to_uptime(long secs)
/** Take <b>bytes</b> and returns a newly allocated human-readable usage /** Take <b>bytes</b> and returns a newly allocated human-readable usage
* string. */ * string. */
static char * STATIC char *
bytes_to_usage(uint64_t bytes) bytes_to_usage(uint64_t bytes)
{ {
char *bw_string = NULL; char *bw_string = NULL;

View File

@ -4,7 +4,15 @@
#ifndef TOR_STATUS_H #ifndef TOR_STATUS_H
#define TOR_STATUS_H #define TOR_STATUS_H
#include "testsupport.h"
int log_heartbeat(time_t now); int log_heartbeat(time_t now);
#ifdef STATUS_PRIVATE
STATIC int count_circuits(void);
STATIC char *secs_to_uptime(long secs);
STATIC char *bytes_to_usage(uint64_t bytes);
#endif
#endif #endif

View File

@ -44,6 +44,7 @@ src_test_test_SOURCES = \
src/test/test_hs.c \ src/test/test_hs.c \
src/test/test_nodelist.c \ src/test/test_nodelist.c \
src/test/test_policy.c \ src/test/test_policy.c \
src/test/test_status.c \
src/ext/tinytest.c src/ext/tinytest.c
src_test_test_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS) src_test_test_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)

View File

@ -1300,6 +1300,7 @@ extern struct testcase_t nodelist_tests[];
extern struct testcase_t routerkeys_tests[]; extern struct testcase_t routerkeys_tests[];
extern struct testcase_t oom_tests[]; extern struct testcase_t oom_tests[];
extern struct testcase_t policy_tests[]; extern struct testcase_t policy_tests[];
extern struct testcase_t status_tests[];
static struct testgroup_t testgroups[] = { static struct testgroup_t testgroups[] = {
{ "", test_array }, { "", test_array },
@ -1329,6 +1330,7 @@ static struct testgroup_t testgroups[] = {
{ "routerkeys/", routerkeys_tests }, { "routerkeys/", routerkeys_tests },
{ "oom/", oom_tests }, { "oom/", oom_tests },
{ "policy/" , policy_tests }, { "policy/" , policy_tests },
{ "status/" , status_tests },
END_OF_GROUPS END_OF_GROUPS
}; };

View File

@ -65,5 +65,97 @@ crypto_pk_t *pk_generate(int idx);
void legacy_test_helper(void *data); void legacy_test_helper(void *data);
extern const struct testcase_setup_t legacy_setup; extern const struct testcase_setup_t legacy_setup;
#define US2_CONCAT_2__(a, b) a ## __ ## b
#define US_CONCAT_2__(a, b) a ## _ ## b
#define US_CONCAT_3__(a, b, c) a ## _ ## b ## _ ## c
#define US_CONCAT_2_(a, b) US_CONCAT_2__(a, b)
#define US_CONCAT_3_(a, b, c) US_CONCAT_3__(a, b, c)
/*
* These macros are helpful for streamlining the authorship of several test
* cases that use mocks.
*
* The pattern is as follows.
* * Declare a top level namespace:
* #define NS_MODULE foo
*
* * For each test case you want to write, create a new submodule in the
* namespace. All mocks and other information should belong to a single
* submodule to avoid interference with other test cases.
* You can simply name the submodule after the function in the module you
* are testing:
* #define NS_SUBMODULE some_function
* or, if you're wanting to write several tests against the same function,
* ie., you are testing an aspect of that function, you can use:
* #define NS_SUBMODULE ASPECT(some_function, behavior)
*
* * Declare all the mocks you will use. The NS_DECL macro serves to declare
* the mock in the current namespace (defined by NS_MODULE and NS_SUBMODULE).
* It behaves like MOCK_DECL:
* NS_DECL(int, dependent_function, (void *));
* Here, dependent_function must be declared and implemented with the
* MOCK_DECL and MOCK_IMPL macros. The NS_DECL macro also defines an integer
* global for use for tracking how many times a mock was called, and can be
* accessed by CALLED(mock_name). For example, you might put
* CALLED(dependent_function)++;
* in your mock body.
*
* * Define a function called NS(main) that will contain the body of the
* test case. The NS macro can be used to reference a name in the current
* namespace.
*
* * In NS(main), indicate that a mock function in the current namespace,
* declared with NS_DECL is to override that in the global namespace,
* with the NS_MOCK macro:
* NS_MOCK(dependent_function)
* Unmock with:
* NS_UNMOCK(dependent_function)
*
* * Define the mocks with the NS macro, eg.,
* int
* NS(dependent_function)(void *)
* {
* CALLED(dependent_function)++;
* }
*
* * In the struct testcase_t array, you can use the TEST_CASE and
* TEST_CASE_ASPECT macros to define the cases without having to do so
* explicitly nor without having to reset NS_SUBMODULE, eg.,
* struct testcase_t foo_tests[] = {
* TEST_CASE_ASPECT(some_function, behavior),
* ...
* END_OF_TESTCASES
* which will define a test case named "some_function__behavior".
*/
#define NAME_TEST_(name) #name
#define NAME_TEST(name) NAME_TEST_(name)
#define ASPECT(test_module, test_name) US2_CONCAT_2__(test_module, test_name)
#define TEST_CASE(function) \
{ \
NAME_TEST(function), \
NS_FULL(NS_MODULE, function, test_main), \
TT_FORK, \
NULL, \
NULL, \
}
#define TEST_CASE_ASPECT(function, aspect) \
{ \
NAME_TEST(ASPECT(function, aspect)), \
NS_FULL(NS_MODULE, ASPECT(function, aspect), test_main), \
TT_FORK, \
NULL, \
NULL, \
}
#define NS(name) US_CONCAT_3_(NS_MODULE, NS_SUBMODULE, name)
#define NS_FULL(module, submodule, name) US_CONCAT_3_(module, submodule, name)
#define CALLED(mock_name) US_CONCAT_2_(NS(mock_name), called)
#define NS_DECL(retval, mock_fn, args) \
static retval NS(mock_fn) args; int CALLED(mock_fn) = 0
#define NS_MOCK(name) MOCK(name, NS(name))
#define NS_UNMOCK(name) UNMOCK(name)
#endif #endif

1109
src/test/test_status.c Normal file

File diff suppressed because it is too large Load Diff