Skip to content

Commit

Permalink
Add support for deferring cleanup operations
Browse files Browse the repository at this point in the history
This patch adds two main features:

 - ability to offer resources to the test system
 - ability to queue cleanup operations

Signed-off-by: Zygmunt Krynicki <me@zygoon.pl>
  • Loading branch information
zyga committed Jun 26, 2020
1 parent 9c0afdb commit 6a25675
Show file tree
Hide file tree
Showing 17 changed files with 709 additions and 14 deletions.
20 changes: 13 additions & 7 deletions GNUmakefile
Original file line number Diff line number Diff line change
Expand Up @@ -30,23 +30,30 @@ $(eval $(call ZMK.Import,GitVersion))

# Manual pages
manpages = \
libzt-test.1 \
libzt.3 \
zt_check.3 \
zt_claim.3 \
ZT_CMP_BOOL.3 \
ZT_CMP_INT.3 \
ZT_CMP_PTR.3 \
ZT_CMP_RUNE.3 \
ZT_CMP_UINT.3 \
ZT_CURRENT_LOCATION.3 \
ZT_FALSE.3 \
ZT_NOT_NULL.3 \
ZT_NULL.3 \
ZT_TRUE.3 \
libzt-test.1 \
libzt.3 \
zt_check.3 \
zt_claim.3 \
zt_closure.3 \
zt_closure_func0.3 \
zt_closure_func1.3 \
zt_defer.3 \
zt_location.3 \
zt_location_at.3 \
zt_main.3 \
ZT_NOT_NULL.3 \
ZT_NULL.3 \
zt_pack_boolean.3 \
zt_pack_closure0.3 \
zt_pack_closure1.3 \
zt_pack_integer.3 \
zt_pack_nothing.3 \
zt_pack_pointer.3 \
Expand All @@ -56,7 +63,6 @@ manpages = \
zt_test.3 \
zt_test_case_func.3 \
zt_test_suite_func.3 \
ZT_TRUE.3 \
zt_value.3 \
zt_visit_test_case.3 \
zt_visitor.3
Expand Down
29 changes: 29 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
@@ -1,3 +1,20 @@
Changes in 0.4 (unreleased):

* Test suites can now offer resources, through the zt_visit_resource()
function. Resources are remembered by the library and used in specific
circumstances. This mode decouples allocation of resources such as memory
or threads from the internals of the library and puts ultimate control in
the hand of the test author.

* Test functions can now defer cleanup operations through the zt_defer()
function and the family of supporting types, functions and macros.
Deferred functions execute when the test function returns, both
successfully and abnormally, through non-local exit.

To use deferred cleanup, the test suite that either directly or indirectly
contains a test function must offer the resource ZT_RESOURCE_DEFER_CLOSURES,
which represent closures that can be prepared and called at a later moment.

Changes in 0.3.1:

* The build system has been updated to ZMK 0.3.5. This should fix
Expand Down Expand Up @@ -49,6 +66,18 @@ Changes in 0.3:
tests cross builds libzt for DOS and tests it with DosBox and DOS/32
extender.

* Test suites can now provide resources available to test functions.
Resources are provided using the new function zt_visit_resource().
The only resource currently available are defer slots.

* Using zt_defer() tests can arrange for reliable execution of functions
even in the case of non-local test exit. Each use requires a defer slot
that must be provided as a resource by the test suite. If defer slots
are unavailable the function that was being registered is invoked
immediately and the test fails as if zt_assert() had failed. This ensures
defer mechanism is reliable and can be used to manage global state
or release system resources.

Changes in 0.2:

