Skip to content

Commit 9219747

Browse files
committed
Add support for deferring cleanup operations
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>
1 parent 0d7c52f commit 9219747

17 files changed

+709
-14
lines changed

GNUmakefile

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,23 +30,30 @@ $(eval $(call ZMK.Import,GitVersion))
3030

3131
# Manual pages
3232
manpages = \
33-
libzt-test.1 \
34-
libzt.3 \
35-
zt_check.3 \
36-
zt_claim.3 \
3733
ZT_CMP_BOOL.3 \
3834
ZT_CMP_INT.3 \
3935
ZT_CMP_PTR.3 \
4036
ZT_CMP_RUNE.3 \
4137
ZT_CMP_UINT.3 \
4238
ZT_CURRENT_LOCATION.3 \
4339
ZT_FALSE.3 \
40+
ZT_NOT_NULL.3 \
41+
ZT_NULL.3 \
42+
ZT_TRUE.3 \
43+
libzt-test.1 \
44+
libzt.3 \
45+
zt_check.3 \
46+
zt_claim.3 \
47+
zt_closure.3 \
48+
zt_closure_func0.3 \
49+
zt_closure_func1.3 \
50+
zt_defer.3 \
4451
zt_location.3 \
4552
zt_location_at.3 \
4653
zt_main.3 \
47-
ZT_NOT_NULL.3 \
48-
ZT_NULL.3 \
4954
zt_pack_boolean.3 \
55+
zt_pack_closure0.3 \
56+
zt_pack_closure1.3 \
5057
zt_pack_integer.3 \
5158
zt_pack_nothing.3 \
5259
zt_pack_pointer.3 \
@@ -56,7 +63,6 @@ manpages = \
5663
zt_test.3 \
5764
zt_test_case_func.3 \
5865
zt_test_suite_func.3 \
59-
ZT_TRUE.3 \
6066
zt_value.3 \
6167
zt_visit_test_case.3 \
6268
zt_visitor.3

NEWS

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,20 @@
1+
Changes in 0.4 (unreleased):
2+
3+
* Test suites can now offer resources, through the zt_visit_resource()
4+
function. Resources are remembered by the library and used in specific
5+
circumstances. This mode decouples allocation of resources such as memory
6+
or threads from the internals of the library and puts ultimate control in
7+
the hand of the test author.
8+
9+
* Test functions can now defer cleanup operations through the zt_defer()
10+
function and the family of supporting types, functions and macros.
11+
Deferred functions execute when the test function returns, both
12+
successfully and abnormally, through non-local exit.
13+
14+
To use deferred cleanup, the test suite that either directly or indirectly
15+
contains a test function must offer the resource ZT_RESOURCE_DEFER_CLOSURES,
16+
which represent closures that can be prepared and called at a later moment.
17+
118
Changes in 0.3:
219

320
* Pointers captured in zt_value by zt_pack_pointer() are now constant.
@@ -44,6 +61,18 @@ Changes in 0.3:
4461
tests cross builds libzt for DOS and tests it with DosBox and DOS/32
4562
extender.
4663

64+
* Test suites can now provide resources available to test functions.
65+
Resources are provided using the new function zt_visit_resource().
66+
The only resource currently available are defer slots.
67+
68+
* Using zt_defer() tests can arrange for reliable execution of functions
69+
even in the case of non-local test exit. Each use requires a defer slot
70+
that must be provided as a resource by the test suite. If defer slots
71+
are unavailable the function that was being registered is invoked
72+
immediately and the test fails as if zt_assert() had failed. This ensures
73+
defer mechanism is reliable and can be used to manage global state
74+
or release system resources.
75+
4776
Changes in 0.2:
4877

4978
* Argument type to all unit test functions was typedef'd

libzt.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,13 @@ EXPORTS
99
zt_cmp_ptr
1010
zt_cmp_rune
1111
zt_cmp_uint
12+
zt_defer
1213
zt_false
1314
zt_main
1415
zt_not_null
1516
zt_null
1617
zt_pack_rune
1718
zt_true
19+
zt_visit_resource
1820
zt_visit_test_case
1921
zt_visit_test_suite

libzt.export_list

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,13 @@ _zt_cmp_int
66
_zt_cmp_ptr
77
_zt_cmp_rune
88
_zt_cmp_uint
9+
_zt_defer
910
_zt_false
1011
_zt_main
1112
_zt_not_null
1213
_zt_null
1314
_zt_pack_rune
1415
_zt_true
16+
_zt_visit_resource
1517
_zt_visit_test_case
1618
_zt_visit_test_suite

