add some header documentation

This commit is contained in:
Conor Hughes 2019-07-15 19:29:29 -07:00
parent c915f3777b
commit fe887a8fa8
9 changed files with 389 additions and 22 deletions

View File

@ -3,23 +3,35 @@
#include <yachtrock/runtime.h>
/**
* Basic assertion.
*/
#define YR_ASSERT(test, ...) do { \
if ( !(test) ) { \
yr_fail_assertion(#test, __FILE__, __LINE__, __FUNCTION__, "" __VA_ARGS__); \
} \
} while (0)
/**
* Assertion that unconditionally fails.
*/
#define YR_FAIL(reason, ...) do { \
yr_fail_assertion("explicit failure", __FILE__, __LINE__, __FUNCTION__, reason, ##__VA_ARGS__); \
} while (0)
/**
* Assert that a condition does not hold.
*/
#define YR_ASSERT_FALSE(test, ...) do { \
if ( (test) ) { \
yr_fail_assertion(#test, __FILE__, __LINE__, __FUNCTION__, "" __VA_ARGS__); \
} \
} while (0)
/**
* Assert that two values are equal.
*/
#define YR_ASSERT_EQUAL(val1, val2, ...) do { \
if ( (val1) != (val2) ) { \
const char *__assertion_desc = #val1 " == " #val2; \
@ -27,20 +39,9 @@
} \
} while (0)
#define YR_ASSERT_STRINGS_EQUAL(val1, val2, ...) do { \
if ( strcmp((val1), (val2)) != 0 ) { \
const char *__assertion_desc = "strcmp("#val1 ", " #val2 ") == 0"; \
yr_fail_assertion(__assertion_desc, __FILE__, __LINE__, __FUNCTION__, "" __VA_ARGS__); \
} \
} while (0)
#define YR_ASSERT_STRINGS_NOT_EQUAL(val1, val2, ...) do { \
if ( strcmp((val1), (val2)) == 0 ) { \
const char *__assertion_desc = "strcmp("#val1 ", " #val2 ") != 0"; \
yr_fail_assertion(__assertion_desc, __FILE__, __LINE__, __FUNCTION__, "" __VA_ARGS__); \
} \
} while (0)
/**
* Assert that two values are not equal.
*/
#define YR_ASSERT_NOT_EQUAL(val1, val2, ...) do { \
if ( (val1) == (val2) ) { \
const char *__assertion_desc = #val1 " != " #val2; \
@ -48,6 +49,9 @@
} \
} while (0)
/**
* Assert that one value is less than another (via the < operator).
*/
#define YR_ASSERT_LESS_THAN(val1, val2, ...) do { \
if ( !((val1) < (val2)) ) { \
const char *__assertion_desc = #val1 " < " #val2; \
@ -55,6 +59,9 @@
} \
} while (0)
/**
* Assert that one value is greater than another (via the > operator).
*/
#define YR_ASSERT_GREATER_THAN(val1, val2, ...) do { \
if ( !((val1) > (val2)) ) { \
const char *__assertion_desc = #val1 " > " #val2; \
@ -62,6 +69,9 @@
} \
} while (0)
/**
* Assert that one value is less than or equal to another (via the <= operator).
*/
#define YR_ASSERT_LESS_THAN_OR_EQUAL(val1, val2, ...) do { \
if ( !((val1) <= (val2)) ) { \
const char *__assertion_desc = #val1 " <= " #val2; \
@ -69,6 +79,9 @@
} \
} while (0)
/**
* Assert that one value is greater than or equal to another (via the >= operator).
*/
#define YR_ASSERT_GREATER_THAN_OR_EQUAL(val1, val2, ...) do { \
if ( !((val1) >= (val2)) ) { \
const char *__assertion_desc = #val1 " >= " #val2; \
@ -76,4 +89,24 @@
} \
} while (0)
/**
* Assert that two strings are equal.
*/
#define YR_ASSERT_STRINGS_EQUAL(val1, val2, ...) do { \
if ( strcmp((val1), (val2)) != 0 ) { \
const char *__assertion_desc = "strcmp("#val1 ", " #val2 ") == 0"; \
yr_fail_assertion(__assertion_desc, __FILE__, __LINE__, __FUNCTION__, "" __VA_ARGS__); \
} \
} while (0)
/**
* Assert that two strings are not equal.
*/
#define YR_ASSERT_STRINGS_NOT_EQUAL(val1, val2, ...) do { \
if ( strcmp((val1), (val2)) == 0 ) { \
const char *__assertion_desc = "strcmp("#val1 ", " #val2 ") != 0"; \
yr_fail_assertion(__assertion_desc, __FILE__, __LINE__, __FUNCTION__, "" __VA_ARGS__); \
} \
} while (0)
#endif

