ericherman / libecheck Goto Github PK
View Code? Open in Web Editor NEW"E(asy)Check": Boiler-plate to make simple testing easier, when I do not want a whole testing framework.
License: GNU Lesser General Public License v3.0
"E(asy)Check": Boiler-plate to make simple testing easier, when I do not want a whole testing framework.
License: GNU Lesser General Public License v3.0
libecheck consists of two main parts: * E(asy)Embed: make writing and testing embedded libraries easier. * E(asy)Check: Boiler-plate to make simple testing easier, when one does not want a whole testing framework. ECheck ------ The basic idea is that you get this kind of interface: check_[TYPE](actual, expected); The function returns 0 on success, and error code (typically 1) on failure. for instance: error_code = check_int(val, 42); assert(error_code == 0); To send error messages to a log, first construct a log object, which is defined in eembed.h (see below). Then use the "lcheck" form: error_code = lcheck_char(log, val, 'a'); In hosted systems which have stdio.h, to send error messages to a FILE*, set the context on the default logger: FILE *logfile = fopen(filename, "w"); echeck_default_log->context = logfile; error_code = check_char(val, 'a'); To include a message for additional context, use the "_m" form: error_code = check_size_t_m(size_of(foo), foo_len, "BAD MOJO!"); And the forms can be combined: error_code = lcheck_str_m(log, actual, "expected str", "BAD MOJO!") To check a byte[] buffer, additional length parameters are required: error_code = check_byte_array(actual, actual_len, expected, expected_len); To check a double, an epsilon is used to approximate equality. For example, the would be equal: actual = 1.0000003; expect = 1.0000004; epsilon = 0.0001; error_code = check_double(actual, expected, epsilon); The following would not be equal: actual = 1.0000003; expect = 1.0000004; epsilon = 0.000000001; error_code = check_double(actual, expected, epsilon); For ease of use, a default scaled epsilon is available: check_double_scaled_epsilon(actual, expected); is the same as, and #defined to be: check_double(actual, expected, ((expected) * DBL_EPSILON)); Thus, if "expected" is a function with side-effects, that the call to "check_double_scaled_epsilon" will call the "expected" function twice. If you wish to return from main() with a count of errors, this can be error-prone, especially if (failures % 256 == 0). This will not be a problem if you use "check_status", because this function caps values at 127 (and -128). return check_status(failures); EEmbed ------ Embedded environments may not support very much more than what is required of "freestanding" implementations, thus it might be as little as: <float.h> <iso646.h> <limits.h> <stdarg.h> <stdbool.h> <stddef.h> <stdint.h> That said however, often there is at least some support for serial I/O even when if it is lacking the lovely full-features of fprintf and sprintf. Additionally, development may be done in a "hosted" environment where the developer has access to the full standard library. The goal of this library is to make it straight-forward to develop libraries which are useful in both freestanding and hosted applications. Since "assert.h" is not in the list of freestanding headers, "eembed-assert.h" can be used. In the standard hosted case, "eembed_assert(expression)" is defined to be "assert(expression)", in the freestanding case, it is defined to make use of "eembed_system_print" and then call the function: void (*eembed_assert_crash)(void); By default, in the hosted case, this calls exit(EXIT_FAILURE) and in the freestanding case, it is a null function pointer which it can be redefined if other action is needed to reset the firmware. Then it can be used just like "assert" and also honors NDEBUG: eembed_assert(a == b); In a hosted environment, the global eembed_err_log is a wrapper for fprintf with stderr, the eembed_out_log is a wrapper for fprintf (stdout), and the eembed_null_log ignores the input. In a freestanding environment, the eembed_out_log and eembed_err_log point to the same eembed_null_log instance, and thus they ignores input. This is the structure of the eembed_log: struct eembed_log { void *context; void (*append_c)(struct eembed_log *log, char c); void (*append_s)(struct eembed_log *log, const char *str); void (*append_ul)(struct eembed_log *log, uint64_t ul); void (*append_l)(struct eembed_log *log, int64_t l); void (*append_f)(struct eembed_log *log, long double f); void (*append_vp)(struct eembed_log *log, const void *ptr); void (*append_eol)(struct eembed_log *log); } extern struct eembed_log *eembed_out_log; extern struct eembed_log *eembed_err_log; extern struct eembed_log *eembed_null_log; Exmple useage: struct eembed_log *out = eembed_out_log; out->append_s(out, "hello, world."); The "eembed-arduino.cpp" gives an example of how the eembed_err_log and the eembed_system_print functions can be set to use serial printing. Because it can be handy to print numbers, simple number-to-string functions are provided. In the hosted case, these simply wrap sprintf; in the freestanding case, straight-forward, if inefficient, implementations are provided: char *eembed_long_to_str(char *buf, size_t len, long l); char *eembed_ulong_to_str(char *buf, size_t len, unsigned long ul); char *eembed_ulong_to_hex(char *buf, size_t len, unsigned long z); char *eembed_float_to_str(char *buf, size_t len, double f); Additionally, there are a handful of functions which are requiried by echeck or are needed by nearly any program. This library provides function pointers to a few of these functions which in the hosted case simply point to the standard library version. In the freestanding case, very basic implementations of a few of these functions are provided: int (*eembed_memcmp)(const void *s1, const void *s2, size_t n); void *(*eembed_memcpy)(void *dest, const void *src, size_t n); void *(*eembed_memmove)(void *dest, const void *src, size_t n); void *(*eembed_memset)(void *dest, int val, size_t n); char *(*eembed_strcat)(char *dest, const char *src); char *(*eembed_strncat)(char *dest, const char *src, size_t n); int (*eembed_strcmp)(const char *s1, const char *s2); int (*eembed_strncmp)(const char *s1, const char *s2, size_t n); char *(*eembed_strcpy)(char *dest, const char *src); char *(*eembed_strncpy)(char *dest, const char *src, size_t n); size_t (*eembed_strlen)(const char *s); size_t (*eembed_strnlen)(const char *s, size_t maxlen); char *(*eembed_strstr)(const char *haystack, const char *needle); The default malloc/free functions are wrapped in the HOSTED case. In the freestanding case, eembed_malloc and eembed_calloc default to functions which return a NULL pointer: void *eembed_malloc(size_t size); void *eembed_calloc(size_t nmemb, size_t size); void *eembed_realloc(void *ptr, size_t size); void *eembed_reallocarray(void *ptr, size_t nmemb, size_t size); void eembed_free(void *ptr); Especially important for testing and for the embedded case, the allocator is plug-able with this structure: struct eembed_allocator { void *context; void *(*malloc)(struct eembed_allocator *ea, size_t size); void *(*calloc)(struct eembed_allocator *ea, size_t nmemb, size_t size); void *(*realloc)(struct eembed_allocator *ea, void *ptr, size_t size); void *(*reallocarray)(struct eembed_allocator *ea, void *ptr, size_t nmemb, size_t size); void (*free)(struct eembed_allocator *ea, void *ptr); }; extern struct eembed_allocator *eembed_global_allocator; The eembed_malloc/free functions make use of the eembed_global_allocator. Especially for the embedded case, eembed.h includes a simple allocator which can be constructed from a byte array which can be provided during firmware initialization: /* globals */ unsigned char bytes[1024]; void setup(void) { eembed_global_allocator = eembed_bytes_allocator(bytes, 1024); } If programs are written using eembed_malloc/free functions, they can be tested for robustness in the face of memory allocation failures using the error injection facilities of EasyCheck. In echeck.h is a structure for tracking memory allocation/frees: struct echeck_err_injecting_context { unsigned long allocs; unsigned long alloc_bytes; unsigned long frees; unsigned long free_bytes; unsigned long fails; unsigned long max_used; unsigned long attempts; unsigned long attempts_to_fail_bitmask; struct eembed_allocator *real; struct eembed_log *log; }; Tests can then make assertions such as the number of frees matches the number of allocs, and the alloc_bytes equals the free_bytes. It may be useful to assert that the max_used never exceeded some threshold. The "attempts_to_fail_bitmask" is used to intercept allocation attempts and return NULL instead of attempting to call the allocation function. The obvious shortcoming of this approach is that it does not currently support failures for more than 32-ish (bits of unsigned long) attempts to allocate. Experience so far has shown that this is plenty, but this may need to be extended in the future. To use EasyCheck's error injection, create a structure for the allocator, and a structure for the context, and pass in references to the logger and the real allocator, e.g.: struct eembed_allocator our_allocator; struct echeck_err_injecting_context our_context; echeck_err_injecting_allocator_init(&our_allocator, eembed_global_allocator, &our_context, eembed_err_log); Firmware Testing ---------------- Firmware testing can be tedious. To enable easier early testing of firmware, EasyCheck and EasyEmbed can be "faked" into a building almost as if in a freestanding environment. Try this: make faux-fs-check Or if hand-rolling, you can set the #defines yourself, a bit like: gcc -DEEMBED_HOSTED=0 -DFAUX_FREESTANDING=1 \ -I$(ECHECK_SRC) $(ECHECK_SRC)/*.c \ src/the_firmware.c \ src/the_firmware_test.c \ -o the_firmware_test The tests in the tests/ directory use echeck.h (and eembed) themselves, thus these can serve as examples for how to make use of the library. The very popular Arduino embedded platform has a notoriously restrictive build. As such, a little extra work has gone into supporting it. Try the 'eembed_arduino_demo' to see how to use the 'eembed_bytes_allocator' and the 'eembed_strlen' functions. The tests for echeck and eembed also can be run on the Arduino with the 'echeck_tests_arduino' sketch. Additionally, a bit of documentation of echeck is provided in the form of the 'echeck_arduino_demo' sketch. Contributing ------------ Patches welcome! Bug reports are also appreciated. The scope of functions to include in core eembed is limited to those which are nearly essential for testing and reporting. That said, if experience shows that another function would be useful, perhaps it will get added. Compilers tend to be good about not including code which is not used. Code should have tests demonstrating the use and have test coverage via: make coverage Including --------- Typically, including the git repo as a submodule is the way to go: git submodule add https://github.com/ericherman/libecheck \ submodules/libecheck git submodule update --init --recursive Alternatively, one can build locally and copy the .h files and .so files from the ./build/ or ./debug/ directory to /usr/local or similar. For embedded work, you may wish to copy or symlink the files directly in to your project's source directory, as can be seen in the sample echeck_arduino_demo directory. License ------- GNU Lesser General Public License (LGPL), version 3 or later. See COPYING, COPYING.LESSER for details.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.