* Argument type to all unit test functions was typedef'd
Expand Down
2 changes: 2 additions & 0 deletions libzt.def
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,13 @@ EXPORTS
zt_cmp_ptr
zt_cmp_rune
zt_cmp_uint
zt_defer
zt_false
zt_main
zt_not_null
zt_null
zt_pack_rune
zt_true
zt_visit_resource
zt_visit_test_case
zt_visit_test_suite
2 changes: 2 additions & 0 deletions libzt.export_list
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@ _zt_cmp_int
_zt_cmp_ptr
_zt_cmp_rune
_zt_cmp_uint
_zt_defer
_zt_false
_zt_main
_zt_not_null
_zt_null
_zt_pack_rune
_zt_true
_zt_visit_resource
_zt_visit_test_case
_zt_visit_test_suite
2 changes: 2 additions & 0 deletions libzt.map
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,6 @@ VERS_0_2 {
VERS_0_3 {
global:
zt_cmp_ptr;
zt_visit_resource;
zt_defer;
} VERS_0_2;
2 changes: 1 addition & 1 deletion man/ZT_TRUE.3.in
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
#define ZT_TRUE(value) \\
zt_true( \\
ZT_CURRENT_LOCATION(), \\
zt_pack_boolean((value), #value)) \\
zt_pack_boolean((value), #value))
.Ed
.Ft zt_claim
.Fo zt_true
Expand Down
12 changes: 9 additions & 3 deletions man/zt_check.3.in
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,16 @@ test on failure.
Non-local exit from
.Fn zt_assert
is implemented using
.Fn siglongjump .
One should not depend on neither C++ destructors nor the GCC cleanup function extension for correct resource management.
.Fn siglongjump
or
.Fn longjump ,
depending on platform support. One should not depend on neither C++
destructors nor the GCC cleanup function extension for correct resource
management. Instead, please use
.Fn zt_defer
to arrange a function to be called after the test function returns.
.Sh RETURN VALUES
Neither function return anything. Failures are remembered inside the opaque
Neither function returns anything. Failures are remembered inside the opaque
.Nm zt_test
structure passed by pointer (aka
.Nm zt_t )
Expand Down
57 changes: 57 additions & 0 deletions man/zt_closure.3.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
.Dd January 14, 2020
.Os libzt @VERSION@
.Dt zt_closure 3 PRM
.Sh NAME
.Nm zt_closure
.Nd structure representing a function closure
.Sh SYNOPSIS
.In zt.h
.Bd -literal
typedef struct zt_closure {
union {
zt_closure_func0 args0;
zt_closure_func1 args1;
} func;
zt_location location;
int nargs;
zt_value args[1];
} zt_closure;
.Be
.Sh DESCRIPTION
The structure
.Nm
contains a function pointer and at most one argument represented as a
.Nm zt_value .
.Pp
Closures are used by
.Fn zt_defer
to facilitate reliable management of memory and other resources inside test
code. It can be also used to setup and restore mocking of application
behaviors. Function arguments are represented by
.Nm zt_value
for convenience, to avoid casting integers and pointers. Test code is
expected to provide wrapper functions that take a
.Nm zt_value
argument and pass the call to another function such as
.Fn free
or
.Fn close .
.Sh IMPLEMENTATION NOTES
The
.Nm location
field describes location in the source code where the closure was packed. It
is used in some diagnostic error messages.
.Sh SEE ALSO
.Xr zt_closure_func0 3 ,
.Xr zt_closure_func1 3 ,
.Xr zt_defer 3 ,
.Xr zt_location 3 ,
.Xr zt_pack_closure0 3 ,
.Xr zt_pack_closure1 3 ,
.Xr zt_value 3
.Sh HISTORY
The structure
.Nm
first appeared in libzt 0.4
.Sh AUTHORS
.An "Zygmunt Krynicki" Aq Mt me@zygoon.pl
24 changes: 24 additions & 0 deletions man/zt_closure_func0.3.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
.Dd January 14, 2020
.Os libzt @VERSION@
.Dt zt_closure_func0 3 PRM
.Sh NAME
.Nm zt_closure_func0
.Nd function type for closures without arguments
.Sh SYNOPSIS
.In zt.h
.Ft typedef void
.Fo (*zt_closure_func0)
.Fa "void"
.Fc
.Sh DESCRIPTION
.Nm
is a function type that takes no arguments. It is used by
.Nm zt_closure .
.Sh SEE ALSO
.Xr zt_closure 3
.Sh HISTORY
The type
.Nm
first appeared in libzt 0.4
.Sh AUTHORS
.An "Zygmunt Krynicki" Aq Mt me@zygoon.pl
25 changes: 25 additions & 0 deletions man/zt_closure_func1.3.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
.Dd January 14, 2020
.Os libzt @VERSION@
.Dt zt_closure_func1 3 PRM
.Sh NAME
.Nm zt_closure_func1
.Nd function type for closures with one argument
.Sh SYNOPSIS
.In zt.h
.Ft typedef void
.Fo (*zt_closure_func1)
.Fa "zt_value arg1"
.Fc
.Sh DESCRIPTION
.Nm
is a function type that takes one argument. It is used by
.Nm zt_closure .
.Sh SEE ALSO
.Xr zt_closure 3 ,
.Xr zt_value 3
.Sh HISTORY
The type
.Nm
first appeared in libzt 0.4
.Sh AUTHORS
.An "Zygmunt Krynicki" Aq Mt me@zygoon.pl
91 changes: 91 additions & 0 deletions man/zt_defer.3.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
.Dd January 14, 2020
.Os libzt @VERSION@
.Dt zt_defer 3 PRM
.Sh NAME
.Nm zt_defer,
.Nd call a function after the current test terminates
.Sh SYNOPSIS
.In zt.h
.Ft void
.Fo zt_defer
.Fa "zt_t t"
.Fa "zt_closure closure"
.Fc
.Sh DESCRIPTION
The function
.Fn
arranges for the function encapsulated by
.Em closure
to be called at after the current test function returns.
This mechanism works even in the case of non-local exit
used by
.Fn zt_assert .
It is designed to allow test authors to reclaim allocated memory, close
files, restore the state of mocked state or other tasks that are required
to ensure program consistency.
.Pp
In the spirit of robustness, the burden of providing memory for deferred
function calls is shifted to the test author. Test authors must provide a
.Em test resource
of the kind
.Em ZT_RESOURCE_DEFER_CLOSURES
with enough capacity to remember all of the deferred calls in all the tests.
This can be done by calling
.Fn zt_visit_resource
anywhere in a test suite function, before visiting the first test that relies
on defer.
.Pp
If insufficient amount of defer resources are available, the defer function
is called immediately, the current test is marked as failed and the test
terminates through a non-local exit. Note that succesfully deferred calls
are also executed regardless of the means of exiting from a test function.
.Sh IMPLEMENTATION NOTES
Defer is implemented using a stack of
.Nm zt_closure
structures. Before visiting a test function, the stack is emptied.
At the end of the test subsequent closures are popped and executed.
.Sh RETURN VALUES
.Nm
does not return any value.
.Sh EXAMPLES
The following example shows a minimal test program using resources and defer
to ensure that allocated memory is not leaked.
.Bd -literal -offset indent
#include <stdlib.h>
#include <zt.h>

static void free_value(zt_value val) {
free(val.as.pointer);
}

#define FREE(ptr) ZT_CLOSURE1(free_value, zt_pack_pointer((ptr), #ptr))

static void test_allocated_memory(zt_t t) {
void *m = malloc(100);
zt_assert(t, ZT_NOT_NULL(m));
zt_defer(t, FREE(m));
/* Do something with the allocated memory. */
}

static void test_suite(zt_visitor v) {
zt_closure closures[1];
zt_visit_resource(v, ZT_RESOURCE_DEFER_CLOSURES,
sizeof closures / sizeof *closures, closures);
ZT_VISIT_TEST_CASE(v, test_allocated_memory);
}

int main(int argc, char** argv, char** envp) {
return zt_main(argc, argv, envp, test_suite);
}
.Ed
.Sh SEE ALSO
.Xr ZT_CLOSURE0 3 ,
.Xr ZT_CLOSURE1 3 ,
.Xr zt_closure 3 ,
.Xr zt_visit_resource 3
.Sh HISTORY
The function
.Nm
first appeared in libzt 0.4
.Sh AUTHORS
.An "Zygmunt Krynicki" Aq Mt me@zygoon.pl
Loading

0 comments on commit 6a25675

Please sign in to comment.