View File

@ -5,6 +5,14 @@
#include <stdlib.h>
#include <stdbool.h>
/**
* A testing result.
*
* YR_RESULT_UNSET: The result has not been set.
* YR_RESULT_PASSED: A passing result.
* YR_RESULT_FAILED: A failing result.
* YR_RESULT_SKIPPED: The item was skipped.
*/
typedef enum yr_result {
YR_RESULT_UNSET,
YR_RESULT_PASSED,
@ -12,11 +20,37 @@ typedef enum yr_result {
YR_RESULT_SKIPPED
} yr_result_t;
/**
* Apply merging logic to a result, updating it with new information and returning the new result.
*/
YACHTROCK_EXTERN yr_result_t yr_merge_result(yr_result_t old_result, yr_result_t new_result);
typedef struct yr_result_store yr_result_store_s;
/**
* Result store.
*
* A result store is a tree structure that organizes test results.
*
* Each node can record some test result, but the result state they actually contain is constrained
* by the results of their children. For instance, a store that has a passing result will implicitly
* and irrevocably move to a failing state if one of its children records a failing result. Thus
* test results percolate up the store tree to yield an overall result at the root.
*
* Each node has a state (initially YR_RESULT_UNSET), a name, and zero or more children.
*/
typedef yr_result_store_s *yr_result_store_t;
/**
* Hooks to invoke when interesting things happen to a result store hierarchy.
*
* context: an arbitrary pointer passed through to the hook functions.
* store_opened: invoked when a store in the hierarchy is opened (including the root store).
* store_closed: invoked when a store in the hierarchy is closed (including the root store).
* store_result_changed: invoked when a store in the hierarchy has its result changed.
*
* A given hook may be set to NULL, in which case it will not be invoked.
*/
struct yr_result_hooks {
void *context;
void (*store_opened)(yr_result_store_t new_store, void *refcon);
@ -24,27 +58,108 @@ struct yr_result_hooks {
void (*store_result_changed)(yr_result_store_t store, void *refcon);
};
/**
* Create a new root store with the given name and no hooks.
*/
YACHTROCK_EXTERN yr_result_store_t yr_result_store_create(const char *name);
/**
* Create a new root store with the given name and the given hooks.
*/
YACHTROCK_EXTERN yr_result_store_t yr_result_store_create_with_hooks(const char *name,
struct yr_result_hooks hooks);
/**
* Destroy a root store and all of its subresults.
*
* The provided store *must* be a root store. You cannot remove a subresult once added.
*/
YACHTROCK_EXTERN void yr_result_store_destroy(yr_result_store_t store);
/**
* Close a store.
*
* Any remaining open subresult stores are closed. If the store's result is still unset, if any
* subresult stores have failed, the store's result is set to failed; otherwise, no subresult stores
* have failed, and the result is set to passed.
*/
YACHTROCK_EXTERN void yr_result_store_close(yr_result_store_t store);
/**
* Test whether a store is closed.
*/
YACHTROCK_EXTERN bool yr_result_store_is_closed(yr_result_store_t store);
/**
* Open a subresult of a store with the given name, returning the opened subresult store.
*/
YACHTROCK_EXTERN yr_result_store_t yr_result_store_open_subresult(yr_result_store_t store, const char *name);
/**
* Record a result.
*
* The effective result is computed via merge_result with the old and provided results.
*/
YACHTROCK_EXTERN void yr_result_store_record_result(yr_result_store_t store, yr_result_t result);
/**
* Extract the result from a store.
*/
YACHTROCK_EXTERN yr_result_t yr_result_store_get_result(yr_result_store_t store);
/**
* Extract the name from a store.
*
* The lifetime of the string is the same as the lifetime of the store.
*/
YACHTROCK_EXTERN const char *yr_result_store_get_name(yr_result_store_t store);
/**
* Look up the parent store for a given store.
*
* Returns NULL for a root store.
*/
YACHTROCK_EXTERN yr_result_store_t yr_result_store_get_parent(yr_result_store_t store);
typedef void (*yr_result_store_enumerator_t)(yr_result_store_t subresult, void *refcon);
/**
* Enumerate the store's children with the given callback.
*/
YACHTROCK_EXTERN void yr_result_store_enumerate(yr_result_store_t store,
yr_result_store_enumerator_t enumerator, void *refcon);
/**
* Get the number of direct subresult stores of this store.
*/
YACHTROCK_EXTERN size_t yr_result_store_count_subresults(yr_result_store_t store);
/**
* Get the subresult of this store with the given index.
*
* Indices are assigned sequentially in the obvious way.
*/
YACHTROCK_EXTERN yr_result_store_t yr_result_store_get_subresult(yr_result_store_t store,
size_t i);
/**
* Construct a string that describes the given store hierarchy.
*
* This fuction writes at most buf_size bytes into buf. The result is always NUL-terminated, unless
* buf_size is zero.
*
* The return value is how many bytes would be written if given infinite space, even if the function
* was actually forced to truncate the result written into buf.
*/
YACHTROCK_EXTERN size_t yr_result_store_get_description(yr_result_store_t store, char *buf, size_t buf_size);
/**
* Construct a string on the heap that describes the given store hierarchy.
*
* The result must be passed to free by the caller.
*/
YACHTROCK_EXTERN char *yr_result_store_copy_description(yr_result_store_t store);
#endif

View File

@ -10,7 +10,13 @@ typedef void (*yr_assertion_failure_callback)(const char *assertion, const char
typedef void (*yr_test_skipped_callback)(const char *file, size_t line, const char *funname,
const char *reason, va_list ap, void *refcon);
// callbacks
/**
* Runtime callbacks.
*
* refcon: Arbitrary pointer passed to the callbacks.
* note_assertion_failed: called when an assertion fails.
* note_skipped: called when a test is skipped.
*/
struct yr_runtime_callbacks
{
void *refcon;
@ -18,14 +24,29 @@ struct yr_runtime_callbacks
yr_test_skipped_callback note_skipped;
};
/**
* Set the runtime callbacks and return the previously set ones.
*
* Callbacks must be set before an assertion is failed or a test is skipped. Yachtrock functions
* that invoke test cases do this; it's only necessary to call this function if you are implementing
* your own test running routines.
*/
YACHTROCK_EXTERN struct yr_runtime_callbacks yr_set_runtime_callbacks(struct yr_runtime_callbacks callbacks);
/**
* Note that an assertion has failed, invoking the runtime callback.
*/
YACHTROCK_EXTERN void yr_fail_assertion(const char *assertion, const char *file, size_t line,
const char *funname, const char *s, ...);
/**
* Note that a test has been marked as skipped, invoking the runtime callback.
*/
YACHTROCK_EXTERN void yr_skip_test(const char *file, size_t line, const char *funname,
const char *reason, ...);
/**
* Helper macros.
*/
#define YR_RECORD_SKIP(reason, ...) do { \
yr_skip_test(__FILE__, __LINE__, __FUNCTION__, reason, ##__VA_ARGS__); \
} while (0)

View File

@ -5,37 +5,109 @@
#include <stdbool.h>
#include <stddef.h>
/**
* The vtable that defines selector behavior.
*
* match: returns true if the testcase matches the selector's criteria.
* copy_context: copies and returns the selector-specific context.
* destroy_context: releases resources held by the selector-specific context.
*/
struct yr_selector_vtable {
bool (*match)(yr_test_case_t testcase, void *context);
void *(*copy_context)(void *context);
void (*destroy_context)(void *context);
};
/**
* A testcase selector.
*/
typedef struct yr_selector {
struct yr_selector_vtable vtable;
void *context;
} *yr_selector_t;
/**
* A set of testcase selectors.
*/
typedef struct yr_selector_set {
size_t selector_count;
struct yr_selector selectors[];
} *yr_selector_set_t;
/**
* Create a "glob" testcase selector.
*
* This takes a specifier of the form [<suite_glob>:]<case_glob>, with both globs having the meaning
* ascribed by fnmatch(3).
*/
YACHTROCK_EXTERN yr_selector_t yr_selector_create_from_glob(const char *sel_specifier);
/**
* Create a selector with the given vtable and context.
*
* The context is used directly, that is, it is not first copied.
*/
YACHTROCK_EXTERN yr_selector_t yr_selector_create(struct yr_selector_vtable vtable, void *context);
/**
* Copy a selector.
*
* A new selector structure is allocated and its vtable and copied context are assigned.
*/
YACHTROCK_EXTERN yr_selector_t yr_selector_copy(yr_selector_t in);
/**
* Destroy a selector.
*
* The selector is freed after the context destroy method is applied to its context.
*/
YACHTROCK_EXTERN void yr_selector_destroy(yr_selector_t selector);
/**
* Returns true if the selector matches the testcase.
*/
YACHTROCK_EXTERN bool yr_selector_match_testcase(yr_selector_t selector,
yr_test_case_t testcase);
/**
* Create a selector set from an array of selectors.
*/
YACHTROCK_EXTERN yr_selector_set_t yr_selector_set_create(size_t selector_count,
yr_selector_t *selectors);
yr_selector_t *selectors);
/**
* Destroy a selector set.
*/
YACHTROCK_EXTERN void yr_selector_set_destroy(yr_selector_set_t set);
/**
* Determine if a selector set matches a testcase.
*
* This is true if any one of the selectors in the set match the testcase.
*/
YACHTROCK_EXTERN bool yr_selector_set_match_testcase(yr_selector_set_t set,
yr_test_case_t testcase);
/**
* Filter a test suite with a selector set.
*
* The resulting test suite contains only cases that match the selector set. It is also a single
* allocation, so all resources of the filtered set can and must be freed by the caller via a call
* to free(3), providing the suite.
*
* This function returns NULL, rather than an empty suite, if all cases were filtered out.
*/
YACHTROCK_EXTERN yr_test_suite_t
yr_test_suite_create_filtered(yr_test_suite_t suite, yr_selector_set_t filter);
/**
* Create a test suite collection from the input collection subject to the selector set's filter.
*
* The resulting suite collection is a single allocation and must be freed by the caller.
*
* This function returns NULL, rather than an empty collection, if all cases were filtered out.
*/
YACHTROCK_EXTERN yr_test_suite_collection_t
yr_test_suite_collection_create_filtered(yr_test_suite_collection_t collection,
yr_selector_set_t filter);

View File

@ -22,14 +22,33 @@ typedef void (*yr_test_suite_setup_function)(yr_test_suite_t suite);
typedef void (*yr_test_suite_teardown_function)(yr_test_suite_t suite);
#define __YR_DEVARIADICIFY_2(dummy, A, ...) A
/**
* Helper macro to define a testcase function.
*/
#define YR_TESTCASE(name, ...) void name(yr_test_case_t __YR_DEVARIADICIFY_2(dummy, ##__VA_ARGS__ , testcase) )
/**
* A single test case.
*
* name: the name of the case.
* testcase: the function that executes the test case.
* suite: a pointer to the suite containing the test case.
*/
struct yr_test_case {
const char *name;
yr_test_case_function testcase;
const struct yr_test_suite *suite;
};
/**
* Callbacks for suite lifecycle events.
*
* setup_case: called when a testcase is about to be invoked.
* teardown_case: called after a testcase is invoked.
* setup_suite: called when a test suite is about to be invoked, before any testcases or testcase setup
* callbacks.
* teardown_suite: called after a suite has been invoked.
*/
struct yr_suite_lifecycle_callbacks {
yr_test_case_setup_function setup_case;
yr_test_case_teardown_function teardown_case;
@ -37,6 +56,15 @@ struct yr_suite_lifecycle_callbacks {
yr_test_suite_teardown_function teardown_suite;
};
/**
* A test suite.
*
* name: the name of the suite.
* refcon: arbitrary pointer passed to suite lifecyle callbacks.
* lifecycle: suite lifecycle callbacks.
* num_cases: the number of cases.
* cases: the case structures themselves, allocated inline.
*/
struct yr_test_suite {
const char *name;
void *refcon;
@ -45,6 +73,9 @@ struct yr_test_suite {
yr_test_case_s cases[];
};
/**
* A set of callbacks that do nothing.
*/
YACHTROCK_EXTERN const struct yr_suite_lifecycle_callbacks YR_NO_CALLBACKS;
YACHTROCK_EXTERN yr_test_suite_t
@ -53,17 +84,33 @@ _yr_create_suite_from_functions(const char *name,
struct yr_suite_lifecycle_callbacks callbacks,
const char *cs_names,
yr_test_case_function first, ...);
/**
* Create a suite from a static set of functions.
*
* The case names are taken from the function names. All storage consumed by the suite is in one
* allocation and can and must be passed to free by the caller.
*/
#define yr_create_suite_from_functions(name, suite_refcon, lifecycle_callbacks, ...) \
_yr_create_suite_from_functions(name, suite_refcon, lifecycle_callbacks, \
# __VA_ARGS__, __VA_ARGS__)
/* Create a blank suite on the heap that you have to fill out.
* It has the right number of cases that have the suite pointers in the cases set correctly.
/**
* Create a blank suite on the heap that you have to fill out.
*
* The suite has the right number of cases that have the suite pointers in the cases set correctly.
* Everything else is zeroed/nulled out. This is the "escape hatch", really.
*/
YACHTROCK_EXTERN yr_test_suite_t
yr_create_blank_suite(size_t num_cases);
/**
* A collection of test suites.
*
* num_suites: the number of suites.
* suites: the suites. When created by libyachtrock functions, all storage in the suites is in the
* same allocation of the collection.
*/
struct yr_test_suite_collection {
size_t num_suites;
// note: storage in the same allocation when returned from libyachtrock functions
@ -72,13 +119,32 @@ struct yr_test_suite_collection {
typedef struct yr_test_suite_collection yr_test_suite_collection_s;
typedef yr_test_suite_collection_s *yr_test_suite_collection_t;
/**
* Create a collection from an array of suites, copying the suites.
*
* The resulting collection contains all suite storage as well and must be passed to free by the
* caller.
*/
YACHTROCK_EXTERN yr_test_suite_collection_t
yr_test_suite_collection_create_from_suites(size_t num_suites, yr_test_suite_t *suites);
/**
* Create a collection from a set of collections, passed variadically.
*
* The resulting collection has copies of all suites in the input collections, containing all of
* their storage, and must be passed to free by the caller.
*/
YACHTROCK_EXTERN yr_test_suite_collection_t
yr_test_suite_collection_create_from_collections(size_t num_collections,
yr_test_suite_collection_t collection1,
...);
/**
* Create a collection from a set of collections, passed as an array.
*
* The resulting collection has copies of all suites in the input collections, containing all of
* their storage, and must be passed to free by the caller.
*/
YACHTROCK_EXTERN yr_test_suite_collection_t
yr_test_suite_collection_create_from_collection_array(size_t num_collections,
yr_test_suite_collection_t *collections);
@ -86,6 +152,9 @@ yr_test_suite_collection_create_from_collection_array(size_t num_collections,
#if YACHTROCK_DLOPEN
/**
* The name of the test suite collection discovery function that test dylibs must export.
*/
#define YACHTROCK_MODULE_DISCOVER_NAME yr_module_create_test_suite_collection
#define YACHTROCK_DISCOVER_VERSION 1
@ -103,6 +172,9 @@ _yr_create_version_mismatch_error(unsigned discover_version,
#define YACHTROCK_DISCOVERER_EXTERN extern
#endif
/**
* Helper macro to define the test suite collection discovery function.
*/
#define YACHTROCK_DEFINE_TEST_SUITE_COLLECTION_DISCOVERER() \
static yr_test_suite_collection_t \
YACHTROCK_MODULE_DISCOVER_NAME ## __impl(unsigned discover_version, \
@ -126,9 +198,19 @@ _yr_create_version_mismatch_error(unsigned discover_version,
char **errmsg)
/**
* Discover a test suite collection provided by a dylib, by providing the path to the dylib.
*
* The resulting collection has all suite's storage and must be freed by the caller.
*/
YACHTROCK_EXTERN yr_test_suite_collection_t
yr_test_suite_collection_create_from_dylib_path(const char *path, char **errmsg);
/**
* Discover a test suite collection provided by a dylib, by providing a handle for the dylib.
*
* The resulting collection has all suite's storage and must be freed by the caller.
*/
YACHTROCK_EXTERN yr_test_suite_collection_t
yr_test_suite_collection_create_from_handle(void *handle, char **errmsg);

View File

@ -3,10 +3,16 @@
#include <yachtrock/base.h>
/**
* Yachtrock version constants.
*/
YACHTROCK_EXTERN const unsigned yr_major;
YACHTROCK_EXTERN const unsigned yr_minor;
YACHTROCK_EXTERN const unsigned yr_patch;
/**
* Precomposed Yachtrock version string.
*/
YACHTROCK_EXTERN const char *yr_version;
#endif

View File

@ -10,17 +10,34 @@
#include <yachtrock/results.h>
#include <yachtrock/selector.h>
/**
* Runtime callbacks and result hooks that print status to stderr.
*/
YACHTROCK_EXTERN const struct yr_runtime_callbacks YR_BASIC_STDERR_RUNTIME_CALLBACKS;
YACHTROCK_EXTERN const struct yr_result_hooks YR_BASIC_STDERR_RESULT_HOOKS;
/**
* Run a suite and print status and progress information to stderr. Returns true if all tests
* passed.
*/
YACHTROCK_EXTERN bool yr_basic_run_suite(yr_test_suite_t suite);
/**
* Run a suite with specified result hooks and runtime callbacks. Returns true if all tests passed.
*/
YACHTROCK_EXTERN bool yr_run_suite_with_result_hooks(yr_test_suite_t suite,
struct yr_result_hooks hooks,
struct yr_runtime_callbacks runtime_callbacks);
/**
* Run a suite with runtime callbacks, recording results in the specified store.
*/
YACHTROCK_EXTERN void yr_run_suite_under_store(yr_test_suite_t suite,
yr_result_store_t store,
struct yr_runtime_callbacks runtime_callbacks);
/**
* Run a suite collection with runtime callbacks, recording results in the specified store.
*/
YACHTROCK_EXTERN void yr_run_suite_collection_under_store(yr_test_suite_collection_t collection,
yr_result_store_t store,
struct yr_runtime_callbacks runtime_callbacks);

View File

@ -115,9 +115,15 @@ yr_selector_t yr_selector_create_from_glob(const char *sel_specifier)
testcase = NULL;
}
struct parsed_glob_specifier *parsed = create_parsed_glob_specifier(suite, testcase);
yr_selector_t result = yr_selector_create(testname_glob_selector_vtable, parsed);
return result;
}
yr_selector_t yr_selector_create(struct yr_selector_vtable vtable, void *context)
{
yr_selector_t result = yr_malloc(sizeof(struct yr_selector));
result->vtable = testname_glob_selector_vtable;
result->context = parsed;
result->vtable = vtable;
result->context = context;
return result;
}

View File

@ -8,11 +8,26 @@ cat <<EOF
/* This file is generated automatically when libyachtrock is built. */
/**
* Whether this libyachtrock is targeting a "UNIX-y" environment.
*/
#define YACHTROCK_UNIXY $YACHTROCK_UNIXY
/**
* Whether this libyachtrock is targeting a "POSIX-y" environment.
*/
#define YACHTROCK_POSIXY $YACHTROCK_POSIXY
/**
* Whether this libyachtrock supports dynamic testcase discovery through the use of dlopen(3).
*/
#define YACHTROCK_DLOPEN $YACHTROCK_DLOPEN
/**
* Whether this libyachtrock supports multi-process test execution.
*/
#define YACHTROCK_MULTIPROCESS $YACHTROCK_MULTIPROCESS
#endif
EOF
EOF