libzt.map

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,6 @@ VERS_0_2 {
2525
VERS_0_3 {
2626
global:
2727
zt_cmp_ptr;
28+
zt_visit_resource;
29+
zt_defer;
2830
} VERS_0_2;

man/ZT_TRUE.3.in

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
#define ZT_TRUE(value) \\
1212
zt_true( \\
1313
ZT_CURRENT_LOCATION(), \\
14-
zt_pack_boolean((value), #value)) \\
14+
zt_pack_boolean((value), #value))
1515
.Ed
1616
.Ft zt_claim
1717
.Fo zt_true

man/zt_check.3.in

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,10 +57,16 @@ test on failure.
5757
Non-local exit from
5858
.Fn zt_assert
5959
is implemented using
60-
.Fn siglongjump .
61-
One should not depend on neither C++ destructors nor the GCC cleanup function extension for correct resource management.
60+
.Fn siglongjump
61+
or
62+
.Fn longjump ,
63+
depending on platform support. One should not depend on neither C++
64+
destructors nor the GCC cleanup function extension for correct resource
65+
management. Instead, please use
66+
.Fn zt_defer
67+
to arrange a function to be called after the test function returns.
6268
.Sh RETURN VALUES
63-
Neither function return anything. Failures are remembered inside the opaque
69+
Neither function returns anything. Failures are remembered inside the opaque
6470
.Nm zt_test
6571
structure passed by pointer (aka
6672
.Nm zt_t )

man/zt_closure.3.in

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
.Dd January 14, 2020
2+
.Os libzt @VERSION@
3+
.Dt zt_closure 3 PRM
4+
.Sh NAME
5+
.Nm zt_closure
6+
.Nd structure representing a function closure
7+
.Sh SYNOPSIS
8+
.In zt.h
9+
.Bd -literal
10+
typedef struct zt_closure {
11+
union {
12+
zt_closure_func0 args0;
13+
zt_closure_func1 args1;
14+
} func;
15+
zt_location location;
16+
int nargs;
17+
zt_value args[1];
18+
} zt_closure;
19+
.Be
20+
.Sh DESCRIPTION
21+
The structure
22+
.Nm
23+
contains a function pointer and at most one argument represented as a
24+
.Nm zt_value .
25+
.Pp
26+
Closures are used by
27+
.Fn zt_defer
28+
to facilitate reliable management of memory and other resources inside test
29+
code. It can be also used to setup and restore mocking of application
30+
behaviors. Function arguments are represented by
31+
.Nm zt_value
32+
for convenience, to avoid casting integers and pointers. Test code is
33+
expected to provide wrapper functions that take a
34+
.Nm zt_value
35+
argument and pass the call to another function such as
36+
.Fn free
37+
or
38+
.Fn close .
39+
.Sh IMPLEMENTATION NOTES
40+
The
41+
.Nm location
42+
field describes location in the source code where the closure was packed. It
43+
is used in some diagnostic error messages.
44+
.Sh SEE ALSO
45+
.Xr zt_closure_func0 3 ,
46+
.Xr zt_closure_func1 3 ,
47+
.Xr zt_defer 3 ,
48+
.Xr zt_location 3 ,
49+
.Xr zt_pack_closure0 3 ,
50+
.Xr zt_pack_closure1 3 ,
51+
.Xr zt_value 3
52+
.Sh HISTORY
53+
The structure
54+
.Nm
55+
first appeared in libzt 0.4
56+
.Sh AUTHORS
57+
.An "Zygmunt Krynicki" Aq Mt me@zygoon.pl

man/zt_closure_func0.3.in

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
.Dd January 14, 2020
2+
.Os libzt @VERSION@
3+
.Dt zt_closure_func0 3 PRM
4+
.Sh NAME
5+
.Nm zt_closure_func0
6+
.Nd function type for closures without arguments
7+
.Sh SYNOPSIS
8+
.In zt.h
9+
.Ft typedef void
10+
.Fo (*zt_closure_func0)
11+
.Fa "void"
12+
.Fc
13+
.Sh DESCRIPTION
14+
.Nm
15+
is a function type that takes no arguments. It is used by
16+
.Nm zt_closure .
17+
.Sh SEE ALSO
18+
.Xr zt_closure 3
19+
.Sh HISTORY
20+
The type
21+
.Nm
22+
first appeared in libzt 0.4
23+
.Sh AUTHORS
24+
.An "Zygmunt Krynicki" Aq Mt me@zygoon.pl

man/zt_closure_func1.3.in

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
.Dd January 14, 2020
2+
.Os libzt @VERSION@
3+
.Dt zt_closure_func1 3 PRM
4+
.Sh NAME
5+
.Nm zt_closure_func1
6+
.Nd function type for closures with one argument
7+
.Sh SYNOPSIS
8+
.In zt.h
9+
.Ft typedef void
10+
.Fo (*zt_closure_func1)
11+
.Fa "zt_value arg1"
12+
.Fc
13+
.Sh DESCRIPTION
14+
.Nm
15+
is a function type that takes one argument. It is used by
16+
.Nm zt_closure .
17+
.Sh SEE ALSO
18+
.Xr zt_closure 3 ,
19+
.Xr zt_value 3
20+
.Sh HISTORY
21+
The type
22+
.Nm
23+
first appeared in libzt 0.4
24+
.Sh AUTHORS
25+
.An "Zygmunt Krynicki" Aq Mt me@zygoon.pl

man/zt_defer.3.in

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
.Dd January 14, 2020
2+
.Os libzt @VERSION@
3+
.Dt zt_defer 3 PRM
4+
.Sh NAME
5+
.Nm zt_defer,
6+
.Nd call a function after the current test terminates
7+
.Sh SYNOPSIS
8+
.In zt.h
9+
.Ft void
10+
.Fo zt_defer
11+
.Fa "zt_t t"
12+
.Fa "zt_closure closure"
13+
.Fc
14+
.Sh DESCRIPTION
15+
The function
16+
.Fn
17+
arranges for the function encapsulated by
18+
.Em closure
19+
to be called at after the current test function returns.
20+
This mechanism works even in the case of non-local exit
21+
used by
22+
.Fn zt_assert .
23+
It is designed to allow test authors to reclaim allocated memory, close
24+
files, restore the state of mocked state or other tasks that are required
25+
to ensure program consistency.
26+
.Pp
27+
In the spirit of robustness, the burden of providing memory for deferred
28+
function calls is shifted to the test author. Test authors must provide a
29+
.Em test resource
30+
of the kind
31+
.Em ZT_RESOURCE_DEFER_CLOSURES
32+
with enough capacity to remember all of the deferred calls in all the tests.
33+
This can be done by calling
34+
.Fn zt_visit_resource
35+
anywhere in a test suite function, before visiting the first test that relies
36+
on defer.
37+
.Pp
38+
If insufficient amount of defer resources are available, the defer function
39+
is called immediately, the current test is marked as failed and the test
40+
terminates through a non-local exit. Note that succesfully deferred calls
41+
are also executed regardless of the means of exiting from a test function.
42+
.Sh IMPLEMENTATION NOTES
43+
Defer is implemented using a stack of
44+
.Nm zt_closure
45+
structures. Before visiting a test function, the stack is emptied.
46+
At the end of the test subsequent closures are popped and executed.
47+
.Sh RETURN VALUES
48+
.Nm
49+
does not return any value.
50+
.Sh EXAMPLES
51+
The following example shows a minimal test program using resources and defer
52+
to ensure that allocated memory is not leaked.
53+
.Bd -literal -offset indent
54+
#include <stdlib.h>
55+
#include <zt.h>
56+
57+
static void free_value(zt_value val) {
58+
free(val.as.pointer);
59+
}
60+
61+
#define FREE(ptr) ZT_CLOSURE1(free_value, zt_pack_pointer((ptr), #ptr))
62+
63+
static void test_allocated_memory(zt_t t) {
64+
void *m = malloc(100);
65+
zt_assert(t, ZT_NOT_NULL(m));
66+
zt_defer(t, FREE(m));
67+
/* Do something with the allocated memory. */
68+
}
69+
70+
static void test_suite(zt_visitor v) {
71+
zt_closure closures[1];
72+
zt_visit_resource(v, ZT_RESOURCE_DEFER_CLOSURES,
73+
sizeof closures / sizeof *closures, closures);
74+
ZT_VISIT_TEST_CASE(v, test_allocated_memory);
75+
}
76+
77+
int main(int argc, char** argv, char** envp) {
78+
return zt_main(argc, argv, envp, test_suite);
79+
}
80+
.Ed
81+
.Sh SEE ALSO
82+
.Xr ZT_CLOSURE0 3 ,
83+
.Xr ZT_CLOSURE1 3 ,
84+
.Xr zt_closure 3 ,
85+
.Xr zt_visit_resource 3
86+
.Sh HISTORY
87+
The function
88+
.Nm
89+
first appeared in libzt 0.4
90+
.Sh AUTHORS
91+
.An "Zygmunt Krynicki" Aq Mt me@zygoon.pl

0 commit comments

Comments
 (0)